1

I'm using AFNetworking 2 to GET data from server and I need to get back the responseObject but no matter what I do i still get <null>.

Here is the method which is sending GET request to server and in response it gets NSDictionary which I want to use in another method...

- (void)getCurrentVersionsForTimetableWithID:(NSString *)timetableID
{
    [[AFHTTPRequestOperationManager manager] GET:[NSString stringWithFormat:VERSIONS, timetableID] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
        // Here I want to get responseObject
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"Couldn't get current versions: %@", [error localizedDescription]);
    }];
}

If I call this method everything works fine. But when I try to make it return NSDictionary like this:

- (NSDictionary *)getCurrentVersionsForTimetableWithID:(NSString *)timetableID
{
    __block NSDictionary *currentVersions;

    [[AFHTTPRequestOperationManager manager] GET:[NSString stringWithFormat:VERSIONS, timetableID] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
        currentVersions = responseObject;
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"Couldn't get current versions: %@", [error localizedDescription]);
    }];

    return currentVersions;
}

I get <null> value. I know this is happening because of async but how to solve this? I've tried to pass another completion block to this method but when I call it inside another one I still cannot assign the result to the variable... Please guys, help me!

cojoj
  • 6,405
  • 4
  • 30
  • 52
  • My guess is that it comes to the return before the block is done. Use breakpoints to look. – Arbitur Apr 07 '14 at 17:28
  • You're right but I don't know how to return it inside block (errors) or wait until the block finishes its tasks – cojoj Apr 07 '14 at 17:31
  • Ive had this kind of problem with blocks what I did was create them before I use them, I can send an answer showing how i did – Arbitur Apr 07 '14 at 17:34

3 Answers3

3

You want to pass in a completion block that takes NSDictionary as parameter:

- (void)getCurrentVersionsForTimetableWithID:(NSString *)timetableID completion:(void (^)(NSDictionary* currentVersions))completion
{
    [[AFHTTPRequestOperationManager manager] GET:[NSString stringWithFormat:VERSIONS, timetableID] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
        currentVersions = responseObject;

        if(completion){
            completion(currentVersions);
        }
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"Couldn't get current versions: %@", [error localizedDescription]);
    }];
}

To use it:

[self getCurrentVersionsForTimetableWithID:@"someId" 
                                completion:^(NSDictionary* currentVersions){
                                    // Do something with currentVersions
                                }];
mspensieri
  • 3,501
  • 15
  • 18
  • But when I call this method and pass previously allocated `NSDictionary` I still get `NULL`. Why? – cojoj Apr 08 '14 at 15:17
  • I'm not sure I understand your question. I've edited my answer with how to use this method - is this how you're doing it? – mspensieri Apr 08 '14 at 15:24
  • Yeah but assume I've created `NSDictionary *versions` before I call `[self getCurrentVersionsForTimetableWithID:completion:];` and inside completion I call `versions = currentVersions`. When I try to get to `versions` later it's empty. – cojoj Apr 08 '14 at 15:35
  • Are you sure you're accessing versions _after_ the network request has completed and the variable has been assigned? – mspensieri Apr 08 '14 at 15:36
  • No... So I guess it'd be easie just to do everything inside completion block :) – cojoj Apr 08 '14 at 15:38
3

I'm sure the problem with async calls, threads run in parallel and you return nil before you get actual data, so easy way (not the prefect! :) ) is to make it synchronous to wait for result:

try:

- (NSDictionary *)getCurrentVersionsForTimetableWithID:(NSString *)timetableID
{
    dispatch_semaphore_t sema = dispatch_semaphore_create(0);

    __block NSDictionary *currentVersions;

    [[AFHTTPRequestOperationManager manager] GET:[NSString stringWithFormat:VERSIONS, timetableID] parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
        currentVersions = responseObject;
        dispatch_semaphore_signal(sema);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"Couldn't get current versions: %@", [error localizedDescription]);
    }];

    while (dispatch_semaphore_wait(sema, DISPATCH_TIME_NOW))
    {
        [[NSRunLoop currentRunLoop]
         runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0]];
    }

    return currentVersions;
}

or better to use Michaels answer to return in completion block if you need it async, its a good way to return cached data before actual data coming.

Vitali K
  • 136
  • 1
  • 8
0

Try this, I dont remember how a blocks is created in my head and im not on my mac. But i think its like this.

__block NSDictionary *currentVersions;

void (^success)(AFHTTPRequestOperation, id) = ^(AFHTTPRequestOperation *operation, id responseObject) {

    currentVersions = responseObject;
}

[[AFHTTPRequestOperationManager manager] GET:[NSString stringWithFormat:VERSIONS, timetableID] parameters:nil success:success
 failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSLog(@"Couldn't get current versions: %@", [error localizedDescription]);
}];
Arbitur
  • 38,684
  • 22
  • 91
  • 128