3

I have the following code inside a class (static method) which I call to get data from an API. I decided to make this a static method just so I can reuse it on some other parts of the app.

+ (NSArray*) getAllRoomsWithEventId:(NSNumber *)eventId{

    NSURL *urlRequest = [NSURL URLWithString:[NSString stringWithFormat:@"http://blablba.com/api/Rooms/GetAll/e/%@/r?%@", eventId, [ServiceRequest getAuth]]];

    NSMutableArray *rooms = [[NSMutableArray alloc] init];

    NSURLRequest *request = [NSURLRequest requestWithURL:urlRequest];

    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
        NSLog(@"Response of getall rooms %@", JSON);


        NSArray *jsonResults = (NSArray*)JSON;
        for(id item in jsonResults){
            Room* room = [[Room alloc]init];
            if([item isKindOfClass:[NSDictionary class]]){
                room.Id = [item objectForKey:@"Id"];
                room.eventId = [item objectForKey:@"EventId"];
                room.UINumber = [item objectForKey:@"RoomUIID"];
                [rooms addObject:room];
            }
        }
    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON){
        NSLog(@"Error");
     }];

    [operation start];
    [operation waitUntilFinished];

    return rooms;
}

Now my issue is, whenever I call this in a ViewController (ViewDidLoad method). The static method will run till the end and will return null on the rooms, but the Nslog will display the "Success" block Nslog a few seconds after. Now I understand that this is asynchronous so it doesn't wait for the success block to execute before it reaches the "return rooms;" line. With all that said, I need some advice as to how to handle this, like maybe a progress bar or something like that? Or something that delays it? I'm not really sure if that's the reight way or if it is, I am not sure how to do it.

Any advice is very much appreciated. Thank you!

gdubs
  • 2,724
  • 9
  • 55
  • 102
  • As `AFNetworking` is an asynchronous process, You ara advised to use `NSNotificationCenter` which will notify your code that now Room has data. – Deepak Khiwani Jun 26 '13 at 04:15
  • 1
    @DeepakKhiwani Is correct about asynchronicity, but messaging would be best done directly with callback blocks, not notifications. – mattt Jun 26 '13 at 16:06

2 Answers2

2

AFNetworking is built around asynchronicity—starting a request, and then executing some piece of code once that request has finished.

waitUntilFinished is an anti-pattern, which can block the user interface.

Instead, your method should have no return type (void), and have a completion block parameter that returns the serialized array of rooms:

- (void)allRoomsWithEventId:(NSNumber *)eventId block:(void (^)(NSArray *rooms))block { // ... }

See the example app in the AFNetworking project for an example of how to do this.

mattt
  • 19,544
  • 7
  • 73
  • 84
  • So does it mean making a static method (that returns an array of rooms) isn't the way to go? I can't seem to find that sample project you're talking about. – gdubs Jun 26 '13 at 20:16
  • @gdubs Yes. And [the example project is in the AFNetworking repository](https://github.com/AFNetworking/AFNetworking/tree/master/Example). – mattt Jun 26 '13 at 22:14
  • it worked! are you the author? thank you so much for replying! btw, any chance you know how to make this work with mbprogresshud instead of the activityindicatorview? – gdubs Jun 27 '13 at 01:13
-1

You can write your method following way:

+ (void) getAllRoomsWithEventId:(NSNumber *)eventId:(void(^)(NSArray *roomArray)) block
{

    NSURL *urlRequest = [NSURL URLWithString:[NSString stringWithFormat:@"http://blablba.com/api/Rooms/GetAll/e/%@/r?%@", eventId, [ServiceRequest getAuth]]];

    NSMutableArray *rooms = [[NSMutableArray alloc] init];

    NSURLRequest *request = [NSURLRequest requestWithURL:urlRequest];

    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request success:^(NSURLRequest *request, NSHTTPURLResponse *response, id JSON) {
        NSLog(@"Response of getall rooms %@", JSON);


        NSArray *jsonResults = (NSArray*)JSON;
        for(id item in jsonResults){
            Room* room = [[Room alloc]init];
            if([item isKindOfClass:[NSDictionary class]]){
                room.Id = [item objectForKey:@"Id"];
                room.eventId = [item objectForKey:@"EventId"];
                room.UINumber = [item objectForKey:@"RoomUIID"];
                [rooms addObject:room];
            }
        }
        block(rooms);
    } failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error, id JSON){
        NSLog(@"Error");
        block(nil); //or any other error message..
     }];

    [operation start];
    [operation waitUntilFinished];

}

you can call this method like followings:

[MyDataClass getAllRoomsWithEventId:@"eventid1":^(NSArray *roomArray) {
      NSLog(@"roomArr == %@",roomArray);
    }];
Abdullah Md. Zubair
  • 3,312
  • 2
  • 30
  • 39
  • wouldn't that force it to sync it? I thought that isn't recommended? – gdubs Jun 26 '13 at 04:51
  • also, when I call this method, how should I handle the "roomArray" part? NSArray *testrooms = [Room getAllRoomsWithEventId:eventId: ^(NSArray......)block; It seems I need to add something on that part? – gdubs Jun 26 '13 at 04:59
  • things are not like that. You are passing your response through this block. You can use NSNotification to detect when async completed. – Abdullah Md. Zubair Jun 26 '13 at 05:02