0

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.

Thanks! 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
Realinstomp
  • 532
  • 2
  • 13
  • 30
  • 1
    You can do two loading in WebListViewController, no need to do it in AppDelegate. – Rashad Feb 13 '14 at 03:07
  • Ahh, is it better going forward to just do it in the `ViewController` instead of `AppDelgate` then? Just wondering. Thanks! – Realinstomp Feb 13 '14 at 18:11
  • Do both web services return data of the same type? Should the results be _sorted_ or _presented successively_ (1st web results, then 2nd web results)? – vokilam Feb 17 '14 at 11:21
  • @vokilam They are both JSON, and the results will be sorted (1st web results and 2nd web resulted together sorted by date). Basically in each table view cell will be a image and one text label. Does that help? Thanks! – Realinstomp Feb 17 '14 at 17:57

2 Answers2

1

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];
}
alexandresoli
  • 918
  • 1
  • 9
  • 18
  • So create `-(void)callBothURLs`, inside of which I would put the code you posted? From there, how would I go about using that array as the datasource, do I put it in the `viewDidLoad`? Sorry, I'm sure what ever you are saying is spot on, I've just never done it and am trying to wrap my head around it. Thanks! – Realinstomp Feb 13 '14 at 23:03
  • I edited my answer to show what I've understood so far, still not totally sure where to put the `NSMutableArray`. Appreciate you continuing to help! – Realinstomp Feb 15 '14 at 20:06
  • Also, do I need to ditch the code in my `AppDelegate` for the `baseURL` since I'm not using just one `API` anymore? – Realinstomp Feb 16 '14 at 00:14
  • 2
    Thanks! You rock, appreciate the help. I'll try to figure out how to implement this. In the meantime, I gave you the 50 bounty regardless! – Realinstomp Feb 19 '14 at 03:05
  • Working on this, do I need to throw out all my `viewDidLoad` code and replace with this, or which parts are usable or not usable? Trying to figure out which parts you'd suggest I replace, and which parts are fine. Thanks! – Realinstomp Feb 23 '14 at 00:40
  • Totally understand conceptually what you're trying to do here, but I'm trying to fit it with what I already have specifically- – Realinstomp Feb 23 '14 at 00:43
  • How about you post your code on github to let others view what are your trying to accomplish? – alexandresoli Feb 23 '14 at 13:40
  • What is the `NSString *str in arrayUrl2` in your recommendation? Trying to figure out what string you're suggesting I grab, the url? – Realinstomp Mar 12 '14 at 00:18
1

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)

Community
  • 1
  • 1
vokilam
  • 10,153
  • 3
  • 45
  • 56
  • Thanks so much for the response! **Assumptions:** All are correct. **Basic idea:** I believe I'm following you here, and have this in my "Edit" as `array`. **Performing API calls:** I think I'm following you here. In my `WebListVC` I am making the call, but just using the `baseURL` set forth in the `AppDelegate` and appending on to it. How do you suggest I re-do that... remove code from `AppDelegate` and then add full URL to `WebListVC` code I already have? Any other changes on that? Also, I have the mapping all set up for API1 and am loading the `UITableView` perfectly w API1 right now. – Realinstomp Feb 18 '14 at 01:39
  • **Respond to API calls:** This makes sense and I will implement it. **Further steps:** This is kind of a big part of my whole question, and what I was asking how to do. I linked over to your question and can't quite figure out how it meshes with my code I posted. Like, do I basically just need 2 of those `RKObjectManager`s, of which I already have one for API1 in my `viewDidLoad`? – Realinstomp Feb 18 '14 at 01:47
  • Thanks so much, I up voted your answer because its helped me already, but I really need the latter parts (i.e. I already have API1 working...what code needed to add API2 in same `viewDidLoad` AND how to get both in `UITableView` together sorted by date. – Realinstomp Feb 18 '14 at 01:48
  • I recommend you to start without object managers. I provided a link to an example in __Performing API calls__ section. Look into it carefully. Here is one more: http://nsscreencast.com/episodes/51-intro-to-restkit-mapping. `callAPI1` and `callAPI2` have _different_ object mappings and base URLs and _identical_ success blocks. I gave you the basic structure, just try to implement the methods. I cannot write code for you. – vokilam Feb 18 '14 at 05:20
  • Thanks! Not asking you to write code for me, appreciate the help. I'll try to figure out how to implement this. How do I give you the 50 bounty too? I gave it to the other guy, but thought there was a way to give you 50 too? – Realinstomp Feb 19 '14 at 03:05
  • 1
    Regardless, I'll place another bounty on this if need be to make sure it gets to you for the help you gave so far, thanks- – Realinstomp Feb 19 '14 at 03:09
  • Also, in the meantime, I added my `Mapping` code in the **"Edit 2"** part of my answer, so you can see how much I do/don't understand about Object Mapping. I think I understand a good bit of it, but reason for my question is can't finish out my `WebListViewController` due to lack of understanding mostly of just how to merge the two arrays. – Realinstomp Feb 20 '14 at 01:39