Realinstomp
Realinstomp

Reputation: 532

One Table View from Two API's

Is there a way for me to populate one UITableView with two separate web APIs? I'm sure there is, but I can't quite figure it out.

Currently I'm pulling perfectly from one web API; but I need to pull from a different one and then merge them into one UITableView.

(I'm using RestKit)

(Second API I'm trying to integrate is Instagram, and will pull pictures/text from a user account)

AppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // RestKit
    NSString *baseURL = @"http://api.firstwebsite.com/v1";
    RKObjectManager *manager = [RKObjectManager sharedManager];
    
    if (!manager) {
        manager = [RKObjectManager objectManagerWithBaseURLString:baseURL];
        manager.client.serviceUnavailableAlertEnabled = YES;
        manager.requestQueue.showsNetworkActivityIndicatorWhenBusy = YES;
    } else {
        manager.client.baseURL = [RKURL URLWithString:baseURL];
    }
    
    return YES;
}

WebListViewController.m

- (void)viewDidLoad
{
    [super viewDidLoad];
    [[RKObjectManager sharedManager] loadObjectsAtResourcePath:
    [NSString stringWithFormat:
    @"/something/?limit=100&something=%@&something=%@&apikey=xxxx", var1, var2]
    usingBlock:^(RKObjectLoader *loader) {
        loader.onDidLoadObjects = ^(NSArray *objects){

            hArray = objects;
            
            [_tableView reloadData];
            
        };
        [loader.mappingProvider setMapping:[Fe mapping] forKeyPath:@"fe"];
        loader.onDidLoadResponse = ^(RKResponse *response){
            //NSLog(@"BodyAsString: %@", [response bodyAsString]);
        };
    }];
}

I'm assuming I'll need to do something different in the AppDelegate.m with the baseURL since it will have two different base URLs.

Will post any extra code as needed!

EDIT

Per alexandresoli answer here's what I've updated so far, but still need help on:

WebListViewController.m

@property (strong, nonatomic) NSMutableArray *array;
@synthesize array;

- (void)callBothURLs {
    // Call both #1 and #2 URL, not sure what specific code goes here
}
        
- (void)viewDidLoad {
    [self callBothURLs];
}
    
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // This is what I currently have, was pulling from a `Headlines` `NSArray` from web #1
    return headlinesArray.count;
    // I believe I need to change it to this next line...
    //return array.count;
}
    
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    // Not sure what to put here with the new `NSMutableArray`
}

EDIT 2

I already have my object mapping set up for the first website, so I can easily do that part for the Instagram API. All my confusion/problems seem to be in the WebListViewController. Anyway here is a sample of mapping.

Mapping.h

@interface Mapping : NSObject

@property (nonatomic, strong) Links *links;
@property (nonatomic, strong) NSString *headline;
@property (nonatomic, strong) NSArray *images;

+ (RKObjectMapping *) mapping;

@end

Mapping.m

@implementation Mapping

+ (RKObjectMapping *)mapping {
    RKObjectMapping *objectMapping = [RKObjectMapping mappingForClass:[self class] usingBlock:^(RKObjectMapping *mapping) {
        [mapping mapKeyPathsToAttributes:
         @"headline", @"headline",
         nil];
        [mapping hasMany:@"images" withMapping:[Images mapping]];
    }];
    
    return objectMapping;
}

@end

Upvotes: 0

Views: 1067

Answers (2)

vokilam
vokilam

Reputation: 10323

Almost A to Z explanation.

Assumptions

  • both APIs have similar responses
  • final result should be sorted
  • items shouldn't be presented until both API calls finish loading

Basic idea

Use NSMutableArray to store result items. Start both API requests simultaneously and add result items into array in appropriate success blocks. When both API calls are finished, sort array and reload table.

Performing API calls

Perform API calls in your view controller (probably in viewDidLoad), not in AppDelegate. The basic example of RestKit object request can be found at github page. Let's name our API calls callAPI1, callAPI2. For example, [self callAPI1] has following steps:

  • define mapping from API1 to result object
  • define response descriptor
  • create API request and specify base URL
  • create request operation and implement completion blocks
  • start operation

Try to use single class for result objects (you'll need to use different mappings for each API) to simplify sorting. [see below]

Respond to API calls

Now we need to know when both requests will finish, in order to reload table. Use a simple counter, which initially set to number of requests and decrements when each API call finishes.

Your method implementation might look like this:

@implementation ViewController
{
    NSMutableArray *_items;
    NSInteger _numberOfPendingCalls;
}

- (void)loadItems 
{
    _numberOfPendingCalls = 2;
    [_items removeAllObjects];

    [self callAPI1];
    [self callAPI2];
}

In success block we decrement pending calls, add items and check for completion:

^(RKObjectRequestOperation *operation, RKMappingResult *result) {
    _numberOfPendingCalls--;

    [_items addObjectsFromArray:result.array];

    if (_numberOfPendingCalls == 0) {
        [self sortAndDisplayItems];
    }
}

If mutable array contains instances of single class (or at least items have same property), you could use NSSortDescriptor for sorting:

NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];
[_items sortUsingDescriptors:@[sortDescriptor]];

Where name is a property of your result object.

I've omitted table view delegate implementation because it is straightforward.

Further steps

If you want to use RestKit object manager and your API have different base URLs, you need to use multiple object managers, one for each API. Reference: More than one RKObjectManager at a time (RestKit)

Upvotes: 1

alexandresoli
alexandresoli

Reputation: 928

Create a method that calls both urls and store the result for each url inside the same NSMutableArray using the method addObject.

NSMutableArray *array = [NSMutableArray new];

// for each object from URL 1, call [array addObject:object];

// for each object from URL 2, call [array addObject:object];

Use this array as the datasource for your tableview;

Update as requested:

  • You can call your populateBothUrlsMethod from viewDidLoad.
  • After that, use your normal uitableview datasource methods numberOfSectionsInTableView and numberOfRowsInSection with this new populated array.

Update 2:

- (void)viewDidLoad
{
    [super viewDidLoad];


    _array = [NSMutableArray new];


    // fetch data from url 1
    NSArray *arrayUrl1 = [self fetchUrl1];


    // add url1 data to array
    for (NSString *str in arrayUrl1) {
        [_array addObject:str];
    }


    // fetch data from url 2
    NSArray *arrayUrl2 = [self fetchUrl2];

    // add url2 data to array
    for (NSString *str in arrayUrl2) {
        [_array addObject:str];
    }

}


- (NSArray *)fetchUrl1
{
    return [NSArray array];
}

- (NSArray *)fetchUrl2
{
    return [NSArray array];
}

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    // total number of objects in array
    return [_array count];
}

Upvotes: 1

Related Questions