NSURLConnection initWithRequest is deprecated

Posted in :

iOS 9.0 會顯示 NSURLConnection connectionWithRequest 已經 deprecated, 所以原本的 code 需要修改成:

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
        completionHandler:^(NSData *data, NSURLResponse *response, NSError *error)
        {
            // do something with the data 
        }];
[dataTask resume];

NSHTTPURLResponse 預設沒有 status code, 需要轉換一下成為 NSHTTPURLResponse 即可。

 

    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
    NSLog(@"response status code: %ld", (long)[httpResponse statusCode]);

You cannot just return the data (because the NSURLSessionDataTask runs asynchronously). You probably want to employ your own completion block pattern, similar to the completionHandler of the dataTaskWithRequest method.

So, you would add your own block parameter to your method, that you’ll invoke from inside the dataTaskWithRequest method’s completionHandler:

+ (NSURLSessionDataTask *)postCall:(NSDictionary *)parameters fromURL:(NSString *)url completionHandler:(void (^)(NSData *data, NSURLResponse *response, NSError *error))completionHandler {

    // create your request here ...

    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:urlRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (completionHandler)
            completionHandler(data, response, error);
    }];

    [dataTask resume];

    return dataTask;
}

Note, I think it’s good to return the NSURLSessionDataTask reference so (a) the caller can make sure the data task was successfully created; and (b) you have the NSURLSessionTask reference that you can use to cancel the task in case, at some future date, you want to be able to cancel the request for some reason (e.g. the user dismisses the view controller from which the request was issued).

Anyway, you’d then invoke this with:

NSURLSessionTask *task = [MyClass postCall:parameters fromURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
    // put whatever code you want to perform when the asynchronous data task completes
}];

if (!task) {
    // handle failure to create task any way you want
}

You ask:

Is my way good or it will cause me problems in the future? I don’t have [an] image to download and I don’t have anything to upload, I just have to send [some] simple string data and receive [simple] string data. Or it will be better to but that code in each function independently?

If you’re receiving simple string data back, I’d suggest composing your response in JSON format, and then having the completion block in postCall use NSJSONSerialization to extract the response. Using JSON makes it easier for the app to differentiate between successful response and a variety of server related problems that might also return string responses.

So, let’s say you modified your server code to return a response like so:

{"response":"some text"}

Then you could modify postCall to parse that response like so:

+ (NSURLSessionDataTask *)postCall:(NSDictionary *)parameters fromURL:(NSString *)url completionHandler:(void (^)(NSString *responseString, NSError *error))completionHandler {

    // create your request here ...

    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:urlRequest completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        if (completionHandler) {
            if (error) {
                completionHandler(nil, error);
            } else {
                NSError *parseError = nil;
                NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];

                completionHandler(responseDictionary[@"response"], parseError);
            }
        }
    }];

    [dataTask resume];

    return dataTask;
}

In terms of your underlying question, whether a method like postCall makes sense, yes, I think it makes perfect sense to put the details of creating the request in a single method. My minor reservation in your implementation was your decision to make it a class method rather than an instance method. You’re currently creating a new NSURLSession for each request. I’d suggest making postCall an instance method (of a singleton if you want) and then saving the session as a class property, which you set once and then re-use on subsequent queries.

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *