From NSURLConnection to NSURLSession

Since NSURLConnection is deprecated, iOS developers are in a point to move to NSURLSession. In this post we will deeply travel through NSURLSession API.

For creating network  requests I see most of the lazy people go for 3rd party frameworks or libraries. But it seems very odd that those are just wrappers around apple’s native APIs. Instead of spending time on those 3rd party libraries start writing your own way of network requests handling. BTW apple makes this job easier by introducing NSURLSession.

NSURLSession has the following objects to create a network request,

1. NSURLSessionConfiguration : This helps to create a session which has 3 types for 3 different purposes

  • defaultSessionConfiguration
  • ephemeralSessionConfiguration
  • backgroundSessionConfiguration

2. NSURLSession : This is the building block for making network requests which can be created using any of the above configuration

3. NSURLSessionTask: This object actually drives the session for uploading and downloading data. Which comes in three types,

  • NSURLSessionDataTask
  • NSURLSessionUploadTask
  • NSURLSessionDownloadTask

A Classic example for making network requests using NSURLSessionDataTask

This example downloads a sample zip file and it is implemented using NSURLSession delegates. There is another approach where you can use blocks (completion handlers) for handling error and success response.

Add NSURLSessionDataDelegate, NSURLSessionDelegate to your Class. We need to implement these delegate methods for handling the data.

Add this line to your .h file

@property (nonatomic, strongNSMutableData *totalContentData;

Create a configuration which suits requirement. Following example is with default configuration. All you need to do is call hitServerForUrl method by passing the file download URL.

- (void)hitServerForUrl:(NSString*)urlString {

    NSURL *requestUrl = [NSURL URLWithString:urlString];    

    NSURLSessionConfiguration *defaultConfigurationObject = [NSURLSessionConfiguration defaultSessionConfiguration];

    NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:defaultConfigurationObject delegate:self delegateQueue: nil];

    NSURLSessionDataTask *dataTask = [defaultSession dataTaskWithURL:requestUrl];

    [dataTask resume];

}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask

didReceiveResponse:(NSURLResponse *)response

  completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {

    completionHandler(NSURLSessionResponseAllow); //This line makes your request to be continued. 

}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task

didCompleteWithError:(nullable NSError *)error {

    if (error) {

       //Handle error here

        _totalContentData = nil;

   }

    else {

        if (task.state == NSURLSessionTaskStateCompleted) {//Once completed write the data as file to documents directory

            NSString *fileName = [task.response suggestedFilename];

            if (_totalContentData) {

                NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);

                NSString *documentsDirectory = [paths objectAtIndex:0];

                NSString *path = [NSString stringWithFormat:@"%@/%@", documentsDirectory, fileName];
 NSLog(@"Path :%@", path);

                [_totalContentData writeToFile:path atomically:YES];

            }

        }

    }

}

- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask

    didReceiveData:(NSData *)data {

    if (_totalContentData == nil) {

        _totalContentData = data.mutableCopy;

}

    else {

        [self mergeData:_totalContentData withData:data.mutableCopy]; //Merging the data we received with already received data

    }
}

- (void)mergeData:(NSMutableData*)alreadyReceivedChunk withData:(NSMutableData*)newlyReceivedChunk {
 //Merges the two parameters
 if (![self isAlreadyMergedData: newlyReceivedChunk]){
 [alreadyReceivedChunk appendBytes:[newlyReceivedChunk bytes] length:[newlyReceivedChunk length]];
 _totalContentData = alreadyReceivedChunk;
 }
}

- (BOOL)isAlreadyMergedData:(NSMutableData*)dataToBeMerged {
 //Checks for the totalContentData whether it has already merged the new content
 
 NSRange range = [_totalContentData rangeOfData:dataToBeMerged options:0 range:NSMakeRange(0, [_totalContentData length])];
 if (range.location != NSNotFound) {
 // assuming the subdata doesn't have a specific range
 return YES;
 }
 
 return NO;
}

An example for file download using NSURLSessionDownloadTask

This example uses NSURLSessionDownloadTask to download files from server. In this I have demonstrated an example of tracking the file download progress. This example also uses the delegate approach.

Apple has provided an easy way to handle data that your app is downloading. You can remove all the messy work(merging chunks, removing duplicated data, writing total data to a file) that we have done in the above example.

Call hitServerForUrl with file download URL as a parameter

//Download Task example
- (void)hitServerForUrl:(NSString*)urlString {
 
 NSURL *requestUrl = [NSURL URLWithString:urlString];
 
 NSURLSessionConfiguration *defaultConfigurationObject = [NSURLSessionConfiguration defaultSessionConfiguration];
 
 NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:defaultConfigurationObject delegate:self delegateQueue: nil];
 
 NSURLSessionDownloadTask *fileDownloadTask = [defaultSession downloadTaskWithURL:requestUrl];
 
 [fileDownloadTask resume];
 
}

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location {
 
 NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
 NSURL *documentsDirectoryURL = [NSURL fileURLWithPath:documentsPath];
 NSURL *documentURL = [documentsDirectoryURL URLByAppendingPathComponent:[downloadTask.response suggestedFilename]];
 NSError *error;
 
 NSString *filePath = [documentsPath stringByAppendingPathComponent:[downloadTask.response suggestedFilename]];
 NSLog(@"file path : %@", filePath);
 if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
 //Remove the old file from directory
 }
 
 [[NSFileManager defaultManager] moveItemAtURL:location
 toURL:documentURL
 error:&error];
 if (error){
 //Handle error here
 }
}

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
 if (error != nil) {
 NSData *resumeData = error.userInfo[NSURLSessionDownloadTaskResumeData];
 self.downloadResumeData = resumeData;
 }
}

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
 didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite {
 dispatch_sync(dispatch_get_main_queue(), ^{
 float progressValue = totalBytesWritten/totalBytesExpectedToWrite;
 [self.downloadProgressView setProgress:progressValue animated:YES]; //Change ur progressView instance here
 });
}

An example for file or content upload using NSURLSessionUploadTask

This example demonstrates how to use NSURLSessionUploadTask for uploading contents to server. I have used completion handlers for this example.

- (void)doUploadFilesToServer:(NSData*)dataToBeUploaded atUrl:(NSURL*)uploadTaskUrl {

    NSMutableURLRequest *uploadUrlRequest = [NSMutableURLRequest requestWithURL:uploadTaskUrl];

    uploadUrlRequest.HTTPMethod = @"POST";

    NSURLSessionConfiguration *defaultConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];

    NSURLSession *fileUploadSession = [NSURLSession sessionWithConfiguration:defaultConfiguration];

    NSURLSessionUploadTask *uploadTask = [fileUploadSession uploadTaskWithRequest:uploadUrlRequest fromData:dataToBeUploaded completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {

        if (error != nil) {

            NSLog(@"File upload failed with error : %@", error.description);

        }

        else  {

            NSLog(@"Successfully uploaded");

            //Handle response here

        }

    }];

    [uploadTask resume];

}

We can also use delegates to handle NSURLSessionUploadTask, but just to cover both delegates & block approach I have used completion handler in this example.

I hope you all enjoyed reading this post. Happy reading !!! 🙂