0

This seems such an easy task, at least it is in VB.net. I simply need to reference an array based on a string that is passed to a method. When a view controller loads a method is called and a string is passed. A URL will be created based on this string and JSON will be fetched from it. What I want is for the method to populate an appropriate array based on this passed string.

Here we see the method "goGetData" being called in class "getData" with one of three string parameters "workshop/speaker/exhibitor":

- (void)viewDidLoad
{


[getData goGetData:@"workshop"];
[getData goGetData:@"speaker"];
[getData goGetData:@"exhibitor"];

getData *getDataInstance = [[getData alloc] init];

NSArray *newTablesArray = getDataInstance.jsonAllTables;
NSLog(@"Json currently = %@", newTablesArray);

[super viewDidLoad];

[[self myTableView]setDelegate:self];
[[self myTableView]setDataSource:self];
arrayTable =[[NSMutableArray alloc]init];
}

For example if "goGetDate" is fired with "speaker" I would need the speaker data to be fetched and then the "_jsonSpeaker" array to be populated. Here is my attempt so far to reference and populate the arrays based on what string was passed in the method call:

#import "getData.h"

@implementation getData



+(void)goGetData:(NSString *)requestedTable
{

getData *getDataInstance = [[getData alloc] init];

[getDataInstance buildArray];
[getDataInstance fetchData:requestedTable];

}

-(void)buildArray{
// I tried putting the arrays in an array but still do no know how to reference them
_jsonAllTables = [[NSMutableArray alloc] initWithObjects:_jsonExhibitor, _jsonSpeaker, _jsonWorkshop, nil];

}

-(void)fetchData:(NSString *)requestedTable{
NSString *varCurrentTable;
varCurrentTable = [NSString stringWithFormat:@"_json%@", requestedTable];
NSString *requestedURL;
requestedURL = [NSString stringWithFormat:@"http://testapi.website.com/api/%@", requestedTable];

NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:requestedURL]];
[NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
 {
     if (response){
         NSHTTPURLResponse *newResp = (NSHTTPURLResponse*)response;

         if (newResp.statusCode == 200) {

             // STUFF FOR TESTING NSLog(@"Response to request: %@ is: %i GOOD", requestedURL, newResp.statusCode);

             if ([data length] >0 && error == nil)
             {
                 // STUFF FOR TESTING NSUInteger indexOfArray = [_jsonAllTables indexOfObject:varCurrentTable];
                 // STUFF FOR TESTING NSString *objectAtIndexOfArray = [_jsonAllTables objectAtIndex:indexOfArray];

             //    This is the part I think I am stuck on:
             //    "CURRENT TABLE TO BE POPULATED" = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];

             }
             else if ([data length] == 0 && error == nil)
             {
                 NSLog(@"Nothing was downloaded");
             }
             else if (error != nil)
             {
                 NSLog(@"Error: %@", error);
             }

         } else if (newResp.statusCode == 404){
              NSLog(@"Response to request: %@ is: %i BAD - URL incorrect", requestedURL, newResp.statusCode);
         } else {
             // add more returned status error handling here
         }

     }else{
         NSLog(@"No response received");
     }

 }];
}

@end

Thanks,

Added for clarification on what I am trying to achieve: To save a LOT of writing out the same thing over and over is the following possibly in Obj-c (please excuse the mish-mash of languages)

NSArray *ListOfTables = [NSArray arrayWithObjects:@"Speaker", @"Exhibitor", @"Workshop", nil];

For i as int = 0 to ListOfTables.count{ 

 [self fetchData:(ListOfTables.objectAtIndex = i) withCompletion:^(NSArray* objects, NSError*error){
    dispatch_async(dispatch_get_main_queue(), ^{
        if (objects) {
            self.(ListOfTables.objectAtIndex = i) = objects;
        }
        else {
            NSLog(@"Error: %error", error);
        }
    });
}];
i++;
Next
};

Notice i don't call a separate method for each table, instead I call the same method but with different table name parameter each time. I can't seem to find a working example with such placeholders in Xcode.

7
  • May be its tough for new learners but still give it a shot use AFNetworking for network calls. Commented Dec 9, 2013 at 10:09
  • 1
    I'm sure there's an easy solution to your problem, just I'm having trouble to figure out what you are actually trying to accomplish. Commented Dec 9, 2013 at 10:11
  • The data I get back with NSURLConnection is fine and If I create a method for each table with trying to be dynamic, they all work fine but this is not practical as in the end there will be 12+ tables I will have to populate. I did look at AFNetworking as a different method for asynchronous transfer and may go back to it later. Commented Dec 9, 2013 at 10:12
  • @CouchDeveloper - I'm trying to use one method, 3 times. Each time a different parameter is passed. This parameter is used to a) build a connection URL b) populate the relevant array with the returned data. Commented Dec 9, 2013 at 10:16
  • Put the network code question aside for a moment, sendAsynchronousRequest is fine for a start. What are you obtaining from the request? A JSON? If, what kind of container: JSON Array or JSON Object? What is the kind of your Model (what's rendered in the Table View, and in a cell)? An array? (yields a simple table view with one section) an NSDictionary? (yields a table view with sections). Please try to describe your case more exact and unambiguous. Commented Dec 9, 2013 at 10:18

1 Answer 1

1

You probably want a method which is asynchronous and returns the result via a completion handler:

typedef void(^completion_t)(NSArray* objects, NSError*error);

-(void)fetchData:(NSString *)tableName 
  withCompletion:(completion_t)completionHandler;

Usage:

- (void) foo {

    [self fetchData:tableName1 withCompletion:^(NSArray* objects, NSError*error){
        dispatch_async(dispatch_get_main_queue(), ^{
            if (objects) {
                self.table1 = objects;
            }
            else {
                NSLog(@"Error: %error", error);
            }
        });
    }];

    [self fetchData:tableName2 withCompletion:^(NSArray* objects, NSError*error){
        dispatch_async(dispatch_get_main_queue(), ^{
            if (objects) {
                self.table2 = objects;
            }
            else {
                NSLog(@"Error: %error", error);
            }
        });
    }];

    [self fetchData:tableName3 withCompletion:^(NSArray* objects, NSError*error){
        dispatch_async(dispatch_get_main_queue(), ^{
            if (objects) {
                self.table3 = objects;
            }
            else {
                NSLog(@"Error: %error", error);
            }
        });
    }];
} 

Implementation:

typedef void(^completion_t)(NSArray* objects, NSError* error);

-(void)fetchData:(NSString *)tableName
  withCompletion:(completion_t)completionHandler
{
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:tableName]];
    // Setup HTTP headers, e.g. "Accept: application/json", etc.
    ...
    [NSURLConnection sendAsynchronousRequest:request
                                       queue:[[NSOperationQueue alloc] init]
                           completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)
    {
         NSError* err = error;
         NSArray* objects; // final result array as a representation of JSON Array
         if (response) {
             NSHTTPURLResponse *newResp = (NSHTTPURLResponse*)response;
             if (newResp.statusCode == 200) {
                 if ([data length] >0 && error == nil)
                 {
                     NSError* localError;
                     objects = ... // Use NSJSONSerialization to obtain a representation
                     if (objects) {
                         if (completionHandler) {
                             completionHandler(object, nil);
                         }
                         return;
                     }
                     else {
                         err = localError;
                     }
                 }
                 else {
                     err = ...
                 }
             }
         }
         if (objects == nil) {
             assert(err);
             if (completionHandler) {
                 completionHandler(nil, err);
             }
         }
    }];
}

Asynchronous Loop

Another example, for loading a bunch of data:

First, implemented a method which is an "asynchronous loop":

typedef void(^completion_t)(id result, NSError* error);

- (void) fetchObjectsWithTableNames:(NSMutableArray*)tableNames 
         completion:(completion_t)completionHandler;

This method is, itself asynchronous, thus the completion handler.

Usage:

- (void) foo 
{
    NSArray* tableNames =  @[@"A", @"B", @"C"]; // possibly 1000

    [self fetchObjectsWithTableNames:[tableNames mutableCopy]:^(id result, NSError*error){
        if (error) {
            NSLog(@"Error: %@", error);
        }
        else {
            // finished fetching a bunch of datas with result:
            NSLog(@"Result: %@", result);
        }
    }];
}

Implementation

- (void) fetchObjectsWithTableNames:(NSMutableArray*)tableNames 
         completion:(completion_t)completionHandler;
{
    if ([tableNames count] > 0) {
        NSString* name = [tableNames firstObject];
        [tableNames removeObjectAtIndex:0];
        [self fetchData:name withCompletion:^(NSArray* objects, NSError*error){
            if (objects) {
                [self.tables addObject:objects];
                [self fetchObjectsWithTableNames:tableNames completion:completionHandler];
            } 
            else  {
               // handle error
            }
        }];
    }
    else {
        // finished
        if (completionHandler) {
            completionHandler(@"finished", nil);
        }
    }
}
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks for that. The usage kind of confirms my fear that this cannot be done dynamically. Odd as in VB you could simply create a method and put that method in a loop for however many variables you have. Is what I am asking unusual in obj-c? What if there were 1000 tables? You would have to write fetchdata:tableNameN a 1000 times?
This can be done, of course. If you had less than a few requests, say up to 8 or so, you could invoke them in parallel. Otherwise, you can invoke the asynchronous tasks sequentially. This is akin to call N times a synchronous method, it's just still asynchronous: stackoverflow.com/questions/20324513/…. A third approach combines these two solutions: have a concurrent queue whose "max concurrent operations" can be configured, then enqueue the tasks and let them run.
I think I understand the connections and retrieval of the json data asynchronously but my main query relates to how to write a method only once and call it many times with different parameters. I have edited my main question to give a kind of example of what I meant.
Please read the link to the answer in my comment above. It is exactly what you need.
I've had a quick read and this does indeed look like it will do what I need. Apologies, I completely didn't think to click on the link first time round :S Also, +1 for use of 'automagically'.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.