8

I have a registration view controller which handles all the user input and calls a function in another class (wsClass) passing to it, that data as an NSDictionary.

The function in wsClass is called and the connection is made and data is returned from the server and available within the session.

My question is how do I return that data to the registration viewcontroller where the function was originally called, it's always coming up empty.

Here is the call in registrationViewController:

        wsClass *ws = [[wsClass alloc] init];
    NSDictionary *testDict = [[NSDictionary alloc] initWithObjectsAndKeys:username.text,@"username",email.text,@"email",password.text,@"password", nil];
    NSDictionary *respDict = [ws sendData:testDict];

Here is the function being called in wsClass.m:

- (NSDictionary *)sendData:(NSDictionary *)sendDict {
NSMutableString *sendStr = [[NSMutableString alloc] init];

[sendDict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    [sendStr appendFormat:@"&%@=%@", key, obj];
}];

NSLog(@"sendStr is: %@",sendStr);
NSString *noteDataString = [NSString stringWithFormat:@"%@%@",REQUIRED,sendStr];

NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlTest]];
request.HTTPBody = [noteDataString dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPMethod = @"POST";

NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request
                                                completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                    // The server answers with an error because it doesn't receive the params
                                                    // handle response
                                                    if(error == nil)
                                                    {
                                                        [getReqAlert dismissWithClickedButtonIndex:0 animated:YES];

                                                        NSError *e = nil;
                                                        jsonArray = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableLeaves error: &e];

                                                        if (!jsonArray) {
                                                            NSLog(@"Error parsing JSON: %@", e);
                                                        } else {

                                                            NSLog(@"resp: %@ = %@",[jsonArray objectForKey:@"status"],[jsonArray objectForKey:@"msg"]);
                                                            NSLog(@"Dictionary count: %lu", jsonArray.count);
                                                        }
                                                    }
                                                }];

 [postDataTask resume];

return jsonArray;

}

DevGW
  • 703
  • 1
  • 8
  • 23

4 Answers4

16

The post completes asynchronously, so your wsClass needs to tell the caller about completion after post is finished. A nice way to do this is to augment the sendData method with a block of code supplied by the caller that should be run upon completion:

Change sendData: to look like this:

// it doesn't return anything, because all it does is launch the post
// but when the post is done, it invokes completion

- (void)sendData:(NSDictionary *)sendDict completion:(void (^)(NSDictionary *))completion {

    // the stuff you're already doing

        NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request
            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
            // the stuff you're already doing
            // now tell the caller that you're done
            completion(jsonArray);
        }];
    }

The caller now looks like this:

// change the UI to say "I'm busy doing the post"
[ws sendData:testDict completion:^(NSDictionary *responseDict) {
    NSLog(@"this runs later, after the post completes %@", responseDict);
    // change the UI to say "The post is done"
}];

Just a couple of notes about this: (1) I didn't add an error param to the block, you probably should. Check that and invoke the block with either nil and an error, or with the json output and error=nil. (2) Your code assumes that the json result parses as a dictionary. Make sure that's always true before you assume it in code. (3) Class names usually begin with caps.

danh
  • 62,181
  • 10
  • 95
  • 136
  • This is exactly what I was looking for! Thank You! ... I will have to do some additional reading to fully understand the nature of what is going on with the completion handlers here. I grasp the concept quite well, just not sure on the syntax for future usage and reference... Thanks again – DevGW Jan 01 '14 at 18:24
  • 1
    Happy to help. This might be helpful as a more general answer about block syntax: http://stackoverflow.com/questions/18220645/ios-passing-blocks-to-functions/18220703#18220703. Also, apple provides this general guide: https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/Blocks/Articles/00_Introduction.html – danh Jan 01 '14 at 19:01
  • Would a creating a delegate and calling back also be good practice here instead ? – Woodstock Mar 28 '15 at 19:56
  • 1
    @Woodstock - yes, good in the sense that it would work, too. Delegation is overused in my opinion because blocks are a relatively recent addition to the language. I prefer delegation only in the case where the delegate can be conceived logically as a whole class (even better when more than one class might act as a delegate). – danh Mar 28 '15 at 20:40
  • Your `completion(jsonArray);` Helps me for my issue. Thanks. – Mihir Oza May 12 '17 at 14:48
2

You can use Semaphores for wiating till the block completes the execution and then return the value from function

- (NSDictionary *)sendData:(NSDictionary *)sendDict {
NSMutableString *sendStr = [[NSMutableString alloc] init];

[sendDict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[sendStr appendFormat:@"&%@=%@", key, obj];
}];

NSLog(@"sendStr is: %@",sendStr);
NSString *noteDataString = [NSString stringWithFormat:@"%@%@",REQUIRED,sendStr];

NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:sessionConfiguration];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlTest]];
request.HTTPBody = [noteDataString dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPMethod = @"POST";


 let semaphore = dispatch_semaphore_create(0)

NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// The server answers with an error because it doesn't receive the params
// handle response
if(error == nil)
{
[getReqAlert dismissWithClickedButtonIndex:0 animated:YES];

NSError *e = nil;
jsonArray = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableLeaves error: &e];

if (!jsonArray) {
NSLog(@"Error parsing JSON: %@", e);
} else {

NSLog(@"resp: %@ = %@",[jsonArray objectForKey:@"status"],[jsonArray objectForKey:@"msg"]);
NSLog(@"Dictionary count: %lu", jsonArray.count);

     dispatch_semaphore_signal(semaphore)
}
}
}];

[postDataTask resume];
 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
return jsonArray;

}

Harsh Thakur
  • 293
  • 2
  • 11
0

This might work.

  1. Add an interface variable nsDictVariable for NSDict or whatever you are retrieving in the class
  2. Within SendData assign it: self.nsDictVariable = ....
John Stack
  • 618
  • 1
  • 4
  • 19
0

Try this . This will work well.

-(void)loadDetails
{

NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];
[[session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {


    _allVehicleLocationsArray = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

    [self afterCompleteDoThis];


}] resume];


}

-(void)afterCompleteDoThis{

for (NSDictionary *vehicleDict in _allVehicleLocationsArray) {

    NSLog(@" PPP %@" , [vehicleDict valueForKey:@"vehicleType"]);

}
}