cdub cdub - 6 months ago 24
JSON Question

Getting data back from an NSURLSession via POST with JSON

Since NSURLConnection is depreated I need to move to an NSURLSession. I have a URL and some data I need to input as JSON. Then the result should be JSON coming back. I see something like so:

NSError *error;

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil];
NSURL *url = [NSURL URLWithString:@"[JSON SERVER"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:60.0];

[request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request addValue:@"application/json" forHTTPHeaderField:@"Accept"];

[request setHTTPMethod:@"POST"];
NSDictionary *mapData = [[NSDictionary alloc] initWithObjectsAndKeys: @"TEST IOS", @"name",
@"IOS TYPE", @"typemap",
nil];
NSData *postData = [NSJSONSerialization dataWithJSONObject:mapData options:0 error:&error];
[request setHTTPBody:postData];


NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

}];

[postDataTask resume];


I this the correct approach?

My requirements are:
1. Turn my key value pairs into JSON.
2. Pass in the URL and JSON to a reusable function.
3. Get the JSON data returned.
4. Parse the JSON data returned.

Answer

You want the callers to your method to provide a completion handler which does things like process the data returned or update the UI to indicate completion.

Just like the SDK, you can do the same as follows. Say we call the function makeRequest and it takes a param to send as part of the request. Declare the method like this:

- (void)makeRequest:(NSString *)param completion:(void (^)(NSDictionary *, NSError *))completion;

Implement it like this:

- (void)makeRequest:(NSString *)param
         completion:(void (^)(NSDictionary *, NSError *))completion {

    // your OP code goes here, e.g.
    NSError *error;
    NSURLSessionConfiguration *configuration = // and so on

    // use the param to form the request if it needs one, then...

    NSURLSessionDataTask *postDataTask = [session dataTaskWithRequest:request 
        completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

        // here, we must abide by the interface of our completion handler.
        // we must call in EVERY code path, so the caller is never left waiting
        if (!error) {
            // convert the NSData response to a dictionary
            NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
            if (error) {
                // there was a parse error...maybe log it here, too
                completion(nil, error);
            } else {
                // success!
                completion(dictionary, nil);
            }
        } else {
            // error from the session...maybe log it here, too
            completion(nil, error);
        }
    }];
    [postDataTask resume];
}

Your code that calls this method will look like this:

// update the UI here to say "I'm busy making a request"
// call your function, which you've given a completion handler
[self makeRequest:@"someParam" completion:^(NSDictionary *someResult, NSError *error) {
    // here, update the UI to say "Not busy anymore"
    if (!error) {
        // update the model, which should cause views that depend on the model to update
        // e.g. [self.someUITableView reloadData];
    } else {
        // update UI to indicate error or take remedial action
    }
}];

Notice a couple things: (1) the return type is void, the caller expects nothing to be returned from this method, and makes no assignment when calling it. The data "returned" is provided as parameters to the completion handler, which is called later, after the asnych request is complete, (2) the signature of the completion handler matches exactly what the caller declared in the completion block ^(NSDictionary *, NSError *), this is just a suggestion, typical for network requests.