Full Code of knightsj/SJNetwork for AI

master d84100447458 cached
134 files
1.1 MB
255.5k tokens
3 symbols
1 requests
Download .txt
Showing preview only (1,201K chars total). Download the full file or copy to clipboard to get everything.
Repository: knightsj/SJNetwork
Branch: master
Commit: d84100447458
Files: 134
Total size: 1.1 MB

Directory structure:
gitextract_p9vvfh07/

├── LICENSE
├── README.md
├── SJNetwork/
│   ├── SJNetwork.h
│   ├── SJNetworkBaseEngine.h
│   ├── SJNetworkBaseEngine.m
│   ├── SJNetworkCacheInfo.h
│   ├── SJNetworkCacheInfo.m
│   ├── SJNetworkCacheManager.h
│   ├── SJNetworkCacheManager.m
│   ├── SJNetworkConfig.h
│   ├── SJNetworkConfig.m
│   ├── SJNetworkDownloadEngine.h
│   ├── SJNetworkDownloadEngine.m
│   ├── SJNetworkDownloadResumeDataInfo.h
│   ├── SJNetworkDownloadResumeDataInfo.m
│   ├── SJNetworkHeader.h
│   ├── SJNetworkManager.h
│   ├── SJNetworkManager.m
│   ├── SJNetworkProtocol.h
│   ├── SJNetworkRequestEngine.h
│   ├── SJNetworkRequestEngine.m
│   ├── SJNetworkRequestModel.h
│   ├── SJNetworkRequestModel.m
│   ├── SJNetworkRequestPool.h
│   ├── SJNetworkRequestPool.m
│   ├── SJNetworkUploadEngine.h
│   ├── SJNetworkUploadEngine.m
│   ├── SJNetworkUtils.h
│   └── SJNetworkUtils.m
├── SJNetwork.podspec
└── SJNetworkDemo/
    ├── Podfile
    ├── Pods/
    │   ├── AFNetworking/
    │   │   ├── AFNetworking/
    │   │   │   ├── AFHTTPSessionManager.h
    │   │   │   ├── AFHTTPSessionManager.m
    │   │   │   ├── AFNetworkReachabilityManager.h
    │   │   │   ├── AFNetworkReachabilityManager.m
    │   │   │   ├── AFNetworking.h
    │   │   │   ├── AFSecurityPolicy.h
    │   │   │   ├── AFSecurityPolicy.m
    │   │   │   ├── AFURLRequestSerialization.h
    │   │   │   ├── AFURLRequestSerialization.m
    │   │   │   ├── AFURLResponseSerialization.h
    │   │   │   ├── AFURLResponseSerialization.m
    │   │   │   ├── AFURLSessionManager.h
    │   │   │   └── AFURLSessionManager.m
    │   │   ├── LICENSE
    │   │   ├── README.md
    │   │   └── UIKit+AFNetworking/
    │   │       ├── AFAutoPurgingImageCache.h
    │   │       ├── AFAutoPurgingImageCache.m
    │   │       ├── AFImageDownloader.h
    │   │       ├── AFImageDownloader.m
    │   │       ├── AFNetworkActivityIndicatorManager.h
    │   │       ├── AFNetworkActivityIndicatorManager.m
    │   │       ├── UIActivityIndicatorView+AFNetworking.h
    │   │       ├── UIActivityIndicatorView+AFNetworking.m
    │   │       ├── UIButton+AFNetworking.h
    │   │       ├── UIButton+AFNetworking.m
    │   │       ├── UIImage+AFNetworking.h
    │   │       ├── UIImageView+AFNetworking.h
    │   │       ├── UIImageView+AFNetworking.m
    │   │       ├── UIKit+AFNetworking.h
    │   │       ├── UIProgressView+AFNetworking.h
    │   │       ├── UIProgressView+AFNetworking.m
    │   │       ├── UIRefreshControl+AFNetworking.h
    │   │       ├── UIRefreshControl+AFNetworking.m
    │   │       ├── UIWebView+AFNetworking.h
    │   │       └── UIWebView+AFNetworking.m
    │   ├── Pods.xcodeproj/
    │   │   ├── project.pbxproj
    │   │   └── xcuserdata/
    │   │       └── SunShijie.xcuserdatad/
    │   │           └── xcschemes/
    │   │               ├── AFNetworking.xcscheme
    │   │               ├── Pods-SJNetworkingDemo.xcscheme
    │   │               └── xcschememanagement.plist
    │   └── Target Support Files/
    │       ├── AFNetworking/
    │       │   ├── AFNetworking-dummy.m
    │       │   ├── AFNetworking-prefix.pch
    │       │   └── AFNetworking.xcconfig
    │       └── Pods-SJNetworkingDemo/
    │           ├── Pods-SJNetworkingDemo-acknowledgements.markdown
    │           ├── Pods-SJNetworkingDemo-acknowledgements.plist
    │           ├── Pods-SJNetworkingDemo-dummy.m
    │           ├── Pods-SJNetworkingDemo-frameworks.sh
    │           ├── Pods-SJNetworkingDemo-resources.sh
    │           ├── Pods-SJNetworkingDemo.debug.xcconfig
    │           └── Pods-SJNetworkingDemo.release.xcconfig
    ├── SJNetworkingDemo/
    │   ├── Other/
    │   │   ├── AppDelegate.h
    │   │   ├── AppDelegate.m
    │   │   ├── Assets.xcassets/
    │   │   │   └── AppIcon.appiconset/
    │   │   │       └── Contents.json
    │   │   ├── Base.lproj/
    │   │   │   ├── LaunchScreen.storyboard
    │   │   │   └── Main.storyboard
    │   │   ├── Info.plist
    │   │   └── main.m
    │   ├── SJNetwork/
    │   │   ├── SJNetwork.h
    │   │   ├── SJNetworkBaseEngine.h
    │   │   ├── SJNetworkBaseEngine.m
    │   │   ├── SJNetworkCacheInfo.h
    │   │   ├── SJNetworkCacheInfo.m
    │   │   ├── SJNetworkCacheManager.h
    │   │   ├── SJNetworkCacheManager.m
    │   │   ├── SJNetworkConfig.h
    │   │   ├── SJNetworkConfig.m
    │   │   ├── SJNetworkDownloadEngine.h
    │   │   ├── SJNetworkDownloadEngine.m
    │   │   ├── SJNetworkDownloadResumeDataInfo.h
    │   │   ├── SJNetworkDownloadResumeDataInfo.m
    │   │   ├── SJNetworkHeader.h
    │   │   ├── SJNetworkManager.h
    │   │   ├── SJNetworkManager.m
    │   │   ├── SJNetworkProtocol.h
    │   │   ├── SJNetworkRequestEngine.h
    │   │   ├── SJNetworkRequestEngine.m
    │   │   ├── SJNetworkRequestModel.h
    │   │   ├── SJNetworkRequestModel.m
    │   │   ├── SJNetworkRequestPool.h
    │   │   ├── SJNetworkRequestPool.m
    │   │   ├── SJNetworkUploadEngine.h
    │   │   ├── SJNetworkUploadEngine.m
    │   │   ├── SJNetworkUtils.h
    │   │   └── SJNetworkUtils.m
    │   └── ViewController/
    │       ├── DownLoadViewController.h
    │       ├── DownLoadViewController.m
    │       ├── UploadViewController.h
    │       ├── UploadViewController.m
    │       ├── ViewController.h
    │       └── ViewController.m
    ├── SJNetworkingDemo.xcodeproj/
    │   ├── project.pbxproj
    │   ├── project.xcworkspace/
    │   │   ├── contents.xcworkspacedata
    │   │   └── xcuserdata/
    │   │       └── SunShijie.xcuserdatad/
    │   │           └── UserInterfaceState.xcuserstate
    │   └── xcuserdata/
    │       └── SunShijie.xcuserdatad/
    │           └── xcschemes/
    │               ├── SJNetworkingDemo.xcscheme
    │               └── xcschememanagement.plist
    ├── SJNetworkingDemo.xcworkspace/
    │   ├── contents.xcworkspacedata
    │   └── xcuserdata/
    │       └── SunShijie.xcuserdatad/
    │           ├── UserInterfaceState (ruwang’s MacBook Air 的冲突副本 2017-11-24).xcuserstate
    │           ├── UserInterfaceState (ruwang’s MacBook Air 的冲突副本 2017-11-28).xcuserstate
    │           ├── UserInterfaceState.xcuserstate
    │           └── xcdebugger/
    │               └── Breakpoints_v2.xcbkptlist
    ├── SJNetworkingDemoTests/
    │   ├── Info.plist
    │   └── SJNetworkingDemoTests.m
    └── SJNetworkingDemoUITests/
        ├── Info.plist
        └── SJNetworkingDemoUITests.m

================================================
FILE CONTENTS
================================================

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2017 J_Knight_

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# SJNetwork

![](https://img.shields.io/badge/build-passing-brightgreen.svg)
![](https://img.shields.io/badge/platform-iOS-lightgrey.svg)
![](https://img.shields.io/badge/language-Objective--C-30A3FC.svg)
![](https://img.shields.io/badge/pod-v1.2.0-orange.svg)
[![](https://img.shields.io/badge/blog-JueJin-007FFF.svg)](https://juejin.im/post/5a3f4ae8f265da4322416967)
[![](https://img.shields.io/badge/weibo-%40J__Knight__-ff0000.svg)](https://weibo.com/1929625262/profile?rightmod=1&wvr=6&mod=personinfo&is_all=1)
[![](https://img.shields.io/badge/License-MIT-ff69b4.svg)](https://github.com/knightsj/SJNetwork/blob/master/LICENSE)

## Introduction



SJNetwork provides a high level network request API  based on AFNetworking and inspired by YTKNetwork: generating network request object according to the configuration of a specific network request(url, method, parameters etc.)  and managing requests by the corresponding objects.

Document for Chinese-convenient reader:[中文文档](https://juejin.im/post/5a3f4ae8f265da4322416967)


## Features



- **Ordinary request**: sending GET,POST,PUT,DELETE network request.
  - write and load caches, configure cache available time duration​
- **Upload request**: sending upload image(s) network request
  - one and more than one image uploading and configure compress ratio before uploading​
- **Download request**: sending download file network request
  - resumable or not
  - background downloading supporting or not
- **Default parameters**:default parameters will be added on request body
- **Custom header**: configuring custom request header(key-value)
- **Base url configuration**  : server url of network requests
- **Request management**:canceling one or more than one current network requests; checking current requests' information
- **Cache operation**: writing, loading or clearing one or more than one cache of network requests ; calculating cache size
- **Debug mode switch**: for convenience of debugging






## Usage


**Method1:**  using Cocoapods:

``pod 'SJNetwork'``

then

```objective-c
#import <SJNetwork/SJNetwork.h>
```



**Method2**: moving ``SJNetwork``folder into your project.

then

```objective-c
#import "SJNetwork.h"
```



### Basic Configuration



#### Server url

```objective-c
[SJNetworkConfig sharedConfig].baseUrl = @"http://v.juhe.cn";
```



#### Default parameters

```objective-c
[SJNetworkConfig sharedConfig].defailtParameters = @{@"app_version":[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"],
                                                        @"platform":@"iOS"};
```



#### Timeout seconds

```objective-c
[SJNetworkConfig sharedConfig].timeoutSeconds = 30;//default is 20s
```



#### Debug mode

```objective-c
[SJNetworkConfig sharedConfig].debugMode = YES;//default is NO
```





#### Add request header

```objective-c
[[SJNetworkConfig sharedConfig] addCustomHeader:@{@"token":@"2j4jd9s74bfm9sn3"}];
```

or

```objective-c
[[SJNetworkManager sharedManager] addCustomHeader:@{@"token":@"2j4jd9s74bfm9sn3"}];
```



> The input key-value pair will be added in network request header.
>
> If a pair with same key-value dose not exist, then add it, if it exists, then replace it.





### Ordinary Network Request


POST request (none writing or none loading cache):

```objective-c
[[SJNetworkManager sharedManager] sendPostRequest:@"toutiao/index"
                                       parameters:@{@"type":@"top",
                                                    @"key" :@"0c60"}
  
     success:^(id responseObject) {

  } failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode) {

  }];
```



POST request ( writing and loading cache):



```objective-c
[[SJNetworkManager sharedManager] sendPostRequest:@"toutiao/index"
                                       parameters:@{@"type":@"top",
                                                    @"key" :@"0c60"}
                                        loadCache:YES
                                    cacheDuration:180
  success:^(id responseObject) {

} failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode) {

}];
```



> If loadcache is set to be YES, then try to load cache before sending network request.
>
> If cacheDuration is set to be more than 0, then write cache after receiving response object.



Flow chart of cache operation in ordinary requests:

![](http://oih3a9o4n.bkt.clouddn.com/request-blackwhite.png)



### Cache Operation

 

#### Loading cache



Loading cache of a specific network request:

```objective-c
[[SJNetworkManager sharedManager] loadCacheWithUrl:@"toutiao/index"
                                            method:@"POST"
                                        parameters:@{@"type":@"top",
                                                     @"key" :@"0c60"}
                                 completionBlock:^(id  _Nullable cacheObject) {                               
}];
```



Loading cache of network requests share the same request url:

```objective-c
[[SJNetworkManager sharedManager] loadCacheWithUrl:@"toutiao/index"
                                   completionBlock:^(NSArray * _Nullable cacheArr) {
}];
```



Loading cache of network requests which share the same request url and method:

```objective-c
[[SJNetworkManager sharedManager] loadCacheWithUrl:@"toutiao/index"
                                            method:@"POST"
                                   completionBlock:^(NSArray * _Nullable cacheArr) {
}];
```



#### Clearing Cache



Clearing cache of one specific network request:

```objective-c
[[SJNetworkManager sharedManager] clearCacheWithUrl:@"toutiao/index"
                                             method:@"POST"
                                         parameters:@{@"type":@"top",
                                                      @"key" :@"0c60"}
                                    completionBlock:^(BOOL isSuccess) {
}];
```





Clearing cache of network requests share the same request url:

```objective-c
[[SJNetworkManager sharedManager] clearCacheWithUrl:@"toutiao/index"
                                    completionBlock:^(BOOL isSuccess) {
}];
```





Clearing cache of network requests which share the same request url and method:

```objective-c
[[SJNetworkManager sharedManager] clearCacheWithUrl:@"toutiao/index"
                                             method:@"POST"
                                    completionBlock:^(BOOL isSuccess) {
}];
```



#### Calculating Cache

Calculating the size of cache folder:

```objective-c
[[SJNetworkManager sharedManager] calculateCacheSizeWithCompletionBlock:^(NSUInteger fileCount, NSUInteger totalSize, NSString *totalSizeString) {
        
        NSLog(@"file count :%lu and total size:%lu total size string:%@",(unsigned long)fileCount,(unsigned long)totalSize, totalSizeString);
        
}];
```



> **fileCount**:file counts in cache folder
>
> **totalSize**: size of cache folder(unit is byte)
>
> **totalSizeString**:size of cache folder (size of unit)  eg.``file count :5 and total size:1298609 total size string:1.2385 MB``







### Uploading Function

Uploading one image, original size:



```objective-c
[[SJNetworkManager sharedManager]  sendUploadImageRequest:@"api"
                                               parameters:nil
                                                    image:image_1
                                                     name:@"universe"
                                                 mimeType:@"png"
                                                 progress:^(NSProgress *uploadProgress) {
                                                        
  self.progressView.observedProgress = uploadProgress;

} success:^(id responseObject) {

} failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode, NSArray<UIImage *> *uploadFailedImages) {

}];
```



> Here, the mimeType can be jpg/JPG, png/PNG, jpeg/JPEG and if the user gives a wrong type, the mimeType will be jpg.



Uploading two images, compress ratio is 0.5(note that if the mineType is 'png' or 'PNG', the compressRatio will be useless):



```objective-c
[[SJNetworkManager sharedManager]  sendUploadImagesRequest:@"api"
                                                parameters:nil
                                                    images:@[image_1,image_2]
                                             compressRatio:0.5
                                                      name:@"images"
                                                  mimeType:@"jpg"
                                                  progress:^(NSProgress *uploadProgress) {

  self.progressView.observedProgress = uploadProgress;

} success:^(id responseObject) {

} failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode, NSArray<UIImage *> *uploadFailedImages) {
                                                    
}];
```





And if the server which is used for uploading is **different from** the server for ordinary requests, you can user this api:

```objective-c
[[SJNetworkManager sharedManager]  sendUploadImagesRequest:@"http://uploads.im/api"
                                               ignoreBaseUrl:YES
                                                  parameters:nil
                                                      images:@[image_1,image_2]
                                               compressRatio:0.5
                                                        name:@"images"
                                                    mimeType:@"jpg"
                                                    progress:^(NSProgress *uploadProgress) {

      self.progressView.observedProgress = uploadProgress;

  } success:^(id responseObject) {

  } failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode, NSArray<UIImage *> *uploadFailedImages) {

  }];
```



> Here, setting ignoreBaseUrl to be YES, and the request url should be complete download url.





### Downloading Function:


We support background downloading(using NSURLSessionDownloadTask object ) and none-background downloading(using NSURLSessionDataTask object) , resumable or none-resumable downloading.



|                            | resumable | none-resumable |
| -------------------------- | --------- | -------------- |
| background supporting      | ✅         | ✅              |
| none-background supporting | ✅         | ✅              |



> **Note**:If a none-background supporting downloading is on going then app enters into background, the downloading task will be canceled. And When app enters into foreground again, an ``auto-resume mechanism`` will make the downloading task restart again.





#### Sending download request



Resumable && none-background supporting download reqeust (default configuration):

```objective-c
[[SJNetworkManager sharedManager] sendDownloadRequest:@"wallpaper.jpg"
                                     downloadFilePath:_imageFileLocalPath
                                             progress:^(NSInteger receivedSize, NSInteger expectedSize, CGFloat progress)
{
       self.progressView.progress = progress;

} success:^(id responseObject) {

} failure:^(NSURLSessionTask *task, NSError *error, NSString *resumableDataPath) {

}];
```



None-resumable && none-background supporting download reqeust:

```objective-c
[[SJNetworkManager sharedManager] sendDownloadRequest:@"half-eatch.jpg"
                                     downloadFilePath:_imageFileLocalPath
                                            resumable:NO
                                    backgroundSupport:NO
                                             progress:^(NSInteger receivedSize, NSInteger expectedSize, CGFloat progress) 
{

    self.progressView.progress = progress;

} success:^(id responseObject) {

} failure:^(NSURLSessionTask *task, NSError *error, NSString *resumableDataPath) {
    
}];
```





Resumable && background supporting download request:

```objective-c
[[SJNetworkManager sharedManager] sendDownloadRequest:@"universe.jpg"
                                     downloadFilePath:_imageFileLocalPath
                                            resumable:YES
                                    backgroundSupport:YES
                                             progress:^(NSInteger receivedSize, NSInteger expectedSize, CGFloat progress)
{

    self.progressView.progress = progress;

} success:^(id responseObject) {

} failure:^(NSURLSessionTask *task, NSError *error, NSString *resumableDataPath) {

}];
```



None-resumable && background supporting download request:

```objective-c
[[SJNetworkManager sharedManager] sendDownloadRequest:@"iceberg.jpg"
                                     downloadFilePath:_imageFileLocalPath
                                            resumable:NO
                                    backgroundSupport:YES
                                             progress:^(NSInteger receivedSize, NSInteger expectedSize, CGFloat progress)
{

    self.progressView.progress = progress;

 } success:^(id responseObject) {

 } failure:^(NSURLSessionTask *task, NSError *error, NSString *resumableDataPath) {

 }];
```



Also supports ignoring base url:

```objective-c
[[SJNetworkManager sharedManager] sendDownloadRequest:@"http://oih3a9o4n.bkt.clouddn.com/wallpaper.jpg"
                                        ignoreBaseUrl:YES
                                     downloadFilePath:_imageFileLocalPath
                                             progress:^(NSInteger receivedSize, NSInteger expectedSize, CGFloat progress)
{
      self.progressView.progress = progress;

} success:^(id responseObject) {

} failure:^(NSURLSessionTask *task, NSError *error, NSString *resumableDataPath) {

}];
```



#### Suspending download request



Suspending one download  current request:

```objective-c
[[SJNetworkManager sharedManager] suspendDownloadRequest:@"universe.jpg"];
```



Suspending one or more than one current download requests:

```objective-c
[[SJNetworkManager sharedManager] suspendDownloadRequests:@[@"universe.jpg",@"wallpaper.jpg"]];
```



Suspending all current download requests:

```objective-c
[[SJNetworkManager sharedManager] suspendAllDownloadRequests];
```



#### Resuming download request



Resuming one download  suspended request:

```objective-c
[[SJNetworkManager sharedManager] resumeDownloadReqeust:@"universe.jpg"];
```



Resuming one or more than one download requests:

```objective-c
[[SJNetworkManager sharedManager] resumeDownloadReqeusts:@[@"universe.jpg",@"wallpaper.jpg"]];
```



Resuming all current suspended requests:

```objective-c
[[SJNetworkManager sharedManager] resumeAllDownloadRequests];
```





#### Canceling download request



Canceling one download request:

```objective-c
[[SJNetworkManager sharedManager] cancelDownloadRequest:@"universe.jpg"];
```



Canceling one or more than one current download requests:

```objective-c
[[SJNetworkManager sharedManager] cancelDownloadRequests:@[@"universe.jpg",@"wallpaper.jpg"]];
```



Canceling all current download requests:

```objective-c
[[SJNetworkManager sharedManager] cancelAllDownloadRequests];
```





### Request Management



#### Current request(s) Information



Checking if there is remaining current request(s):

```objective-c
BOOL remaining =  [[SJNetworkManager sharedManager] remainingCurrentRequests];
if (remaining) {
    NSLog(@"There is remaining request");
}
```



Calculating count of current request(s):

```objective-c
NSUInteger count = [[SJNetworkManager sharedManager] currentRequestCount];
if (count > 0) {
    NSLog(@"There is %lu requests",(unsigned long)count);
}
```



Logging all current request(s):

```objective-c
[[SJNetworkManager sharedManager] logAllCurrentRequests];
```



#### Canceling request



Canceling one network request:

```objective-c
[[SJNetworkManager sharedManager] cancelCurrentRequestWithUrl:@"toutiao/index"
                                                       method:@"POST"
                                                   parameters:@{@"type":@"top",
                                                                @"key" :@"0c60"}];
```



Canceling network request(s) with the same url:

```objective-c
[[SJNetworkManager sharedManager] cancelCurrentRequestWithUrl:@"toutiao/index"];
```



Canceling network request(s) with the same urls:

```objective-c
[[SJNetworkManager sharedManager] cancelDownloadRequests:@[@"toutiao/index",@"weixin/query"]];
```



Canceling all current network request(s):

```objective-c
[[SJNetworkManager sharedManager] cancelAllCurrentRequests];
```



 	

### Log Output



If debug mode is set to be yes, detail log will be provided:

```objective-c
[SJNetworkConfig sharedConfig].debugMode = YES;
```



Loading cache before sending network request, but cache is expired:

```objective-c
=========== Load cache info failed, reason:Cache is expired, begin to clear cache...
=========== Load cache failed: Cache info is invalid 
=========== Failed to load cache, start to sending network request...
=========== Start requesting...
=========== url:http://v.juhe.cn/toutiao/index
=========== method:GET
=========== parameters:{
    app_version = 1.0;
    key = 0c60;
    platform = iOS;
    type = top;
}
=========== Request succeed! 
=========== Request url:http://v.juhe.cn/toutiao/index
=========== Response object:{
  code = 200,
  msg = "",
  data = {}
}
=========== Write cache succeed!
=========== cache object: {
  code = 200,
  msg = "",
  data = {}
}
=========== Cache path: /Users/*******/
=========== Available duration: 180 seconds
```



## Acknowledgements



Thanks for these service:

- [JuHe.cn](https://www.juhe.cn/): GET/POST api
- [Uploads.im](http://uploads.im) : Uploading api
- [QINIU](https://portal.qiniu.com/): Multimedia cloud server(for downloading files)



And also thanks for these two excellent framework:

- [AFNetworking](https://github.com/AFNetworking/AFNetworking)
- [YTKNetwork](https://github.com/yuantiku/YTKNetwork)





## Lisence

SJNetwork is released under the [MIT License](https://github.com/knightsj/SJNetwork/blob/master/LICENSE).









================================================
FILE: SJNetwork/SJNetwork.h
================================================
//
//  SJNetwork.h
//  SJNetworkingDemo
//
//  Created by Sun Shijie on 2017/12/27.
//  Copyright © 2017年 Shijie. All rights reserved.
//

#ifndef SJNetwork_h
#define SJNetwork_h

#import "SJNetworkManager.h"
#import "SJNetworkConfig.h"

#endif /* SJNetwork_h */


================================================
FILE: SJNetwork/SJNetworkBaseEngine.h
================================================
//
//  SJNetworkBaseEngine.h
//  SJNetworkingDemo
//
//  Created by Sun Shijie on 2017/12/26.
//  Copyright © 2017年 Shijie. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "SJNetworkRequestModel.h"


@interface SJNetworkBaseEngine : NSObject


/**
 *  This method is used to add customed headers, for subclass to override
 */
- (void)addCustomHeaders;



/**
 *  This method is used to add default parameters with custom parameters, for subclass to override
 *
 *  @param parameters        custom parameters
 *
 */
- (id)addDefaultParametersWithCustomParameters:(id)parameters;




/**
 *  This method is used to execute some operation with the request model when the corresponding request succeed, for subclass to override
 *
 *  @param requestModel      request model of a network request
 *
 */
- (void)requestDidSucceedWithRequestModel:(SJNetworkRequestModel *)requestModel;



@end


================================================
FILE: SJNetwork/SJNetworkBaseEngine.m
================================================
//
//  SJNetworkBaseEngine.m
//  SJNetworkingDemo
//
//  Created by Sun Shijie on 2017/12/26.
//  Copyright © 2017年 Shijie. All rights reserved.
//

#import "SJNetworkBaseEngine.h"

@implementation SJNetworkBaseEngine


- (void)addCustomHeaders{
    
}



- (id)addDefaultParametersWithCustomParameters:(id)parameters{
    return nil;
}



- (void)requestDidSucceedWithRequestModel:(SJNetworkRequestModel *)requestModel{
    
    
}



- (void)requestDidFailedWithRequestModel:(SJNetworkRequestModel *)requestModel error:(NSError *)error{
    
    
}

@end


================================================
FILE: SJNetwork/SJNetworkCacheInfo.h
================================================
//
//  SJNetworkCacheInfo.h
//  SJNetworkingDemo
//
//  Created by Sun Shijie on 2017/11/25.
//  Copyright © 2017年 Shijie. All rights reserved.
//


#import <Foundation/Foundation.h>


/* =============================
 *
 * SJNetworkCacheInfo
 *
 * SJNetworkCacheInfo is in charge of recording the infomation of cache which is related to a specific network request
 *
 * =============================
 */

@interface SJNetworkCacheInfo : NSObject<NSSecureCoding>

// Record the creation date of the cache
@property (nonatomic, readwrite, strong) NSDate *creationDate;

// Record the length of the period of validity (unit is second)
@property (nonatomic, readwrite, strong) NSNumber *cacheDuration;

// Record the app version when the cache is created
@property (nonatomic, readwrite, copy)   NSString *appVersionStr;

// Record the request identifier of the cache
@property (nonatomic, readwrite, copy)   NSString *reqeustIdentifer;

@end


================================================
FILE: SJNetwork/SJNetworkCacheInfo.m
================================================
//
//  SJNetworkCacheInfo.m
//  SJNetworkingDemo
//
//  Created by Sun Shijie on 2017/11/25.
//  Copyright © 2017年 Shijie. All rights reserved.
//

#import "SJNetworkCacheInfo.h"

@implementation SJNetworkCacheInfo

+ (BOOL)supportsSecureCoding {
    return YES;
}

- (void)encodeWithCoder:(NSCoder *)aCoder{
    
    [aCoder encodeObject:self.cacheDuration forKey:NSStringFromSelector(@selector(cacheDuration))];
    [aCoder encodeObject:self.creationDate forKey:NSStringFromSelector(@selector(creationDate))];
    [aCoder encodeObject:self.appVersionStr forKey:NSStringFromSelector(@selector(appVersionStr))];
    [aCoder encodeObject:self.reqeustIdentifer forKey:NSStringFromSelector(@selector(reqeustIdentifer))];
}

- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
    
    self = [self init];
    
    if (self) {
        
        self.cacheDuration = [aDecoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(cacheDuration))];
        self.creationDate = [aDecoder decodeObjectOfClass:[NSDate class] forKey:NSStringFromSelector(@selector(creationDate))];
        self.appVersionStr = [aDecoder decodeObjectOfClass:[NSString class] forKey:NSStringFromSelector(@selector(appVersionStr))];
        self.reqeustIdentifer = [aDecoder decodeObjectOfClass:[NSString class] forKey:NSStringFromSelector(@selector(reqeustIdentifer))];
    }

    return self;
}

- (NSString *)description{

    return [NSString stringWithFormat:@"{cacheDuration:%@},{creationDate:%@},{appVersion:%@},{requestIdentifer:%@}",_cacheDuration,_creationDate,_appVersionStr,_reqeustIdentifer];
}

@end


================================================
FILE: SJNetwork/SJNetworkCacheManager.h
================================================
//
//  SJNetworkCache.h
//  SJNetwork
//
//  Created by Sun Shijie on 2017/8/16.
//  Copyright © 2017年 Shijie. All rights reserved.
//

#import <Foundation/Foundation.h>

@class SJNetworkRequestModel;
@class SJNetworkDownloadResumeDataInfo;


// Callback when the cache is cleared
typedef void(^SJClearCacheCompletionBlock)(BOOL isSuccess);

// Callback when the cache is loaded
typedef void(^SJLoadCacheCompletionBlock)(id _Nullable cacheObject);

// Callback when cache array is loaded
typedef void(^SJLoadCacheArrCompletionBlock)(NSArray * _Nullable cacheArr);

// Callback when the size of cache is calculated
typedef void(^SJCalculateSizeCompletionBlock)(NSUInteger fileCount, NSUInteger totalSize, NSString * _Nonnull totalSizeString);



/* =============================
 *
 * SJNetworkCacheManager
 *
 * SJNetworkCacheManager is in charge of managing operations of oridinary request cache(and cache info) and resume data (and resume data info)of a certain download request
 *
 * =============================
 */

@interface SJNetworkCacheManager : NSObject



/**
 *  SJNetworkCacheManager Singleton
 *
 *  @return SJNetworkCacheManager singleton instance
 */
+ (SJNetworkCacheManager *_Nonnull)sharedManager;





//============================ Write Cache ============================//

/**
 *  This method is used to write cache(cache data and cache info), 
    can only be called by SJNetworkManager instance
 *
 *  @param requestModel        the model holds the configuration of a specific request
 *  @param asynchronously      if write cache asynchronously
 *
 */
- (void)writeCacheWithReqeustModel:(SJNetworkRequestModel * _Nonnull)requestModel asynchronously:(BOOL)asynchronously;




//============================= Load cache =============================//


/**
 *  This method is used to load cache which is related to a specific url,
    no matter what request method is or parameters are
 *
 *
 *  @param url                  the url of related network requests
 *  @param completionBlock      callback
 *
 */
- (void)loadCacheWithUrl:(NSString * _Nonnull)url completionBlock:(SJLoadCacheArrCompletionBlock _Nullable)completionBlock;




- (void)loadCacheWithUrl:(NSString * _Nonnull)url
                  method:(NSString * _Nonnull)method
         completionBlock:(SJLoadCacheArrCompletionBlock _Nullable)completionBlock;



/**
 *  This method is used to load cache which is related to a specific url,method and parameters
 *
 *  @param url                  the url of the network request
 *  @param method               the method of the network request
 *  @param parameters           the parameters of the network request
 *  @param completionBlock      callback
 *
 */
- (void)loadCacheWithUrl:(NSString * _Nonnull)url
                  method:(NSString * _Nonnull)method
              parameters:(id _Nullable)parameters
         completionBlock:(SJLoadCacheCompletionBlock _Nullable)completionBlock;





/**
 *  This method is used to load cache which is related to a identier which is the unique to a network request,
    can only be called by SJNetworkManager instance
 *
 *  @param requestIdentifer     the unique identier of a specific  network request
 *  @param completionBlock      callback
 *
 */
- (void)loadCacheWithRequestIdentifer:(NSString * _Nonnull)requestIdentifer completionBlock:(SJLoadCacheCompletionBlock _Nullable)completionBlock;





//============================ calculate cache ============================//

/**
 *  This method is used to calculate the size of the cache folder (include ordinary request cache and download resume data file and resume data info file)
 *
 *  @param completionBlock      finish callback
 *
 */
- (void)calculateAllCacheSizecompletionBlock:(SJCalculateSizeCompletionBlock _Nullable)completionBlock;





//============================== clear cache ==============================//

/**
 *  This method is used to clear all cache which is in the cache folder
 *
 *  @param completionBlock      callback
 *
 */
- (void)clearAllCacheCompletionBlock:(SJClearCacheCompletionBlock _Nullable)completionBlock;




/**
 *  This method is used to clear the cache which is related the specific url,
    no matter what request method is or parameters are
 *
 *  @param url                   the url of network request
 *  @param completionBlock       callback
 *
 */
- (void)clearCacheWithUrl:(NSString * _Nonnull)url completionBlock:(SJClearCacheCompletionBlock _Nullable)completionBlock;




- (void)clearCacheWithUrl:(NSString * _Nonnull)url
                   method:(NSString * _Nonnull)method
          completionBlock:(SJClearCacheCompletionBlock _Nullable)completionBlock;


/**
 *  This method is used to clear cache which is related to a specific url,method and parameters
 *
 *  @param url                  the url of the network request
 *  @param method               the method of the network request
 *  @param parameters           the parameters of the network request
 *  @param completionBlock      callback
 *
 */
- (void)clearCacheWithUrl:(NSString * _Nonnull)url
                   method:(NSString * _Nonnull)method
               parameters:(id _Nullable)parameters
          completionBlock:(SJClearCacheCompletionBlock _Nullable)completionBlock;



//============================== Update resume data or resume data info ==============================//

/**
 *  This method is used to update resume data info after suspending a download request
 *
 *  @param requestModel      request model of a network requst
 *
 */
- (void)updateResumeDataInfoAfterSuspendWithRequestModel:(SJNetworkRequestModel *_Nonnull)requestModel;




/**
 *  This method is used to remove resume data and resume data info files
 *
 *  @param requestModel      request model of a network requst
 *
 */
- (void)removeResumeDataAndResumeDataInfoFileWithRequestModel:(SJNetworkRequestModel *_Nonnull)requestModel;





/**
 *  This method is used to remove download data to target download file path and clear the resume data info file
 *
 *  @param requestModel      request model of a network requst
 *
 */
- (void)removeCompleteDownloadDataAndClearResumeDataInfoFileWithRequestModel:(SJNetworkRequestModel *_Nonnull)requestModel;




//============================== Load resume data info ==============================//

/**
 *  This method is used to load resume data info in a given file path
 *
 *  @param filePath          file path
 *
 */
- (SJNetworkDownloadResumeDataInfo *_Nullable)loadResumeDataInfo:(NSString *_Nonnull)filePath;


@end


================================================
FILE: SJNetwork/SJNetworkCacheManager.m
================================================
//
//  SJNetworkCache.m
//  SJNetwork
//
//  Created by Sun Shijie on 2017/8/16.
//  Copyright © 2017年 Shijie. All rights reserved.
//

#import "SJNetworkCacheManager.h"
#import "SJNetworkRequestModel.h"
#import "SJNetworkConfig.h"
#import "SJNetworkUtils.h"
#import "SJNetworkCacheInfo.h"
#import "SJNetworkDownloadResumeDataInfo.h"



#ifndef NSFoundationVersionNumber_iOS_8_0
#define NSFoundationVersionNumber_With_QoS_Available 1140.11
#else
#define NSFoundationVersionNumber_With_QoS_Available NSFoundationVersionNumber_iOS_8_0
#endif


static dispatch_queue_t sj_cache_io_queue() {
    
    static dispatch_queue_t queue;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        dispatch_queue_attr_t attr = DISPATCH_QUEUE_SERIAL;
        if (NSFoundationVersionNumber >= NSFoundationVersionNumber_With_QoS_Available) {
            attr = dispatch_queue_attr_make_with_qos_class(attr, QOS_CLASS_BACKGROUND, 0);
        }
        queue = dispatch_queue_create("com.sj.caching.io", attr);
    });
    
    return queue;
}





@implementation SJNetworkCacheManager{
    
    NSFileManager *_fileManager;
    NSString *_cacheBasePath;
    BOOL _isDebugMode;
}


#pragma mark- ============== Life Cycle Methods ==============


+ (SJNetworkCacheManager *_Nonnull)sharedManager{
    
    static SJNetworkCacheManager *sharedManager = nil;
    static dispatch_once_t onceToken;
    
    dispatch_once(&onceToken, ^{
        sharedManager = [[SJNetworkCacheManager alloc] init];
    });
    return sharedManager;
}


- (instancetype)init{
    
    self = [super init];
    if (self) {
        
        _fileManager = [NSFileManager defaultManager];
        _cacheBasePath = [SJNetworkUtils createCacheBasePath];
        _isDebugMode = [SJNetworkConfig sharedConfig].debugMode;
        
    }
    return self;
}

//==================== Write Cache ====================//

#pragma mark- ============== Public Methods ==============


#pragma mark Write Cache

- (void)writeCacheWithReqeustModel:(SJNetworkRequestModel * _Nonnull)requestModel asynchronously:(BOOL)asynchronously{
    
    
    if (asynchronously) {
        
        dispatch_async(sj_cache_io_queue(), ^{
            [self p_wrtieCacheWithRequestModel:requestModel];
        });
        
    }else{
        
        [self p_wrtieCacheWithRequestModel:requestModel];
    }
}



#pragma mark Load Cache



- (void)loadCacheWithUrl:(NSString * _Nonnull)url completionBlock:(SJLoadCacheArrCompletionBlock _Nullable)completionBlock{
    
    NSString *partialIdentifier = [SJNetworkUtils generatePartialIdentiferWithBaseUrlStr:[SJNetworkConfig sharedConfig].baseUrl
                                                                           requestUrlStr:url
                                                                               methodStr:nil];
    
    [self p_loadCacheWithPartialIdentifier:partialIdentifier completionBlock:completionBlock];

}


- (void)loadCacheWithUrl:(NSString * _Nonnull)url
                  method:(NSString * _Nonnull)method
         completionBlock:(SJLoadCacheArrCompletionBlock _Nullable)completionBlock{
    
    
    NSString *partialIdentifier = [SJNetworkUtils generatePartialIdentiferWithBaseUrlStr:[SJNetworkConfig sharedConfig].baseUrl
                                                                           requestUrlStr:url
                                                                               methodStr:method];
    
    [self p_loadCacheWithPartialIdentifier:partialIdentifier completionBlock:completionBlock];
    
}




- (void)loadCacheWithUrl:(NSString * _Nonnull)url
                  method:(NSString * _Nonnull)method
              parameters:(id _Nullable)parameters
         completionBlock:(SJLoadCacheCompletionBlock _Nullable)completionBlock{

    NSString *requestIdentifer = [SJNetworkUtils generateRequestIdentiferWithBaseUrlStr:[SJNetworkConfig sharedConfig].baseUrl
                                                                          requestUrlStr:url
                                                                              methodStr:method
                                                                             parameters:parameters];
    
    [self loadCacheWithRequestIdentifer:requestIdentifer completionBlock:^(NSArray * _Nullable cacheArr) {
        
        if (completionBlock) {
            completionBlock(cacheArr);
        }
    
    }];
    
}



- (void)loadCacheWithRequestIdentifer:(NSString * _Nonnull)requestIdentifer
                      completionBlock:(SJLoadCacheCompletionBlock _Nullable)completionBlock{
    
    NSString *cacheDataFilePath = [SJNetworkUtils cacheDataFilePathWithRequestIdentifer:requestIdentifer];
    NSString *cacheInfoFilePath = [SJNetworkUtils cacheDataInfoFilePathWithRequestIdentifer:requestIdentifer];
    
    //load cache info
    SJNetworkCacheInfo *cacheInfo = [self p_loadCacheInfoWithRequestIdentifier:requestIdentifer];
    
    if (!cacheInfo) {
        
        if (_isDebugMode) {
            SJLog(@"=========== Load cache failed: Cache info dose not exists in path:%@",cacheInfoFilePath);
        }
        
        [self removeCacheDataFile:cacheDataFilePath cacheInfoFile:cacheInfoFilePath];
        
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completionBlock) {
                completionBlock(nil);
            }
        });
        
        return;
    }
    
    BOOL cacheValidation = [self p_checkCacheValidation:cacheInfo];
    
    if (!cacheValidation) {
        
        if (_isDebugMode) {
            SJLog(@"=========== Load cache failed: Cache info is invalid");
        }
        
        [self removeCacheDataFile:cacheDataFilePath cacheInfoFile:cacheInfoFilePath];
        
        
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completionBlock) {
                completionBlock(nil);
            }
        });
        
        return;
        
    }
    
    id cacheObject = [self p_loadCacheObjectWithCacheFilePath:cacheDataFilePath];
    
    if (!cacheObject) {
        
        if (_isDebugMode) {
            SJLog(@"=========== Load cache failed: Cache data is missing");
        }
        
        [self removeCacheDataFile:cacheDataFilePath cacheInfoFile:cacheInfoFilePath];
        
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completionBlock) {
                completionBlock(nil);
            }
        });
        
        return;
        
    }else {
        
        
        if (_isDebugMode) {
            SJLog(@"=========== Load cache succeed: Cache loacation:%@",cacheDataFilePath);
        }
        
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completionBlock) {
                completionBlock(cacheObject);
            }
        });
    }
}




#pragma mark Calculate Cache

- (void)calculateAllCacheSizecompletionBlock:(SJCalculateSizeCompletionBlock _Nullable)completionBlock{

    NSURL *diskCacheURL = [NSURL fileURLWithPath:_cacheBasePath isDirectory:YES];
    
    dispatch_async(sj_cache_io_queue(), ^{
        
        NSUInteger fileCount = 0;
        NSUInteger totalSize = 0;
        
        NSDirectoryEnumerator *fileEnumerator = [_fileManager enumeratorAtURL:diskCacheURL
                                                   includingPropertiesForKeys:@[NSFileSize]
                                                                      options:NSDirectoryEnumerationSkipsHiddenFiles
                                                                 errorHandler:NULL];
        
        for (NSURL *fileURL in fileEnumerator) {
            NSNumber *fileSize;
            [fileURL getResourceValue:&fileSize forKey:NSURLFileSizeKey error:NULL];
            totalSize += fileSize.unsignedIntegerValue;
            fileCount += 1;
        }
        
        NSString *totalSizeStr = nil;
        NSUInteger mb = 1024 *1024;
        if (totalSize <mb) {
            totalSizeStr = [NSString stringWithFormat:@"%.4f KB",(totalSize * 1.0/1024)];
        }else{
            totalSizeStr = [NSString stringWithFormat:@"%.4f MB",totalSize * 1.0/(mb)];
        }
        if (_isDebugMode) {
            SJLog(@"=========== Calculate cache size succeed:total fileCount:%ld & totalSize:%@",(unsigned long)fileCount,totalSizeStr);
        }
        if (completionBlock) {
            dispatch_async(dispatch_get_main_queue(), ^{                
                completionBlock(fileCount, totalSize, totalSizeStr);
            });
        }
    });

}


#pragma mark Clear Cache

- (void)clearAllCacheCompletionBlock:(SJClearCacheCompletionBlock _Nullable)completionBlock{

    dispatch_async(sj_cache_io_queue(), ^{
        
        NSError *removeCacheFolderError = nil;
        NSError *createCacheFolderError = nil;
        [_fileManager removeItemAtPath:_cacheBasePath error:&removeCacheFolderError];
        
        if (!removeCacheFolderError) {
            
            [_fileManager createDirectoryAtPath:_cacheBasePath
                    withIntermediateDirectories:YES
                                     attributes:nil
                                          error:&createCacheFolderError];
            
            if (!createCacheFolderError) {
                
                dispatch_async(dispatch_get_main_queue(), ^{
                SJLog(@"=========== Clearing all cache successfully");
                if (completionBlock) {
                        completionBlock(YES);
                        return;
                }
                });
            }else{
                
                dispatch_async(dispatch_get_main_queue(), ^{
                if (_isDebugMode) {
                    SJLog(@"=========== Clearing cache error: Failed to create cache folder after removing it");
                }
                if(completionBlock) {
                    
                        completionBlock(NO);
                        return;
                      }
                });
              
            }
        }else{
            
            dispatch_async(dispatch_get_main_queue(), ^{
                if (_isDebugMode) {
                    SJLog(@"=========== Clearing cache error: Failed to remove cache folder");
                }
                if (completionBlock) {
                    completionBlock(NO);
                    return;
                }
            });
               
        };
    });
}



- (void)clearCacheWithUrl:(NSString * _Nonnull)url completionBlock:(SJClearCacheCompletionBlock _Nullable)completionBlock{

    
    NSString *partiticalIdentifier = [SJNetworkUtils generatePartialIdentiferWithBaseUrlStr:[SJNetworkConfig sharedConfig].baseUrl
                                                                              requestUrlStr:url
                                                                                  methodStr:nil];
    
    [self p_clearCacheWithIdentifier:partiticalIdentifier completionBlock:completionBlock];
    
}




- (void)clearCacheWithUrl:(NSString * _Nonnull)url
                   method:(NSString * _Nonnull)method
          completionBlock:(SJClearCacheCompletionBlock _Nullable)completionBlock{
    
    NSString *partiticalIdentifier = [SJNetworkUtils generatePartialIdentiferWithBaseUrlStr:[SJNetworkConfig sharedConfig].baseUrl
                                                                              requestUrlStr:url
                                                                                  methodStr:method];
    
    [self p_clearCacheWithIdentifier:partiticalIdentifier completionBlock:completionBlock];
}




- (void)clearCacheWithUrl:(NSString * _Nonnull)url
                   method:(NSString * _Nonnull)method
               parameters:(id _Nullable)parameters
          completionBlock:(SJClearCacheCompletionBlock _Nullable)completionBlock{

    NSString *requestIdentifer = [SJNetworkUtils generateRequestIdentiferWithBaseUrlStr:[SJNetworkConfig sharedConfig].baseUrl
                                                                          requestUrlStr:url
                                                                              methodStr:method
                                                                             parameters:parameters];
    
    [self p_clearCacheWithIdentifier:requestIdentifer completionBlock:completionBlock];
    
}



#pragma mark Update resume data or resume data info

- (void)updateResumeDataInfoAfterSuspendWithRequestModel:(SJNetworkRequestModel *_Nonnull)requestModel{
    
    NSData *resumeData = requestModel.task.error.userInfo[NSURLSessionDownloadTaskResumeData];
    [resumeData writeToFile:requestModel.resumeDataFilePath options:NSDataWritingAtomic error:nil];
    
    int64_t downloadedByte = requestModel.task.countOfBytesReceived;
    int64_t totalByte = requestModel.task.countOfBytesExpectedToReceive;
    CGFloat percent = 1.0 *downloadedByte/totalByte;
    SJNetworkDownloadResumeDataInfo *dataInfo = [self loadResumeDataInfo:requestModel.resumeDataInfoFilePath];
    dataInfo.resumeDataLength = [NSString stringWithFormat:@"%lld",downloadedByte];
    dataInfo.totalDataLength = [NSString stringWithFormat:@"%lld",totalByte];
    dataInfo.resumeDataRatio = [NSString stringWithFormat:@"%.2f",percent];
    [NSKeyedArchiver archiveRootObject:dataInfo toFile:requestModel.resumeDataInfoFilePath];
}




- (void)removeResumeDataAndResumeDataInfoFileWithRequestModel:(SJNetworkRequestModel *_Nonnull)requestModel{
    
    [_fileManager removeItemAtPath:requestModel.resumeDataFilePath error:nil];
    [_fileManager removeItemAtPath:requestModel.resumeDataInfoFilePath error:nil];
    
}



- (void)removeCompleteDownloadDataAndClearResumeDataInfoFileWithRequestModel:(SJNetworkRequestModel *_Nonnull)requestModel{
    
    NSError *moveFileError = nil;
    [_fileManager moveItemAtPath:requestModel.resumeDataFilePath toPath:requestModel.downloadFilePath error:&moveFileError];
    if (moveFileError.code == 516) {
        [_fileManager removeItemAtPath:requestModel.resumeDataFilePath error:nil];
    }
    [_fileManager removeItemAtPath:requestModel.resumeDataInfoFilePath error:nil];
    
}



- (void)removeCacheDataFile:(NSString *)cacheDataFilePath cacheInfoFile:(NSString *)cacheInfoFilePath{
    
    if([_fileManager fileExistsAtPath:cacheDataFilePath]){
        [_fileManager removeItemAtPath:cacheDataFilePath error:nil];
    }
    
    if([_fileManager fileExistsAtPath:cacheInfoFilePath]){
        [_fileManager removeItemAtPath:cacheInfoFilePath error:nil];
    }
    
}

#pragma mark load resume data info


- (SJNetworkDownloadResumeDataInfo *)loadResumeDataInfo:(NSString *)filePath {
    
    SJNetworkDownloadResumeDataInfo *dataInfo = nil;
    if ([_fileManager fileExistsAtPath:filePath isDirectory:nil]) {
        dataInfo = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
        if ([dataInfo isKindOfClass:[SJNetworkDownloadResumeDataInfo class]]) {
            return dataInfo;
        }else{
            return nil;
        }
    }
    return nil;
}





#pragma mark- ============== Private Methods ==============


- (void)p_wrtieCacheWithRequestModel:(SJNetworkRequestModel *)requestModel{
    
    if (requestModel.responseData) {
        
        //path of cache file
        [requestModel.responseData writeToFile:requestModel.cacheDataFilePath atomically:YES];
        
        //write cache info data
        SJNetworkCacheInfo *cacheInfo = [[SJNetworkCacheInfo alloc] init];
        cacheInfo.creationDate = [NSDate date];
        cacheInfo.cacheDuration = [NSNumber numberWithInteger:requestModel.cacheDuration];
        cacheInfo.appVersionStr = [SJNetworkUtils appVersionStr];
        cacheInfo.reqeustIdentifer = requestModel.requestIdentifer;
        
        //write cache info
        [NSKeyedArchiver archiveRootObject:cacheInfo toFile:requestModel.cacheDataInfoFilePath];
        
        if (_isDebugMode) {
            SJLog(@"=========== Write cache succeed!\n =========== cache object: %@\n =========== Cache path: %@\n =========== Available duration: %@ seconds",requestModel.responseObject,requestModel.cacheDataFilePath,cacheInfo.cacheDuration);
        }
        
    }else{
        if (_isDebugMode) {
            SJLog(@"=========== Write cache failed! reason: There is no responeseData");
        }
    }
}

- (void)p_loadCacheWithPartialIdentifier:(NSString *)partialIdentifier completionBlock:(SJLoadCacheArrCompletionBlock _Nullable)completionBlock{
    
    
    NSDirectoryEnumerator *enumerator = [_fileManager enumeratorAtPath:_cacheBasePath];
    NSMutableArray *requestIdentifersArr = [[NSMutableArray alloc] initWithCapacity:2];
    
    
    for (NSString *fileName in enumerator){
        
        if ([fileName containsString:partialIdentifier]) {
            
            if ([fileName containsString:SJNetworkCacheFileSuffix]) {
                
                NSString *identifier = [fileName substringWithRange:NSMakeRange(0, (fileName.length - SJNetworkCacheFileSuffix.length - 1))];
                [requestIdentifersArr addObject:identifier];
                
            }else{
                //do not match cache data file
            }
        }
    }
    
    if ([requestIdentifersArr count] > 0) {
        NSMutableArray *cacheObjArr = [[NSMutableArray alloc] initWithCapacity:2];
        
        for (NSString* requestIdentifer in requestIdentifersArr) {
            [self loadCacheWithRequestIdentifer:requestIdentifer completionBlock:^(id  _Nullable cacheObject) {
                if (cacheObject) {
                    [cacheObjArr addObject:cacheObject];
                }
            }];
        }
        
        if (_isDebugMode) {
            SJLog(@"=========== Load cache succeed: Found cache corresponding this url");
        }
        
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completionBlock) {
                completionBlock([cacheObjArr copy]);
            }
        });
        
        
        
    }else{
        
        if (_isDebugMode) {
            SJLog(@"=========== Load cache failed: There is no any cache corresponding this url");
        }
        
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completionBlock) {
                completionBlock(nil);
            }
        });
    }
    
}


- (BOOL)p_checkCacheValidation:(SJNetworkCacheInfo *)cacheInfo{
    
    if (!cacheInfo || ![cacheInfo isKindOfClass:[SJNetworkCacheInfo class]]) {
        return NO;
    }
    
    //check duration
    NSDate *creationDate = cacheInfo.creationDate;
    NSTimeInterval pastDuration = - [creationDate timeIntervalSinceNow];
    NSTimeInterval cacheDuration = [cacheInfo.cacheDuration integerValue];
    
    if (cacheDuration <= 0 ) {
        if (_isDebugMode) {
            SJLog(@"=========== Load cache info failed, reason: Did not set duration time, begin to clear cache...");
        }
        [self p_clearCacheWithIdentifier:cacheInfo.reqeustIdentifer completionBlock:nil];
        return NO;
    }
    
    
    if (pastDuration < 0 || pastDuration > cacheDuration) {
        
        if (_isDebugMode) {
            SJLog(@"=========== Load cache info failed, reason:Cache is expired, begin to clear cache...");
        }
        
        [self p_clearCacheWithIdentifier:cacheInfo.reqeustIdentifer completionBlock:nil];
        return NO;
    }
    
    
    //check app version
    NSString *cacheAppVersionStr = cacheInfo.appVersionStr;
    NSString *currentAppVersionStr = [SJNetworkUtils appVersionStr];
    
    if ( (!cacheAppVersionStr) && (!currentAppVersionStr)) {
        if (_isDebugMode) {
            SJLog(@"=========== Load cache info failed, reason: Failed to load app version, begin to clear cache...");
        }
        [self p_clearCacheWithIdentifier:cacheInfo.reqeustIdentifer completionBlock:nil];
        return NO;
    }
    
    if (cacheAppVersionStr.length != currentAppVersionStr.length || ![cacheAppVersionStr isEqualToString:currentAppVersionStr]) {
        if (_isDebugMode) {
            SJLog(@"=========== Load cache info failed, reason: Failed to match app version, begin to clear cache...");
        }
        [self p_clearCacheWithIdentifier:cacheInfo.reqeustIdentifer completionBlock:nil];
        return NO;
    }
    
    return YES;
    
}



- (void)p_clearCacheWithIdentifier:(NSString *)identifier completionBlock:(SJClearCacheCompletionBlock _Nullable)completionBlock{
    
    
    NSMutableArray *deleteFileNamesArr = [[NSMutableArray alloc] initWithCapacity:2];
    NSDirectoryEnumerator *enumerator = [_fileManager enumeratorAtPath:_cacheBasePath];
    
    for (NSString *fileName in enumerator){
        if ([fileName containsString:identifier]) {
            NSString *deleteFilePath = [_cacheBasePath stringByAppendingPathComponent:fileName];
            [deleteFileNamesArr addObject:deleteFilePath];
        }
    }
    
    if ([deleteFileNamesArr count] > 0) {
        
        for (NSInteger index = 0; index < deleteFileNamesArr.count; index++) {
            
            dispatch_async(sj_cache_io_queue(), ^{
                
                [_fileManager removeItemAtPath:deleteFileNamesArr[index] error:nil];
                
                if (index == deleteFileNamesArr.count - 1) {
                    
                    dispatch_async(dispatch_get_main_queue(), ^{
                        if (_isDebugMode) {
                            SJLog(@"=========== Clearing cache successfully!");
                        }
                        if (completionBlock) {
                            completionBlock(YES);
                            return;
                        }
                    });
                }
            });
            
        }
        
    }else{
        
        dispatch_async(dispatch_get_main_queue(), ^{
            if (_isDebugMode) {
                SJLog(@"=========== Clearing cache error: there is no corresponding cache info");
            }
            if (completionBlock) {
                completionBlock(NO);
                return;
            }
        });
        
    }
    
}



- (id)p_loadCacheObjectWithCacheFilePath:(NSString *)cacheFilePath{
    
    id cacheObject = nil;
    NSError *error = nil;
    
    if ([_fileManager fileExistsAtPath:cacheFilePath isDirectory:nil]) {
        NSData *data = [NSData dataWithContentsOfFile:cacheFilePath];
        cacheObject = [NSJSONSerialization JSONObjectWithData:data options:(NSJSONReadingOptions)0 error:&error];
        if (cacheObject) {
            return cacheObject;
        }
    }
    return cacheObject;
}



- (SJNetworkCacheInfo *)p_loadCacheInfoWithRequestIdentifier:(NSString *)requestIdentifer {
    
    NSString *cacheInfoFilePath = [SJNetworkUtils cacheDataInfoFilePathWithRequestIdentifer:requestIdentifer];
    
    SJNetworkCacheInfo *cacheInfo = nil;
    if ([_fileManager fileExistsAtPath:cacheInfoFilePath isDirectory:nil]) {
        
        cacheInfo = [NSKeyedUnarchiver unarchiveObjectWithFile:cacheInfoFilePath];
        if ([cacheInfo isKindOfClass:[SJNetworkCacheInfo class]]) {
            return cacheInfo;
        }else{
            return nil;
        }
    }
    return nil;
}


@end


================================================
FILE: SJNetwork/SJNetworkConfig.h
================================================
//
//  SJNetworkConfig.h
//  SJNetwork
//
//  Created by Sun Shijie on 2017/8/16.
//  Copyright © 2017年 Shijie. All rights reserved.
//

#import <Foundation/Foundation.h>

/* =============================
 *
 * SJNetworkConfig
 *
 * SJNetworkConfig is in charge of the configuration of all related network requests
 *
 * =============================
 */

@interface SJNetworkConfig : NSObject

// Base url of requests, default is nil
@property (nonatomic, strong) NSString *_Nullable baseUrl;

// Default parameters, default is nil
@property (nonatomic, strong) NSDictionary * _Nullable defailtParameters;

// Custom headers, default is nil
@property (nonatomic, readonly, strong) NSDictionary * _Nullable customHeaders;

// Request timeout seconds, default is 20 (unit is second)
@property (nonatomic, assign) NSTimeInterval timeoutSeconds;

// If debugMode is set to be YES, then print all detail log
@property (nonatomic, assign) BOOL debugMode;



/**
 *  SJNetworkConfig Singleton
 *
 *  @return sharedConfig singleton instance
 */
+ (SJNetworkConfig *_Nullable)sharedConfig;



/**
 *  This method is used to add request headers (key-value pair(or pairs))
 *
 *  @param header               custom header to be added into request
 *
 */
- (void)addCustomHeader:(NSDictionary *_Nonnull)header;


@end


================================================
FILE: SJNetwork/SJNetworkConfig.m
================================================
//
//  SJNetworkConfig.m
//  SJNetwork
//
//  Created by Sun Shijie on 2017/8/16.
//  Copyright © 2017年 Shijie. All rights reserved.
//

#import "SJNetworkConfig.h"

@interface SJNetworkConfig()

@property (nonatomic, readwrite, strong) NSDictionary *customHeaders;

@end

@implementation SJNetworkConfig

+ (SJNetworkConfig *)sharedConfig {
    
    static SJNetworkConfig *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
        sharedInstance.timeoutSeconds = 20;
    });
    return sharedInstance;
}


- (void)addCustomHeader:(NSDictionary *)header{
    
    if (![header isKindOfClass:[NSDictionary class]]) {
        return;
    }
    
    if ([[header allKeys] count] == 0) {
        return;
    }
    
    if (!_customHeaders) {
         _customHeaders = header;
        return;
    }
    
    //add custom header
    NSMutableDictionary *headers_m = [_customHeaders mutableCopy];
    [header enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL * _Nonnull stop) {
        [headers_m setObject:value forKey:key];
    }];
    
    _customHeaders = [headers_m copy];
    
}


@end


================================================
FILE: SJNetwork/SJNetworkDownloadEngine.h
================================================
//
//  SJNetworkDownloadManager.h
//  SJNetworkingDemo
//
//  Created by Sun Shijie on 2017/11/25.
//  Copyright © 2017年 Shijie. All rights reserved.
//

#import <Foundation/Foundation.h>
#import "SJNetworkBaseEngine.h"


/* =============================
 *
 * SJNetworkDownloadEngine
 *
 * SJNetworkDownloadEngine is in charge of sending downloading requests.
 *
 * =============================
 */

@interface SJNetworkDownloadEngine : SJNetworkBaseEngine


//========================= Request API upload images ==========================//



/**
 *  This method offers the most number of parameters of a certain download request.
 
 *  @note:
 *        1. All the other download file API will call this method.
 *
 *        2. If 'ignoreBaseUrl' is set to be YES, the base url which is holden by
 *           SJNetworkConfig will be ignored, so the 'url' will be the complete request
 *           url of this request.(default is set to be YES)
 *
 *        3. If 'resumable' is set to be YES, incomplete download data will be saved and
 *           when the same request is sent, the saved data will be used.(default is set to be YES)
 *
 *  @param url                      request url
 *  @param ignoreBaseUrl            consider whether to ignore configured base url
 *  @param downloadFilePath         target path of download file
 *  @param resumable                consider whether to save or load resumble data
 *  @param downloadProgressBlock    download progress callback
 *  @param downloadSuccessBlock     download success callback
 *  @param downloadFailureBlock     download failure callback
 *
 */
- (void)sendDownloadRequest:(NSString * _Nonnull)url
              ignoreBaseUrl:(BOOL)ignoreBaseUrl
           downloadFilePath:(NSString *_Nonnull)downloadFilePath
                  resumable:(BOOL)resumable
          backgroundSupport:(BOOL)backgroundSupport
                   progress:(SJDownloadProgressBlock _Nullable)downloadProgressBlock
                    success:(SJDownloadSuccessBlock _Nullable)downloadSuccessBlock
                    failure:(SJDownloadFailureBlock _Nullable)downloadFailureBlock;





/**
 *  This method is used to suspend all current download requests
 */
- (void)suspendAllDownloadRequests;




/**
 *  This method is used to suspend a download request with given url
 *
 *  @param url                   download url
 *
 */
- (void)suspendDownloadRequest:(NSString * _Nonnull)url;




/**
 *  This method is used to suspend a download request with given url which contains the baseUrl or not
 *
 *  @param url                   download url
 *  @param ignoreBaseUrl         ignore baseUrl or not
 *
 */
- (void)suspendDownloadRequest:(NSString * _Nonnull)url ignoreBaseUrl:(BOOL)ignoreBaseUrl;




/**
 *  This method is used to suspend one nor more download requests with given urls array
 *
 *  @param urls                   download url array
 *
 */
- (void)suspendDownloadRequests:(NSArray *_Nonnull)urls;




/**
 *  This method is used to suspend one nor more download requests with given urls which contain the baseUrl or not
 *
 *  @param urls                   download url array
 *  @param ignoreBaseUrl          ignore baseUrl or not
 *
 */
- (void)suspendDownloadRequests:(NSArray *_Nonnull)urls ignoreBaseUrl:(BOOL)ignoreBaseUrl;





/**
 *  This method is used to resume all suspended download requests
 */
- (void)resumeAllDownloadRequests;





/**
 *  This method is used to resume a suspended request with given url
 *
 *  @param url                   download url
 *
 */
- (void)resumeDownloadReqeust:(NSString *_Nonnull)url;




/**
 *  This method is used to resume a suspended request with given url which contains the baseUrl or not
 *
 *  @param url                   download url
 *  @param ignoreBaseUrl         ignore baseUrl or not
 *
 */
- (void)resumeDownloadReqeust:(NSString *_Nonnull)url ignoreBaseUrl:(BOOL)ignoreBaseUrl;




/**
 *  This method is used to resume one nor more suspended requests with given urls array
 *
 *  @param urls                   download url array
 *
 */
- (void)resumeDownloadReqeusts:(NSArray *_Nonnull)urls;




/**
 *  This method is used to suspend one nor more suspended requests with given urls which contain the baseUrl or not
 *
 *  @param urls                   download url array
 *  @param ignoreBaseUrl          ignore baseUrl or not
 *
 */
- (void)resumeDownloadReqeusts:(NSArray *_Nonnull)urls ignoreBaseUrl:(BOOL)ignoreBaseUrl;





/**
 *  This method is used to cancel all current download requests
 */
- (void)cancelAllDownloadRequests;





/**
 *  This method is used to cancel a current download request with given url
 *
 *  @param url                   download url
 *
 */
- (void)cancelDownloadRequest:(NSString * _Nonnull)url;





/**
 *  This method is used to cancel a current download request with given url which contains the baseUrl or not
 *
 *  @param url                   download url
 *  @param ignoreBaseUrl         ignore baseUrl or not
 *
 */
- (void)cancelDownloadRequest:(NSString * _Nonnull)url ignoreBaseUrl:(BOOL)ignoreBaseUrl;





/**
 *  This method is used to cancel one nor more current download requests with given urls array
 *
 *  @param urls                   download url array
 *
 */
- (void)cancelDownloadRequests:(NSArray *_Nonnull)urls;




/**
 *  This method is used to cancel one nor more current download requests with given urls which contain the baseUrl or not
 *
 *  @param urls                   download url array
 *  @param ignoreBaseUrl          ignore baseUrl or not
 *
 */
- (void)cancelDownloadRequests:(NSArray *_Nonnull)urls ignoreBaseUrl:(BOOL)ignoreBaseUrl;






/**
 *  This method is used to get incomplete download data ratio of a download request with a given download url
 *
 *  @param url                    download url
 *
 *  @return incomplete download data ratio
 */
- (CGFloat)resumeDataRatioOfRequest:(NSString *_Nonnull)url;





/**
 *  This method is used to get incomplete download data ratio of a download request with a given download url which contains the baseUrl or not
 *
 *  @param url                    download url
 *  @param ignoreBaseUrl          ignore baseUrl or not
 *
 *  @return incomplete download data ratio
 */
- (CGFloat)resumeDataRatioOfRequest:(NSString *_Nonnull)url ignoreBaseUrl:(BOOL)ignoreBaseUrl;


@end


================================================
FILE: SJNetwork/SJNetworkDownloadEngine.m
================================================
//
//  SJNetworkDownloadManager.m
//  SJNetworkingDemo
//
//  Created by Sun Shijie on 2017/11/25.
//  Copyright © 2017年 Shijie. All rights reserved.
//

#import "SJNetworkDownloadEngine.h"
#import "SJNetworkDownloadResumeDataInfo.h"
#import "SJNetworkCacheManager.h"
#import "SJNetworkRequestPool.h"
#import "SJNetworkConfig.h"
#import "SJNetworkUtils.h"
#import "SJNetworkProtocol.h"


@interface SJNetworkDownloadEngine()<NSURLSessionDelegate,NSURLSessionDownloadDelegate,SJNetworkProtocol>

@property (nonatomic, strong) NSURLSession *downloadSession;
@property (nonatomic, strong) NSURLSession *backgroundDownloadSession;

@property (nonatomic, strong) SJNetworkCacheManager *cacheManager;

@end


@implementation SJNetworkDownloadEngine
{
    NSFileManager *_fileManager;
    BOOL _isDebugMode;
}

#pragma mark- ============== Life Cycle Methods ==============


- (instancetype)init{
    
    self = [super init];
    if (self) {
        
        //file  manager
        _fileManager = [NSFileManager defaultManager];
        
        //debug mode
        _isDebugMode = [SJNetworkConfig sharedConfig].debugMode;
        
        //cache manager
        _cacheManager = [SJNetworkCacheManager sharedManager];
    }
    
    return self;
}


- (void)dealloc{

    [_backgroundDownloadSession invalidateAndCancel];
    [_backgroundDownloadSession resetWithCompletionHandler:^{
        
    }];
    
    [_downloadSession invalidateAndCancel];
    [_downloadSession resetWithCompletionHandler:^{
        
    }];
    
}


#pragma mark- ============== Setter and Getter ==============

- (NSURLSession *)downloadSession
{
    static NSURLSession *downloadSession = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSURLSessionConfiguration* sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
        sessionConfig.timeoutIntervalForRequest = [SJNetworkConfig sharedConfig].timeoutSeconds;
        downloadSession = [NSURLSession sessionWithConfiguration:sessionConfig
                                                        delegate:self
                                                   delegateQueue:[NSOperationQueue mainQueue]];
      
 
    });
    return downloadSession;
    
}



- (NSURLSession *)backgroundDownloadSession
{
    static NSURLSession *backgroundDownloadSession = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSString *identifier = @"SJNetworkBackgroundSession";
        NSURLSessionConfiguration* sessionConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier];
        sessionConfig.timeoutIntervalForRequest = [SJNetworkConfig sharedConfig].timeoutSeconds;
        backgroundDownloadSession = [NSURLSession sessionWithConfiguration:sessionConfig
                                                                  delegate:self
                                                             delegateQueue:[NSOperationQueue mainQueue]];
    });
    return backgroundDownloadSession;
    
}


#pragma mark- ============== Public Methods ==============

#pragma mark ============== Download API ==============


- (void)sendDownloadRequest:(NSString * _Nonnull)url
              ignoreBaseUrl:(BOOL)ignoreBaseUrl
           downloadFilePath:(NSString *_Nonnull)downloadFilePath
                  resumable:(BOOL)resumable
          backgroundSupport:(BOOL)backgroundSupport
                   progress:(SJDownloadProgressBlock _Nullable)downloadProgressBlock
                    success:(SJDownloadSuccessBlock _Nullable)downloadSuccessBlock
                    failure:(SJDownloadFailureBlock _Nullable)downloadFailureBlock{
    
    
    //complete request url
    NSString *completeUrlStr = nil;
    
    // a unique identifer of a download request
    NSString *requestIdentifer = nil;
    
    if (ignoreBaseUrl) {
        
        completeUrlStr   = url;
        requestIdentifer = [SJNetworkUtils generateDownloadRequestIdentiferWithBaseUrlStr:nil
                                                                            requestUrlStr:url];
    }else{
        
        completeUrlStr   = [[SJNetworkConfig sharedConfig].baseUrl stringByAppendingPathComponent:url];
        requestIdentifer = [SJNetworkUtils generateDownloadRequestIdentiferWithBaseUrlStr:[SJNetworkConfig sharedConfig].baseUrl
                                                                            requestUrlStr:url];
    }
    
    //Check if a download task with same download url exists
    __block BOOL sameUrlTaskExists = NO;
    if ([[SJNetworkRequestPool sharedPool] remainingCurrentRequests]) {
        [[SJNetworkRequestPool sharedPool].currentRequestModels enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, SJNetworkRequestModel * _Nonnull requestModel, BOOL * _Nonnull stop) {
            if ([requestModel.requestUrl isEqualToString:completeUrlStr]) {
                sameUrlTaskExists = YES;
                SJLog(@"=========== Download request can not be started, since there is a task with the same download url:%@",completeUrlStr);
                return;
            }
        }];
    }
    if (sameUrlTaskExists) {
        return;
    }
    
    NSString *downloadTargetFilePath = nil;
    BOOL isDirectory;
    if(![[NSFileManager defaultManager] fileExistsAtPath:downloadFilePath isDirectory:&isDirectory]) {
        isDirectory = NO;
    }
    
    // If given downloadFilePath is a directory, then append it with file name to get downloadTargetFilePath(download file path)
    if (isDirectory) {
        NSString *fileName = [completeUrlStr lastPathComponent];
        downloadTargetFilePath = [NSString pathWithComponents:@[downloadFilePath, fileName]];
    } else {
        downloadTargetFilePath = downloadFilePath;
    }
    
    
    
    //remove same file in target download path
    if ([_fileManager fileExistsAtPath:downloadTargetFilePath]) {
        [_fileManager removeItemAtPath:downloadTargetFilePath error:nil];
    }
    
    
    
    
    //default method is GET
    NSString *methodStr = @"GET";
    
    //create corresponding request model and send request with it
    SJNetworkRequestModel *requestModel = [[SJNetworkRequestModel alloc] init];
    requestModel.requestUrl = completeUrlStr;
    requestModel.method = methodStr;
    requestModel.requestIdentifer = requestIdentifer;
    requestModel.downloadFilePath = downloadTargetFilePath;
    requestModel.resumableDownload = resumable;
    requestModel.backgroundDownloadSupport = backgroundSupport;
    requestModel.manualOperation = SJDownloadManualOperationStart;
    requestModel.downloadSuccessBlock = downloadSuccessBlock;
    requestModel.downloadProgressBlock = downloadProgressBlock;
    requestModel.downloadFailureBlock = downloadFailureBlock;
    
    NSURLSessionTask *downloadTask = nil;
    
    if (requestModel.backgroundDownloadSupport) {
        
        //downloadTask class : NSURLSessionDownloadTask
        downloadTask = [self p_backgroundDownloadTaskWithRequestModel:requestModel];
        
    }else{
        
        //downloadTask class : NSURLSessionDataTask
        downloadTask = [self p_noneBackgroundDownloadTaskWithRequestModel:requestModel];
    }
    
    //keep task in request model
    requestModel.task = downloadTask;
    
    //move this request model into requests set
    [[SJNetworkRequestPool sharedPool] addRequestModel:requestModel];
    
    SJLog(@"=========== start downloading:\n =========== url:%@\n =========== downloadPath:%@",completeUrlStr,requestModel.downloadFilePath);
    
    
    //start request
    [downloadTask resume];
    
    
}

#pragma mark ============== Suspend API ==============


- (void)suspendAllDownloadRequests{
    
    if(![[SJNetworkRequestPool sharedPool] remainingCurrentRequests]){
        return;
    }
    
    __block BOOL hasDownloadRequests = NO;
    [[SJNetworkRequestPool sharedPool].currentRequestModels enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, SJNetworkRequestModel * _Nonnull requestModel, BOOL * _Nonnull stop) {
        
        if (requestModel.requestType == SJRequestTypeDownload) {
            hasDownloadRequests = YES;
            
            if (requestModel.task) {
                
                if (requestModel.task.state == NSURLSessionTaskStateRunning) {
                    
                    if (requestModel.backgroundDownloadSupport) {
                        
                        requestModel.manualOperation = SJDownloadManualOperationSuspend;
                        NSURLSessionDownloadTask *downloadTask = (NSURLSessionDownloadTask*)requestModel.task;
                        [downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
                        }];
                        
                    }else{
                        
                        [requestModel.task suspend];
                        [_cacheManager updateResumeDataInfoAfterSuspendWithRequestModel:requestModel];
                        SJLog(@"=========== Suspended request:%@",requestModel);
                        
                    }
                    
                    
                }else {
                    SJLog(@"=========== Request %@ can not be suspended,since it is not running",requestModel);
                }
                
            }else {
                SJLog(@"=========== There is no donwload task of this request");
            }
        }
    }];
    
    if (!hasDownloadRequests) {
        SJLog(@"=========== There is no donwload task to suspend");
    }
}



- (void)suspendDownloadRequest:(NSString * _Nonnull)url;{
    
    if (url.length == 0) {
        if (_isDebugMode) {
            SJLog(@"=========== The input url is an empty string!");
        }
        return;
    }
    
    if(![[SJNetworkRequestPool sharedPool] remainingCurrentRequests]){
        return;
    }
    
    //a unique identifer of a download request
    NSString *downloadRequestIdentifier =  [SJNetworkUtils generateDownloadRequestIdentiferWithBaseUrlStr:[SJNetworkConfig sharedConfig].baseUrl
                                                                                            requestUrlStr:url];
    [self p_suspendDownloadRequestWithDownloadRequestIdentifier:downloadRequestIdentifier];
    
}


- (void)suspendDownloadRequest:(NSString * _Nonnull)url ignoreBaseUrl:(BOOL)ignoreBaseUrl{
    
    
    if (url.length == 0) {
        if (_isDebugMode) {
            SJLog(@"=========== The input url is an empty string!");
        }
        return;
    }
    
    if(![[SJNetworkRequestPool sharedPool] remainingCurrentRequests]){
        return;
    }
    
    NSString *baseUrl = nil;
    
    if (!ignoreBaseUrl) {
        baseUrl = [SJNetworkConfig sharedConfig].baseUrl;
    }
    
    [self p_suspendDownloadRequestWithDownloadRequestIdentifier:[SJNetworkUtils generateDownloadRequestIdentiferWithBaseUrlStr:baseUrl requestUrlStr:url]];
    
}


- (void)suspendDownloadRequests:(NSArray * _Nonnull)urls{
    
    
    if ([urls count] == 0) {
        if (_isDebugMode) {
            SJLog(@"=========== There is no input donwload urls!");
        }
        return;
    }
    
    if(![[SJNetworkRequestPool sharedPool] remainingCurrentRequests]){
        return;
    }
    
    [urls enumerateObjectsUsingBlock:^(NSString * url, NSUInteger idx, BOOL * _Nonnull stop) {
        [self suspendDownloadRequest:url];
    }];
}


- (void)suspendDownloadRequests:(NSArray *_Nonnull)urls ignoreBaseUrl:(BOOL)ignoreBaseUrl{
    
    if ([urls count] == 0) {
        if (_isDebugMode) {
            SJLog(@"=========== There is no input donwload urls!");
        }
        return;
    }
    
    if(![[SJNetworkRequestPool sharedPool] remainingCurrentRequests]){
        return;
    }
    
    [urls enumerateObjectsUsingBlock:^(NSString * url, NSUInteger idx, BOOL * _Nonnull stop) {
        [self suspendDownloadRequest:url ignoreBaseUrl:ignoreBaseUrl];
    }];
    
}


#pragma mark ============== Resume API ==============


- (void)resumeAllDownloadRequests{
    
    __block BOOL hasDownloadRequests = NO;
    
    [[SJNetworkRequestPool sharedPool].currentRequestModels enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, SJNetworkRequestModel * _Nonnull requestModel, BOOL * _Nonnull stop) {
        
        
        if (requestModel.requestType == SJRequestTypeDownload) {
            
            hasDownloadRequests = YES;
            
            if (requestModel.task) {
                
                if(requestModel.backgroundDownloadSupport){
                    
                    NSString *resumeDataFilePath = requestModel.resumeDataFilePath;
                    
                    if ([_fileManager fileExistsAtPath:resumeDataFilePath]) {
                        NSData *resumeData = [NSData dataWithContentsOfFile:resumeDataFilePath];
                        if (resumeData) {
                            NSString *oldTaskKey = [NSString stringWithFormat:@"%ld",(unsigned long)requestModel.task.taskIdentifier];
                            NSURLSessionTask * downloadTask = [self.backgroundDownloadSession downloadTaskWithResumeData:resumeData];
                            requestModel.task = downloadTask;
                            //change request model
                            [[SJNetworkRequestPool sharedPool] changeRequestModel:requestModel forKey:oldTaskKey];
                            [downloadTask resume];
                            SJLog(@"=========== Resumed background support download request: %@",requestModel);
                        }else{
                            SJLog(@"=========== Can not resume background support download request: %@, since resume data is not available",requestModel);
                        }
                    }else{
                        SJLog(@"=========== Can not resume background support download request: %@, since there is no resume data in path %@",requestModel,resumeDataFilePath);
                    }
                    
                    
                }else{
                    if (requestModel.task.state == NSURLSessionTaskStateSuspended) {
                        [requestModel.task resume];
                        SJLog(@"=========== Resumed request: %@",requestModel);
                    }else{
                        SJLog(@"=========== Can not resume request: %@, since it is not suspended",requestModel);
                    }
                }
                
            }else {
                SJLog(@"=========== There is no download task of this request");
            }
        }
    }];
    
    if (!hasDownloadRequests) {
        SJLog(@"=========== There is no donwload task to resume");
    }
    
}

- (void)resumeDownloadReqeust:(NSString *_Nonnull)url{
    
    
    if (url.length == 0) {
        if (_isDebugMode) {
            SJLog(@"=========== The input url is an empty string!");
        }
        return;
    }
    
    if(![[SJNetworkRequestPool sharedPool] remainingCurrentRequests]){
        return;
    }
    
    //a unique identifer of a download request
    NSString *downloadRequestIdentifier =  [SJNetworkUtils generateDownloadRequestIdentiferWithBaseUrlStr:[SJNetworkConfig sharedConfig].baseUrl
                                                                                            requestUrlStr:url];
    [self p_resumeDownloadRequestWithDownloadRequestIdentifier:downloadRequestIdentifier];
    
    
}

- (void)resumeDownloadReqeust:(NSString *_Nonnull)url ignoreBaseUrl:(BOOL)ignoreBaseUrl{
    
    if (url.length == 0) {
        if (_isDebugMode) {
            SJLog(@"=========== The input url is an empty string!");
        }
        return;
    }
    if(![[SJNetworkRequestPool sharedPool] remainingCurrentRequests]){
        return;
    }
    
    NSString *baseUrl = nil;
    
    if (!ignoreBaseUrl) {
        baseUrl = [SJNetworkConfig sharedConfig].baseUrl;
    }
    
    //a unique identifer of a download request
    NSString *downloadRequestIdentifier =  [SJNetworkUtils generateDownloadRequestIdentiferWithBaseUrlStr:baseUrl
                                                                                            requestUrlStr:url];
    [self p_resumeDownloadRequestWithDownloadRequestIdentifier:downloadRequestIdentifier];
}


- (void)resumeDownloadReqeusts:(NSArray *_Nonnull)urls{
    
    if ([urls count] == 0) {
        if (_isDebugMode) {
            SJLog(@"=========== There is no input donwload urls!");
        }
        return;
    }
    
    if(![[SJNetworkRequestPool sharedPool] remainingCurrentRequests]){
        return;
    }
    
    [urls enumerateObjectsUsingBlock:^(NSString * url, NSUInteger idx, BOOL * _Nonnull stop) {
        [self resumeDownloadReqeust:url];
    }];
    
}

- (void)resumeDownloadReqeusts:(NSArray *_Nonnull)urls ignoreBaseUrl:(BOOL)ignoreBaseUrl{
    
    if ([urls count] == 0) {
        if (_isDebugMode) {
            SJLog(@"=========== There is no input donwload urls!");
        }
        return;
    }
    
    [urls enumerateObjectsUsingBlock:^(NSString * url, NSUInteger idx, BOOL * _Nonnull stop) {
        [self resumeDownloadReqeust:url ignoreBaseUrl:ignoreBaseUrl];
    }];
    
}




#pragma mark ============== Cancel API ==============


- (void)cancelAllDownloadRequests{
    
    if(![[SJNetworkRequestPool sharedPool] remainingCurrentRequests]){
        return;
    }
    
    __block BOOL hasDownloadRequests = NO;
    [[SJNetworkRequestPool sharedPool].currentRequestModels enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, SJNetworkRequestModel * _Nonnull requestModel, BOOL * _Nonnull stop) {
        
        if (requestModel.requestType == SJRequestTypeDownload) {
            
            hasDownloadRequests = YES;
            if (requestModel.task) {
                
                if (requestModel.backgroundDownloadSupport) {
                    
                    NSURLSessionDownloadTask *downloadTask = (NSURLSessionDownloadTask*)requestModel.task;
                    requestModel.manualOperation = SJDownloadManualOperationCancel;
                    [downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
                    }];
                    
                }else{
                    [requestModel.task cancel];
                }
                SJLog(@"=========== Canceled request:%@",requestModel);
            }else {
                SJLog(@"=========== There is no donwload task of this request");
            }
        }
    }];
    
    if (!hasDownloadRequests) {
        SJLog(@"=========== There is no donwload task to cancel");
    }
}




- (void)cancelDownloadRequest:(NSString * _Nonnull)url{
    
    if (url.length == 0) {
        if (_isDebugMode) {
            SJLog(@"=========== The input url is an empty string!");
        }
        return;
    }
    
    [[SJNetworkRequestPool sharedPool] cancelCurrentRequestWithUrl:url];
}


- (void)cancelDownloadRequest:(NSString * _Nonnull)url ignoreBaseUrl:(BOOL)ignoreBaseUrl{
    
    if (url.length == 0) {
        if (_isDebugMode) {
            SJLog(@"=========== The input url is an empty string!");
        }
         return;
    }
    
    
    NSString *requestUrl = nil;
    if (!ignoreBaseUrl) {
        requestUrl = [[SJNetworkConfig sharedConfig].baseUrl stringByAppendingPathComponent:url];
    }else{
        requestUrl = url;
    }
    
    [[SJNetworkRequestPool sharedPool] cancelCurrentRequestWithUrl:requestUrl];
    
    
}


- (void)cancelDownloadRequests:(NSArray *_Nonnull)urls{
    
    if ([urls count] == 0) {
        if (_isDebugMode) {
            SJLog(@"=========== The input url array is empty!");
        }
         return;
    }
    
    [urls enumerateObjectsUsingBlock:^(NSString *url, NSUInteger idx, BOOL * _Nonnull stop) {
        [[SJNetworkRequestPool sharedPool] cancelCurrentRequestWithUrl:url];
    }];
    
}




- (void)cancelDownloadRequests:(NSArray *_Nonnull)urls ignoreBaseUrl:(BOOL)ignoreBaseUrl{
    
    if ([urls count] == 0) {
        if (_isDebugMode) {
            SJLog(@"=========== The input url array is empty!");
        }
        return;
    }
    
    [urls enumerateObjectsUsingBlock:^(NSString *url, NSUInteger idx, BOOL * _Nonnull stop) {
        
        NSString *requestUrl = nil;
        if (!ignoreBaseUrl) {
            requestUrl = [[SJNetworkConfig sharedConfig].baseUrl stringByAppendingPathComponent:url];
        }else{
            requestUrl = url;
        }
        [[SJNetworkRequestPool sharedPool] cancelCurrentRequestWithUrl:requestUrl];
    }];
}






- (CGFloat)resumeDataRatioOfRequest:(NSString *_Nonnull)url{
    
    if (url.length == 0) {
        if (_isDebugMode) {
            SJLog(@"=========== The input url is an empty string!");
        }
        return 0.0;
    }
    
    //a unique identifer of a download request
    NSString *downloadRequestIdentifier =  [SJNetworkUtils generateDownloadRequestIdentiferWithBaseUrlStr:[SJNetworkConfig sharedConfig].baseUrl
                                                                                            requestUrlStr:url];
    
    return [self p_resumeDataRatioWithRequestIdentifier:downloadRequestIdentifier];
    
}




- (CGFloat)resumeDataRatioOfRequest:(NSString *_Nonnull)url ignoreBaseUrl:(BOOL)ignoreBaseUrl{
    
    if (url.length == 0) {
        if (_isDebugMode) {
            SJLog(@"=========== The input url is an empty string!");
        }
        return 0.0;
    }
    
    NSString *baseUrl = nil;
    
    if (!ignoreBaseUrl) {
        baseUrl = [SJNetworkConfig sharedConfig].baseUrl;
    }
    
    //a unique identifer of a download request
    NSString *downloadRequestIdentifier =  [SJNetworkUtils generateDownloadRequestIdentiferWithBaseUrlStr:baseUrl requestUrlStr:url];
    
    return [self p_resumeDataRatioWithRequestIdentifier:downloadRequestIdentifier];
    
}


#pragma mark- ============== Private Methods ==============


- (NSURLSessionTask *)p_backgroundDownloadTaskWithRequestModel:(SJNetworkRequestModel *)requestModel{
    
    
    //init download request with request url
    NSMutableURLRequest *downloadRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:requestModel.requestUrl]];
    
    //add custom header
    [self p_addRequestHeaderInRequest:downloadRequest];
   
    
    //temp download file
    NSString *resumeDataFilePath = requestModel.resumeDataFilePath;
    
    NSString *resumeDataInfoFilePath = requestModel.resumeDataInfoFilePath;
    
    
    if (![_fileManager fileExistsAtPath:resumeDataInfoFilePath]) {
        SJNetworkDownloadResumeDataInfo  *dataInfo = [[SJNetworkDownloadResumeDataInfo alloc] init];
        [NSKeyedArchiver archiveRootObject:dataInfo toFile:resumeDataInfoFilePath];
    }
    
    NSURLSessionDownloadTask *downloadTask = nil;
    
    
    if ([_fileManager fileExistsAtPath:resumeDataFilePath]) {
        
        
        NSData *resumeData = [NSData dataWithContentsOfFile:resumeDataFilePath];
        
        if (resumeData) {
            
            if (requestModel.resumableDownload) {
                downloadTask = [self.backgroundDownloadSession downloadTaskWithResumeData:resumeData];
                
            }else{
                [_fileManager removeItemAtPath:resumeDataFilePath error:nil];
                downloadTask = [self.backgroundDownloadSession downloadTaskWithRequest:downloadRequest];
            }
            
        }else{
            
            [_fileManager removeItemAtPath:resumeDataFilePath error:nil];
            downloadTask = [self.backgroundDownloadSession downloadTaskWithRequest:downloadRequest];
        }
        
    }else{
        
        downloadTask = [self.backgroundDownloadSession downloadTaskWithRequest:downloadRequest];
    }
    
    
    
    return downloadTask;
}



- (NSURLSessionTask *)p_noneBackgroundDownloadTaskWithRequestModel:(SJNetworkRequestModel *)requestModel{
    
    //init download request with request url
    NSMutableURLRequest *downloadRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:requestModel.requestUrl]];
    
    //add custom header
    [self p_addRequestHeaderInRequest:downloadRequest];
    
    
    //temp download file
    NSString *resumDataFilePath = requestModel.resumeDataFilePath;
    
    NSString *resumeDataInfoFilePath = requestModel.resumeDataInfoFilePath;
    
    
    //create steam
    NSOutputStream *stream = [NSOutputStream outputStreamToFileAtPath:resumDataFilePath append:YES];
    requestModel.stream = stream;
    
    if (requestModel.resumableDownload) {
        
        if ([_fileManager fileExistsAtPath:resumeDataInfoFilePath] ) {
            
            //load resume data info
            SJNetworkDownloadResumeDataInfo *dataInfo = [_cacheManager loadResumeDataInfo:resumeDataInfoFilePath];
            
            //check if resume data info exsists
            if (dataInfo) {
                
                NSInteger resumeDataLength = [dataInfo.resumeDataLength integerValue];
                if (resumeDataLength > 0) {
                    NSString *range = [NSString stringWithFormat:@"bytes=%zd-", resumeDataLength];
                    [downloadRequest setValue:range forHTTPHeaderField:@"Range"];
                }
                
            }else{
                
                //if resume data info was not available and the corresponding data exists, then delete the corresponding data
                if ([_fileManager fileExistsAtPath:resumDataFilePath]) {
                    [_fileManager removeItemAtPath:resumeDataInfoFilePath error:nil];
                }
                
            }
        }else {
            
            //if this is a not a resumable download request and there is no resume data info, then create one
            SJNetworkDownloadResumeDataInfo  *dataInfo = [[SJNetworkDownloadResumeDataInfo alloc] init];
            [NSKeyedArchiver archiveRootObject:dataInfo toFile:resumeDataInfoFilePath];
            
            
        }
    }
    
    NSURLSessionDataTask *downloadTask = [self.downloadSession dataTaskWithRequest:downloadRequest];
    return downloadTask;
    
}


- (void)p_addRequestHeaderInRequest:(NSMutableURLRequest *)request{
    
    NSDictionary *customHeaders = [SJNetworkConfig sharedConfig].customHeaders;
    if ([customHeaders allKeys] > 0) {
        NSArray *allKeys = [customHeaders allKeys];
        if ([allKeys count] >0) {
            [customHeaders enumerateKeysAndObjectsUsingBlock:^(NSString *key, NSString *value, BOOL * _Nonnull stop) {
                [request setValue:value forHTTPHeaderField:key];
                if (_isDebugMode) {
                    SJLog(@"=========== added header:key:%@ value:%@",key,value);
                }
            }];
        }
    }
}


- (void)p_suspendDownloadRequestWithDownloadRequestIdentifier:(NSString *)downloadRequestIdentifier{
    
    [[SJNetworkRequestPool sharedPool].currentRequestModels enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, SJNetworkRequestModel * _Nonnull requestModel, BOOL * _Nonnull stop) {
        
        if ([requestModel.requestIdentifer isEqualToString:downloadRequestIdentifier]) {
            
            if (requestModel.task) {
                
                if (requestModel.task.state == NSURLSessionTaskStateRunning) {
                    
                    if (requestModel.backgroundDownloadSupport) {
                        
                        requestModel.manualOperation = SJDownloadManualOperationSuspend;
                        NSURLSessionDownloadTask *downloadTask = (NSURLSessionDownloadTask*)requestModel.task;
                        [downloadTask cancelByProducingResumeData:^(NSData * _Nullable resumeData) {
                        }];
                        
                    }else{
                        
                        [requestModel.task suspend];
                        [_cacheManager updateResumeDataInfoAfterSuspendWithRequestModel:requestModel];
                        SJLog(@"=========== Suspended request:%@",requestModel);
                        
                    }
                    
                }else {
                    SJLog(@"=========== Request %@ can not be suspended,since it is not running",requestModel);
                }
                
            }else {
                SJLog(@"=========== There is no donwload task of this request");
            }
        }
    }];
    
}

- (void)p_resumeDownloadRequestWithDownloadRequestIdentifier:(NSString *)downloadRequestIdentifier{
    
    [[SJNetworkRequestPool sharedPool].currentRequestModels enumerateKeysAndObjectsUsingBlock:^(NSString * _Nonnull key, SJNetworkRequestModel * _Nonnull requestModel, BOOL * _Nonnull stop) {
        
        if ([requestModel.requestIdentifer isEqualToString:downloadRequestIdentifier]) {
            
            if (requestModel.task) {
                
                if (requestModel.backgroundDownloadSupport) {
                    
                    if (requestModel.manualOperation == SJDownloadManualOperationSuspend) {
                        
                        NSString *resumeDataFilePath = requestModel.resumeDataFilePath;
                        
                        if ([_fileManager fileExistsAtPath:resumeDataFilePath]) {
                            
                            NSData *resumeData = [NSData dataWithContentsOfFile:resumeDataFilePath];
                            if (resumeData) {
                                
                                NSString *oldTaskKey = [NSString stringWithFormat:@"%ld",(unsigned long)requestModel.task.taskIdentifier];
                                NSURLSessionTask * downloadTask = [self.backgroundDownloadSession downloadTaskWithResumeData:resumeData];
                                requestModel.manualOperation = SJDownloadManualOperationResume;
                                requestModel.task = downloadTask;
                                [[SJNetworkRequestPool sharedPool] changeRequestModel:requestModel forKey:oldTaskKey];
                                [downloadTask resume];
                                SJLog(@"=========== Resumed background support download request: %@",requestModel);
                                
                            }else{
                                
                                SJLog(@"=========== Can not resume background support download request: %@, since resume data is not available",requestModel);
                                
                            }
                            
                        }else{
                            
                            SJLog(@"=========== Can not resume background support download request: %@, since there is no resume data in path %@",requestModel,resumeDataFilePath);
                        }
                        
                        
                    }else{
                        
                        SJLog(@"=========== Can not resume background support download request: %@, since it is not suspended(canceled) by user",requestModel);
                    }
                    
                }else{
                    
                    if (requestModel.task.state == NSURLSessionTaskStateSuspended) {
                        [requestModel.task resume];
                        SJLog(@"=========== Resumed request: %@",requestModel);
                        
                    }else{
                        
                        SJLog(@"=========== Can not resume request: %@, since it is not suspended",requestModel);
                    }
                }
                
            }else {
                SJLog(@"=========== There is no download task of this request");
            }
        }
    }];
    
}

- (CGFloat)p_resumeDataRatioWithRequestIdentifier:(NSString *)requestIdentifier{
    
    NSString *resumeDataInfoFilePath = [SJNetworkUtils resumeDataInfoFilePathWithRequestIdentifer:requestIdentifier];
    SJNetworkDownloadResumeDataInfo *dataInfo = [_cacheManager loadResumeDataInfo:resumeDataInfoFilePath];
    
    if (dataInfo) {
        return [dataInfo.resumeDataRatio floatValue];
    }else{
        return 0.00;
    }
}


#pragma mark- ============== SJNetworkProtocol ==============

- (void)handleRequesFinished:(SJNetworkRequestModel *)requestModel{
    
    //clear all blocks
    [requestModel clearAllBlocks];
    
    //remove this requst model from request queue
    [[SJNetworkRequestPool sharedPool] removeRequestModel:requestModel];
    
}


#pragma mark - ============== NSURLSessionTaskDelegate ==============

- (void)URLSession:(NSURLSession *)session
              task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error{

    if (_isDebugMode) {
        SJLog(@"=========== Download request did complete of task:%@",task);
    }

    SJNetworkRequestModel * requestModel = [[SJNetworkRequestPool sharedPool].currentRequestModels objectForKey:[NSString stringWithFormat:@"%ld",(unsigned long)task.taskIdentifier]];

    if (requestModel) {
        
        if (requestModel.backgroundDownloadSupport) {
            
            if (error) {
                
                //cancel request
                if (error.code == -999) {
                    
                    [requestModel.task suspend];
                    
                    NSData *resumeData = requestModel.task.error.userInfo[NSURLSessionDownloadTaskResumeData];
                    NSError *moveDownloadFileError = nil;
                    
                    if (requestModel.resumableDownload) {
                        
                        [resumeData writeToFile:requestModel.resumeDataFilePath options:NSDataWritingAtomic error:&moveDownloadFileError];
                        
                    }else{
                        
                        if (requestModel.manualOperation == SJDownloadManualOperationSuspend){
                            
                             [resumeData writeToFile:requestModel.resumeDataFilePath options:NSDataWritingAtomic error:&moveDownloadFileError];
                            
                        }else{
                            
                            if (_isDebugMode) {
                                SJLog(@"=========== Because this is not resumable downloading:\n remove resume data in path:%@ \n and resume data info in path:%@",requestModel.resumeDataFilePath,requestModel.resumeDataInfoFilePath);
                            }
                            
                            [_cacheManager removeResumeDataAndResumeDataInfoFileWithRequestModel:requestModel];
                            
                        }
                        
                       
                        
                    }
                    
                    if (requestModel.manualOperation == SJDownloadManualOperationSuspend) {
                        
                        SJLog(@"=========== Suspended background support download request:%@",requestModel);
                        
                    }else{
                        
                        SJLog(@"=========== Canceled background support download request:%@",requestModel);
                        dispatch_async(dispatch_get_main_queue(), ^{
                            if (requestModel.downloadFailureBlock) {
                                    requestModel.downloadFailureBlock(requestModel.task, error,requestModel.resumeDataFilePath);
                                
                            }
                            [self handleRequesFinished:requestModel];
                       });
                        
                    }
                    
                    
                }else{
                    
                    
                    
                    SJLog(@"=========== Background support download failed, download file path:%@",requestModel.downloadFilePath);
                    
                    dispatch_async(dispatch_get_main_queue(), ^{
                        
                        if (requestModel.downloadFailureBlock) {
                                requestModel.downloadFailureBlock(requestModel.task, error,requestModel.resumeDataFilePath);
                        }                        
                        [self handleRequesFinished:requestModel];
                    });
                }
                
                
            }else{
                
                
                SJLog(@"=========== Download succeed,download file path:%@",requestModel.downloadFilePath);
                 dispatch_async(dispatch_get_main_queue(), ^{\
                     
                    if (requestModel.downloadSuccessBlock) {
                        requestModel.downloadSuccessBlock(requestModel.downloadFilePath);
                    }
                    [self handleRequesFinished:requestModel];
                     
                });
            }
            
            
        }else {
            
            
            if (error) {
                
                if (error.code == -997) {
                    
                    // The eror code equals to -997 means this app enters into background and then lost connect.
                    // We offer an 'auto start request mechanism' to cope with this situation
                   
                    SJNetworkDownloadResumeDataInfo *dataInfo = [_cacheManager loadResumeDataInfo:requestModel.resumeDataInfoFilePath];
                    NSDictionary *attributeDict = [_fileManager attributesOfItemAtPath:requestModel.resumeDataFilePath error:nil];
                    NSInteger resumeDataLength = [attributeDict[NSFileSize] integerValue];
                    dataInfo.resumeDataLength = [NSString stringWithFormat:@"%ld",(long)resumeDataLength];
                    
                    //ratio
                    CGFloat ratio = 1.0 * ([dataInfo.resumeDataLength integerValue])/([dataInfo.totalDataLength integerValue]);
                    dataInfo.resumeDataRatio = [NSString stringWithFormat:@"%.2f",ratio];
                    [NSKeyedArchiver archiveRootObject:dataInfo toFile:requestModel.resumeDataInfoFilePath];
                    
                    
                    [requestModel.stream close];
                     requestModel.stream = nil;
                    
                    //lost connection background and the task is canceled
                    NSString *oldTaskKey = [NSString stringWithFormat:@"%ld",(unsigned long)requestModel.task.taskIdentifier];
                    //init download request with request url
                    NSURLSessionTask * downloadTask = [self p_noneBackgroundDownloadTaskWithRequestModel:requestModel];
                    requestModel.task = downloadTask;
                    //change request model
                    [[SJNetworkRequestPool sharedPool] changeRequestModel:requestModel forKey:oldTaskKey];
                    [requestModel.task resume];
                    
                    
                    
                }else{
                    
                    if (requestModel.resumableDownload) {
                        
                        SJNetworkDownloadResumeDataInfo *dataInfo = [_cacheManager loadResumeDataInfo:requestModel.resumeDataInfoFilePath];
                        NSDictionary *attributeDict = [_fileManager attributesOfItemAtPath:requestModel.resumeDataFilePath error:nil];
                        NSInteger resumeDataLength = [attributeDict[NSFileSize] integerValue];
                        dataInfo.resumeDataLength = [NSString stringWithFormat:@"%ld",(long)resumeDataLength];

                        //ratio
                        CGFloat ratio = 1.0 * ([dataInfo.resumeDataLength integerValue])/([dataInfo.totalDataLength integerValue]);
                        dataInfo.resumeDataRatio = [NSString stringWithFormat:@"%.2f",ratio];
                        [NSKeyedArchiver archiveRootObject:dataInfo toFile:requestModel.resumeDataInfoFilePath];
                        
                        
                        if (_isDebugMode) {
                            SJLog(@"=========== Keep resume data in path:%@ \n and save resume data info in path:%@, since this is a resumable donwloading",requestModel.resumeDataFilePath,requestModel.resumeDataInfoFilePath);
                        }
                        
                    }else {
                        
                        if (_isDebugMode) {
                            SJLog(@"=========== Remove resume data in path:%@ \n and resume data info in path:%@, since this is not a resumable donwloading",requestModel.resumeDataFilePath,requestModel.resumeDataInfoFilePath);
                        }
                        
                        [_cacheManager removeResumeDataAndResumeDataInfoFileWithRequestModel:requestModel];
                        
                    }
                    
                    
                    [requestModel.stream close];
                     requestModel.stream = nil;
                    
                    
                    SJLog(@"=========== Download failed,download file path:%@",requestModel.downloadFilePath);
                    dispatch_async(dispatch_get_main_queue(), ^{
                        
                        if (requestModel.downloadFailureBlock) {
                                requestModel.downloadFailureBlock(requestModel.task, error,requestModel.resumeDataFilePath);
                        }
                        [self handleRequesFinished:requestModel];
                    });
                    
                }
                
            }else {
                
                //download succeed
                [_cacheManager removeCompleteDownloadDataAndClearResumeDataInfoFileWithRequestModel:requestModel];
                
                [requestModel.stream close];
                 requestModel.stream = nil;
                
                SJLog(@"=========== Download succeed,download file path:%@",requestModel.downloadFilePath);
                dispatch_async(dispatch_get_main_queue(), ^{
                    
                    if (requestModel.downloadSuccessBlock) {
                        requestModel.downloadSuccessBlock(requestModel.downloadFilePath);
                    }
                    [self handleRequesFinished:requestModel];
                });
            }
        }
    }
}



- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSHTTPURLResponse *)response
 completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{

    if (_isDebugMode) {
        SJLog(@"=========== Did received response:%@ \n of task:%@",response,dataTask);
    }

    SJNetworkRequestModel * requestModel = [[SJNetworkRequestPool sharedPool].currentRequestModels objectForKey:[NSString stringWithFormat:@"%ld",(unsigned long)dataTask.taskIdentifier]];

    if (requestModel) {
        
        
        
        NSInteger statusCode  = 0;
        if ([dataTask.response isKindOfClass:[NSHTTPURLResponse class]]) {
            statusCode = [(NSHTTPURLResponse*)dataTask.response statusCode];
        }
        
        NSString *resumeDataInfoFilePath = requestModel.resumeDataInfoFilePath;
        NSString *resumeDataFilePath= requestModel.resumeDataFilePath;
        
        if (statusCode > 400) {
            
            NSError *error = [NSError errorWithDomain:@"request error" code:statusCode userInfo:nil];
            
            [_fileManager removeItemAtPath:resumeDataInfoFilePath error:nil];
            
            if ([_fileManager fileExistsAtPath:resumeDataFilePath]) {
                [_fileManager removeItemAtPath:resumeDataFilePath error:nil];
            }
            
            
            SJLog(@"=========== Download failed,download file path:%@",requestModel.downloadFilePath);
            dispatch_async(dispatch_get_main_queue(), ^{
                
                if (requestModel.downloadFailureBlock) {
                    requestModel.downloadFailureBlock(requestModel.task, error,nil);
                }
                [self handleRequesFinished:requestModel];
            });
            
            return;
            
        }
        
        //no error, open stream
        [requestModel.stream open];

        //load resume data info
        SJNetworkDownloadResumeDataInfo *dataInfo = [_cacheManager loadResumeDataInfo:requestModel.resumeDataInfoFilePath];
        if (!dataInfo) {
            dataInfo = [[SJNetworkDownloadResumeDataInfo alloc] init];
        }

        //resume data file length
        NSDictionary *attributeDict = [_fileManager attributesOfItemAtPath:resumeDataFilePath error:nil];
        NSInteger resumeDataLength = [attributeDict[NSFileSize] integerValue];
        dataInfo.resumeDataLength = [NSString stringWithFormat:@"%ld",(long)resumeDataLength];

        //total data file length
        NSInteger totalLength = [response.allHeaderFields[@"Content-Length"] integerValue] + [dataInfo.resumeDataLength integerValue];
        dataInfo.totalDataLength = [NSString stringWithFormat:@"%ld",(long)totalLength];
        requestModel.totalLength = totalLength;

        //ratio
        CGFloat ratio = 1.0 * ([dataInfo.resumeDataLength integerValue])/([dataInfo.totalDataLength integerValue]);
        dataInfo.resumeDataRatio = [NSString stringWithFormat:@"%.2f",ratio];

        [NSKeyedArchiver archiveRootObject:dataInfo toFile:requestModel.resumeDataInfoFilePath];

        completionHandler(NSURLSessionResponseAllow);

    }
}


- (void)URLSession:(NSURLSession *)session
          dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data
{

    SJNetworkRequestModel * requestModel = [[SJNetworkRequestPool sharedPool].currentRequestModels objectForKey:[NSString stringWithFormat:@"%ld",(unsigned long)dataTask.taskIdentifier]];

    if (requestModel) {

        //write data in stream
        [requestModel.stream write:data.bytes maxLength:data.length];

        //update resume data info
        SJNetworkDownloadResumeDataInfo *dataInfo = [_cacheManager loadResumeDataInfo: requestModel.resumeDataInfoFilePath];
        NSDictionary *attributeDict = [_fileManager attributesOfItemAtPath:requestModel.resumeDataFilePath error:nil];
        NSInteger resumeDataLength = [attributeDict[NSFileSize] integerValue];
        dataInfo.resumeDataLength = [NSString stringWithFormat:@"%ld",(long)resumeDataLength];
        
        CGFloat ratio = 1.0 * ([dataInfo.resumeDataLength integerValue])/([dataInfo.totalDataLength integerValue]);
        dataInfo.resumeDataRatio = [NSString stringWithFormat:@"%.2f",ratio];
        [NSKeyedArchiver archiveRootObject:dataInfo toFile:requestModel.resumeDataInfoFilePath];

        if (_isDebugMode) {
            SJLog(@"=========== Download progress:%@ of task:%@",dataInfo.resumeDataRatio,requestModel.task);
        }
        if (requestModel.downloadProgressBlock) {
            requestModel.downloadProgressBlock([dataInfo.resumeDataLength integerValue] ,requestModel.totalLength,ratio);
        }

    }
}


#pragma mark - ==============  NSURLSessionDownloadDelegate ==============




- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
    
    SJNetworkRequestModel * requestModel = [[SJNetworkRequestPool sharedPool].currentRequestModels objectForKey:[NSString stringWithFormat:@"%ld",(unsigned long)downloadTask.taskIdentifier]];
    
    
    if (requestModel) {
        
        
        NSInteger statusCode  = 0;
        if ([downloadTask.response isKindOfClass:[NSHTTPURLResponse class]]) {
            statusCode = [(NSHTTPURLResponse*)downloadTask.response statusCode];
        }
                
        NSString *resumeDataInfoFilePath = requestModel.resumeDataInfoFilePath;
        NSString *resumeDataFilePath= requestModel.resumeDataFilePath;
        
        if (statusCode > 400) {
            
            NSError *error = nil;
            if (statusCode == 416) {
                error = [NSError errorWithDomain:@"range error" code:statusCode userInfo:nil];
            }
            
            [_fileManager removeItemAtPath:resumeDataInfoFilePath error:nil];
            
            if ([_fileManager fileExistsAtPath:resumeDataFilePath]) {
                [_fileManager removeItemAtPath:resumeDataFilePath error:nil];
            }
            
            
            SJLog(@"=========== Download failed,download file path:%@",requestModel.downloadFilePath);
            dispatch_async(dispatch_get_main_queue(), ^{
                
                if (requestModel.downloadFailureBlock) {
                    requestModel.downloadFailureBlock(requestModel.task, error,nil);
                }
                [self handleRequesFinished:requestModel];
            });
            
        }else{
        
                
            NSData *tmpDownloadFileData = [NSData dataWithContentsOfURL:location];
            NSUInteger downloadDataLength = tmpDownloadFileData.length;
        
        
            //download succeed
            NSError *moveDownloadFileError = nil;
            
            //move temp download data to target file path
            [_fileManager moveItemAtURL:location toURL:[NSURL fileURLWithPath:requestModel.downloadFilePath] error:&moveDownloadFileError];
            
            //remove data info file path
            [_fileManager removeItemAtPath:resumeDataInfoFilePath error:nil];
            
            if ([_fileManager fileExistsAtPath:resumeDataFilePath]) {
                [_fileManager removeItemAtPath:resumeDataFilePath error:nil];
            }
            
            if (moveDownloadFileError &&  moveDownloadFileError.code != 516) {
                
                SJLog(@"=========== Download failed,download file path:%@",requestModel.downloadFilePath);
                
                 dispatch_async(dispatch_get_main_queue(), ^{
                     
                    if (requestModel.downloadFailureBlock) {
                            requestModel.downloadFailureBlock(requestModel.task, moveDownloadFileError,nil);
                    }
                     
                    [self handleRequesFinished:requestModel];
                 });
                
            }else {
                
                if (requestModel.downloadProgressBlock) {
                    requestModel.downloadProgressBlock(downloadDataLength, downloadDataLength, 1);
                }
                
                if (moveDownloadFileError.code == 516) {
                    [_fileManager removeItemAtPath:location.absoluteString error:nil];
                }
                
                //succeed block
                SJLog(@"=========== Download succeed,download file path:%@",requestModel.downloadFilePath);
                dispatch_async(dispatch_get_main_queue(), ^{
                    
                    if (requestModel.downloadSuccessBlock) {
                        requestModel.downloadSuccessBlock(requestModel.downloadFilePath);
                    }
                    [self handleRequesFinished:requestModel];
                });
            }
            }
        }
}



- (void)URLSession:(NSURLSession *)session
      downloadTask:(NSURLSessionDownloadTask *)downloadTask
      didWriteData:(int64_t)bytesWritten
 totalBytesWritten:(int64_t)totalBytesWritten
totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
{
    
    SJNetworkRequestModel * requestModel = [[SJNetworkRequestPool sharedPool].currentRequestModels objectForKey:[NSString stringWithFormat:@"%ld",(unsigned long)downloadTask.taskIdentifier]];
    
    if (requestModel) {
        
        if (!requestModel.totalLength) {
             requestModel.totalLength = (NSInteger)totalBytesExpectedToWrite;
        }
        
        CGFloat ratio = 1.0 *totalBytesWritten/requestModel.totalLength;
        NSString *resumeDataInfoFilePath = requestModel.resumeDataInfoFilePath;
        SJNetworkDownloadResumeDataInfo *dataInfo = [_cacheManager loadResumeDataInfo:resumeDataInfoFilePath];
        
        dataInfo.resumeDataLength = [NSString stringWithFormat:@"%lld",totalBytesWritten];
        dataInfo.totalDataLength = [NSString stringWithFormat:@"%ld",(long)requestModel.totalLength];
        dataInfo.resumeDataRatio = [NSString stringWithFormat:@"%.2f",ratio];

        [NSKeyedArchiver archiveRootObject:dataInfo toFile:resumeDataInfoFilePath];
        
        if (_isDebugMode) {
            SJLog(@"=========== Download progress:%@ of task:%@",dataInfo.resumeDataRatio,requestModel.task);
        }
        if (requestModel.downloadProgressBlock) {
            requestModel.downloadProgressBlock((NSInteger)bytesWritten ,requestModel.totalLength,ratio);
        }
    }
    
    
}



@end


================================================
FILE: SJNetwork/SJNetworkDownloadResumeDataInfo.h
================================================
//
//  SJNetworkResumeDataInfo.h
//  SJNetworkingDemo
//
//  Created by Sun Shijie on 2017/11/28.
//  Copyright © 2017年 Shijie. All rights reserved.
//

#import <Foundation/Foundation.h>


/* =============================
 *
 * SJNetworkDownloadResumeDataInfo
 *
 * SJNetworkDownloadResumeDataInfo is in charge of recording the infomation of resume data of the corresponding download request
 *
 * =============================
 */

@interface SJNetworkDownloadResumeDataInfo : NSObject<NSSecureCoding>

// Record the resume data length
@property (nonatomic, readwrite, copy) NSString *resumeDataLength;

// Record total length of the download data
@property (nonatomic, readwrite, copy) NSString *totalDataLength;

// Record the ratio of resume data length and total length of download data (resumeDataLength/dataTotalLength)
@property (nonatomic, readwrite, copy) NSString *resumeDataRatio;


@end



================================================
FILE: SJNetwork/SJNetworkDownloadResumeDataInfo.m
================================================
//
//  SJNetworkResumeDataInfo.m
//  SJNetworkingDemo
//
//  Created by Sun Shijie on 2017/11/28.
//  Copyright © 2017年 Shijie. All rights reserved.
//

#import "SJNetworkDownloadResumeDataInfo.h"

@implementation SJNetworkDownloadResumeDataInfo

#pragma mark- ============== Life Cycle Methods ==============

- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {
    
    self = [self init];
    
    if (self) {
        
        self.resumeDataRatio = [aDecoder decodeObjectOfClass:[NSString class] forKey:NSStringFromSelector(@selector(resumeDataRatio))];
        self.resumeDataLength = [aDecoder decodeObjectOfClass:[NSString class] forKey:NSStringFromSelector(@selector(resumeDataLength))];
        self.totalDataLength = [aDecoder decodeObjectOfClass:[NSString class] forKey:NSStringFromSelector(@selector(totalDataLength))];
    }    
    return self;
}

#pragma mark- ============== Override Methods ==============

+ (BOOL)supportsSecureCoding {
    
    return YES;
}

- (void)encodeWithCoder:(NSCoder *)aCoder{
    
    [aCoder encodeObject:self.resumeDataLength forKey:NSStringFromSelector(@selector(resumeDataLength))];
    [aCoder encodeObject:self.totalDataLength forKey:NSStringFromSelector(@selector(totalDataLength))];
    [aCoder encodeObject:self.resumeDataRatio forKey:NSStringFromSelector(@selector(resumeDataRatio))];
}



- (NSString *)description{
    
    return [NSString stringWithFormat:@"<%@: %p>:{resume data length:%@}, {total data length:%@},{ratio:%@}",NSStringFromClass([self class]), self,_resumeDataLength, _totalDataLength, _resumeDataRatio];
}

@end


================================================
FILE: SJNetwork/SJNetworkHeader.h
================================================
//
//  SJNetworkHeader.h
//  SJNetworkingDemo
//
//  Created by Sun Shijie on 2017/12/26.
//  Copyright © 2017年 Shijie. All rights reserved.
//

#ifndef SJNetworkHeader_h
#define SJNetworkHeader_h

#import <AFNetworking/AFNetworking.h>

//Log used to debug
#ifdef DEBUG
#define SJLog(...) NSLog(@"%s line number:%d \n %@\n\n",__func__,__LINE__,[NSString stringWithFormat:__VA_ARGS__])
#else
#define SJLog(...)
#endif


//============== Callbacks: Only for ordinary request ==================//
typedef void(^SJSuccessBlock)(id responseObject);
typedef void(^SJFailureBlock)(NSURLSessionTask *task, NSError *error, NSInteger statusCode);


//============== Callbacks: Only for upload request ==================//
typedef void(^SJUploadSuccessBlock)(id responseObject);
typedef void(^SJUploadProgressBlock)(NSProgress *uploadProgress);
typedef void(^SJUploadFailureBlock)(NSURLSessionTask *task, NSError *error, NSInteger statusCode, NSArray<UIImage *>*uploadFailedImages);


//============== Callbacks: Only for download request ==================//
typedef void(^SJDownloadSuccessBlock)(id responseObject);
typedef void(^SJDownloadProgressBlock)(NSInteger receivedSize, NSInteger expectedSize, CGFloat progress);
typedef void(^SJDownloadFailureBlock)(NSURLSessionTask *task, NSError *error, NSString* resumableDataPath);


/**
 *  HTTP Request method
 */
typedef NS_ENUM(NSInteger, SJRequestMethod) {
    
    SJRequestMethodGET = 60000,
    SJRequestMethodPOST,
    SJRequestMethodPUT,
    SJRequestMethodDELETE,
    
};


/**
 *  Request type
 */
typedef NS_ENUM(NSInteger, SJRequestType) {
    
    SJRequestTypeOrdinary = 70000,
    SJRequestTypeUpload,
    SJRequestTypeDownload
    
};


/**
 *  Manual operation by user (start,suspend,resume,cancel)
 */
typedef NS_ENUM(NSInteger, SJDownloadManualOperation) {
    
    SJDownloadManualOperationStart = 80000,
    SJDownloadManualOperationSuspend,
    SJDownloadManualOperationResume,
    SJDownloadManualOperationCancel,
    
};


#endif /* SJNetworkHeader_h */


================================================
FILE: SJNetwork/SJNetworkManager.h
================================================
//
//  SJNetworkManager.h
//  SJNetwork
//
//  Created by Sun Shijie on 2017/8/16.
//  Copyright © 2017年 Shijie. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

#import "SJNetworkRequestModel.h"
#import "SJNetworkCacheManager.h"


/* =============================
 *
 * SJNetworkManager
 *
 * SJNetworkManager is in charge of managing operations of network request
 *
 * =============================
 */

@interface SJNetworkManager : NSObject


/**
 *  SJNetworkManager Singleton
 *
 *  @return SJNetworkManager singleton instance
 */
+ (SJNetworkManager *_Nullable)sharedManager;



/**
 *  can not use new method
 */
+ (instancetype _Nullable)new NS_UNAVAILABLE;



/**
 *  This method is used to add custom header
 *
 *  @param header            custom header added by user
 *
 */
- (void)addCustomHeader:(NSDictionary *_Nonnull)header;



/**
 *  This method is used to return custom header
 *
 *  @return custom header
 */
- (NSDictionary *_Nullable)customHeaders;


#pragma mark- Request API using GET method

/**
 *  This method is used to send GET request,
 not consider whether to write cache & not consider whether to load cache
 *
 *  @param url                 request url
 *  @param successBlock        success callback
 *  @param failureBlock        failure callback
 *
 */
- (void)sendGetRequest:(NSString * _Nonnull)url
               success:(SJSuccessBlock _Nullable)successBlock
               failure:(SJFailureBlock _Nullable)failureBlock;




/**
 *  This method is used to send GET request,
 not consider whether to write cache & not consider whether to load cache
 *
 *  @param url                 request url
 *  @param parameters          parameters
 *  @param successBlock        success callback
 *  @param failureBlock        failure callback
 *
 */
- (void)sendGetRequest:(NSString * _Nonnull)url
            parameters:(id _Nullable)parameters
               success:(SJSuccessBlock _Nullable)successBlock
               failure:(SJFailureBlock _Nullable)failureBlock;



/**
 *  This method is used to send GET request,
 consider whether to load cache but not consider whether to write cache
 *
 *  @param url                 request url
 *  @param parameters          parameters
 *  @param loadCache           consider whether to load cache
 *  @param successBlock        success callback
 *  @param failureBlock        failure callback
 *
 */
- (void)sendGetRequest:(NSString * _Nonnull)url
            parameters:(id _Nullable)parameters
             loadCache:(BOOL)loadCache
               success:(SJSuccessBlock _Nullable)successBlock
               failure:(SJFailureBlock _Nullable)failureBlock;



/**
 *  This method is used to send GET request,
 consider whether to write cache but not consider whether to load cache
 *
 *  @param url                 request url
 *  @param parameters          parameters
 *  @param cacheDuration       consider whether to write cache
 *  @param successBlock        success callback
 *  @param failureBlock        failure callback
 *
 */
- (void)sendGetRequest:(NSString * _Nonnull)url
            parameters:(id _Nullable)parameters
         cacheDuration:(NSTimeInterval)cacheDuration
               success:(SJSuccessBlock _Nullable)successBlock
               failure:(SJFailureBlock _Nullable)failureBlock;


/**
 *  This method is used to send GET request,
 consider whether to load cache and consider whether to write cache
 *
 *  @param url                 request url
 *  @param parameters          parameters
 *  @param loadCache           consider whether to load cache
 *  @param cacheDuration       consider whether to write cache
 *  @param successBlock        success callback
 *  @param failureBlock        failure callback
 *
 */
- (void)sendGetRequest:(NSString * _Nonnull)url
            parameters:(id _Nullable)parameters
             loadCache:(BOOL)loadCache
         cacheDuration:(NSTimeInterval)cacheDuration
               success:(SJSuccessBlock _Nullable)successBlock
               failure:(SJFailureBlock _Nullable)failureBlock;



#pragma mark- Request API using POST method

/**
 *  This method is used to send POST request,
 not consider whether to write cache & not consider whether to load cache
 *
 *  @param url                request url
 *  @param parameters         parameters
 *  @param successBlock       success callback
 *  @param failureBlock       failure callback
 *
 */
- (void)sendPostRequest:(NSString * _Nonnull)url
             parameters:(id _Nullable)parameters
                success:(SJSuccessBlock _Nullable)successBlock
                failure:(SJFailureBlock _Nullable)failureBlock;



/**
 *  This method is used to send POST request,
 consider whether to load cache but not consider whether to write cache
 *
 *  @param url                 request url
 *  @param parameters          parameters
 *  @param loadCache           consider whether to load cache
 *  @param successBlock        success callback
 *  @param failureBlock        failure callback
 *
 */
- (void)sendPostRequest:(NSString * _Nonnull)url
             parameters:(id _Nullable)parameters
              loadCache:(BOOL)loadCache
                success:(SJSuccessBlock _Nullable)successBlock
                failure:(SJFailureBlock _Nullable)failureBlock;



/**
 *  This method is used to send POST request,
 consider whether to write cache but not consider whether to load cache
 *
 *  @param url                 request url
 *  @param parameters          parameters
 *  @param cacheDuration       consider whether to write cache
 *  @param successBlock        success callback
 *  @param failureBlock        failure callback
 *
 */
- (void)sendPostRequest:(NSString * _Nonnull)url
             parameters:(id _Nullable)parameters
          cacheDuration:(NSTimeInterval)cacheDuration
                success:(SJSuccessBlock _Nullable)successBlock
                failure:(SJFailureBlock _Nullable)failureBlock;


/**
 *  This method is used to send POST request,
 consider whether to load cache and consider whether to write cache
 *
 *  @param url                 request url
 *  @param parameters          parameters
 *  @param loadCache           consider whether to load cache
 *  @param cacheDuration       consider whether to write cache
 *  @param successBlock        success callback
 *  @param failureBlock        failure callback
 *
 */
- (void)sendPostRequest:(NSString * _Nonnull)url
             parameters:(id _Nullable)parameters
              loadCache:(BOOL)loadCache
          cacheDuration:(NSTimeInterval)cacheDuration
                success:(SJSuccessBlock _Nullable)successBlock
                failure:(SJFailureBlock _Nullable)failureBlock;



#pragma mark- Request API using PUT method


/**
 *  This method is used to send PUT request,
 not consider whether to write cache & not consider whether to load cache
 *
 *  @param url                 request url
 *  @param parameters          parameters
 *  @param successBlock        success callback
 *  @param failureBlock        failure callback
 *
 */
- (void)sendPutRequest:(NSString * _Nonnull)url
            parameters:(id _Nullable)parameters
               success:(SJSuccessBlock _Nullable)successBlock
               failure:(SJFailureBlock _Nullable)failureBlock;


/**
 *  This method is used to send POST request,
 consider whether to load cache but not consider whether to write cache
 *
 *  @param url                 request url
 *  @param parameters          parameters
 *  @param loadCache           consider whether to load cache
 *  @param successBlock        success callback
 *  @param failureBlock        failure callback
 *
 */
- (void)sendPutRequest:(NSString * _Nonnull)url
            parameters:(id _Nullable)parameters
             loadCache:(BOOL)loadCache
               success:(SJSuccessBlock _Nullable)successBlock
               failure:(SJFailureBlock _Nullable)failureBlock;



/**
 *  This method is used to send POST request,
 consider whether to write cache but not consider whether to load cache
 *
 *  @param url                 request url
 *  @param parameters          parameters
 *  @param cacheDuration       consider whether to write cache
 *  @param successBlock        success callback
 *  @param failureBlock        failure callback
 *
 */
- (void)sendPutRequest:(NSString * _Nonnull)url
            parameters:(id _Nullable)parameters
         cacheDuration:(NSTimeInterval)cacheDuration
               success:(SJSuccessBlock _Nullable)successBlock
               failure:(SJFailureBlock _Nullable)failureBlock;


/**
 *  This method is used to send POST request,
 consider whether to load cache and consider whether to write cache
 *
 *  @param url                 request url
 *  @param parameters          parameters
 *  @param loadCache           consider whether to load cache
 *  @param cacheDuration       consider whether to write cache
 *  @param successBlock        success callback
 *  @param failureBlock        failure callback
 *
 */
- (void)sendPutRequest:(NSString * _Nonnull)url
            parameters:(id _Nullable)parameters
             loadCache:(BOOL)loadCache
         cacheDuration:(NSTimeInterval)cacheDuration
               success:(SJSuccessBlock _Nullable)successBlock
               failure:(SJFailureBlock _Nullable)failureBlock;



#pragma mark- Request API using DELETE method

/**
 *  This method is used to send DELETE request,
 not consider whether to write cache & not consider whether to load cache
 *
 *  @param url                 request url
 *  @param parameters          parameters
 *  @param successBlock        success callback
 *  @param failureBlock        failure callback
 *
 */
- (void)sendDeleteRequest:(NSString * _Nonnull)url
               parameters:(id _Nullable)parameters
                  success:(SJSuccessBlock _Nullable)successBlock
                  failure:(SJFailureBlock _Nullable)failureBlock;



/**
 *  This method is used to send POST request,
 consider whether to load cache but not consider whether to write cache
 *
 *  @param url                 request url
 *  @param parameters          parameters
 *  @param loadCache           consider whether to load cache
 *  @param successBlock        success callback
 *  @param failureBlock        failure callback
 *
 */
- (void)sendDeleteRequest:(NSString * _Nonnull)url
               parameters:(id _Nullable)parameters
                loadCache:(BOOL)loadCache
                  success:(SJSuccessBlock _Nullable)successBlock
                  failure:(SJFailureBlock _Nullable)failureBlock;



/**
 *  This method is used to send POST request,
 consider whether to write cache but not consider whether to load cache
 *
 *  @param url                 request url
 *  @param parameters          parameters
 *  @param cacheDuration       consider whether to write cache
 *  @param successBlock        success callback
 *  @param failureBlock        failure callback
 *
 */
- (void)sendDeleteRequest:(NSString * _Nonnull)url
               parameters:(id _Nullable)parameters
            cacheDuration:(NSTimeInterval)cacheDuration
                  success:(SJSuccessBlock _Nullable)successBlock
                  failure:(SJFailureBlock _Nullable)failureBlock;


/**
 *  This method is used to send POST request,
 consider whether to load cache and consider whether to write cache
 *
 *  @param url                 request url
 *  @param parameters          parameters
 *  @param loadCache           consider whether to load cache
 *  @param cacheDuration       consider whether to write cache
 *  @param successBlock        success callback
 *  @param failureBlock        failure callback
 *
 */
- (void)sendDeleteRequest:(NSString * _Nonnull)url
               parameters:(id _Nullable)parameters
                loadCache:(BOOL)loadCache
            cacheDuration:(NSTimeInterval)cacheDuration
                  success:(SJSuccessBlock _Nullable)successBlock
                  failure:(SJFailureBlock _Nullable)failureBlock;



#pragma mark- Request API using specific parameters

/**
 *  These methods are used to send request with specific parameters:
 
 1. if the parameters object is nil,then send GET request
 2. if the parameters object is not nil,then send POST request
 */


/**
 *  This method is used to send request with specific parameters,
 not consider whether to write cache & not consider whether to load cache
 *
 *  @param url                request url
 *  @param parameters         parameters
 *  @param successBlock       success callback
 *  @param failureBlock       failure callback
 *
 */
- (void)sendRequest:(NSString * _Nonnull)url
         parameters:(id _Nullable)parameters
            success:(SJSuccessBlock _Nullable)successBlock
            failure:(SJFailureBlock _Nullable)failureBlock;



/**
 *  This method is used to send request with specific parameters,
 not consider whether to write cache but consider whether to load cache
 
 *
 *  @param url                request url
 *  @param parameters         parameters
 *  @param loadCache          consider whether to load cache
 *  @param successBlock       success callback
 *  @param failureBlock       failure callback
 *
 */
- (void)sendRequest:(NSString * _Nonnull)url
         parameters:(id _Nullable)parameters
          loadCache:(BOOL)loadCache
            success:(SJSuccessBlock _Nullable)successBlock
            failure:(SJFailureBlock _Nullable)failureBlock;


/**
 * This method is used to send request with specific parameters,
 not consider whether to load cache but consider whether to write cache
 
 *
 *  @param url                request url
 *  @param parameters         parameters
 *  @param cacheDuration      consider whether to write cache
 *  @param successBlock       success callback
 *  @param failureBlock       failure callback
 *
 */
- (void)sendRequest:(NSString * _Nonnull)url
         parameters:(id _Nullable)parameters
      cacheDuration:(NSTimeInterval)cacheDuration
            success:(SJSuccessBlock _Nullable)successBlock
            failure:(SJFailureBlock _Nullable)failureBlock;


/**
 *  This method is used to send request with specific parameters,
 consider whether to load cache and consider whether to write cache
 
 *
 *  @param url                request url
 *  @param parameters         parameters
 *  @param loadCache          consider whether to load cache
 *  @param cacheDuration      consider whether to write cache
 *  @param successBlock       success callback
 *  @param failureBlock       failure callback
 *
 */
- (void)sendRequest:(NSString * _Nonnull)url
         parameters:(id _Nullable)parameters
          loadCache:(BOOL)loadCache
      cacheDuration:(NSTimeInterval)cacheDuration
            success:(SJSuccessBlock _Nullable)successBlock
            failure:(SJFailureBlock _Nullable)failureBlock;




//================== Request API using specific request method ==================//


/**
 *  This method is used to send request with specific method(GET,POST,PUT,DELETE),
 *  not consider whether to write cache & not consider whether to load cache
 *
 *  @param url                 request url
 *  @param method              request method
 *  @param parameters          parameters
 *  @param successBlock        success callback
 *  @param failureBlock        failure callback
 *
 */
- (void)sendRequest:(NSString * _Nonnull)url
             method:(SJRequestMethod)method
         parameters:(id _Nullable)parameters
            success:(SJSuccessBlock _Nullable)successBlock
            failure:(SJFailureBlock _Nullable)failureBlock;




/**
 *  This method is used to send request with specific method(GET,POST,PUT,DELETE),
 *  not consider whether to write cache but consider whether to load cache
 *
 *  @param url                request url
 *  @param method             request method
 *  @param parameters         parameters
 *  @param loadCache          consider whether to load cache
 *  @param successBlock       success callback
 *  @param failureBlock       failure callback
 *
 */
- (void)sendRequest:(NSString * _Nonnull)url
             method:(SJRequestMethod)method
         parameters:(id _Nullable)parameters
          loadCache:(BOOL)loadCache
            success:(SJSuccessBlock _Nullable)successBlock
            failure:(SJFailureBlock _Nullable)failureBlock;






/**
 *  This method is used to send request with specific method(GET,POST,PUT,DELETE),
 *  not consider whether to load cache but consider whether to write cache
 *
 *  @param url                request url
 *  @param method             request method
 *  @param parameters         parameters
 *  @param cacheDuration      consider whether to write cache
 *  @param successBlock       success callback
 *  @param failureBlock       failure callback
 *
 */
- (void)sendRequest:(NSString * _Nonnull)url
             method:(SJRequestMethod)method
         parameters:(id _Nullable)parameters
      cacheDuration:(NSTimeInterval)cacheDuration
            success:(SJSuccessBlock _Nullable)successBlock
            failure:(SJFailureBlock _Nullable)failureBlock;




/**
 *  This method is used to send request with specific method(GET,POST,PUT,DELETE),
 *  consider whether to load cache but consider whether to write cache
 *
 *  @param url                request url
 *  @param method             request method
 *  @param parameters         parameters
 *  @param loadCache          consider whether to load cache
 *  @param cacheDuration      consider whether to write cache
 *  @param successBlock       success callback
 *  @param failureBlock       failure callback
 *
 */
- (void)sendRequest:(NSString * _Nonnull)url
             method:(SJRequestMethod)method
         parameters:(id _Nullable)parameters
          loadCache:(BOOL)loadCache
      cacheDuration:(NSTimeInterval)cacheDuration
            success:(SJSuccessBlock _Nullable)successBlock
            failure:(SJFailureBlock _Nullable)failureBlock;



#pragma mark- Request API upload images


/**
 *  This method is used to upload image
 *
 *  @param url                   request url
 *  @param parameters            parameters
 *  @param image                 UIImage object
 *  @param name                  file name
 *  @param mimeType              file type
 *  @param uploadProgressBlock   upload progress callback
 *  @param uploadSuccessBlock    uploadSuccess allback
 *  @param uploadFailureBlock    upload failure callback
 *
 */
- (void)sendUploadImageRequest:(NSString * _Nonnull)url
                    parameters:(id _Nullable)parameters
                         image:(UIImage * _Nonnull)image
                          name:(NSString * _Nonnull)name
                      mimeType:(NSString * _Nullable)mimeType
                      progress:(SJUploadProgressBlock _Nullable)uploadProgressBlock
                       success:(SJUploadSuccessBlock _Nullable)uploadSuccessBlock
                       failure:(SJUploadFailureBlock _Nullable)uploadFailureBlock;


/**
 *  This method is used to upload image
 *
 *  @param url                   request url
 *  @param ignoreBaseUrl         consider whether to ignore configured base url
 *  @param parameters            parameters
 *  @param image                 UIImage object
 *  @param name                  file name
 *  @param mimeType              file type
 *  @param uploadProgressBlock   upload progress callback
 *  @param uploadSuccessBlock    upload success callback
 *  @param uploadFailureBlock    upload failure callback
 *
 */
- (void)sendUploadImageRequest:(NSString * _Nonnull)url
                 ignoreBaseUrl:(BOOL)ignoreBaseUrl
                    parameters:(id _Nullable)parameters
                         image:(UIImage * _Nonnull)image
                          name:(NSString * _Nonnull)name
                      mimeType:(NSString * _Nullable)mimeType
                      progress:(SJUploadProgressBlock _Nullable)uploadProgressBlock
                       success:(SJUploadSuccessBlock _Nullable)uploadSuccessBlock
                       failure:(SJUploadFailureBlock _Nullable)uploadFailureBlock;



/**
 *  This method is used to upload images(or only one image)
 *
 *  @param url                   request url
 *  @param parameters            parameters
 *  @param images                UIImage object array
 *  @param name                  file name
 *  @param mimeType              file type
 *  @param uploadProgressBlock   upload progress callback
 *  @param uploadSuccessBlock    upload success callback
 *  @param uploadFailureBlock    upload failure callback
 *
 */
- (void)sendUploadImagesRequest:(NSString * _Nonnull)url
                     parameters:(id _Nullable)parameters
                         images:(NSArray<UIImage *> * _Nonnull)images
                           name:(NSString * _Nonnull)name
                       mimeType:(NSString * _Nullable)mimeType
                       progress:(SJUploadProgressBlock _Nullable)uploadProgressBlock
                        success:(SJUploadSuccessBlock _Nullable)uploadSuccessBlock
                        failure:(SJUploadFailureBlock _Nullable)uploadFailureBlock;



/**
 *  This method is used to upload images(or only one image)
 *
 *  @param url                   request url
 *  @param ignoreBaseUrl         consider whether to ignore configured base url
 *  @param parameters            parameters
 *  @param images                UIImage object array
 *  @param name                  file name
 *  @param mimeType              file type
 *  @param uploadProgressBlock   upload progress callback
 *  @param uploadSuccessBlock    upload success callback
 *  @param uploadFailureBlock    upload failure callback
 *
 */
- (void)sendUploadImagesRequest:(NSString * _Nonnull)url
                  ignoreBaseUrl:(BOOL)ignoreBaseUrl
                     parameters:(id _Nullable)parameters
                         images:(NSArray<UIImage *> * _Nonnull)images
                           name:(NSString * _Nonnull)name
                       mimeType:(NSString * _Nullable)mimeType
                       progress:(SJUploadProgressBlock _Nullable)uploadProgressBlock
                        success:(SJUploadSuccessBlock _Nullable)uploadSuccessBlock
                        failure:(SJUploadFailureBlock _Nullable)uploadFailureBlock;



/**
 *  This method is used to upload image
 *
 *  @param url                   request url
 *  @param parameters            parameters
 *  @param image                 UIImage object
 *  @param compressRatio         compress ratio of images
 *  @param name                  file name
 *  @param mimeType              file type
 *  @param uploadProgressBlock   upload progress callback
 *  @param uploadSuccessBlock    upload success callback
 *  @param uploadFailureBlock    upload failure callback
 *
 */
- (void)sendUploadImageRequest:(NSString * _Nonnull)url
                    parameters:(id _Nullable)parameters
                         image:(UIImage * _Nonnull)image
                 compressRatio:(float)compressRatio
                          name:(NSString * _Nonnull)name
                      mimeType:(NSString * _Nullable)mimeType
                      progress:(SJUploadProgressBlock _Nullable)uploadProgressBlock
                       success:(SJUploadSuccessBlock _Nullable)uploadSuccessBlock
                       failure:(SJUploadFailureBlock _Nullable)uploadFailureBlock;


/**
 *  This method is used to upload image
 *
 *  @param url                   request url
 *  @param ignoreBaseUrl         consider whether to ignore configured base url
 *  @param parameters            parameters
 *  @param image                 UIImage object
 *  @param compressRatio         compress ratio of images
 *  @param name                  file name
 *  @param mimeType              file type
 *  @param uploadProgressBlock   upload progress callback
 *  @param uploadSuccessBlock    upload success callback
 *  @param uploadFailureBlock    upload failure callback
 *
 */
- (void)sendUploadImageRequest:(NSString * _Nonnull)url
                 ignoreBaseUrl:(BOOL)ignoreBaseUrl
                    parameters:(id _Nullable)parameters
                         image:(UIImage * _Nonnull)image
                 compressRatio:(float)compressRatio
                          name:(NSString * _Nonnull)name
                      mimeType:(NSString * _Nullable)mimeType
                      progress:(SJUploadProgressBlock _Nullable)uploadProgressBlock
                       success:(SJUploadSuccessBlock _Nullable)uploadSuccessBlock
                       failure:(SJUploadFailureBlock _Nullable)uploadFailureBlock;




/**
 *  This method is used to upload images(or only one image)
 *
 *  @param url                   request url
 *  @param parameters            parameters
 *  @param images                UIImage object array
 *  @param compressRatio         compress ratio of images
 *  @param name                  file name
 *  @param mimeType              file type
 *  @param uploadProgressBlock   upload progress callback
 *  @param uploadSuccessBlock    upload success callback
 *  @param uploadFailureBlock    upload failure callback
 *
 */
- (void)sendUploadImagesRequest:(NSString * _Nonnull)url
                     parameters:(id _Nullable)parameters
                         images:(NSArray<UIImage *> * _Nonnull)images
                  compressRatio:(float)compressRatio
                           name:(NSString * _Nonnull)name
                       mimeType:(NSString * _Nullable)mimeType
                       progress:(SJUploadProgressBlock _Nullable)uploadProgressBlock
                        success:(SJUploadSuccessBlock _Nullable)uploadSuccessBlock
                        failure:(SJUploadFailureBlock _Nullable)uploadFailureBlock;




/**
 *
 *  @param url                   request url
 *  @param ignoreBaseUrl         consider whether to ignore configured base url
 *  @param parameters            parameters
 *  @param images                UIImage object array
 *  @param compressRatio         compress ratio of images
 *  @param name                  file name
 *  @param mimeType              file type
 *  @param uploadProgressBlock   upload progress callback
 *  @param uploadSuccessBlock    upload success callback
 *  @param uploadFailureBlock    upload failure callback
 *
 */
- (void)sendUploadImagesRequest:(NSString * _Nonnull)url
                  ignoreBaseUrl:(BOOL)ignoreBaseUrl
                     parameters:(id _Nullable)parameters
                         images:(NSArray<UIImage *> * _Nonnull)images
                  compressRatio:(float)compressRatio
                           name:(NSString * _Nonnull)name
                       mimeType:(NSString * _Nullable)mimeType
                       progress:(SJUploadProgressBlock _Nullable)uploadProgressBlock
                        success:(SJUploadSuccessBlock _Nullable)uploadSuccessBlock
                        failure:(SJUploadFailureBlock _Nullable)uploadFailureBlock;






#pragma mark- Request API download files

/**
 *  These methods are used to download file
 *
 *  @param url                      request url
 *  @param downloadFilePath         target path of download file
 *  @param downloadProgressBlock    download progress callback
 *  @param downloadSuccessBlock     download success callback
 *  @param downloadFailureBlock     download failure callback
 *
 */
- (void)sendDownloadRequest:(NSString * _Nonnull)url
           downloadFilePath:(NSString *_Nonnull)downloadFilePath
                   progress:(SJDownloadProgressBlock _Nullable)downloadProgressBlock
                    success:(SJDownloadSuccessBlock _Nullable)downloadSuccessBlock
                    failure:(SJDownloadFailureBlock _Nullable)downloadFailureBlock;





/**
 *  These methods are used to download file
 *
 *  @param url                      request url
 *  @param ignoreBaseUrl            consider whether to ignore configured base url
 *  @param downloadFilePath         target path of download file
 *  @param downloadProgressBlock    download progress callback
 *  @param downloadSuccessBlock     download success callback
 *  @param downloadFailureBlock     download failure callback
 *
 */
- (void)sendDownloadRequest:(NSString * _Nonnull)url
              ignoreBaseUrl:(BOOL)ignoreBaseUrl
           downloadFilePath:(NSString *_Nonnull)downloadFilePath
                   progress:(SJDownloadProgressBlock _Nullable)downloadProgressBlock
                    success:(SJDownloadSuccessBlock _Nullable)downloadSuccessBlock
                    failure:(SJDownloadFailureBlock _Nullable)downloadFailureBlock;






/**
 *  These methods are used to download file
 *
 *  @param url                      request url
 *  @param downloadFilePath         target path of download file
 *  @param resumable                consider whether to save or load resumble data
 *  @param downloadProgressBlock    download progress callback
 *  @param downloadSuccessBlock     download success callback
 *  @param downloadFailureBlock     download failure callback
 *
 */
- (void)sendDownloadRequest:(NSString * _Nonnull)url
           downloadFilePath:(NSString *_Nonnull)downloadFilePath
                  resumable:(BOOL)resumable
                   progress:(SJDownloadProgressBlock _Nullable)downloadProgressBlock
                    success:(SJDownloadSuccessBlock _Nullable)downloadSuccessBlock
                    failure:(SJDownloadFailureBlock _Nullable)downloadFailureBlock;



/**
 *  These methods are used to download file
 *
 *  @param url                      request url
 *  @param ignoreBaseUrl            consider whether to ignore configured base url
 *  @param downloadFilePath         target path of download file
 *  @param resumable                consider whether to save or load resumble data
 *  @param downloadProgressBlock    download progress callback
 *  @param downloadSuccessBlock     download success callback
 *  @param downloadFailureBlock     download failure callback
 *
 */
- (void)sendDownloadRequest:(NSString * _Nonnull)url
              ignoreBaseUrl:(BOOL)ignoreBaseUrl
           downloadFilePath:(NSString *_Nonnull)downloadFilePath
                  resumable:(BOOL)resumable
                   progress:(SJDownloadProgressBlock _Nullable)downloadProgressBlock
                    success:(SJDownloadSuccessBlock _Nullable)downloadSuccessBlock
                    failure:(SJDownloadFailureBlock _Nullable)downloadFailureBlock;



/**
 *  These methods are used to download file
 *
 *  @param url                      request url
 *  @param downloadFilePath         target path of download file
 *  @param backgroundSupport        consider whether to support background downlaod
 *  @param downloadProgressBlock    download progress callback
 *  @param downloadSuccessBlock     download success callback
 *  @param downloadFailureBlock     download failure callback
 *
 */
- (void)sendDownloadRequest:(NSString * _Nonnull)url
           downloadFilePath:(NSString *_Nonnull)downloadFilePath
          backgroundSupport:(BOOL)backgroundSupport
                   progress:(SJDownloadProgressBlock _Nullable)downloadProgressBlock
                    success:(SJDownloadSuccessBlock _Nullable)downloadSuccessBlock
                    failure:(SJDownloadFailureBlock _Nullable)downloadFailureBlock;




/**
 *  These methods are used to download file
 *
 *  @param url                      request url
 *  @param ignoreBaseUrl            consider whether to ignore configured base url
 *  @param downloadFilePath         target path of download file
 *  @param backgroundSupport        consider whether to support background downlaod
 *  @param downloadProgressBlock    download progress callback
 *  @param downloadSuccessBlock     download success callback
 *  @param downloadFailureBlock     download failure callback
 *
 */
- (void)sendDownloadRequest:(NSString * _Nonnull)url
              ignoreBaseUrl:(BOOL)ignoreBaseUrl
           downloadFilePath:(NSString *_Nonnull)downloadFilePath
          backgroundSupport:(BOOL)backgroundSupport
                   progress:(SJDownloadProgressBlock _Nullable)downloadProgressBlock
                    success:(SJDownloadSuccessBlock _Nullable)downloadSuccessBlock
                    failure:(SJDownloadFailureBlock _Nullable)downloadFailureBlock;


/**
 *  These methods are used to download file
 *
 *  @param url                      request url
 *  @param downloadFilePath         target path of download file
 *  @param resumable                consider whether to save or load resumble data
 *  @param backgroundSupport        consider whether to support background downlaod
 *  @param downloadProgressBlock    download progress callback
 *  @param downloadSuccessBlock     download success callback
 *  @param downloadFailureBlock     download failure callback
 *
 */
- (void)sendDownloadRequest:(NSString * _Nonnull)url
           downloadFilePath:(NSString *_Nonnull)downloadFilePath
                  resumable:(BOOL)resumable
          backgroundSupport:(BOOL)backgroundSupport
                   progress:(SJDownloadProgressBlock _Nullable)downloadProgressBlock
                    success:(SJDownloadSuccessBlock _Nullable)downloadSuccessBlock
                    failure:(SJDownloadFailureBlock _Nullable)downloadFailureBlock;




/**
 *  These methods are used to download file
 *
 *  @param url                      request url
 *  @param ignoreBaseUrl            consider whether to ignore configured base url
 *  @param downloadFilePath         target path of download file
 *  @param resumable                consider whether to save or load resumble data
 *  @param backgroundSupport        consider whether to support background downlaod
 *  @param downloadProgressBlock    download progress callback
 *  @param downloadSuccessBlock     download success callback
 *  @param downloadFailureBlock     download failure callback
 *
 */
- (void)sendDownloadRequest:(NSString * _Nonnull)url
              ignoreBaseUrl:(BOOL)ignoreBaseUrl
           downloadFilePath:(NSString *_Nonnull)downloadFilePath
                  resumable:(BOOL)resumable
          backgroundSupport:(BOOL)backgroundSupport
                   progress:(SJDownloadProgressBlock _Nullable)downloadProgressBlock
                    success:(SJDownloadSuccessBlock _Nullable)downloadSuccessBlock
                    failure:(SJDownloadFailureBlock _Nullable)downloadFailureBlock;


//=============================== Suspend download requests ==================================//


/**
 *  This method is used to suspend all current download requests
 */
- (void)suspendAllDownloadRequests;





/**
 *  This method is used to suspend a download request with given url
 *
 *  @param url                   download url
 *
 */
- (void)suspendDownloadRequest:(NSString * _Nonnull)url;



/**
 *  This method is used to suspend a download request with given url which contains the baseUrl or not
 *
 *  @param url                   download url
 *  @param ignoreBaseUrl         ignore baseUrl or not
 *
 */
- (void)suspendDownloadRequest:(NSString * _Nonnull)url ignoreBaseUrl:(BOOL)ignoreBaseUrl;



/**
 *  This method is used to suspend one nor more download requests with given urls array
 *
 *  @param urls                   download url array
 *
 */
- (void)suspendDownloadRequests:(NSArray *_Nonnull)urls;




/**
 *  This method is used to suspend one nor more download requests with given urls which contain the baseUrl or not
 *
 *  @param urls                   download url array
 *  @param ignoreBaseUrl          ignore baseUrl or not
 *
 */
- (void)suspendDownloadRequests:(NSArray *_Nonnull)urls ignoreBaseUrl:(BOOL)ignoreBaseUrl;



//=============================== Resume download requests ==================================//

/**
 *  This method is used to resume all suspended download requests
 */
- (void)resumeAllDownloadRequests;




/**
 *  This method is used to resume a suspended request with given url
 *
 *  @param url                   download url
 *
 */
- (void)resumeDownloadReqeust:(NSString *_Nonnull)url;




/**
 *  This method is used to resume a suspended request with given url which contains the baseUrl or not
 *
 *  @param url                   download url
 *  @param ignoreBaseUrl         ignore baseUrl or not
 *
 */
- (void)resumeDownloadReqeust:(NSString *_Nonnull)url ignoreBaseUrl:(BOOL)ignoreBaseUrl;



/**
 *  This method is used to resume one nor more suspended requests with given urls array
 *
 *  @param urls                   download url array
 *
 */
- (void)resumeDownloadReqeusts:(NSArray *_Nonnull)urls;



/**
 *  This method is used to suspend one nor more suspended requests with given urls which contain the baseUrl or not
 *
 *  @param urls                   download url array
 *  @param ignoreBaseUrl          ignore baseUrl or not
 *
 */
- (void)resumeDownloadReqeusts:(NSArray *_Nonnull)urls ignoreBaseUrl:(BOOL)ignoreBaseUrl;





//=============================== Cancel download requests ==================================//


/**
 *  This method is used to cancel all current download requests
 */
- (void)cancelAllDownloadRequests;



/**
 *  This method is used to cancel a current download request with given url
 *
 *  @param url                   download url
 *
 */
- (void)cancelDownloadRequest:(NSString * _Nonnull)url;



/**
 *  This method is used to cancel a current download request with given url which contains the baseUrl or not
 *
 *  @param url                   download url
 *  @param ignoreBaseUrl         ignore baseUrl or not
 *
 */
- (void)cancelDownloadRequest:(NSString * _Nonnull)url ignoreBaseUrl:(BOOL)ignoreBaseUrl;




/**
 *  This method is used to cancel one nor more current download requests with given urls array
 *
 *  @param urls                   download url array
 *
 */
- (void)cancelDownloadRequests:(NSArray *_Nonnull)urls;




/**
 *  This method is used to cancel one nor more current download requests with given urls which contain the baseUrl or not
 *
 *  @param urls                   download url array
 *  @param ignoreBaseUrl          ignore baseUrl or not
 *
 */
- (void)cancelDownloadRequests:(NSArray *_Nonnull)urls ignoreBaseUrl:(BOOL)ignoreBaseUrl;



/**
 *  This method is used to get incomplete download data ratio of a download request with a given download url
 *
 *  @param url                    download url
 *
 *  @return incomplete download data ratio
 */
- (CGFloat)resumeDataRatioOfRequest:(NSString *_Nonnull)url;





/**
 *  This method is used to get incomplete download data ratio of a download request with a given download url which contains the baseUrl or not
 *
 *  @param url                    download url
 *  @param ignoreBaseUrl          ignore baseUrl or not
 *
 *  @return incomplete download data ratio
 */
- (CGFloat)resumeDataRatioOfRequest:(NSString *_Nonnull)url ignoreBaseUrl:(BOOL)ignoreBaseUrl;



#pragma mark- Cancel requests



/**
 *  This method is used to cancel all current requests
 */
- (void)cancelAllCurrentRequests;




/**
 *  This method is used to cancel all current requests corresponding a reqeust url,
    no matter what the method is and parameters are
 *
 *  @param url              request url
 *
 */
- (void)cancelCurrentRequestWithUrl:(NSString * _Nonnull)url;






/**
 *  This method is used to cancel all current requests corresponding a specific reqeust url, method and parameters
 *
 *  @param url              request url
 *  @param method           request method
 *  @param parameters       parameters
 *
 */
- (void)cancelCurrentRequestWithUrl:(NSString * _Nonnull)url
                             method:(NSString * _Nonnull)method
                         parameters:(id _Nullable)parameters;




#pragma mark- Cache operations


//=============================== Load cache ==================================//

/**
 *  This method is used to load cache which is related to a specific url,
    no matter what request method is or parameters are
 *
 *
 *  @param url                  the url of related network requests
 *  @param completionBlock      callback
 *
 */
- (void)loadCacheWithUrl:(NSString * _Nonnull)url
         completionBlock:(SJLoadCacheArrCompletionBlock _Nullable)completionBlock;






/**
 *  This method is used to load cache which is related to a specific url,method and parameters
 *
 *  @param url                  the url of the network request
 *  @param method               the method of the network request
 *  @param parameters           the parameters of the network request
 *  @param completionBlock      callback
 *
 */
- (void)loadCacheWithUrl:(NSString * _Nonnull)url
                  method:(NSString * _Nonnull)method
              parameters:(id _Nullable)parameters
         completionBlock:(SJLoadCacheCompletionBlock _Nullable)completionBlock;




//=============================== calculate cache ===========================//

/**
 *  This method is used to calculate the size of the cache folder
 *
 *  @param completionBlock      callback
 *
 */
- (void)calculateCacheSizeCompletionBlock:(SJCalculateSizeCompletionBlock _Nullable)completionBlock;





//================================= clear cache ==============================//

/**
 *  This method is used to clear all cache which is in the cache file
 *
 *  @param completionBlock      callback
 *
 */
- (void)clearAllCacheCompletionBlock:(SJClearCacheCompletionBlock _Nullable)completionBlock;




/**
 *  This method is used to clear the cache which is related the specific url,
    no matter what request method is or parameters are
 *
 *  @param url                   the url of network request
 *  @param completionBlock       callback
 *
 */
- (void)clearCacheWithUrl:(NSString * _Nonnull)url
          completionBlock:(SJClearCacheCompletionBlock _Nullable)completionBlock;





- (void)clearCacheWithUrl:(NSString * _Nonnull)url
                   method:(NSString * _Nonnull)method
          completionBlock:(SJClearCacheCompletionBlock _Nullable)completionBlock;




/**
 *  This method is used to clear the cache which is related the specific url and method,
    no matter what parameters are
 *
 *  @param url                   the url of network request
 *  @param method                request method
 *  @param completionBlock       callback
 *
 */
- (void)loadCacheWithUrl:(NSString * _Nonnull)url
                  method:(NSString * _Nonnull)method
         completionBlock:(SJLoadCacheArrCompletionBlock _Nullable)completionBlock;



/**
 *  This method is used to clear cache which is related to a specific url,method and parameters
 *
 *  @param url                  the url of the network request
 *  @param method               the method of the network request
 *  @param parameters           the parameters of the network request
 *  @param completionBlock      callback
 *
 */
- (void)clearCacheWithUrl:(NSString * _Nonnull)url
                   method:(NSString * _Nonnull)method
               parameters:(id _Nonnull)parameters
          completionBlock:(SJClearCacheCompletionBlock _Nullable)completionBlock;



#pragma mark- Request Info


/**
 *  This method is used to log all current requests' information
 */
- (void)logAllCurrentRequests;



/**
 *  This method is used to check if there is remaining curent request
 *
 *  @return if there is remaining requests
 */
- (BOOL)remainingCurrentRequests;



/**
 *  This method is used to calculate the count of current requests
 *
 *  @return the count of current requests
 */
- (NSInteger)currentRequestCount;




@end


================================================
FILE: SJNetwork/SJNetworkManager.m
================================================
//
//  SJNetworkManager.m
//  SJNetwork
//
//  Created by Sun Shijie on 2017/8/16.
//  Copyright © 2017年 Shijie. All rights reserved.
//

#import "SJNetworkManager.h"

#import "SJNetworkConfig.h"
#import "SJNetworkRequestPool.h"

#import "SJNetworkRequestEngine.h"
#import "SJNetworkUploadEngine.h"
#import "SJNetworkDownloadEngine.h"

@interface SJNetworkManager()

@property (nonatomic, strong) SJNetworkRequestEngine *requestEngine;
@property (nonatomic, strong) SJNetworkUploadEngine *uploadEngine;
@property (nonatomic, strong) SJNetworkDownloadEngine *downloadEngine;

@property (nonatomic, strong) SJNetworkRequestPool *requestPool;
@property (nonatomic, strong) SJNetworkCacheManager *cacheManager;

@end


@implementation SJNetworkManager


#pragma mark- ============== Life Cycle ===========

+ (SJNetworkManager *_Nullable)sharedManager {
    
    static SJNetworkManager *sharedManager = NULL;
    static dispatch_once_t onceToken;
    
    dispatch_once(&onceToken, ^{
         sharedManager = [[SJNetworkManager alloc] init];
    });
    return sharedManager;
}


- (void)dealloc{
    
    [self cancelAllCurrentRequests];
}

#pragma mark- ============== Public Methods ==============


- (void)addCustomHeader:(NSDictionary *_Nonnull)header{
    
    [[SJNetworkConfig sharedConfig] addCustomHeader:header];
}




- (NSDictionary *_Nullable)customHeaders{
    
    return [SJNetworkConfig sharedConfig].customHeaders;
}


#pragma mark- ============== Request API using GET method ==============


- (void)sendGetRequest:(NSString * _Nonnull)url
               success:(SJSuccessBlock _Nullable)successBlock
               failure:(SJFailureBlock _Nullable)failureBlock{
    
     [self.requestEngine sendRequest:url
                              method:SJRequestMethodGET
                          parameters:nil
                           loadCache:NO
                       cacheDuration:0
                             success:successBlock
                             failure:failureBlock];
    
}





- (void)sendGetRequest:(NSString * _Nonnull)url
            parameters:(id _Nullable)parameters
               success:(SJSuccessBlock _Nullable)successBlock
               failure:(SJFailureBlock _Nullable)failureBlock{
    
     [self.requestEngine sendRequest:url
                              method:SJRequestMethodGET
                          parameters:parameters
                           loadCache:NO
                       cacheDuration:0
                             success:successBlock
                             failure:failureBlock];
}




- (void)sendGetRequest:(NSString * _Nonnull)url
            parameters:(id _Nullable)parameters
             loadCache:(BOOL)loadCache
               success:(SJSuccessBlock _Nullable)successBlock
               failure:(SJFailureBlock _Nullable)failureBlock{
    
     [self.requestEngine sendRequest:url
                              method:SJRequestMethodGET
                          parameters:parameters
                           loadCache:loadCache
                       cacheDuration:0
                             success:successBlock
                             failure:failureBlock];
}





- (void)sendGetRequest:(NSString * _Nonnull)url
            parameters:(id _Nullable)parameters
         cacheDuration:(NSTimeInterval)cacheDuration
               success:(SJSuccessBlock _Nullable)successBlock
               failure:(SJFailureBlock _Nullable)failureBlock{

     [self.requestEngine sendRequest:url
                              method:SJRequestMethodGET
                          parameters:parameters
                           loadCache:NO
                       cacheDuration:cacheDuration
                             success:successBlock
                             failure:failureBlock];
}





- (void)sendGetRequest:(NSString * _Nonnull)url
            parameters:(id _Nullable)parameters
             loadCache:(BOOL)loadCache
         cacheDuration:(NSTimeInterval)cacheDuration
               success:(SJSuccessBlock _Nullable)successBlock
               failure:(SJFailureBlock _Nullable)failureBlock{
    
     [self.requestEngine sendRequest:url
                              method:SJRequestMethodGET
                          parameters:parameters
                           loadCache:loadCache
                       cacheDuration:cacheDuration
                             success:successBlock
                             failure:failureBlock];
}



#pragma mark- ============== Request API using POST method ==============

- (void)sendPostRequest:(NSString * _Nonnull)url
             parameters:(id _Nullable)parameters
                success:(SJSuccessBlock _Nullable)successBlock
                failure:(SJFailureBlock _Nullable)failureBlock{
    
     [self.requestEngine sendRequest:url
                              method:SJRequestMethodPOST
                          parameters:parameters
                           loadCache:NO
                       cacheDuration:0
                             success:successBlock
                             failure:failureBlock];
}




- (void)sendPostRequest:(NSString * _Nonnull)url
             parameters:(id _Nullable)parameters
              loadCache:(BOOL)loadCache
                success:(SJSuccessBlock _Nullable)successBlock
                failure:(SJFailureBlock _Nullable)failureBlock{
    
     [self.requestEngine sendRequest:url
                              method:SJRequestMethodPOST
                          parameters:parameters
                           loadCache:loadCache
                       cacheDuration:0
                             success:successBlock
                             failure:failureBlock];
}



- (void)sendPostRequest:(NSString * _Nonnull)url
             parameters:(id _Nullable)parameters
          cacheDuration:(NSTimeInterval)cacheDuration
                success:(SJSuccessBlock _Nullable)successBlock
                failure:(SJFailureBlock _Nullable)failureBlock{
    
     [self.requestEngine sendRequest:url
                              method:SJRequestMethodPOST
                          parameters:parameters
                           loadCache:NO
                       cacheDuration:cacheDuration
                             success:successBlock
                             failure:failureBlock];
}





- (void)sendPostRequest:(NSString * _Nonnull)url
             parameters:(id _Nullable)parameters
              loadCache:(BOOL)loadCache
          cacheDuration:(NSTimeInterval)cacheDuration
                success:(SJSuccessBlock _Nullable)successBlock
                failure:(SJFailureBlock _Nullable)failureBlock{
    
     [self.requestEngine sendRequest:url
                              method:SJRequestMethodPOST
                          parameters:parameters
                           loadCache:loadCache
                       cacheDuration:cacheDuration
                             success:successBlock
                             failure:failureBlock];
}





#pragma mark- ============== Request API using PUT method ==============

- (void)sendPutRequest:(NSString * _Nonnull)url
            parameters:(id _Nullable)parameters
               success:(SJSuccessBlock _Nullable)successBlock
               failure:(SJFailureBlock _Nullable)failureBlock{
    
     [self.requestEngine sendRequest:url
                              method:SJRequestMethodPUT
                          parameters:parameters
                           loadCache:NO
                       cacheDuration:0
                             success:successBlock
                             failure:failureBlock];
}




- (void)sendPutRequest:(NSString * _Nonnull)url
            parameters:(id _Nullable)parameters
             loadCache:(BOOL)loadCache
               success:(SJSuccessBlock _Nullable)successBlock
               failure:(SJFailureBlock _Nullable)failureBlock{
    
     [self.requestEngine sendRequest:url
                              method:SJRequestMethodPUT
                          parameters:parameters
                           loadCache:loadCache
                       cacheDuration:0
                             success:successBlock
                             failure:failureBlock];
}




- (void)sendPutRequest:(NSString * _Nonnull)url
            parameters:(id _Nullable)parameters
         cacheDuration:(NSTimeInterval)cacheDuration
               success:(SJSuccessBlock _Nullable)successBlock
               failure:(SJFailureBlock _Nullable)failureBlock{
    
     [self.requestEngine sendRequest:url
                              method:SJRequestMethodPUT
                          parameters:parameters
                           loadCache:NO
                       cacheDuration:cacheDuration
                             success:successBlock
                             failure:failureBlock];
}




- (void)sendPutRequest:(NSString * _Nonnull)url
            parameters:(id _Nullable)parameters
             loadCache:(BOOL)loadCache
         cacheDuration:(NSTimeInterval)cacheDuration
               success:(SJSuccessBlock _Nullable)successBlock
               failure:(SJFailureBlock _Nullable)failureBlock{
    
     [self.requestEngine sendRequest:url
                              method:SJRequestMethodPUT
                          parameters:parameters
                           loadCache:loadCache
                       cacheDuration:cacheDuration
                             success:successBlock
                             failure:failureBlock];
    
}



#pragma mark- ============== Request API using DELETE method ==============

- (void)sendDeleteRequest:(NSString * _Nonnull)url
               parameters:(id _Nullable)parameters
                  success:(SJSuccessBlock _Nullable)successBlock
                  failure:(SJFailureBlock _Nullable)failureBlock{

     [self.requestEngine sendRequest:url
                              method:SJRequestMethodDELETE
                          parameters:parameters
                           loadCache:NO
                       cacheDuration:0
                             success:successBlock
                             failure:failureBlock];
}



- (void)sendDeleteRequest:(NSString * _Nonnull)url
               parameters:(id _Nullable)parameters
                loadCache:(BOOL)loadCache
                  success:(SJSuccessBlock _Nullable)successBlock
                  failure:(SJFailureBlock _Nullable)failureBlock{
    
     [self.requestEngine sendRequest:url
                              method:SJRequestMethodDELETE
                          parameters:parameters
                           loadCache:loadCache
                       cacheDuration:0
                             success:successBlock
                             failure:failureBlock];
    
}




- (void)sendDeleteRequest:(NSString * _Nonnull)url
               parameters:(id _Nullable)parameters
            cacheDuration:(NSTimeInterval)cacheDuration
                  success:(SJSuccessBlock _Nullable)successBlock
                  failure:(SJFailureBlock _Nullable)failureBlock{
    
     [self.requestEngine sendRequest:url
                              method:SJRequestMethodDELETE
                          parameters:parameters
                           loadCache:NO
                       cacheDuration:cacheDuration
                             success:successBlock
                             failure:failureBlock];
}




- (void)sendDeleteRequest:(NSString * _Nonnull)url
               parameters:(id _Nullable)parameters
                loadCache:(BOOL)loadCache
            cacheDuration:(NSTimeInterval)cacheDuration
                  success:(SJSuccessBlock _Nullable)successBlock
                  failure:(SJFailureBlock _Nullable)failureBlock{
    
     [self.requestEngine sendRequest:url
                              method:SJRequestMethodDELETE
                          parameters:parameters
                           loadCache:loadCache
                       cacheDuration:cacheDuration
                             success:successBlock
                             failure:failureBlock];
}





#pragma mark- ============== Request API using specific parameters ==============


- (void)sendRequest:(NSString * _Nonnull)url
         parameters:(id _Nullable)parameters
            success:(SJSuccessBlock _Nullable)successBlock
            failure:(SJFailureBlock _Nullable)failureBlock{
    
    if (parameters) {
        
         [self.requestEngine sendRequest:url
                                  method:SJRequestMethodPOST
                              parameters:parameters
                               loadCache:NO
                           cacheDuration:0
                                 success:successBlock
                                 failure:failureBlock];
        
    }else{
        
         [self.requestEngine sendRequest:url
                                  method:SJRequestMethodGET
                              parameters:nil
                               loadCache:NO
                           cacheDuration:0
                                 success:successBlock
                                 failure:failureBlock];
    }
}





- (void)sendRequest:(NSString * _Nonnull)url
         parameters:(id _Nullable)parameters
          loadCache:(BOOL)loadCache
            success:(SJSuccessBlock _Nullable)successBlock
            failure:(SJFailureBlock _Nullable)failureBlock{
    
    if (parameters) {
    
         [self.requestEngine sendRequest:url
                                  method:SJRequestMethodPOST
                              parameters:parameters
                               loadCache:loadCache
                           cacheDuration:0
                                 success:successBlock
                                 failure:failureBlock];
        
    }else{
        
         [self.requestEngine sendRequest:url
                                  method:SJRequestMethodGET
                              parameters:nil
                               loadCache:loadCache
                           cacheDuration:0
                                 success:successBlock
                                 failure:failureBlock];
    }
}




- (void)sendRequest:(NSString * _Nonnull)url
         parameters:(id _Nullable)parameters
      cacheDuration:(NSTimeInterval)cacheDuration
            success:(SJSuccessBlock _Nullable)successBlock
            failure:(SJFailureBlock _Nullable)failureBlock{
    
    if (parameters) {
        
         [self.requestEngine sendRequest:url
                                  method:SJRequestMethodPOST
                              parameters:parameters
                               loadCache:NO
                           cacheDuration:cacheDuration
                                 success:successBlock
                                 failure:failureBlock];
        
    }else{
        
        
         [self.requestEngine sendRequest:url
                                  method:SJRequestMethodGET
                              parameters:nil
                               loadCache:NO
                           cacheDuration:cacheDuration
                                 success:successBlock
                                 failure:failureBlock];
    }
}




- (void)sendRequest:(NSString * _Nonnull)url
         parameters:(id _Nullable)parameters
          loadCache:(BOOL)loadCache
      cacheDuration:(NSTimeInterval)cacheDuration
            success:(SJSuccessBlock _Nullable)successBlock
            failure:(SJFailureBlock _Nullable)failureBlock{
    
    
    if (parameters) {
        
         [self.requestEngine sendRequest:url
                                  method:SJRequestMethodPOST
                              parameters:parameters
                               loadCache:loadCache
                           cacheDuration:cacheDuration
                                 success:successBlock
                                 failure:failureBlock];
    }else{
        
        
         [self.requestEngine sendRequest:url
                                  method:SJRequestMethodGET
                              parameters:nil
                               loadCache:loadCache
                           cacheDuration:cacheDuration
                                 success:successBlock
                                 failure:failureBlock];
    }
}



#pragma mark- ============== Request API using specific request method ==============

- (void)sendRequest:(NSString * _Nonnull)url
             method:(SJRequestMethod)method
         parameters:(id _Nullable)parameters
            success:(SJSuccessBlock _Nullable)successBlock
            failure:(SJFailureBlock _Nullable)failureBlock{
  
     [self.requestEngine sendRequest:url
                              method:method
                          parameters:parameters
                           loadCache:NO
                       cacheDuration:0
                             success:successBlock
                             failure:failureBlock];
}




- (void)sendRequest:(NSString * _Nonnull)url
             method:(SJRequestMethod)method
         parameters:(id _Nullable)parameters
          loadCache:(BOOL)loadCache
            success:(SJSuccessBlock _Nullable)successBlock
            failure:(SJFailureBlock _Nullable)failureBlock{
    
     [self.requestEngine sendRequest:url
                              method:method
                          parameters:parameters
                           loadCache:loadCache
                       cacheDuration:0
                             success:successBlock
                             failure:failureBlock];
}




- (void)sendRequest:(NSString * _Nonnull)url
             method:(SJRequestMethod)method
         parameters:(id _Nullable)parameters
      cacheDuration:(NSTimeInterval)cacheDuration
            success:(SJSuccessBlock _Nullable)successBlock
            failure:(SJFailureBlock _Nullable)failureBlock{
    
     [self.requestEngine sendRequest:url
                              method:method
                          parameters:parameters
                           loadCache:NO
                       cacheDuration:cacheDuration
                             success:successBlock
                             failure:failureBlock];
}




- (void)sendRequest:(NSString * _Nonnull)url
             method:(SJRequestMethod)method
         parameters:(id _Nullable)parameters
          loadCache:(BOOL)loadCache
      cacheDuration:(NSTimeInterval)cacheDuration
            success:(SJSuccessBlock _Nullable)successBlock
            failure:(SJFailureBlock _Nullable)failureBlock{
    
     [self.requestEngine sendRequest:url
                              method:method
                          parameters:parameters
                           loadCache:loadCache
                       cacheDuration:cacheDuration
                             success:successBlock
                             failure:failureBlock];
}




#pragma mark- ============== Request API uploading ==============


- (void)sendUploadImageRequest:(NSString * _Nonnull)url
                    parameters:(id _Nullable)parameters
                         image:(UIImage * _Nonnull)image
                          name:(NSString * _Nonnull)name
                      mimeType:(NSString * _Nullable)mimeType
                      progress:(SJUploadProgressBlock _Nullable)uploadProgressBlock
                       success:(SJUploadSuccessBlock _Nullable)uploadSuccessBlock
                       failure:(SJUploadFailureBlock _Nullable)uploadFailureBlock{
    
    
     [self.uploadEngine sendUploadImagesRequest:url
                                  ignoreBaseUrl:NO
                                     parameters:parameters
                                         images:@[image]
                                  compressRatio:1
                                           name:name
                                       mimeType:mimeType
                                       progress:uploadProgressBlock
                                        success:uploadSuccessBlock
                                        failure:uploadFailureBlock];
}





- (void)sendUploadImageRequest:(NSString * _Nonnull)url
                 ignoreBaseUrl:(BOOL)ignoreBaseUrl
                    parameters:(id _Nullable)parameters
                         image:(UIImage * _Nonnull)image
                          name:(NSString * _Nonnull)name
                      mimeType:(NSString * _Nullable)mimeType
                      progress:(SJUploadProgressBlock _Nullable)uploadProgressBlock
                       success:(SJUploadSuccessBlock _Nullable)uploadSuccessBlock
                       failure:(SJUploadFailureBlock _Nullable)uploadFailureBlock{

    
     [self.uploadEngine sendUploadImagesRequest:url
                                  ignoreBaseUrl:ignoreBaseUrl
                                     parameters:parameters
                                         images:@[image]
                                  compressRatio:1
                                           name:name
                                       mimeType:mimeType
                                       progress:uploadProgressBlock
                                        success:uploadSuccessBlock
                                        failure:uploadFailureBlock];

}




- (void)sendUploadImagesRequest:(NSString * _Nonnull)url
                     parameters:(id _Nullable)parameters
                         images:(NSArray<UIImage *> * _Nonnull)images
                           name:(NSString * _Nonnull)name
                       mimeType:(NSString * _Nullable)mimeType
                       progress:(SJUploadProgressBlock _Nullable)uploadProgressBlock
                        success:(SJUploadSuccessBlock _Nullable)uploadSuccessBlock
                        failure:(SJUploadFailureBlock _Nullable)uploadFailureBlock{
    
    
     [self.uploadEngine sendUploadImagesRequest:url
                                  ignoreBaseUrl:NO
                                     parameters:parameters
                                         images:images
                                  compressRatio:1
                                           name:name
                                       mimeType:mimeType
                                       progress:uploadProgressBlock
                                        success:uploadSuccessBlock
                                        failure:uploadFailureBlock];

}




- (void)sendUploadImagesRequest:(NSString * _Nonnull)url
                  ignoreBaseUrl:(BOOL)ignoreBaseUrl
                     parameters:(id _Nullable)parameters
                         images:(NSArray<UIImage *> * _Nonnull)images
                           name:(NSString * _Nonnull)name
                       mimeType:(NSString * _Nullable)mimeType
                       progress:(SJUploadProgressBlock _Nullable)uploadProgressBlock
                        success:(SJUploadSuccessBlock _Nullable)uploadSuccessBlock
                        failure:(SJUploadFailureBlock _Nullable)uploadFailureBlock{
    
    
     [self.uploadEngine sendUploadImagesRequest:url
                                  ignoreBaseUrl:ignoreBaseUrl
                                     parameters:parameters
                                         images:images
                                  compressRatio:1
                                           name:name
                                       mimeType:mimeType
                                       progress:uploadProgressBlock
                                        success:uploadSuccessBlock
                                        failure:uploadFailureBlock];
 
}




- (void)sendUploadImageRequest:(NSString * _Nonnull)url
                    parameters:(id _Nullable)parameters
                         image:(UIImage * _Nonnull)image
                 compressRatio:(float)compressRatio
                          name:(NSString * _Nonnull)name
                      mimeType:(NSString * _Nullable)mimeType
                      progress:(SJUploadProgressBlock _Nullable)uploadProgressBlock
                       success:(SJUploadSuccessBlock _Nullable)uploadSuccessBlock
                       failure:(SJUploadFailureBlock _Nullable)uploadFailureBlock{
    
     [self.uploadEngine sendUploadImagesRequest:url
                                  ignoreBaseUrl:NO
                                     parameters:parameters
                                         images:@[image]
                                  compressRatio:compressRatio
                                           name:name
                                       mimeType:mimeType
                                       progress:uploadProgressBlock
                                        success:uploadSuccessBlock
                                        failure:uploadFailureBlock];
    

}




- (void)sendUploadImageRequest:(NSString * _Nonnull)url
                 ignoreBaseUrl:(BOOL)ignoreBaseUrl
                    parameters:(id _Nullable)parameters
                         image:(UIImage * _Nonnull)image
                 compressRatio:(float)compressRatio
                          name:(NSString * _Nonnull)name
                      mimeType:(NSString * _Nullable)mimeType
                      progress:(SJUploadProgressBlock _Nullable)uploadProgressBlock
                       success:(SJUploadSuccessBlock _Nullable)uploadSuccessBlock
                       failure:(SJUploadFailureBlock _Nullable)uploadFailureBlock{

    
     [self.uploadEngine sendUploadImagesRequest:url
                                  ignoreBaseUrl:ignoreBaseUrl
                                     parameters:parameters
                                         images:@[image]
                                  compressRatio:compressRatio
                                           name:name
                                       mimeType:mimeType
                                       progress:uploadProgressBlock
                                        success:uploadSuccessBlock
                                        failure:uploadFailureBlock];
}




- (void)sendUploadImagesRequest:(NSString * _Nonnull)url
                     parameters:(id _Nullable)parameters
                         images:(NSArray<UIImage *> * _Nonnull)images
                  compressRatio:(float)compressRatio
                           name:(NSString * _Nonnull)name
                       mimeType:(NSString * _Nullable)mimeType
                       progress:(SJUploadProgressBlock _Nullable)uploadProgressBlock
                        success:(SJUploadSuccessBlock _Nullable)uploadSuccessBlock
                        failure:(SJUploadFailureBlock _Nullable)uploadFailureBlock{
    
     [self.uploadEngine sendUploadImagesRequest:url
                                  ignoreBaseUrl:NO
                                     parameters:parameters
                                         images:images
                                  compressRatio:compressRatio
                                           name:name
                                       mimeType:mimeType
                                       progress:uploadProgressBlock
                                        success:uploadSuccessBlock
                                        failure:uploadFailureBlock];
}




- (void)sendUploadImagesRequest:(NSString * _Nonnull)url
                  ignoreBaseUrl:(BOOL)ignoreBaseUrl
                     parameters:(id _Nullable)parameters
                         images:(NSArray<UIImage *> * _Nonnull)images
                  compressRatio:(float)compressRatio
                           name:(NSString * _Nonnull)name
                       mimeType:(NSString * _Nullable)mimeType
                       progress:(SJUploadProgressBlock _Nullable)uploadProgressBlock
                        success:(SJUploadSuccessBlock _Nullable)uploadSuccessBlock
                        failure:(SJUploadFailureBlock _Nullable)uploadFailureBlock{

     [self.uploadEngine sendUploadImagesRequest:url
                                  ignoreBaseUrl:ignoreBaseUrl
                                     parameters:parameters
                                         images:images
                                  compressRatio:compressRatio
                                           name:name
                                       mimeType:mimeType
                                       progress:uploadProgressBlock
                                        success:uploadSuccessBlock
                                        failure:uploadFailureBlock];
}




#pragma mark- ============== Request API downloading ==============

- (void)sendDownloadRequest:(NSString * _Nonnull)url
           downloadFilePath:(NSString *_Nonnull)downloadFilePath
                   progress:(SJDownloadProgressBlock _Nullable)downloadProgressBlock
                    success:(SJDownloadSuccessBlock _Nullable)downloadSuccessBlock
                    failure:(SJDownloadFailureBlock _Nullable)downloadFailureBlock{
    
    
     [self.downloadEngine sendDownloadRequest:url
                                ignoreBaseUrl:NO
                             downloadFilePath:downloadFilePath
                                    resumable:YES
                            backgroundSupport:NO
                                     progress:downloadProgressBlock
                                      success:downloadSuccessBlock
                                      failure:downloadFailureBlock];
}



- (void)sendDownloadRequest:(NSString * _Nonnull)url
              ignoreBaseUrl:(BOOL)ignoreBaseUrl
           downloadFilePath:(NSString *_Nonnull)downloadFilePath
                   progress:(SJDownloadProgressBlock _Nullable)downloadProgressBlock
                    success:(SJDownloadSuccessBlock _Nullable)downloadSuccessBlock
                    failure:(SJDownloadFailureBlock _Nullable)downloadFailureBlock{
    
    
     [self.downloadEngine sendDownloadRequest:url
                                ignoreBaseUrl:ignoreBaseUrl
                             downloadFilePath:downloadFilePath
                                    resumable:YES
                            backgroundSupport:NO
                                     progress:downloadProgressBlock
                                      success:downloadSuccessBlock
                                      failure:downloadFailureBlock];
    
}




- (void)sendDownloadRequest:(NSString * _Nonnull)url
           downloadFilePath:(NSString *_Nonnull)downloadFilePath
                  resumable:(BOOL)resumable
                   progress:(SJDownloadProgressBlock _Nullable)downloadProgressBlock
                    success:(SJDownloadSuccessBlock _Nullable)downloadSuccessBlock
                    failure:(SJDownloadFailureBlock _Nullable)downloadFailureBlock{
    
    
     [self.downloadEngine sendDownloadRequest:url
                                ignoreBaseUrl:NO
                             downloadFilePath:downloadFilePath
                                    resumable:resumable
                            backgroundSupport:NO
                                     progress:downloadProgressBlock
                                      success:downloadSuccessBlock
                                      failure:downloadFailureBlock];
}




- (void)sendDownloadRequest:(NSString * _Nonnull)url
              ignoreBaseUrl:(BOOL)ignoreBaseUrl
           downloadFilePath:(NSString *_Nonnull)downloadFilePath
                  resumable:(BOOL)resumable
                   progress:(SJDownloadProgressBlock _Nullable)downloadProgressBlock
                    success:(SJDownloadSuccessBlock _Nullable)downloadSuccessBlock
                    failure:(SJDownloadFailureBlock _Nullable)downloadFailureBlock{
    
    
     [self.downloadEngine sendDownloadRequest:url
                                ignoreBaseUrl:ignoreBaseUrl
                             downloadFilePath:downloadFilePath
                                    resumable:resumable
                            backgroundSupport:NO
                                     progress:downloadProgressBlock
                                      success:downloadSuccessBlock
                                      failure:downloadFailureBlock];
}




- (void)sendDownloadRequest:(NSString * _Nonnull)url
           downloadFilePath:(NSString *_Nonnull)downloadFilePath
          backgroundSupport:(BOOL)backgroundSupport
                   progress:(SJDownloadProgressBlock _Nullable)downloadProgressBlock
                    success:(SJDownloadSuccessBlock _Nullable)downloadSuccessBlock
                    failure:(SJDownloadFailureBlock _Nullable)downloadFailureBlock{
    
     [self.downloadEngine sendDownloadRequest:url
                                ignoreBaseUrl:NO
                             downloadFilePath:downloadFilePath
                                    resumable:YES
                            backgroundSupport:NO
                                     progress:downloadProgressBlock
                                      success:downloadSuccessBlock
                                      failure:downloadFailureBlock];
    
}




- (void)sendDownloadRequest:(NSString * _Nonnull)url
              ignoreBaseUrl:(BOOL)ignoreBaseUrl
           downloadFilePath:(NSString *_Nonnull)downloadFilePath
          backgroundSupport:(BOOL)backgroundSupport
                   progress:(SJDownloadProgressBlock _Nullable)downloadProgressBlock
                    success:(SJDownloadSuccessBlock _Nullable)downloadSuccessBlock
                    failure:(SJDownloadFailureBlock _Nullable)downloadFailureBlock{
    
     [self.downloadEngine sendDownloadRequest:url
                                ignoreBaseUrl:ignoreBaseUrl
                             downloadFilePath:downloadFilePath
                                    resumable:YES
                            backg
Download .txt
gitextract_p9vvfh07/

├── LICENSE
├── README.md
├── SJNetwork/
│   ├── SJNetwork.h
│   ├── SJNetworkBaseEngine.h
│   ├── SJNetworkBaseEngine.m
│   ├── SJNetworkCacheInfo.h
│   ├── SJNetworkCacheInfo.m
│   ├── SJNetworkCacheManager.h
│   ├── SJNetworkCacheManager.m
│   ├── SJNetworkConfig.h
│   ├── SJNetworkConfig.m
│   ├── SJNetworkDownloadEngine.h
│   ├── SJNetworkDownloadEngine.m
│   ├── SJNetworkDownloadResumeDataInfo.h
│   ├── SJNetworkDownloadResumeDataInfo.m
│   ├── SJNetworkHeader.h
│   ├── SJNetworkManager.h
│   ├── SJNetworkManager.m
│   ├── SJNetworkProtocol.h
│   ├── SJNetworkRequestEngine.h
│   ├── SJNetworkRequestEngine.m
│   ├── SJNetworkRequestModel.h
│   ├── SJNetworkRequestModel.m
│   ├── SJNetworkRequestPool.h
│   ├── SJNetworkRequestPool.m
│   ├── SJNetworkUploadEngine.h
│   ├── SJNetworkUploadEngine.m
│   ├── SJNetworkUtils.h
│   └── SJNetworkUtils.m
├── SJNetwork.podspec
└── SJNetworkDemo/
    ├── Podfile
    ├── Pods/
    │   ├── AFNetworking/
    │   │   ├── AFNetworking/
    │   │   │   ├── AFHTTPSessionManager.h
    │   │   │   ├── AFHTTPSessionManager.m
    │   │   │   ├── AFNetworkReachabilityManager.h
    │   │   │   ├── AFNetworkReachabilityManager.m
    │   │   │   ├── AFNetworking.h
    │   │   │   ├── AFSecurityPolicy.h
    │   │   │   ├── AFSecurityPolicy.m
    │   │   │   ├── AFURLRequestSerialization.h
    │   │   │   ├── AFURLRequestSerialization.m
    │   │   │   ├── AFURLResponseSerialization.h
    │   │   │   ├── AFURLResponseSerialization.m
    │   │   │   ├── AFURLSessionManager.h
    │   │   │   └── AFURLSessionManager.m
    │   │   ├── LICENSE
    │   │   ├── README.md
    │   │   └── UIKit+AFNetworking/
    │   │       ├── AFAutoPurgingImageCache.h
    │   │       ├── AFAutoPurgingImageCache.m
    │   │       ├── AFImageDownloader.h
    │   │       ├── AFImageDownloader.m
    │   │       ├── AFNetworkActivityIndicatorManager.h
    │   │       ├── AFNetworkActivityIndicatorManager.m
    │   │       ├── UIActivityIndicatorView+AFNetworking.h
    │   │       ├── UIActivityIndicatorView+AFNetworking.m
    │   │       ├── UIButton+AFNetworking.h
    │   │       ├── UIButton+AFNetworking.m
    │   │       ├── UIImage+AFNetworking.h
    │   │       ├── UIImageView+AFNetworking.h
    │   │       ├── UIImageView+AFNetworking.m
    │   │       ├── UIKit+AFNetworking.h
    │   │       ├── UIProgressView+AFNetworking.h
    │   │       ├── UIProgressView+AFNetworking.m
    │   │       ├── UIRefreshControl+AFNetworking.h
    │   │       ├── UIRefreshControl+AFNetworking.m
    │   │       ├── UIWebView+AFNetworking.h
    │   │       └── UIWebView+AFNetworking.m
    │   ├── Pods.xcodeproj/
    │   │   ├── project.pbxproj
    │   │   └── xcuserdata/
    │   │       └── SunShijie.xcuserdatad/
    │   │           └── xcschemes/
    │   │               ├── AFNetworking.xcscheme
    │   │               ├── Pods-SJNetworkingDemo.xcscheme
    │   │               └── xcschememanagement.plist
    │   └── Target Support Files/
    │       ├── AFNetworking/
    │       │   ├── AFNetworking-dummy.m
    │       │   ├── AFNetworking-prefix.pch
    │       │   └── AFNetworking.xcconfig
    │       └── Pods-SJNetworkingDemo/
    │           ├── Pods-SJNetworkingDemo-acknowledgements.markdown
    │           ├── Pods-SJNetworkingDemo-acknowledgements.plist
    │           ├── Pods-SJNetworkingDemo-dummy.m
    │           ├── Pods-SJNetworkingDemo-frameworks.sh
    │           ├── Pods-SJNetworkingDemo-resources.sh
    │           ├── Pods-SJNetworkingDemo.debug.xcconfig
    │           └── Pods-SJNetworkingDemo.release.xcconfig
    ├── SJNetworkingDemo/
    │   ├── Other/
    │   │   ├── AppDelegate.h
    │   │   ├── AppDelegate.m
    │   │   ├── Assets.xcassets/
    │   │   │   └── AppIcon.appiconset/
    │   │   │       └── Contents.json
    │   │   ├── Base.lproj/
    │   │   │   ├── LaunchScreen.storyboard
    │   │   │   └── Main.storyboard
    │   │   ├── Info.plist
    │   │   └── main.m
    │   ├── SJNetwork/
    │   │   ├── SJNetwork.h
    │   │   ├── SJNetworkBaseEngine.h
    │   │   ├── SJNetworkBaseEngine.m
    │   │   ├── SJNetworkCacheInfo.h
    │   │   ├── SJNetworkCacheInfo.m
    │   │   ├── SJNetworkCacheManager.h
    │   │   ├── SJNetworkCacheManager.m
    │   │   ├── SJNetworkConfig.h
    │   │   ├── SJNetworkConfig.m
    │   │   ├── SJNetworkDownloadEngine.h
    │   │   ├── SJNetworkDownloadEngine.m
    │   │   ├── SJNetworkDownloadResumeDataInfo.h
    │   │   ├── SJNetworkDownloadResumeDataInfo.m
    │   │   ├── SJNetworkHeader.h
    │   │   ├── SJNetworkManager.h
    │   │   ├── SJNetworkManager.m
    │   │   ├── SJNetworkProtocol.h
    │   │   ├── SJNetworkRequestEngine.h
    │   │   ├── SJNetworkRequestEngine.m
    │   │   ├── SJNetworkRequestModel.h
    │   │   ├── SJNetworkRequestModel.m
    │   │   ├── SJNetworkRequestPool.h
    │   │   ├── SJNetworkRequestPool.m
    │   │   ├── SJNetworkUploadEngine.h
    │   │   ├── SJNetworkUploadEngine.m
    │   │   ├── SJNetworkUtils.h
    │   │   └── SJNetworkUtils.m
    │   └── ViewController/
    │       ├── DownLoadViewController.h
    │       ├── DownLoadViewController.m
    │       ├── UploadViewController.h
    │       ├── UploadViewController.m
    │       ├── ViewController.h
    │       └── ViewController.m
    ├── SJNetworkingDemo.xcodeproj/
    │   ├── project.pbxproj
    │   ├── project.xcworkspace/
    │   │   ├── contents.xcworkspacedata
    │   │   └── xcuserdata/
    │   │       └── SunShijie.xcuserdatad/
    │   │           └── UserInterfaceState.xcuserstate
    │   └── xcuserdata/
    │       └── SunShijie.xcuserdatad/
    │           └── xcschemes/
    │               ├── SJNetworkingDemo.xcscheme
    │               └── xcschememanagement.plist
    ├── SJNetworkingDemo.xcworkspace/
    │   ├── contents.xcworkspacedata
    │   └── xcuserdata/
    │       └── SunShijie.xcuserdatad/
    │           ├── UserInterfaceState (ruwang’s MacBook Air 的冲突副本 2017-11-24).xcuserstate
    │           ├── UserInterfaceState (ruwang’s MacBook Air 的冲突副本 2017-11-28).xcuserstate
    │           ├── UserInterfaceState.xcuserstate
    │           └── xcdebugger/
    │               └── Breakpoints_v2.xcbkptlist
    ├── SJNetworkingDemoTests/
    │   ├── Info.plist
    │   └── SJNetworkingDemoTests.m
    └── SJNetworkingDemoUITests/
        ├── Info.plist
        └── SJNetworkingDemoUITests.m
Download .txt
SYMBOL INDEX (3 symbols across 3 files)

FILE: SJNetwork/SJNetworkRequestPool.h
  type NSMutableDictionary (line 29) | typedef NSMutableDictionary<NSString *, SJNetworkRequestModel *> SJCurre...

FILE: SJNetworkDemo/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h
  type AFSSLPinningModeNone (line 25) | typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {

FILE: SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkRequestPool.h
  type NSMutableDictionary (line 29) | typedef NSMutableDictionary<NSString *, SJNetworkRequestModel *> SJCurre...
Condensed preview — 134 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,226K chars).
[
  {
    "path": "LICENSE",
    "chars": 1066,
    "preview": "MIT License\n\nCopyright (c) 2017 J_Knight_\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\n"
  },
  {
    "path": "README.md",
    "chars": 18182,
    "preview": "# SJNetwork\n\n![](https://img.shields.io/badge/build-passing-brightgreen.svg)\n![](https://img.shields.io/badge/platform-i"
  },
  {
    "path": "SJNetwork/SJNetwork.h",
    "chars": 263,
    "preview": "//\n//  SJNetwork.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/12/27.\n//  Copyright © 2017年 Shijie. All ri"
  },
  {
    "path": "SJNetwork/SJNetworkBaseEngine.h",
    "chars": 906,
    "preview": "//\n//  SJNetworkBaseEngine.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/12/26.\n//  Copyright © 2017年 Shij"
  },
  {
    "path": "SJNetwork/SJNetworkBaseEngine.m",
    "chars": 557,
    "preview": "//\n//  SJNetworkBaseEngine.m\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/12/26.\n//  Copyright © 2017年 Shij"
  },
  {
    "path": "SJNetwork/SJNetworkCacheInfo.h",
    "chars": 940,
    "preview": "//\n//  SJNetworkCacheInfo.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/25.\n//  Copyright © 2017年 Shiji"
  },
  {
    "path": "SJNetwork/SJNetworkCacheInfo.m",
    "chars": 1614,
    "preview": "//\n//  SJNetworkCacheInfo.m\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/25.\n//  Copyright © 2017年 Shiji"
  },
  {
    "path": "SJNetwork/SJNetworkCacheManager.h",
    "chars": 6531,
    "preview": "//\n//  SJNetworkCache.h\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/16.\n//  Copyright © 2017年 Shijie. All right"
  },
  {
    "path": "SJNetwork/SJNetworkCacheManager.m",
    "chars": 23252,
    "preview": "//\n//  SJNetworkCache.m\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/16.\n//  Copyright © 2017年 Shijie. All right"
  },
  {
    "path": "SJNetwork/SJNetworkConfig.h",
    "chars": 1307,
    "preview": "//\n//  SJNetworkConfig.h\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/16.\n//  Copyright © 2017年 Shijie. All righ"
  },
  {
    "path": "SJNetwork/SJNetworkConfig.m",
    "chars": 1198,
    "preview": "//\n//  SJNetworkConfig.m\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/16.\n//  Copyright © 2017年 Shijie. All righ"
  },
  {
    "path": "SJNetwork/SJNetworkDownloadEngine.h",
    "chars": 6341,
    "preview": "//\n//  SJNetworkDownloadManager.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/25.\n//  Copyright © 2017年"
  },
  {
    "path": "SJNetwork/SJNetworkDownloadEngine.m",
    "chars": 53146,
    "preview": "//\n//  SJNetworkDownloadManager.m\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/25.\n//  Copyright © 2017年"
  },
  {
    "path": "SJNetwork/SJNetworkDownloadResumeDataInfo.h",
    "chars": 901,
    "preview": "//\n//  SJNetworkResumeDataInfo.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/28.\n//  Copyright © 2017年 "
  },
  {
    "path": "SJNetwork/SJNetworkDownloadResumeDataInfo.m",
    "chars": 1598,
    "preview": "//\n//  SJNetworkResumeDataInfo.m\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/28.\n//  Copyright © 2017年 "
  },
  {
    "path": "SJNetwork/SJNetworkHeader.h",
    "chars": 2019,
    "preview": "//\n//  SJNetworkHeader.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/12/26.\n//  Copyright © 2017年 Shijie. "
  },
  {
    "path": "SJNetwork/SJNetworkManager.h",
    "chars": 43625,
    "preview": "//\n//  SJNetworkManager.h\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/16.\n//  Copyright © 2017年 Shijie. All rig"
  },
  {
    "path": "SJNetwork/SJNetworkManager.m",
    "chars": 43539,
    "preview": "//\n//  SJNetworkManager.m\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/16.\n//  Copyright © 2017年 Shijie. All rig"
  },
  {
    "path": "SJNetwork/SJNetworkProtocol.h",
    "chars": 515,
    "preview": "//\n//  SJNetworkProtocol.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/12/6.\n//  Copyright © 2017年 Shijie."
  },
  {
    "path": "SJNetwork/SJNetworkRequestEngine.h",
    "chars": 2604,
    "preview": "//\n//  SJNetworkRequestManager.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/26.\n//  Copyright © 2017年 "
  },
  {
    "path": "SJNetwork/SJNetworkRequestEngine.m",
    "chars": 14135,
    "preview": "//\n//  SJNetworkRequestManager.m\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/26.\n//  Copyright © 2017年 "
  },
  {
    "path": "SJNetwork/SJNetworkRequestModel.h",
    "chars": 5345,
    "preview": "//\n//  SJNetworkRequestModel.h\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/17.\n//  Copyright © 2017年 Shijie. Al"
  },
  {
    "path": "SJNetwork/SJNetworkRequestModel.m",
    "chars": 5152,
    "preview": "//\n//  SJNetworkRequestModel.m\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/17.\n//  Copyright © 2017年 Shijie. Al"
  },
  {
    "path": "SJNetwork/SJNetworkRequestPool.h",
    "chars": 3417,
    "preview": "//\n//  SJNetworkRequestPool.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/25.\n//  Copyright © 2017年 Shi"
  },
  {
    "path": "SJNetwork/SJNetworkRequestPool.m",
    "chars": 10403,
    "preview": "//\n//  SJNetworkRequestPool.m\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/25.\n//  Copyright © 2017年 Shi"
  },
  {
    "path": "SJNetwork/SJNetworkUploadEngine.h",
    "chars": 2352,
    "preview": "//\n//  SJNetworkUploadManager.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/26.\n//  Copyright © 2017年 S"
  },
  {
    "path": "SJNetwork/SJNetworkUploadEngine.m",
    "chars": 14825,
    "preview": "//\n//  SJNetworkUploadManager.m\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/26.\n//  Copyright © 2017年 S"
  },
  {
    "path": "SJNetwork/SJNetworkUtils.h",
    "chars": 5597,
    "preview": "//\n//  SJNetworkUtils.h\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/17.\n//  Copyright © 2017年 Shijie. All right"
  },
  {
    "path": "SJNetwork/SJNetworkUtils.m",
    "chars": 10482,
    "preview": "//\n//  SJNetworkUtils.m\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/17.\n//  Copyright © 2017年 Shijie. All right"
  },
  {
    "path": "SJNetwork.podspec",
    "chars": 707,
    "preview": "\nPod::Spec.new do |s|\n\n\n  s.name         = \"SJNetwork\"\n  s.version      = \"1.2.0\"\n  s.summary      = \"SJNetwork is a hig"
  },
  {
    "path": "SJNetworkDemo/Podfile",
    "chars": 83,
    "preview": "platform :ios, '8.0'\n\ntarget 'SJNetworkingDemo' do\npod 'AFNetworking', '~> 3.0'\nend"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.h",
    "chars": 19803,
    "preview": "// AFHTTPSessionManager.h\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n// Permi"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m",
    "chars": 15021,
    "preview": "// AFHTTPSessionManager.m\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n// Permi"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h",
    "chars": 8066,
    "preview": "// AFNetworkReachabilityManager.h\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m",
    "chars": 9803,
    "preview": "// AFNetworkReachabilityManager.m\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/AFNetworking/AFNetworking.h",
    "chars": 1599,
    "preview": "// AFNetworking.h\n//\n// Copyright (c) 2013 AFNetworking (http://afnetworking.com/)\n// \n// Permission is hereby granted, "
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h",
    "chars": 5898,
    "preview": "// AFSecurityPolicy.h\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n// Permissio"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m",
    "chars": 12989,
    "preview": "// AFSecurityPolicy.m\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n// Permissio"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.h",
    "chars": 22819,
    "preview": "// AFURLRequestSerialization.h\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n// "
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/AFNetworking/AFURLRequestSerialization.m",
    "chars": 51353,
    "preview": "// AFURLRequestSerialization.m\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n// "
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h",
    "chars": 12236,
    "preview": "// AFURLResponseSerialization.h\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n//"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.m",
    "chars": 27056,
    "preview": "// AFURLResponseSerialization.m\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n//"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/AFNetworking/AFURLSessionManager.h",
    "chars": 30163,
    "preview": "// AFURLSessionManager.h\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n// Permis"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/AFNetworking/AFURLSessionManager.m",
    "chars": 55292,
    "preview": "// AFURLSessionManager.m\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n// Permis"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/LICENSE",
    "chars": 1102,
    "preview": "Copyright (c) 2011–2016 Alamofire Software Foundation (http://alamofire.org/)\n\nPermission is hereby granted, free of cha"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/README.md",
    "chars": 14519,
    "preview": "<p align=\"center\" >\n  <img src=\"https://raw.github.com/AFNetworking/AFNetworking/assets/afnetworking-logo.png\" alt=\"AFNe"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.h",
    "chars": 5847,
    "preview": "// AFAutoPurgingImageCache.h\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n// Pe"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/UIKit+AFNetworking/AFAutoPurgingImageCache.m",
    "chars": 7623,
    "preview": "// AFAutoPurgingImageCache.m\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n// Pe"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.h",
    "chars": 9076,
    "preview": "// AFImageDownloader.h\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n// Permissi"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/UIKit+AFNetworking/AFImageDownloader.m",
    "chars": 17592,
    "preview": "// AFImageDownloader.m\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n// Permissi"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.h",
    "chars": 5492,
    "preview": "// AFNetworkActivityIndicatorManager.h\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ "
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/UIKit+AFNetworking/AFNetworkActivityIndicatorManager.m",
    "chars": 9851,
    "preview": "// AFNetworkActivityIndicatorManager.m\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ "
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.h",
    "chars": 2000,
    "preview": "// UIActivityIndicatorView+AFNetworking.h\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.or"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/UIKit+AFNetworking/UIActivityIndicatorView+AFNetworking.m",
    "chars": 5049,
    "preview": "// UIActivityIndicatorView+AFNetworking.m\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.or"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.h",
    "chars": 10054,
    "preview": "// UIButton+AFNetworking.h\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n// Perm"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/UIKit+AFNetworking/UIButton+AFNetworking.m",
    "chars": 12934,
    "preview": "// UIButton+AFNetworking.m\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n// Perm"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/UIKit+AFNetworking/UIImage+AFNetworking.h",
    "chars": 1312,
    "preview": "//\n//  UIImage+AFNetworking.h\n//  \n//\n//  Created by Paulo Ferreira on 08/07/15.\n//\n// Permission is hereby granted, fre"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.h",
    "chars": 5946,
    "preview": "// UIImageView+AFNetworking.h\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n// P"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/UIKit+AFNetworking/UIImageView+AFNetworking.m",
    "chars": 6326,
    "preview": "// UIImageView+AFNetworking.m\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n// P"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/UIKit+AFNetworking/UIKit+AFNetworking.h",
    "chars": 1760,
    "preview": "// UIKit+AFNetworking.h\n//\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n// Perm"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.h",
    "chars": 2468,
    "preview": "// UIProgressView+AFNetworking.h\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n/"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/UIKit+AFNetworking/UIProgressView+AFNetworking.m",
    "chars": 5158,
    "preview": "// UIProgressView+AFNetworking.m\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n/"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.h",
    "chars": 2011,
    "preview": "// UIRefreshControl+AFNetworking.m\n//\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/UIKit+AFNetworking/UIRefreshControl+AFNetworking.m",
    "chars": 4800,
    "preview": "// UIRefreshControl+AFNetworking.m\n//\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.h",
    "chars": 4529,
    "preview": "// UIWebView+AFNetworking.h\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n// Per"
  },
  {
    "path": "SJNetworkDemo/Pods/AFNetworking/UIKit+AFNetworking/UIWebView+AFNetworking.m",
    "chars": 6761,
    "preview": "// UIWebView+AFNetworking.m\n// Copyright (c) 2011–2016 Alamofire Software Foundation ( http://alamofire.org/ )\n//\n// Per"
  },
  {
    "path": "SJNetworkDemo/Pods/Pods.xcodeproj/project.pbxproj",
    "chars": 44431,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "SJNetworkDemo/Pods/Pods.xcodeproj/xcuserdata/SunShijie.xcuserdatad/xcschemes/AFNetworking.xcscheme",
    "chars": 2547,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"0910\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "SJNetworkDemo/Pods/Pods.xcodeproj/xcuserdata/SunShijie.xcuserdatad/xcschemes/Pods-SJNetworkingDemo.xcscheme",
    "chars": 2583,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"0910\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "SJNetworkDemo/Pods/Pods.xcodeproj/xcuserdata/SunShijie.xcuserdatad/xcschemes/xcschememanagement.plist",
    "chars": 565,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "SJNetworkDemo/Pods/Target Support Files/AFNetworking/AFNetworking-dummy.m",
    "chars": 128,
    "preview": "#import <Foundation/Foundation.h>\n@interface PodsDummy_AFNetworking : NSObject\n@end\n@implementation PodsDummy_AFNetworki"
  },
  {
    "path": "SJNetworkDemo/Pods/Target Support Files/AFNetworking/AFNetworking-prefix.pch",
    "chars": 379,
    "preview": "#ifdef __OBJC__\n#import <UIKit/UIKit.h>\n#else\n#ifndef FOUNDATION_EXPORT\n#if defined(__cplusplus)\n#define FOUNDATION_EXPO"
  },
  {
    "path": "SJNetworkDemo/Pods/Target Support Files/AFNetworking/AFNetworking.xcconfig",
    "chars": 708,
    "preview": "CONFIGURATION_BUILD_DIR = $PODS_CONFIGURATION_BUILD_DIR/AFNetworking\nGCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPO"
  },
  {
    "path": "SJNetworkDemo/Pods/Target Support Files/Pods-SJNetworkingDemo/Pods-SJNetworkingDemo-acknowledgements.markdown",
    "chars": 1254,
    "preview": "# Acknowledgements\nThis application makes use of the following third party libraries:\n\n## AFNetworking\n\nCopyright (c) 20"
  },
  {
    "path": "SJNetworkDemo/Pods/Target Support Files/Pods-SJNetworkingDemo/Pods-SJNetworkingDemo-acknowledgements.plist",
    "chars": 2127,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "SJNetworkDemo/Pods/Target Support Files/Pods-SJNetworkingDemo/Pods-SJNetworkingDemo-dummy.m",
    "chars": 146,
    "preview": "#import <Foundation/Foundation.h>\n@interface PodsDummy_Pods_SJNetworkingDemo : NSObject\n@end\n@implementation PodsDummy_P"
  },
  {
    "path": "SJNetworkDemo/Pods/Target Support Files/Pods-SJNetworkingDemo/Pods-SJNetworkingDemo-frameworks.sh",
    "chars": 4423,
    "preview": "#!/bin/sh\nset -e\n\necho \"mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}\"\nmkdir -p \"${CONFIGURATION_BUILD_D"
  },
  {
    "path": "SJNetworkDemo/Pods/Target Support Files/Pods-SJNetworkingDemo/Pods-SJNetworkingDemo-resources.sh",
    "chars": 5528,
    "preview": "#!/bin/sh\nset -e\n\nmkdir -p \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}\"\n\nRESOURCES_TO_COPY=${PODS_ROOT}/re"
  },
  {
    "path": "SJNetworkDemo/Pods/Target Support Files/Pods-SJNetworkingDemo/Pods-SJNetworkingDemo.debug.xcconfig",
    "chars": 712,
    "preview": "GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1\nHEADER_SEARCH_PATHS = $(inherited) \"${PODS_ROOT}/Headers/Public\""
  },
  {
    "path": "SJNetworkDemo/Pods/Target Support Files/Pods-SJNetworkingDemo/Pods-SJNetworkingDemo.release.xcconfig",
    "chars": 712,
    "preview": "GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1\nHEADER_SEARCH_PATHS = $(inherited) \"${PODS_ROOT}/Headers/Public\""
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/Other/AppDelegate.h",
    "chars": 284,
    "preview": "//\n//  AppDelegate.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/23.\n//  Copyright © 2017年 Shijie. All "
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/Other/AppDelegate.m",
    "chars": 2556,
    "preview": "//\n//  AppDelegate.m\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/23.\n//  Copyright © 2017年 Shijie. All "
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/Other/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "chars": 1495,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"iphone\",\n      \"size\" : \"20x20\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\""
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/Other/Base.lproj/LaunchScreen.storyboard",
    "chars": 1681,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/Other/Base.lproj/Main.storyboard",
    "chars": 56775,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/Other/Info.plist",
    "chars": 1561,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/Other/main.m",
    "chars": 341,
    "preview": "//\n//  main.m\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/23.\n//  Copyright © 2017年 Shijie. All rights "
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetwork.h",
    "chars": 263,
    "preview": "//\n//  SJNetwork.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/12/27.\n//  Copyright © 2017年 Shijie. All ri"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkBaseEngine.h",
    "chars": 906,
    "preview": "//\n//  SJNetworkBaseEngine.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/12/26.\n//  Copyright © 2017年 Shij"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkBaseEngine.m",
    "chars": 557,
    "preview": "//\n//  SJNetworkBaseEngine.m\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/12/26.\n//  Copyright © 2017年 Shij"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkCacheInfo.h",
    "chars": 940,
    "preview": "//\n//  SJNetworkCacheInfo.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/25.\n//  Copyright © 2017年 Shiji"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkCacheInfo.m",
    "chars": 1614,
    "preview": "//\n//  SJNetworkCacheInfo.m\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/25.\n//  Copyright © 2017年 Shiji"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkCacheManager.h",
    "chars": 6531,
    "preview": "//\n//  SJNetworkCache.h\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/16.\n//  Copyright © 2017年 Shijie. All right"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkCacheManager.m",
    "chars": 23252,
    "preview": "//\n//  SJNetworkCache.m\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/16.\n//  Copyright © 2017年 Shijie. All right"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkConfig.h",
    "chars": 1307,
    "preview": "//\n//  SJNetworkConfig.h\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/16.\n//  Copyright © 2017年 Shijie. All righ"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkConfig.m",
    "chars": 1198,
    "preview": "//\n//  SJNetworkConfig.m\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/16.\n//  Copyright © 2017年 Shijie. All righ"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkDownloadEngine.h",
    "chars": 6341,
    "preview": "//\n//  SJNetworkDownloadManager.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/25.\n//  Copyright © 2017年"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkDownloadEngine.m",
    "chars": 53146,
    "preview": "//\n//  SJNetworkDownloadManager.m\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/25.\n//  Copyright © 2017年"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkDownloadResumeDataInfo.h",
    "chars": 901,
    "preview": "//\n//  SJNetworkResumeDataInfo.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/28.\n//  Copyright © 2017年 "
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkDownloadResumeDataInfo.m",
    "chars": 1598,
    "preview": "//\n//  SJNetworkResumeDataInfo.m\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/28.\n//  Copyright © 2017年 "
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkHeader.h",
    "chars": 2019,
    "preview": "//\n//  SJNetworkHeader.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/12/26.\n//  Copyright © 2017年 Shijie. "
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkManager.h",
    "chars": 43625,
    "preview": "//\n//  SJNetworkManager.h\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/16.\n//  Copyright © 2017年 Shijie. All rig"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkManager.m",
    "chars": 43539,
    "preview": "//\n//  SJNetworkManager.m\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/16.\n//  Copyright © 2017年 Shijie. All rig"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkProtocol.h",
    "chars": 515,
    "preview": "//\n//  SJNetworkProtocol.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/12/6.\n//  Copyright © 2017年 Shijie."
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkRequestEngine.h",
    "chars": 2604,
    "preview": "//\n//  SJNetworkRequestManager.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/26.\n//  Copyright © 2017年 "
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkRequestEngine.m",
    "chars": 14135,
    "preview": "//\n//  SJNetworkRequestManager.m\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/26.\n//  Copyright © 2017年 "
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkRequestModel.h",
    "chars": 5345,
    "preview": "//\n//  SJNetworkRequestModel.h\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/17.\n//  Copyright © 2017年 Shijie. Al"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkRequestModel.m",
    "chars": 5152,
    "preview": "//\n//  SJNetworkRequestModel.m\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/17.\n//  Copyright © 2017年 Shijie. Al"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkRequestPool.h",
    "chars": 3417,
    "preview": "//\n//  SJNetworkRequestPool.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/25.\n//  Copyright © 2017年 Shi"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkRequestPool.m",
    "chars": 10403,
    "preview": "//\n//  SJNetworkRequestPool.m\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/25.\n//  Copyright © 2017年 Shi"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkUploadEngine.h",
    "chars": 2352,
    "preview": "//\n//  SJNetworkUploadManager.h\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/26.\n//  Copyright © 2017年 S"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkUploadEngine.m",
    "chars": 14825,
    "preview": "//\n//  SJNetworkUploadManager.m\n//  SJNetworkingDemo\n//\n//  Created by Sun Shijie on 2017/11/26.\n//  Copyright © 2017年 S"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkUtils.h",
    "chars": 5597,
    "preview": "//\n//  SJNetworkUtils.h\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/17.\n//  Copyright © 2017年 Shijie. All right"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/SJNetwork/SJNetworkUtils.m",
    "chars": 10482,
    "preview": "//\n//  SJNetworkUtils.m\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/17.\n//  Copyright © 2017年 Shijie. All right"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/ViewController/DownLoadViewController.h",
    "chars": 227,
    "preview": "//\n//  DownLoadViewController.h\n//  WWNetwork\n//\n//  Created by Sun Shijie on 2017/9/9.\n//  Copyright © 2017年 Shijie. Al"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/ViewController/DownLoadViewController.m",
    "chars": 8316,
    "preview": "//\n//  DownLoadViewController.m\n//  WWNetwork\n//\n//  Created by Sun Shijie on 2017/9/9.\n//  Copyright © 2017年 Shijie. Al"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/ViewController/UploadViewController.h",
    "chars": 223,
    "preview": "//\n//  UploadViewController.h\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/9/9.\n//  Copyright © 2017年 Shijie. All "
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/ViewController/UploadViewController.m",
    "chars": 4199,
    "preview": "//\n//  UploadViewController.m\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/9/9.\n//  Copyright © 2017年 Shijie. All "
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/ViewController/ViewController.h",
    "chars": 214,
    "preview": "//\n//  ViewController.h\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/16.\n//  Copyright © 2017年 Shijie. All right"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo/ViewController/ViewController.m",
    "chars": 10925,
    "preview": "//\n//  ViewController.m\n//  SJNetwork\n//\n//  Created by Sun Shijie on 2017/8/16.\n//  Copyright © 2017年 Shijie. All right"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo.xcodeproj/project.pbxproj",
    "chars": 39955,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 48;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 161,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:SJNetworkingDem"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo.xcodeproj/xcuserdata/SunShijie.xcuserdatad/xcschemes/SJNetworkingDemo.xcscheme",
    "chars": 4369,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"0910\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo.xcodeproj/xcuserdata/SunShijie.xcuserdatad/xcschemes/xcschememanagement.plist",
    "chars": 668,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo.xcworkspace/contents.xcworkspacedata",
    "chars": 234,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:SJNetworkingDe"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemo.xcworkspace/xcuserdata/SunShijie.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist",
    "chars": 7219,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Bucket\n   type = \"0\"\n   version = \"2.0\">\n   <Breakpoints>\n      <BreakpointProxy"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemoTests/Info.plist",
    "chars": 701,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemoTests/SJNetworkingDemoTests.m",
    "chars": 933,
    "preview": "//\n//  SJNetworkingDemoTests.m\n//  SJNetworkingDemoTests\n//\n//  Created by Sun Shijie on 2017/11/23.\n//  Copyright © 201"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemoUITests/Info.plist",
    "chars": 701,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "SJNetworkDemo/SJNetworkingDemoUITests/SJNetworkingDemoUITests.m",
    "chars": 1240,
    "preview": "//\n//  SJNetworkingDemoUITests.m\n//  SJNetworkingDemoUITests\n//\n//  Created by Sun Shijie on 2017/11/23.\n//  Copyright ©"
  }
]

// ... and 4 more files (download for full content)

About this extraction

This page contains the full source code of the knightsj/SJNetwork GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 134 files (1.1 MB), approximately 255.5k tokens, and a symbol index with 3 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!