The original link

In “iOS Networking — NSURLSession”, we introduced the architecture of NSURLSession and how it works. In this article, we introduce the design architecture of AFNetworking by reading the source code (version 2.6.3).

AFNetwoking overview

AFNetworking is a network library for iOS, macOS, watchOS and tvOS. Versions after AFNetworking 2.0 are built on top of the Foundation URL loading system based on NSURLSession. AFNetworking extends the powerful high-level network abstractions built into Cocoa, with a modular design and rich features, and is one of the most widely used open source projects.

AFNetworking architecture

The AFNetworking framework consists of two parts:

  • AFNetworking core features
  • UIKit+AFNetworking classification function

Here we mainly introduce the core functions of AFNetworking, and its overall architecture is shown in the figure below. AFNetworking consists of six classes:

  • AFURLSessionManager: the core class of AFNetworking.
  • AFHTTPSessionManager:AFURLSessionManagerA subclass of, primarily for HTTP requests.
  • AFURLRequestSerialization: request the serializer, used for the parameters of the code for the query string, the HTTP body, and according to the need to set the appropriate HTTP header fields.
  • AFURLResponseSerialization: response serializer, used in the data decoding as an object, can also respond to incoming and data validation.
  • AFSecurityPolicy: Evaluates the server’s trust in fixed X.509 certificates and public keys over a secure connection.
  • AFNetworkReachabilityManager: monitor network accessibility.

Now let’s take a look at the core of AFNetworking — AFurlssession Manager.

AFURLSessionManager

The following figure shows the internal structure of AFURLSessionManager. Next, we will use the structure diagram as a guide to introduce the working principle of AFurlssession Manager.

Hold the property

Let’s start by looking at the properties held by the Afurlssession Manager.

/// External public attributes
@interface AFURLSessionManager
/// Manage Session
@property (readonly.nonatomic.strong) NSURLSession *session;
/// Queue for agent callback execution
@property (readonly.nonatomic.strong) NSOperationQueue *operationQueue;
/ / / using dataTaskWithRequest: success, failure: create, using the GET or POST data of execution task response. The default value is AFJSONResponseSerializer
@property (nonatomic.strong) id <AFURLResponseSerialization> responseSerializer;
/// Session Specifies the security policy used to evaluate the server's trust in secure connections. AFURLSessionManager uses the defaultPolicy defaultPolicy
@property (nonatomic.strong) AFSecurityPolicy *securityPolicy;
/// Network accessibility manager. AFURLSessionManager Uses sharedManager by default
@property (readwrite.nonatomic.strong) AFNetworkReachabilityManager *reachabilityManager;

/// ---------------------

/// Managed session All types of tasks currently running, such as data, upload, download, etc
@property (readonly.nonatomic.strong) NSArray <NSURLSessionTask *> *tasks;
/// Managed session Data task currently running
@property (readonly.nonatomic.strong) NSArray <NSURLSessionDataTask *> *dataTasks;
/// Managed session Upload task currently running
@property (readonly.nonatomic.strong) NSArray <NSURLSessionUploadTask *> *uploadTasks;
/// Managed session Current download task
@property (readonly.nonatomic.strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;
/// The queue of the completionBlock execution. If NULL, it is executed on the main thread
@property (nonatomic.strong.nullable) dispatch_queue_t completionQueue;
/// completionBlock The group of the execution. If NULL, it is executed in a private group
@property (nonatomic.strong.nullable) dispatch_group_t completionGroup;

@end
Copy the code
@interface AFURLSessionManager () @property (readWrite, nonatomic, strong) NSURLSessionConfiguration *sessionConfiguration; @property (readwrite, nonatomic, strong) NSOperationQueue *operationQueue; @property (readwrite, nonatomic, strong) NSMutableDictionary *mutableTaskDelegatesKeyedByTaskIdentifier; @property (readonly, nonatomic, copy) NSString *taskDescriptionForSessionTasks;
@property (readwrite, nonatomic, strong) NSLock *lock;

/// ---------------------

@property (readwrite, nonatomic, copy) AFURLSessionDidBecomeInvalidBlock sessionDidBecomeInvalid;
@property (readwrite, nonatomic, copy) AFURLSessionDidReceiveAuthenticationChallengeBlock sessionDidReceiveAuthenticationChallenge;
@property (readwrite, nonatomic, copy) AFURLSessionDidFinishEventsForBackgroundURLSessionBlock didFinishEventsForBackgroundURLSession AF_API_UNAVAILABLE(macos);
@property (readwrite, nonatomic, copy) AFURLSessionTaskWillPerformHTTPRedirectionBlock taskWillPerformHTTPRedirection;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidReceiveAuthenticationChallengeBlock taskDidReceiveAuthenticationChallenge;
@property (readwrite, nonatomic, copy) AFURLSessionTaskNeedNewBodyStreamBlock taskNeedNewBodyStream;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidSendBodyDataBlock taskDidSendBodyData;
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidCompleteBlock taskDidComplete;
#if AF_CAN_INCLUDE_SESSION_TASK_METRICS
@property (readwrite, nonatomic, copy) AFURLSessionTaskDidFinishCollectingMetricsBlock taskDidFinishCollectingMetrics;
#endif
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveResponseBlock dataTaskDidReceiveResponse;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidBecomeDownloadTaskBlock dataTaskDidBecomeDownloadTask;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskDidReceiveDataBlock dataTaskDidReceiveData;
@property (readwrite, nonatomic, copy) AFURLSessionDataTaskWillCacheResponseBlock dataTaskWillCacheResponse;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidWriteDataBlock downloadTaskDidWriteData;
@property (readwrite, nonatomic, copy) AFURLSessionDownloadTaskDidResumeBlock downloadTaskDidResume;
@end
Copy the code

Initialize the

First of all, AFURLSessionManager object holds a NSURLSessionConfiguration object (session configuration object). A session configuration object is used to initialize and hold an NSURLSession object (session object). The initialization of the session object is as follows:

- (NSURLSession *)session {
    @synchronized (self) {
        if(! _session) { _session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue]; }}return _session;
}
Copy the code

Note that after initializing the session object, the session object sets AFURLSessionManager as its own agent. Therefore, the Afurlssession Manager needs to implement the methods declared by the relevant protocols. These agreements are:

  • NSURLSessionDelegate
  • NSURLSessionTaskDelegate
  • NSURLSessionDataDelegate
  • NSURLSessionDownloadDelegate

To enable developers to implement custom handling of session – and task-level events, AFURLSessionManager provides a number of methods for setting callbacks, covering almost every method declared by the protocol. These callbacks are held internally by AFURLSessionManager and are executed only when the corresponding protocol method is called.

Schedule management

AFURLSessionManager acts as a proxy for session objects and can basically process all task objects. However, it is cumbersome and unreasonable to use AFurlssession Manager to manage the progress of uploading and downloading tasks. The main reasons are as follows:

  • A progress object needs to be held throughout the life cycle of both upload and download tasksNSProgress.
  • You also need to hold a receive data object for continuous receipt of data throughout the life of the download task.
  • AFURLSessionManagerPrimarily used to manage sessions, it should not manage the details of task objects according to the single responsibility principle.

Therefore, AFNetworking provides AFURLSessionManagerTaskDelegate to a class to manage the progress of a specific task, data.

AFURLSessionManager internal mutableTaskDelegatesKeyedByTaskIdentifier maintains a mutable dictionary attributes, with task object identifier taskIdentifier as the key, Taking AFURLSessionManagerTaskDelegate object as the value. The initialization task object, is binding a AFURLSessionManagerTaskDelegate object, to handle the progress, download the data. The specific code is as follows:

// Create an NSURLSessionDataTask based on the specific request
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                               uploadProgress:(nullable void(^) (NSProgress *uploadProgress)) uploadProgressBlock
                             downloadProgress:(nullable void(^) (NSProgress *downloadProgress)) downloadProgressBlock
                            completionHandler:(nullable void(^) (NSURLResponse *response, id _Nullable responseObject,  NSError * _Nullable error))completionHandler 
{
    // Create a task object
    __block NSURLSessionDataTask *dataTask = nil;
    url_session_manager_create_task_safely(^{
        dataTask = [self.session dataTaskWithRequest:request];
    });

    // Set the proxy object, upload progress callback, download progress callback, complete callback for the task object
    [self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];

    return dataTask;
}

- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
                uploadProgress:(nullable void(^) (NSProgress *uploadProgress)) uploadProgressBlock
              downloadProgress:(nullable void(^) (NSProgress *downloadProgress)) downloadProgressBlock
             completionHandler:(void(^) (NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
    / / create a AFURLSessionManagerTaskDelegate object for processing progress, download data
    AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
    delegate.manager = self;
    delegate.completionHandler = completionHandler;

    // Identifies the SessionManager to which the task object belongs
    dataTask.taskDescription = self.taskDescriptionForSessionTasks;
    [self setDelegate:delegate forTask:dataTask];

    delegate.uploadProgressBlock = uploadProgressBlock;
    delegate.downloadProgressBlock = downloadProgressBlock;
}

- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
            forTask:(NSURLSessionTask *)task
{
    NSParameterAssert(task);
    NSParameterAssert(delegate);

    / / save NSURLSessionTask < - > AFURLSessionManagerTaskDelegate mapping relationship
    [self.lock lock];
    self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
    [self addNotificationObserverForTask:task];
    [self.lock unlock];
}
Copy the code

AFURLSessionManagerTaskDelegate mainly implements the following agreement method. In these methods to achieve progress management, download management. AFURLSessionManager although also implement these agreements, but will eventually call AFURLSessionManagerTaskDelegate in implementation.

  • NSURLSessionTaskDelegate
    • URLSession:task:didCompleteWithError:
    • URLSession:task:didFinishCollectingMetrics:
  • NSURLSessionDataDelegate
    • URLSession:dataTask:didReceiveData:
    • URLSession:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:

Inform the forwarding

NSURLSession defines a set of notifications. In order to provide a better encapsulation, AFURLSessionManager captures these notifications, converts them into its own defined notifications, and forwards them.

Here, the taskDescription property of the task object is used. The taskDescription attribute describes the Session Manager to which the task object belongs. Multiple AFurlsessionManagers may be initialized in your application. The Session Manager encapsulates and forwards notifications only after it captures them from the task objects it manages. Such as:

  • AFNetworkingTaskDidResumeNotification
  • AFNetworkingTaskDidSuspendNotification

AFHTTPSessionManager

Next, we will introduce AFURLSessionManager subclass AFHTTPSessionManager.

First, let’s look at the attributes that are unique to Afhttpssession Manager.

@interface AFHTTPSessionManager : AFURLSessionManager <NSSecureCoding.NSCopying>

@property (readonly.nonatomic.strong.nullable) NSURL *baseURL;
@property (nonatomic.strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;
@property (nonatomic.strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;
@property (nonatomic.strong) AFSecurityPolicy *securityPolicy;

@end
Copy the code

Since afhttpssession Manager is primarily used for HTTP requests, it provides a request serializer and a response serializer for HTTP requests, with default values set to HTTP and JSON, respectively.

In addition, AFHTTPSessionManager also encapsulates some of the complex task object initialization methods of AFURLSessionManager and provides some very simple convenience methods to serve various HTTP requests. As follows:

- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
                            parameters:(nullable id)parameters
                               headers:(nullable NSDictionary <NSString *, NSString *> *)headers
                              progress:(nullable void(^) (NSProgress *downloadProgress))downloadProgress
                               success:(nullable void(^) (NSURLSessionDataTask *task, id _Nullable responseObject))success
                               failure:(nullable void(^) (NSURLSessionDataTask * _Nullable task, NSError *error))failure;

- (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString
                             parameters:(nullable id)parameters
                                headers:(nullable NSDictionary <NSString *, NSString *> *)headers
                                success:(nullable void(^) (NSURLSessionDataTask *task))success
                                failure:(nullable void(^) (NSURLSessionDataTask * _Nullable task, NSError *error))failure;
                                
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                             parameters:(nullable id)parameters
                                headers:(nullable NSDictionary <NSString *, NSString *> *)headers
              constructingBodyWithBlock:(nullable void(^) (id <AFMultipartFormData> formData))block
                               progress:(nullable void(^) (NSProgress *uploadProgress))uploadProgress
                                success:(nullable void(^) (NSURLSessionDataTask *task, id _Nullable responseObject))success
                                failure:(nullable void(^) (NSURLSessionDataTask * _Nullable task, NSError *error))failure;
                                
- (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString
                            parameters:(nullable id)parameters
                               headers:(nullable NSDictionary <NSString *, NSString *> *)headers
                               success:(nullable void(^) (NSURLSessionDataTask *task, id _Nullable responseObject))success
                               failure:(nullable void(^) (NSURLSessionDataTask * _Nullable task, NSError *error))failure;

- (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString
                              parameters:(nullable id)parameters
                                 headers:(nullable NSDictionary <NSString *, NSString *> *)headers
                                 success:(nullable void(^) (NSURLSessionDataTask *task, id _Nullable responseObject))success
                                 failure:(nullable void(^) (NSURLSessionDataTask * _Nullable task, NSError *error))failure;

- (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString
                               parameters:(nullable id)parameters
                                  headers:(nullable NSDictionary <NSString *, NSString *> *)headers
                                  success:(nullable void(^) (NSURLSessionDataTask *task, id _Nullable responseObject))success
                                  failure:(nullable void(^) (NSURLSessionDataTask * _Nullable task, NSError *error))failure;
Copy the code

conclusion

In a word, AFNetworking is actually a high encapsulation of NSURLSession. It provides some easy-to-use apis for us to make network requests in iOS development and build network layer components faster and provide reasonable interfaces on it.

Later, we will take a closer look at AFNetworking’s other auxiliary classes and classification functions.

reference

  1. AFNetworking
  2. Overview of AFNetworking (1)

(after)