Handle network request dependencies gracefully

Keywords: network git github simulator

When dealing with the network, we often encounter the situation that the network requests depend on each other. For example, the parameter of B request depends on the callback result of A request. If B is requested directly in the callback of A request, it will lead to the nesting of callbacks, which leads to the tight coupling between requests. Here's an elegant way to take advantage of the operation dependency of NSOperation.

  • This example attempts to download two pictures, assuming that picture 2 is always displayed after picture 1.
  • Download framework AFNetworking3

To use the operation dependency of NSOperation, an NSOperationQueue is required first:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
queue.name = @"SessionQueue";

Because the AFHTTPSessionManager in AFNetworking3 is not a subclass of NSOperation, which is very different from AFNetworking2. It can't be directly added to NSOperationQueue. It needs to be encapsulated. Fortunately, a third-party library has done all the work. First, it is introduced to the Library:

 pod 'AFSessionOperation', :git => 'https://github.com/robertmryan/AFHTTPSessionOperation.git'

AFHTTPSessionOperation is a subclass of NSOperation. At the same time, AFNetworking is called to implement HTTP request. The following is the implementation of request operation:

NSString *imangeUrl1 = @"https://upload-images.jianshu.io/upload_images/2025746-368aa3a508d2fbac.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240";
NSString *imangeUrl2 = @"https://upload-images.jianshu.io/upload_images/2025746-27bbf45bea40162c.JPEG?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240";
NSOperation *op1 = [AFHTTPSessionOperation operationWithManager:manager HTTPMethod:@"GET" URLString:imangeUrl1 parameters:nil uploadProgress:nil downloadProgress:nil success:^(NSURLSessionDataTask *task, id responseObject) {
    NSLog(@"finished 1");
    UIImage *image = [UIImage imageWithData:responseObject];
    _imageView1.image = image;
} failure:^(NSURLSessionDataTask *task, NSError *error) {
    NSLog(@"failed 1 - error = %@", error.localizedDescription);
}];

NSOperation *op2 = [AFHTTPSessionOperation operationWithManager:manager HTTPMethod:@"GET" URLString:imangeUrl2 parameters:nil uploadProgress:nil downloadProgress:nil success:^(NSURLSessionDataTask *task, id responseObject) {
    NSLog(@"finished 2");
    UIImage *image = [UIImage imageWithData:responseObject];
    _imageView2.image = image;
} failure:^(NSURLSessionDataTask *task, NSError *error) {
    NSLog(@"failed 2 - error = %@", error.localizedDescription);
}];

op2 depends on op1:

[op2 addDependency:op1];

Finally, add op1 and op2 to the operation queue, which will start executing the network request:

[queue addOperations:@[op1, op2] waitUntilFinished:false];

Execution result:


Screenshot of simulator

console output

According to our design, op2 is executed after op1 is completed.

When using, be careful not to create a dependency ring, such as this:

[op1 addDependency:op2];
[op2 addDependency:op1];

Neither operation will be performed.

Posted by cyberdesi on Tue, 03 Dec 2019 04:50:05 -0800