one Introduction to the

AFNetworking is a networking library for iOS, macOS, watchOS and tvOS. It is built on top of the Foundation URL system, extending the powerful high-level web abstractions built into Cocoa. It has a modular architecture, a well-designed, feature-rich API, and is very simple to use. This paper focuses on two modules: cache and security.

2. Organization chart

Iii. The overall process, cache module and security module are introduced

3.1 Overall Flow chart

3.1.1 Flowchart of sending a Get Request

3.2 the cache

3.2.1 profile

AFNetWorking is based on NSURLSession(the network request framework for iOS7 and above) and has three options for caching configuration when generating the configuration:

// Default session mode: Memory cache and disk cache are added by default. + (NSURLSessionConfiguration *)defaultSessionConfiguration; // Instantaneous session mode: only memory cache is added, disk cache is not implemented. + (NSURLSessionConfiguration *)ephemeralSessionConfiguration; // Background session mode: memory and disk are not cached. + (NSURLSessionConfiguration *)backgroundSessionConfiguration:(NSString *)identifier;Copy the code

We can also set the size of the cache by initializing NSURLCache.

3.2.2 Initialization

In in – application: didFinishLaunchingWithOptions: [NSURLCache sharedURLCache] initialize Settings:

NSURLCache *URLCache = [[NSURLCache alloc] initWithMemoryCapacity:4 * 1024 * 1024
                                                         diskCapacity:20 * 1024 * 1024
                                                             diskPath:nil];
[NSURLCache setSharedURLCache:URLCache];
Copy the code

The configuration of NSURLSession can also be set separately. In AFNetWorking, 20M memory cache and 150M hard disk cache are set for image network requests.

+ (NSURLCache *)defaultURLCache {
return [[NSURLCache alloc] initWithMemoryCapacity:20 * 1024 * 1024
                                     diskCapacity:150 * 1024 * 1024
	                                       diskPath:@"com.alamofire.imagedownloader"];
}
Copy the code

3.2.3 Cache Policy

Summary: Cache policy refers to whether to cache or not to cache network requests:

NSURLRequestUseProtocolCachePolicy: for a particular URL request using the network protocol to implement caching logic, this is the default policy. NSURLRequestReloadIgnoringLocalCacheData: data needs to be loaded from the original address, do not use the existing cache. NSURLRequestReloadIgnoringLocalAndRemoteCacheData: not only ignore the local cache, also ignore the proxy server or other medium among the currently existing, agreement to allow the cache. NSURLRequestReturnCacheDataElseLoad: whether the cache expiration, using local cache data first. If there is no data for the request in the cache, the data is loaded from the original address. NSURLRequestReturnCacheDataDontLoad: whether the cache expiration, using local cache data first. If there is no data for the request in the cache, then loading data from the original address is abandoned and the request is considered a failure (i.e., "offline" mode). NSURLRequestReloadRevalidatingCacheData: from the original address, after confirm the legitimacy of the cached data cache data can be used, otherwise the load from the original address.Copy the code

3.2.4 Image hard disk Cache

Introduction: is what we often say to save data locally, such as FMDB, CoreData, archive, NSUserDefaults, NSFileManager, etc., here will not say more. AFNetWorking3.0 does not do image hard disk caching directly, but does hard disk caching through URL caching. In other words, if the memory cache does not read the image, the download logic will be called, through the memory cache disk cache of the download cache to get the downloaded image, if not downloaded, it will be downloaded again. If we do our own picture hard disk cache recommended to use NSFileManager, because the general picture data will be relatively large, the test proved that the path cache will have higher performance than in the database.

3.2.5 Memory cache mapping class: AFAutoPurgingImageCache

1) introduction

AFAutoPurgingImageCache is a protocol that defines a set of apis for adding, removing, and retrieving images synchronously from the cache.

2) The main API is introduced as follows

3) Memory cache flow chart

4) Introduction to memory caching process

Before adding images to the cache after each image download, check the current size of the cached memoryUsage. If the size exceeds the total cache memoryCapacity, obtain the size of the cache to be cleared. Need to clear the cache size = total cache size – the reserved buffer size (memoryCapacity – preferredMemoryUsageAfterPurge), then to insert images of the cache in accordance with the time sequence, insert the first picture, in turn, delete, If the size of the cache to be deleted is larger than the size of the cache to be cleared, the end is displayed.

5) Memory cache core source code parsing
- (void)addImage:(UIImage *)image withIdentifier:(NSString *)identifier { dispatch_barrier_async(self.synchronizationQueue, ^{ AFCachedImage *cacheImage = [[AFCachedImage alloc] initWithImage:image identifier:identifier]; // Step 1: If identifier's cache object exists, assign a new cache object to identifier, And subtract AFCachedImage *previousCachedImage = self.cachedimages [identifier]; if (previousCachedImage ! = nil) { self.currentMemoryUsage -= previousCachedImage.totalBytes; } self.cachedImages[identifier] = cacheImage; self.currentMemoryUsage += cacheImage.totalBytes; }); Dispatch_barrier_async (self synchronizationQueue, ^ {the if (self. CurrentMemoryUsage > self. MemoryCapacity) {/ * the second step: Calculate the going to remove the memory size of * / UInt64 bytesToPurge = self. CurrentMemoryUsage - self. PreferredMemoryUsageAfterPurge; /* Step 3: arrange all caches in order of their last use time, and then delete them one by one. Each time you delete the cache, you add the size of the cache to be deleted, and stop deleting the cache when the size of the cache to be removed is greater than or equal to the size of the cache to be removed. */ NSMutableArray <AFCachedImage*> *sortedImages = [NSMutableArray arrayWithArray:self.cachedImages.allValues]; NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"lastAccessDate" ascending:YES]; [sortedImages sortUsingDescriptors:@[sortDescriptor]]; UInt64 bytesPurged = 0; for (AFCachedImage *cachedImage in sortedImages) { [self.cachedImages removeObjectForKey:cachedImage.identifier]; bytesPurged += cachedImage.totalBytes; if (bytesPurged >= bytesToPurge) { break ; */ self.currentMemoryUsage -= bytesPurged; */ self.currentMemoryUsage -= bytesPurged; }}); }Copy the code

3.3 AFSecurityPolicy

First, HTTPS:

HTTPS encryption is a mixture of shared key encryption and public key encryption. Shared key encryption, encryption and decryption use the same key, that is, symmetric encryption. Public key encryption is divided into public key and private key. The public key encryption is used publicly, and the private key is used for decryption. HTTPS uses the public key for encryption during key exchange and the shared key during packet exchange. First, the public key encryption is used to securely exchange the key that will be used in the shared key encryption later on. Then, the shared key encryption is used to communicate with each other on the premise of ensuring the security of the exchanged key.

3.3.1 profile

The AFSecurityPolicy class does only one thing, which is HTTPS authentication, and is a further encapsulation of the system library <Security/ security.h >. The default certificate authentication process of AFNetworking is single client authentication. If two-way authentication is required, both the server and client need to send digital certificates to each other for authentication, which needs to be implemented by users themselves.

3.3.2 Individual authentication process of AFNetworking

3.3.3 Code detailed analysis

There are three validation modes for AFSecurityPolicy: Typedef NS_ENUM (NSUInteger AFSSLPinningMode) {AFSSLPinningModeNone, / / representative unconditional trust the server's certificate AFSSLPinningModePublicKey, / / representatives to the PublicKey returned by the server certificate to verify AFSSLPinningModeCertificate, / / representatives for all returned by the server certificate with local calibration} statement next four properties: SSLPinningMode: Return the type of SSLPinning, default is AFSSLPinningModeNone; PinnedCertificates: keep all can be used as a collection of certificate validation, evaluateServerTrust: forDomain: will return true, by check; AllowInvalidCertificates: Use certificates that are allowed to be invalid or expire. Default is NO. ValidatesDomainName: specifies whether to validate the domain name in the certificate. The default value is YES.Copy the code

We do the flow chart

3.3.5 Certificate Challenge Core Code Parsing

- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential completionHandler * the credential)) {/ / challenges processing type as the default NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling; __block NSURLCredential *credential = nil; // Custom methods, Server side authentication challenge to how to deal with the if (self. SessionDidReceiveAuthenticationChallenge) {disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential); } else {/ / how to solve the receiving server challenge whether trust certificate if ([challenge. ProtectionSpace. AuthenticationMethod IsEqualToString: NSURLAuthenticationMethodServerTrust]) {/ / to verify the server certificate is safe, namely the HTTPS single authentication, this is the default processing of AF authentication, Other authentication methods can only be done by our own block to the if ([self. The securityPolicy evaluateServerTrust: challenge. ProtectionSpace. ServerTrust forDomain:challenge.protectionSpace.host]) { credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]; If (the credential) {/ / certificates challenge disposition = NSURLSessionAuthChallengeUseCredential; } else {/ / challenge mode (certificate) are provided by default disposition = NSURLSessionAuthChallengePerformDefaultHandling; }} else {/ / cancel the challenge (connection) disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; }} else {/ / challenge mode (certificate) are provided by default disposition = NSURLSessionAuthChallengePerformDefaultHandling; }} /* Complete the challenge and send the credential to the server */ if (completionHandler) {completionHandler(disposition, credential); }}Copy the code

3.3.6 Core Code Analysis of Certificate Authentication

// According to the current security policy, EvaluateServerTrust :(SecTrustRef)serverTrust forDomain:(NSString *)domain {/* evaluation must be guaranteed to be a valid process. If invalid certificates are allowed, but no certificates are available, or if AFSSLPinningModeNone mode is used, you will be told that this is an invalid validation process when all other information is available. */ if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) { NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning."); return NO; } // Create a security policy. If domain name authentication is required, create an SSL security policy with the reference domain name. Otherwise, create an X.509 based security policy. NSMutableArray *policies = [NSMutableArray array]; if (self.validatesDomainName) { [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)]; } else { [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()]; } // Add the created security policy to the trust assessment given by the server. The evaluation authentication will be evaluated against the local certificate or public key to obtain the result. SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies); // In AFSSLPinningModeNone, public key or certificate authentication is not performed. Just make sure the trust assessment given by the server is valid (to get the CA root certificate). Alternatively, if the user Settings allow invalid certificates, it will simply return pass. if (self.SSLPinningMode == AFSSLPinningModeNone) { return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust); } else if (! AFServerTrustIsValid(serverTrust) && ! self.allowInvalidCertificates) { return NO; } // Switch (self.SSLPinningMode) {case AFSSLPinningModeNone: default: return NO; // AFSSLPinningModeNone has already been created. Return NO case AFSSLPinningModeCertificate: {/ / verification certificate and server sent here trust are screened. // There may be many "pinnedCertificates" used locally, so convert them to CFData and put them into arrays. NSMutableArray *pinnedCertificates = [NSMutableArray array]; for (NSData *certificateData in self.pinnedCertificates) { [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)]; } / / set the pinnedCertificates into need to participate in the validation of the Anchor Certificate (Certificate of Anchor, to participate in the calibration Certificate of Anchor through SecTrustSetAnchorCertificates set up later, SecTrustEvaluate is used to verify that the certificate is a child of the anchor certificate. SecTrustEvaluate is used to verify that the anchor certificate is a child of the anchor certificate. SecTrustEvaluate is used to verify that the anchor certificate is a child of the anchor certificate. SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates); if (! AFServerTrustIsValid(serverTrust)) { return NO; } // Get the certificate chains for all servers, note that this is not the same as AnchorCertificates. NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust); For (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {// If the certificate chain contains a local certificate, Note serverTrust is a valid serverTrust certificate. Returns YES if ([self pinnedCertificates containsObject: trustChainCertificate]) {return YES; } } return NO; } case AFSSLPinningModePublicKey: {/ / verify the trust of the local public key and server sent to identify / / access to the server public key chain NSUInteger trustedPublicKeyCount = 0; NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust); If at least one public key matches, the authentication succeeds. for (id trustChainPublicKey in publicKeys) { for (id pinnedPublicKey in self.pinnedPublicKeys) { if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) { trustedPublicKeyCount += 1; } } } return trustedPublicKeyCount > 0; } } return NO; }Copy the code

Author’s brief introduction

Profile: Yan Mingyue, iOS development engineer of Firefly Mobile financial development platform, User experience Technology Department, Minsheng Technology Co., LTD.