0

I got a async problem with my code. I got all of my webrequests in 1 class. One of my requests needs to return an NSMutableArray that another class needs to use. My webRequest code is here:

- (NSMutableArray*) getTournamentsInClub:(NSString *)clubGUID withDelegateViewController:(UIViewController *)viewController {

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSMutableArray *responseArray = [[[NSMutableArray alloc] init] autorelease];
NSString *URL = [[NSString alloc]initWithFormat:@"SomeURL=%@",clubGUID];
[manager POST:URL parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {

    for (id obj in responseObject){
        //NSLog(@"obj: %@",[obj valueForKey:@"CustomerName"]);
        [responseArray addObject:obj];
    }

} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Error: %@", error);
}];
return responseArray;
}

I call the method from a viewController like this:

[self handleClubTournaments:[[TournamentsWebService sharedToursWS] getTournamentsInClub:

//Show load screen. (hide in handler function)
GP_MobilAppDelegate *xdelegate = [[UIApplication sharedApplication] delegate];
[xdelegate showLoadingScreen:self.clubToursTableView andStatus:NSLocalizedString(@"loadTours", @"")];

And my handleClubTournaments function looks like this:

-(void) handleClubTournaments:(id)result {

GP_MobilAppDelegate *xdelegate = [[UIApplication sharedApplication] delegate];

if([result isKindOfClass: [NSError class]]) {
    // If an error has occurred, handle it
    [xdelegate hideLoadingScreen];
    [[TournamentsWebService sharedToursWS] showErrorMessageAccordingToFault:result];
    return;
}

if([result isKindOfClass: [SoapFault class]]) {
    [xdelegate hideLoadingScreen];
    // If a server error has occurred, handle it
    [[TournamentsWebService sharedToursWS] showErrorMessageAccordingToFault:result];
    return;
}

//Do something with result...

if ([result count] > 0) {

    NSLog(@"Antal klubturneringer: %d", [result count]);
    //Start by removing excisting tours
    [self.tournamentsSourceArray removeAllObjects];
    NSMutableArray *tempArray=[NSMutableArray array];

    for (GGTournamentData *t in result) { //cast object in result list and add them to array

        [tempArray addObject:t];

    }
    self.tournamentsSourceArray = [self sortByStringDate:tempArray]; //sort by date

    [tempArray release];

    NSLog(NSLocalizedString(@"tourLoadet", @""));
}

[self.clubToursTableView reloadData];
[xdelegate hideLoadingScreen];

//Scroll view
if (self.tournamentsSourceArray.count > 0) { //hvis det er turneringer..
    NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:[self findIndexOfMonthClosestToDate]];
    [self.clubToursTableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
}

So my problem is that the NSMutableArray gets returned before my async task is done. I know a async task behaves like that, but how do i make sure that my handleClubTournaments function don't run before my webrequest(getTournamentsInClub) got some data for it?

Thanks in advance.

user2408952
  • 2,011
  • 5
  • 24
  • 26

1 Answers1

2

I don't think you know how Asynchronous operations work. The NSMutableArray will never be set, because it is returned synchronously. In your case, I suggest you to work with delegates.

- (void)getTournamentsInClub:(NSString *)clubGUID withDelegateViewController:(UIViewController *)viewController completionBlock:(void (^)(NSMutableArray *result))completionBlock {
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    NSMutableArray *responseArray = [[[NSMutableArray alloc] init] autorelease];
    NSString *URL = [[NSString alloc]initWithFormat:@"SomeURL=%@",clubGUID];
    [manager POST:URL parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {

        for (id obj in responseObject) {
            [responseArray addObject:obj];
        }

        // Request finished. Call the block.
        completionBlock(responseArray);

    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"Error: %@", error);
    }];
}

- (void)handleClubTournaments {
    GP_MobilAppDelegate *xdelegate = [[UIApplication sharedApplication] delegate];
    [[TournamentsWebService sharedToursWS] getTournamentsInClub:^(NSMutableArray *result) 
    {
         // Hide the Loading Indicator. Do something with the Result.
    }];

    // You can't access the result synchronously, therefore it's impossible to depend on it synchronously.
}

another way to return the data asynchronously would be blocks, similar to the AFNetworking solution.

You can read more about getting started with blocks here and how to use delegates here.

Community
  • 1
  • 1
Leandros
  • 16,805
  • 9
  • 69
  • 108
  • I'm quite new to objective c programming does [self.delegate requestFinished:responseArray]; function like a return? just that it wont return before the task is done? – user2408952 May 06 '14 at 08:51
  • I've tried solving this by using blocks, but without success. Again I'm quite new so I'm not sure i did it the right way – user2408952 May 06 '14 at 08:53
  • No, not really. What you want is a blocking task, but this isn't how asynchronous works. The block, inside the `AFNetworking` statement, is called whenever the actual data has been downloaded. This is in the future, you can't know when. So your other program has to wait for it, you could show a `UILoadingIndicator` and dismiss it, when the delegate returns the data. – Leandros May 06 '14 at 08:53
  • I got a loading indicator, take a look at my code (//Show load screen. (hide in handler function)) – user2408952 May 06 '14 at 08:55
  • 1
    See my edit. You definitely have to read about asynchronous functions, as they're important and I don't think you understood them. – Leandros May 06 '14 at 09:01
  • I understand how they work and the hole concept about multithreading, but i have no idea on how to handle them in objective c. – user2408952 May 06 '14 at 09:04
  • As said, with blocks (see my example above) or with delegates. – Leandros May 06 '14 at 09:04
  • What's the completionBlock in your example? – user2408952 May 06 '14 at 09:05
  • It's a block, a method parameter I added to `getTournamentsInClub`. – Leandros May 06 '14 at 09:07
  • 1
    Ok, i see you linked some stuff, ill read up on them, REALLY appreciate the help. Thanks Thanks Thanks:) – user2408952 May 06 '14 at 09:09
  • 1
    Hey just wanted to let you know that i got it working by the example you posted i just did a little correction in the caller so it patches the parameters i needed to pass: [[TournamentsWebService sharedToursWS] getTournamentsInClub:[self.club valueForKey:@"GUID"] completionBlock:^(NSMutableArray *result). So thx again ill mark it as the right answer. – user2408952 May 06 '14 at 13:11