First of all,

As for the network layer, AFNetworking or Alamofire in Swift may come to mind first. It is also very simple to use directly, but it is not enough to use a slightly complicated project directly. First of all, apart from third-party coupling, it is difficult to maintain the request callback scattered everywhere. Therefore, targeted re-encapsulation is generally carried out. In the early stage, the business may be relatively simple with few aspects to consider. In the later stage, the network layer may need to be reconstructed as the business increases. Recently, I also read the source code of YTKNetwork and related blogs, and wrote down some of my own interpretation of the network layer on the shoulders of my predecessors. ##### Interworking with the service layer There are two common interworking modes for the service layer:

  • Intensive:

The most typical ones are AFNetworking and Alamofire mentioned above. All network requests are initiated in one class, and the request callback is realized through blocks and closures. Block and closure callback has good flexibility, which can easily initiate requests from any location, but may also be bad. Network request callbacks are scattered and not easy to maintain. The following is an intensive network request, have you ever encountered a scenario where the request callback needs to do a lot of processing, when there is a lot of code, you will define a private method to write the code inside, call the private method inside the Block. Doing so is essentially the same as doing callbacks through proxies.

[_manager GET:url parameters:param success:^(AFHTTPRequestOperation *operation, id responseObject) {
     //The data processing, Rendering interface
 } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                   
 }];

Copy the code
  • Discrete:

Corresponding is the next YTKNetwork, discrete biggest characteristics is a network request corresponding to a single class, in this class internal packaging request address, methods, parameters, calibration and data processing requests came back, agent callback, usually need to cross layer also use notification callback, when the data transfer is more centralized, for data processing in the internal processing, The form of the returned data (modeled or otherwise) does not matter to the Controller, the Controller only needs the data returned by the agent to render the UI directly, making the Controller more lightweight.

The initiating

    NSString *userId = @"1";
    GetUserInfoApi *api = [[GetUserInfoApi alloc] initWithUserId:userId];
    [api start];
    api.delegate = self;

Copy the code

The Delegate callback

- (void)requestFinished:(YTKBaseRequest *)request {
    NSLog(@"----- succeed ---- %@", request.responseJSONObject);
    //Rendering interface
}
- (void)requestFailed:(YTKBaseRequest *)request {
    NSLog(@"failed");
}

Copy the code

#####YTKNetwork parsing first look at YTKNetwork class file:

This diagram should not have too many problems understanding the source code:

  • YTKBaseRequest: YTKRequest’s parent class, which defines the Request properties, blocks, and delegates. Default implementation for external interfaces, and common logic.

  • YTKRequest: mainly processes the cache, such as updating the cache, reading the cache, and manually writing to the cache. Whether to ignore the cache? Archived cache is adopted here. Request mode, root path, request address, request parameters, APP version number, sensitive data stitching and MD5 are used as the file name of the cache to ensure uniqueness. It also provides the setting of the cache storage duration. The main implementation is to determine whether a request is really initiated by comparing the time obtained from the last modification of the cache file with the set cache duration. The following are some logical judgments for initiating a request:

- (void)start {if (self.ignoreCache) {// If cache is ignored -> network request [super start]; return; } // check cache time if ([self cacheTimeInSeconds] < 0) {// Check cache time -> network request [super start]; return; } // check cache version long long cacheVersionFileContent = [self cacheVersionFileContent]; if (cacheVersionFileContent ! = [self cacheVersion]) {// validate the cacheVersion number, if inconsistent -> network request [super start]; return; } // check cache existance NSString *path = [self cacheFilePath]; // NSFileManager *fileManager = [NSFileManager defaultManager]; if (! [fileManager fileExistsAtPath:path isDirectory:nil]) {// According to the file path, verify whether the cache exists, does not exist -> network request [super start]; return; } // Check cache time Comparison between the last cache configuration time and the current cache validity time int seconds = [self cacheFileDuration:path]; If (seconds < 0 | | seconds > [self cacheTimeInSeconds]) {/ / cache file last time how long time from now > cache time [super start] effectively; return; } // load cache _cacheJson = [NSKeyedUnarchiver unarchiveObjectWithFile:path]; If (_cacheJson == nil) {// Fetch cache if no -> network request [super start]; return; } _dataFromCache = YES; // Cache the data after the request succeeds [self requestCompleteFilter]; // proxy YTKRequest *strongSelf = self; [strongSelf.delegate requestFinished:strongSelf]; If (strongSelf successCompletionBlock) {/ / block callback strongSelf successCompletionBlock (strongSelf); } [strongSelf clearCompletionBlock]; }Copy the code

Store network requested data by archiving:

- (void)saveJsonResponseToCacheFile:(id)jsonResponse {
    if ([self cacheTimeInSeconds] > 0 && ![self isDataFromCache]) {
        NSDictionary *json = jsonResponse;
        if (json != nil) {
            [NSKeyedArchiver archiveRootObject:json toFile:[self cacheFilePath]];
            [NSKeyedArchiver archiveRootObject:@([self cacheVersion]) toFile:[self cacheVersionFilePath]];
        }
    }
}

Copy the code
  • YTKNetworkAgent: the class that actually initiates the network request, inaddRequestMethod to call AFN method, this section can easily replace the third-party library, also includes some request cancellation, plug-in proxy method call, etc., all network request failure or success will call the following method:
- (void)handleRequestResult:(AFHTTPRequestOperation *)operation { NSString *key = [self requestHashKey:operation]; YTKBaseRequest *request = _requestsRecord[key]; YTKLog(@"Finished Request: %@", NSStringFromClass([request class])); if (request) { BOOL succeed = [self checkResult:request]; If (succeed) {/ / request success [request toggleAccessoriesWillStopCallBack]; [request requestCompleteFilter]; if (request.delegate ! // Delegate requestFinished:request]; } the if (request. SuccessCompletionBlock) {/ / request Block success callback request. SuccessCompletionBlock (request); } [request toggleAccessoriesDidStopCallBack]; YTKLog(@"Request % @failed, status code = %ld", NSStringFromClass([Request class]), (long)request.responseStatusCode); [request toggleAccessoriesWillStopCallBack]; [requestFailedFilter]; if (request.delegate ! = nil) {// Delegate requestFailed:request]; } the if (request. FailureCompletionBlock) {/ / request failure Block callback request. FailureCompletionBlock (request); } [request toggleAccessoriesDidStopCallBack]; } } [self removeOperation:operation]; [request clearCompletionBlock]; }Copy the code
  • YTKNetworkConfig: sets the request root path and DNS address.

  • YTKNetworkPrivate: can be understood as a utility class, concatenate addresses, provide encryption methods, define classes, etc.

  • YTKBatchRequest, YTKChainRequest: this is the two advanced usage, YKTNetwork batch network Request and chain network, the equivalent of a deposit Request container, first define the following properties, finishedCount to note down the number of batch Request to complete:

@interface YTKBatchRequest() <YTKRequestDelegate>
@property (nonatomic) NSInteger finishedCount;
@end
Copy the code

FinishedCount ++ does not complete the callback until finishedCount equals the number of requests.

#pragma mark - Network Request Delegate - (void)requestFinished:(YTKRequest *)request { _finishedCount++; if (_finishedCount == _requestArray.count) { [self toggleAccessoriesWillStopCallBack]; if ([_delegate respondsToSelector:@selector(batchRequestFinished:)]) { [_delegate batchRequestFinished:self]; } if (_successCompletionBlock) { _successCompletionBlock(self); } [self clearCompletionBlock]; [self toggleAccessoriesDidStopCallBack]; }}Copy the code

Bind an Index to Request

@interface YTKChainRequest()<YTKRequestDelegate>
@property (assign, nonatomic) NSUInteger nextRequestIndex;
@end
Copy the code

Select * from requestArray (nextRequestIndex++, nextRequestIndex++, nextrequestindex +);

- (void)start { if (_nextRequestIndex > 0) { YTKLog(@"Error! Chain request has already started."); return; } if ([_requestArray count] > 0) { [self toggleAccessoriesWillStartCallBack]; [self startNextRequest]; [[YTKChainRequestAgent sharedInstance] addChainRequest:self]; } else { YTKLog(@"Error! Chain request array is empty."); }}Copy the code
// Next network request - (BOOL)startNextRequest {if (_nextRequestIndex < [_requestArray count]) {YTKBaseRequest *request = _requestArray[_nextRequestIndex]; _nextRequestIndex++; request.delegate = self; [request start]; return YES; } else { return NO; }}Copy the code