introduce
The framework is a generic network layer that can be invoked by the business layer of different apps. The framework encapsulates AFNetworking and in some ways takes a page from the design of YTKNetwork: encapsulate and manage requests as objects.
Functionally, it supports:
- Send request methods for GET, POST, PUT, DELETE ordinary network request function
- Image upload function (single or multiple images upload, specify the image compression ratio before uploading)
- Download function (support resumable and background download)
- Cache management (write, read, clear cache, calculate size) function
- Request management (view the status of ongoing requests, single requests and batch cancellations) functionality
- Set the default key pair in the request body (eg. Need to add version number for versioning)
- Add request headers (eg. For services that require a token)
- Setting the Server ADDRESS
- Set debug mode (print logs that are convenient for debugging, such as reading the specific cause of cache failure)
GitHub: SJNetwork
There is demo in the project: SJNetworkDemo project folder
architecture
Before looking at the architecture diagram, a brief introduction to the responsibilities of each class in the framework:
Responsibility division
The name of the class | Duties and responsibilities |
---|---|
SJNetwork | Header file, you just need to introduce this file to use all the functionality of the framework |
SJNetworkProtocol | The processing method after the request ends is customized and may be extended in the future |
SJNetworkHeader | Defines callback blocks and enumeration types |
SJNetworkManager | A class that is directly connected to the business layer and contains all interfaces for network request functions except the configuration interface |
SJNetworkBaseEngine | All the base classes responsible for sending the request class |
SJNetworkRequestEngine | Classes that send (GET,POST,PUT,DELETE) requests: supports setting cache expiration, reading, writing, and clearing the cache |
SJNetworkUploadEngine | Send upload request class: support to set the image type and compression upload, batch upload |
SJNetworkDownloadEngine | Classes that send download requests: support breakpoint continuations and background downloads |
SJNetworkRequestModel | Request object class: holds some data for a network request; Such as request URL, request body, etc.) |
SJNetworkCacheManager | Cache handling class: cache write, read, delete |
SJNetworkConfig | Configuration class: configure the server address, debug mode, etc |
SJNetworkUtils | Utility class: can be used to generate cache path, app version number, etc |
SJNetworkRequestPool | Request object pool: Used to hold in-progress request objects |
SJNetworkCacheInfo | Cache metadata: Records information about the corresponding cache data (version number, cache expiration time) |
SJNetworkDownloadResumeDataInfo | Metadata for undownloaded data: Records information about undownloaded data (proportion of downloaded data, total length of downloaded data, length of downloaded data) |
Architecture diagram
As can be seen from the architecture diagram:
- Business side call
SJNetworkManager
To send requests (or perform operations such as operation requests), while the class that actually does the work isSJNetworkRequestEngine
.SJNetworkUploadEngine
.SJNetworkDownloadEngine
.SJNetworkCacheManager
These classes. - All requests are encapsulated into one
SJNetworkRequestModel
Instance to be managed bySJNetworkRequestPool
. - Within that framework,
SJNetworkConfig
andSJNetworkUtils
Can be called anywhere, because it is often necessary to get some configuration done by the user and to call some common utility class methods.
Method of use
Step1: download and import the framework
Through the Cocoa pods:
pod 'SJNetwork'
The latest version is 1.2.0
Or manually drag the SJNetwork folder into the project.
Step2: Import header file:
If you are using Cocoapods:
import <SJNetwork/SJNetwork.h>
Copy the code
If it is manually dragged:
#import "SJNetwork.h"
Copy the code
Function is introduced
Basic configuration
Because the configuration object is a singleton (SJNetworkConfig), it can be used anywhere in the project. Take a look at what configuration items the framework supports:
Server Address:
[SJNetworkConfig sharedConfig].baseUrl = @"http://v.juhe.cn";
Copy the code
Default parameters:
[SJNetworkConfig sharedConfig].defailtParameters = @{@"app_version":[SJNetworkUtils appVersionStr],
@"platform":@"iOS"};
Copy the code
Default parameters are concatenated in the request body of all requests;
If it is a GET request, the concatenation is in the URL.
Timeout period:
[SJNetworkConfig sharedConfig].timeoutSeconds = 30;
Copy the code
The default timeout period is 20s.
The Debug mode:
[SJNetworkConfig sharedConfig].debugMode = YES; // The default is NOCopy the code
If the debug mode is set to YES, many detailed logs are printed for debugging.
If set to NO, there is NO log.
Add a request header key-value pair:
[[SJNetworkConfig sharedConfig] addCustomHeader:@{@"token":@"2j4jd9s74bfm9sn3"}];
Copy the code
or
[[SJNetworkManager sharedManager] addCustomHeader:@{@"token":@"2j4jd9s74bfm9sn3"}]; // The SJNetworkConfig addCustomHeader method is actually calledCopy the code
The added header key-value pairs are automatically added to all request headers.
Add a key-value pair if it does not already exist; If it exists, replace it.
Common Network request
Here we define GET, POST, PUT, DELETE requests as ordinary network requests, which are implemented by SJNetworkRequestManager. All of these common network requests support either write or read caching, but are not supported by default, leaving it up to the user to decide whether to write or read the cache.
Send a POST request that does not support either write or read caching:
[[SJNetworkManager sharedManager] sendPostRequest:@"toutiao/index"
parameters:@{@"type":@"top",
@"key" :@"0c60"}
success:^(id responseObject) {
NSLog(@"request succeed:%@",responseObject);
} failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode) {
NSLog(@"request failed:%@",error);
}];
Copy the code
Send a POST request that supports a write duration of 180 seconds and reads the cache while it is valid:
[[SJNetworkManager sharedManager] sendPostRequest:@"toutiao/index"
parameters:@{@"type":@"top",
@"key" :@"0c60"}
loadCache:YES
cacheDuration:180
success:^(id responseObject) {
NSLog(@"request succeed:%@",responseObject);
} failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode) {
NSLog(@"request failed:%@",error);
}];
Copy the code
CacheDuration: cacheDuration, in seconds.
- If it is greater than 0, cache it.
- If the value is less than or equal to 0, no caching is performed.
LoadCache: If set to YES, check whether the cache is valid before sending the request (if set to NO, the network request will be made with or without the cache) :
- If the cache exists and is valid, the cache is returned and no network request is made.
- If the cache does not exist, or if it exists but is invalid (time expired), the cache is removed (if it exists) and the network request is made.
A complete flow chart of a normal network request with caching judgment:
Cache management
Cache management is implemented by SJNetworkCacheManager singleton, functions are divided into cache read, delete and calculation. Let’s take a look at cache reads:
Cache read
The framework supports reads from a single cache and reads from multiple caches:
- A single cache read returns either a cache object (dictionary, or array) or nil.
- Multiple cache reads return an array or nil.
A single cache read:
If we know the url, method, or body of this cache, we can try to get the cache object that corresponds to it:
For example, if you want to cache a network request that has been written to the cache above, you can use the following API:
[[SJNetworkManager sharedManager] loadCacheWithUrl:@"toutiao/index"
method:@"POST"
parameters:@{@"type":@"top",
@"key" :@"0c60"}
completionBlock:^(id _Nullable cacheObject) {
NSLog(@"%@",cacheObject);
}];
Copy the code
Note that the following situations occur during cache reads:
If the cache for this request does not exist, then nil will be passed from the block.
If the cache for the request exists but expires, the cache will be cleared and the request will be passed nil in the block.
If the cache corresponding to the request exists and is valid, the cache object will be passed from the block.
Multiple cache reads:
If some requests are cached using the same URL (but with different request methods or parameters), they can be cached using the following methods:
[[SJNetworkManager sharedManager] loadCacheWithUrl:@"toutiao/index"
completionBlock:^(NSArray * _Nullable cacheArr) {
NSLog(@"%@",cacheArr);
}];
Copy the code
If some requests use the same URL and request method, but the request parameters are different, they can be cached (in an array) by:
[[SJNetworkManager sharedManager] loadCacheWithUrl:@"toutiao/index" method:@"POST" completionBlock:^(NSArray * _Nullable cacheArr) { NSLog(@"%@",cacheArr); }];Copy the code
Now that we know that the framework supports single and batch cache reads, let’s look at cache removal:
Cache deletion
Similarly, the framework supports single and batch deletes of caches.
If you want to remove requests from the cache that belong to a particular URL, method, or request parameter, you can use the following API:
[[SJNetworkManager sharedManager] clearCacheWithUrl:@"toutiao/index"
method:@"POST"
parameters:@{@"type":@"top",
@"key" :@"0c60"}
completionBlock:^(BOOL isSuccess) {
if (isSuccess) {
NSLog(@"Clearing cache successfully!");
}
}];
Copy the code
If you want to remove cached requests that use the same URL (but different request methods or parameters), use the following API:
[[SJNetworkManager sharedManager] clearCacheWithUrl:@"toutiao/index"
completionBlock:^(BOOL isSuccess) {
if (isSuccess) {
NSLog(@"Clearing cache successfully!");
}
}];
Copy the code
If you want to remove cached requests that use the same URL and method, but different request parameters, use the following API:
[[SJNetworkManager sharedManager] clearCacheWithUrl:@"toutiao/index"
method:@"POST"
withCompletionBlock:^(BOOL isSuccess) {
if (isSuccess) {
NSLog(@"Clearing cache successfully!");
}
}];
Copy the code
Having looked at cache reads and deletes, let’s look at the cache calculation:
Cache calculation
The cache calculation provides only one interface, and the block callback will return a number of files, the size of all caches, and a string of KB or MB:
[[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);
}];
Copy the code
FileCount: indicates the number of cached files. The value is an integer
Total size: The unit is byte
TotalSizeString: string with KB and MB conversion: in KB units up to 1024*1024 bytes; Other units are in MB. For example, file count :5 and total size:1298609 Total size string:1.2385 MB
** Note: ** the caches calculated include the caches of all common requests as well as the data that has not been downloaded and needs to be downloaded later.
Upload function
The function of uploading images is implemented by the singleton of SJNetworkUploadManager class: support for uploading single or multiple UIImage objects, can set compression ratio (default is 1 when not set, no compression).
Single picture, original upload
Upload a single UIImage object without compressing the image before uploading:
[[SJNetworkManager sharedManager] sendUploadImageRequest:@"api"
parameters:nil
image:image_1
name:@"color"
mimeType:@"png"
progress:^(NSProgress *uploadProgress)
{
self.progressView.observedProgress = uploadProgress;
} success:^(id responseObject) {
NSLog(@"upload succeed");
} failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode, NSArray<UIImage *> *uploadFailedImages) {
NSLog(@"upload failed, failed images:%@",uploadFailedImages);
}];
Copy the code
Multiple images, compressed in half
Upload multiple UIImage objects with a compression ratio of 0.5:
[[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) { NSLog(@"upload succeed"); } failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode, NSArray<UIImage *> *uploadFailedImages) { NSLog(@"upload failed, failed images:%@",uploadFailedImages); }];Copy the code
Here, the mimeType can be set to JPG /JPG, PNG /PNG, jpeg/JPEG, as the image type when uploaded to the server. Note that if mimeType is PNG /PNG, the compression ratio is invalid and will be uploaded at the original size.
Ignore the set BaseUrl
Considering that the server on which the image is uploaded may be different from the server on which the normal request is made, a special parameter is added: ignoreBaseUrl. If the Boolean value is set to YES, the baseUrl set in the SJNetworkConfig singleton is ignored and the user needs to write the full request URL as the first parameter of the request:
[[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) { NSLog(@"upload succeed"); } failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode, NSArray<UIImage *> *uploadFailedImages) { NSLog(@"upload failed, failed images:%@",uploadFailedImages); }];Copy the code
Another option is to force a change to the baseUrl of the SJNetworkConfig singleton:
[SJNetworkConfig sharedConfig].baseUrl = @"http://uploads.im"; [[SJNetworkManager sharedManager] sendUploadImagesRequest:@"api" ignoreBaseUrl:NO parameters:nil Images :@[image_3,image_4] compressRatio: @"color" mimeType:@" PNG "progress:^(NSProgress *uploadProgress) { self.progressView.observedProgress = uploadProgress; } success:^(id responseObject) { NSLog(@"upload succeed"); } failure:^(NSURLSessionTask *task, NSError *error, NSInteger statusCode, NSArray<UIImage *> *uploadFailedImages) { NSLog(@"upload failed, failed images:%@",uploadFailedImages); }];Copy the code
It doesn’t look very elegant, but it’s doable: just change the baseUrl back again before requesting all the normal network requests.
For the time being, the framework does not support multiple baseUrl functions, but this will be added if further research is done.
Download function
Download function is by SJNetworkDownloadManager singleton to achieve, support breakpoint continuation and background download.
- If it is set to support background download, the generated task class internally is:
NSURLSessionDownloadTask
. After the phone exits the foreground and enters the background, it can still be downloaded. - If the background download is not supported, then the generated task class is:
NSURLSessionDataTask
. The download cannot continue after the phone exits the foreground, but through the frame internalAutomatically restore the download mechanismAfter returning to the foreground, the download will continue. And it combinesNSOutputStream
Example, the downloaded data is written in the sandbox bit by bit, reducing the pressure on memory, which is to support large file downloads. - If resumable data is supported, the data that has not been downloaded will be saved after the download fails due to network disconnection or cancellation. After the download task is started later, the download continues.
- If breakpoint continuation is not supported, the downloaded data will not be retained. You can only start from scratch after you start the download later.
In summary, there are four situations:
Support breakpoint continuation | Breakpoint continued is not supported | |
---|---|---|
Support background download | ✅ | ✅ |
Background download is not supported | ✅ | ✅ |
The default configuration is: support breakpoint resume, not support background download (because unless it is a special app such as music video, background download operation may be rejected by Apple).
Download the interface
Default download function (resumable download is supported, background download is not supported) :
[[SJNetworkManager sharedManager] sendDownloadRequest:@"wallpaper.jpg" downloadFilePath:_imageFileLocalPath progress:^(NSInteger receivedSize, NSInteger expectedSize, CGFloat progress) { self.progressView.progress = progress; } success:^(id responseObject) { NSLog(@"Download succeed!"); } failure:^(NSURLSessionTask *task, NSError *error, NSString *resumableDataPath) { NSLog(@"Download failed!"); }];Copy the code
If resumableDataPath is supported, the path of the undownloaded data will be passed in the callback that fails to return: resumableDataPath.
Breakpoint continuation is not supported, and background download is not supported:
[[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) { NSLog(@"Download succeed!"); } failure:^(NSURLSessionTask *task, NSError *error, NSString *resumableDataPath) { NSLog(@"Download failed!"); }];Copy the code
Support breakpoint continuation, support background download:
[[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) { NSLog(@"Download succeed!"); } failure:^(NSURLSessionTask *task, NSError *error, NSString *resumableDataPath) { NSLog(@"Download failed!"); }];Copy the code
Resumable transfer is not supported, but background download is supported:
[[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) { NSLog(@"Download succeed!"); } failure:^(NSURLSessionTask *task, NSError *error, NSString *resumableDataPath) { NSLog(@"Download failed!"); }];Copy the code
As with upload, the download interface also supports ignoring baseUrl:
[[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) { NSLog(@"Download succeed!"); } failure:^(NSURLSessionTask *task, NSError *error, NSString *resumableDataPath) { NSLog(@"Download failed!"); }];Copy the code
Suspend, resume and cancel downloads
All download requests support pause, resume, and cancel operations. And these operations are supported individually and in batches:
Suspension of downloads
Pause individual download requests:
[[SJNetworkManager sharedManager] suspendDownloadRequest:@"universe.jpg"];
Copy the code
Pause multiple download requests:
[[SJNetworkManager sharedManager] suspendDownloadRequests:@[@"universe.jpg",@"wallpaper.jpg"]];
Copy the code
Suspend all download requests:
[[SJNetworkManager sharedManager] suspendAllDownloadRequests];
Copy the code
Download recovery
Resume individual suspended download requests:
[[SJNetworkManager sharedManager] resumeDownloadReqeust:@"universe.jpg"];
Copy the code
Resume multiple suspended download requests:
[[SJNetworkManager sharedManager] resumeDownloadReqeusts:@[@"universe.jpg",@"wallpaper.jpg"]];
Copy the code
Resume all pending download requests:
[[SJNetworkManager sharedManager] resumeAllDownloadRequests];
Copy the code
Cancellation of download
Cancel a separate download request:
[[SJNetworkManager sharedManager] cancelDownloadRequest:@"universe.jpg"];
Copy the code
Cancel multiple download requests:
[[SJNetworkManager sharedManager] cancelDownloadRequests:@[@"universe.jpg",@"wallpaper.jpg"]];
Copy the code
Cancel all download requests:
[[SJNetworkManager sharedManager] cancelAllDownloadRequests];
Copy the code
Management of requests
In this framework, both normal download requests, upload requests, and download requests save the parameters passed in by the user in an instance of the special request object SJNetworkRequestModel before sending the request. These instances are managed by singletons of the SJNetworkRequestPool class:
- Before the request begins, the request instance is managed in one of the dictionaries.
- When the request is complete, the corresponding request instance is removed.
In addition to adding and removing request objects, SJNetworkRequestPool manages requests by:
- A query of the status of an ongoing request
- Cancellation of a request in progress.
A query of the status of the request
Are there still ongoing requests:
BOOL remaining = [[SJNetworkManager sharedManager] remainingCurrentRequests];
if (remaining) {
NSLog(@"There is remaining request");
}
Copy the code
Number of requests in progress:
NSUInteger count = [[SJNetworkManager sharedManager] currentRequestCount];
if (count > 0) {
NSLog(@"There is %lu requests",(unsigned long)count);
}
Copy the code
Print all request objects in progress:
[[SJNetworkManager sharedManager] logAllCurrentRequests];
Copy the code
Cancellation of request
Cancellations of requests are also divided into individual and batch cancellations:
Cancel a request:
[[SJNetworkManager sharedManager] cancelCurrentRequestWithUrl:@"toutiao/index"
method:@"POST"
parameters:@{@"type":@"top",
@"key" :@"0c60"}];
Copy the code
Cancel requests for the same URL:
[[SJNetworkManager sharedManager] cancelCurrentRequestWithUrl:@"toutiao/index"];
Copy the code
Cancel multiple requests at the specified URL:
[[SJNetworkManager sharedManager] cancelDownloadRequests:@[@"toutiao/index",@"weixin/query"]];
Copy the code
Cancel all ongoing requests:
[[SJNetworkManager sharedManager] cancelAllCurrentRequests];
Copy the code
The Log output
If the debug mode is set to YES, many debug logs are printed:
[SJNetworkConfig sharedConfig].debugMode = YES;
Copy the code
Log of the request object
The description method of SJNetworkRequestModel is overwritten, so when the object is printed, ordinary network request, upload request, download request have their own log. Here is a common request log:
{ <SJNetworkRequestModel: 0x6040001fc100> type: ordinary request method: GET url: http://v.juhe.cn/toutiao/index parameters: {" app_version "=" 1.0 "; key = 0c60; platform = iOS; type = top; } loadCache: YES cacheDuration: 5 seconds requestIdentifer:b4b36793efabad54a14389cf09bc8133_a6a72ddee1dd86825cb5707c500784f5_7b65261ff298c6a386c89a632bd17b39_30c9 b994c268547f38a2f9af6f8c171f task: <__NSCFLocalDataTask: 0x7f8e075320a0>{ taskIdentifier: 1 } { completed } }Copy the code
Requested and cached logs
Consider a case where a network request needs to be cached but the cache expires:
=========== Load cache info failed, reason:Cache is expired, begin to clear cache... =========== Load cache failed: Cache info is invalid =========== Faild to load cache, start to sending network request... =========== Start requesting... = = = = = = = = = = = url: http://v.juhe.cn/toutiao/index = = = = = = = = = = = method: GET = = = = = = = = = = = the 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/****/***/***/*******.cacheData =========== Available duration: 180 secondsCopy the code
Thank you
I’ve used a lot of web resources, read a lot of articles, and got a lot of help while debugging this framework, so I have to mention this:
API service
- Free interface service: aggregate data
- Upload image interface service: Uploads
- Download pictures of the chart bed: seven cattle cloud developer platform
Use and reference frames
- AFNetworking
- YTKNetwork
Refer to the article
- IOS development network article file download, large file download, breakpoint download
- IOS uses NSURLSession for downloads (including background downloads, breakpoint downloads)
- 2. NSURLSessionDownloadTask file download
- MCDownloadManager ios file download manager
- IOS file download, breakpoint download (Network request based on NSURLSession)
The last word
At present, there are many frameworks for AFNetworking, uploading pictures, and downloading, but I always want to write a framework of my own code style, and the network layer is a little challenging for me, so I want to try it.
Excluding the interval, the framework took more than a month to complete, but it took another half a month to write the full English comments, finish the refactoring (modifying the architecture, separating some classes), naming the specification, fixing bugs, optimizing, and so on. Especially because they are not familiar with the download this piece, especially the breakpoint to continue, the background download of these two aspects are not practical experience, in writing the time also spent a lot of time.
I hope you can give me more valuable opinions and suggestions, and I will update if I find any shortcomings
This post has been synchronized to a personal blog: Portal
— — — — — — — — — — — — — — — — — — — — — — — — — — — — on July 17, 2018 update — — — — — — — — — — — — — — — — — — — — — — — — — — — —
Pay attention!!
The author recently opened a personal public account, mainly to share programming, reading notes, thinking articles.
- Programming articles: a selection of my previous technical articles, as well as subsequent technical articles (mainly original), will gradually move away from iOS content and focus on improving programming capabilities.
- Reading notes: Share your reading notes on programming, thinking, psychology, and workplace books.
- Thinking articles: share my thoughts on technology and life.
Because there is a limit to the number of messages published on the official account every day, so far we have not published all the selected articles in the past on the official account, and will be published gradually in the future.
And because of the various limitations of the major blog platform, later will be published on the public account some short and concise, to see the big dry goods articles oh ~
Scan the qr code below and click follow, looking forward to growing together with you ~