Contact the BUG
A few days ago, I suddenly received a message from a friend, saying that I encountered a new BUG on iOS 12, asking me what do I think? Isn’t it normal for a new system to have bugs? What’s going on? According to my friend’s explanation, he used a third-party download manager to download videos. Obviously, background download was set. However, once the App was pushed to the background and then back to the foreground, the download progress stopped, but the task still continued to download. IOS 12, iPhone 7.
BUG details
At first, I thought there was something wrong with the progress processing written by the third party. But after I downloaded and ran the third party’s Demo, I found it was not a problem with the third party at all, but with the system. System agent – [NSURLSessionDownloadTask URLSession: downloadTask: didWriteData: totalBytesWritten: totalBytesExpectedToWrite:] It’s not called at all, So the download progress can’t continue to calculate. Then I changed to use KVO monitored NSURLSessionDownloadTask countOfBytesReceived and countOfBytesExpectedToReceive Attribute to calculate the current download progress, but it is a pity that the two values did not continue to change after the return to the foreground. It was preliminarily determined that there was an anomaly in the system when processing data reception, which led to the omission of the change of values. The last time I encountered the BUG of this kind of system breaking laws and failing was in iOS 11.1/11.2, when I developed screen recording and live broadcasting. System approach – [RPBroadcastSampleHandler processSampleBuffer: withType:] is not call, pit off a big function module directly, but fortunately, this time the BUG is not serious, there is some solution.
To begin testing
This progress BUG will not appear on virtual machines, it must be a real machine, and after testing, it is found that it will only appear on iOS 12/12.1, iPhone 8 and below. During testing, it is also found that the background download task will be cancelled directly after the App completely exits. After entering the foreground, manually pause -> Continue and the agent /KVO will continue working.
Try to fix the BUG
Since manual pause -> continue fixes the BUG, can we just repeat it in code? Don’t worry, things aren’t so simple. Direct in – [AppDelegate applicationWillEnterForeground:] began to iterate through all the download tasks, perform the suspended again – > to continue operation, this method is very simple, very rough, but this doesn’t work! Then use – [NSURLSessionDownloadTask cancelByProducingResumeData:] – > [NSURLSession downloadTaskWithResumeData:] instead of suspended – > to continue ? Yes, it’s an improvement to realize that the current NSURLSessionDownloadTask may have dirty data, but it still doesn’t work!
The last of the last, or testing out, must be in [AppDelegate applicationDidBecomeActive:] traversal in use Cancel – > restore to be successful
About the wheels of the downloader
Friends say you write a download third party, now the download device did not use a few. At that time, I didn’t think I was going to write a download app for GitHub, because there are so many wheels on GitHub, and it’s not going to be any better than the popular one, and AFNetworking is going to be the base of it. NSURLSessionDataTask does not support background download!! Well, Apple🐂🍺🤪 I also looked at my friend’s XXDownload, although the star is a little short, but this one is just right. Although the use of underline variables in a wide range of implementations, and the use of proxies on singletons, feels like a bit of a sore throat, at least the change will work, as this third party is simply providing a framework. On GitHub, there are a number of projects that have stopped maintenance and are still being updated. Because task persistence uses databases and refers to other third parties, it may cause library conflicts, and the clean version that is still being maintained cannot adapt to some requirements scenarios. HWIFileDownload is one of the updated and clean third parties that can be used for general projects. However, it is a little overshadowed in some special requirements, such as support for time-dependent download links, persistent task lists, file verification, in-depth processing of recovered data, etc. Of course, this is not the point, the point is that the background download scene is too rare, you can write one by yourself, but also what third party. I will not do this thankless operation without Star.
FKDownloader – finally wrote
Now that it’s written, it needs to be as perfect as possible, in addition to fixing/circumventing iOS bugs, and of course supporting special needs. The overall structure of FKDownloader is listed:
-
The main class
-
FKDownloadManager
- Self-loading, without explicitly calling to create a singleton
- Uninheritable, unique
- Manage tasks and perform addition, deletion and query operations
- Start/pause/resume/cancel Task, but the implementation and state filtering are solely done by Task
- Download progress of all tasks
- The AppDelegate handles some functions, such as background downloads, loading task archives, and solving iOS bugs
-
FKConfigure
- Unified management of special configurations
- Set up the Session Identifier
- Set whether to enable background download
- Set whether to automatically clear completed/failed tasks
- Sets whether to start tasks automatically when loading local archive tasks
- Customize the request timeout period
-
FKTask
- Concrete implementation of start/pause/resume/cancel
- The sponsor of the Block/Delegate/Notification
- Check the file
- Download speed/estimated remaining time
- Additional information can be added, including save file names, verification information, custom request information, and so on
-
-
Auxiliary class
- FKResumeHelper
- Unpack or pack data to recover
- Fix faulty recovery data in iOS specific versions
- Update the URL of the restored data
- FKResumeHelper
-
other
- FKDefine: Declaration enumeration, C methods, string constants
- FKReachability: Network status detection and monitoring
- FKDownloadExecutor: Unified Processing System agent
- FKTaskStorage: indicates the return file of the management task
- FKHashHelper: computing the Hash
- FKSystemHelper: Obtains the device version and system version
FKDownloader does not rely on any other third parties, keeps its purity, and most of its methods tend to be simple externally, complex internally, and avoid high coupling as much as possible.
FKDownloader support and installation
Must have iOS 8 or above and use ARC. Support CocoaPods and Carthage installations. If you have other requirements, you can put the FKDownloader folder directly into the project.
FKDownloader characteristics
-
Self-loading uses +[NSObject Load] to load singletons without explicitly calling them to create them. So you can listen for the AppDelegate notifications ahead of time, and fixing progress bugs will take care of itself without showing calls.
-
To restore the task progress in downloading when rebooting the App is to start a background download task and run the App again after quitting the App completely, you need to retrieve the progress and status of the download task to achieve the effect that the task is still running on the UI. I have only seen one or two third parties that implement this function. Which is the focus of the – [NSURLSession getTasksWithCompletionHandler:] this system method, it can be with the identifier NSURLSession all background tasks is available.
-
After obtaining the FKTask URL, run -[FKTask resumeFilePath] to obtain the save path of ResumeData. +[FKTask updateURL:] [FKTask updateURL:] [FKResumeHelper updateResumeData:url:] FKDownloader only uses the scheme://host/path of the URL to create identifiers, so parameters can be modified at will. If you use request headers to complete expired operations, you can use custom request headers.
-
Perform specific operations based on the network status to check the current network status. If there is no network, the task in progress is suspended and the task in wait is cancelled. When the network is restored, the task interrupted because of no network will be downloaded again.
-
Use NSCoding to download tasks persistently, save task information directly without relying on database, including URL, task status, save file name, verification information, custom request header, total file size, received bytes and other information. Ensure that the UI information after restarting the App is consistent with that before exiting the App. The cost is that the data to be saved cannot be highly customized, but the attributes exposed by FKTask fully meet the requirements of external data processing, and you can also use the existing database in the project to customize the management.
-
Predictive processing the status/progress setting agent triggers all current protocol methods once to keep the UI up to date.
-
Task status/progress monitoring can freely use the Block/Delegate/Notification, to maximize the application scenario.
-
Customized task Additional Information Currently supports file names, file verification values, and customized request headers.
-
Variable URL parameters are supported. FKTask only uses scheme://host/path to create identifiers. Parameters information is ignored to identify time-sensitive URL download tasks.
-
Fine task status none/Preprocessing/waiting/In progress/completed/cancelled/paused/resumed/checksum/error basically has dual levels of will and DID.
-
File verification supports MD5, SHA1, SHA256, and SHA512. However, when verifying oversized files, the CPU usage is too large. Therefore, the authentication function is disabled by default.
-
Swift compatibility Supports use in Swift projects.
FKDownloader simple to use
- Task management
[[FKDownloadManager manager] add:@ "URL"]; [[FKDownloadManager Manager] addInfo:@{FKTaskInfoURL: {FKTaskInfoURL: {FKTaskInfoURL: url, FKTaskInfoFileName: @"xCode7", FKTaskInfoVerificationType: @(VerifyTypeMD5), FKTaskInfoVerification: @"5f75fe52c15566a12b012db21808ad8c", FKTaskInfoRequestHeader: @{} }]; [[FKDownloadManager manager] start:@ "URL"]; [[FKDownloadManager Manager] acquire:@ "URL"]; // Suspend the task [[FKDownloadManager Manager] suspend:@ "URL"]; // Resume task [[FKDownloadManager Manager] resume:@ "URL"]; // Cancel the task [[FKDownloadManager Manager] Cancel :@ "URL"]; // Remove task [[FKDownloadManager Manager] remove:@ "URL"]; // Set task agent [[FKDownloadManager Manager] acquire:@ "URL"]. Delegate = self; // Set task Block [[FKDownloadManager Manager] acquire:@ "URL"]. StatusBlock = ^(FKTask *task) {// Call when status changes}; [[FKDownloadManager Manager] acquire:@ "URL"]. SpeedBlock = ^(FKTask *task) {// Download speed, default 1s call once}; [[FKDownloadManager Manager] acquire:@ "URL"].ProgressBlock = ^(FKTask *task) {// Called when progress changes};Copy the code
- Notifications of supported tasks
/ / and the agent for the same price, can be used in accordance with the agent use notice. Extern FKNotificationName const FKTaskPrepareNotification; extern FKNotificationName const FKTaskDidIdleNotification; extern FKNotificationName const FKTaskWillExecuteNotification; extern FKNotificationName const FKTaskDidExecuteNotication; extern FKNotificationName const FKTaskProgressNotication; extern FKNotificationName const FKTaskDidResumingNotification; extern FKNotificationName const FKTaskWillChecksumNotification; extern FKNotificationName const FKTaskDidChecksumNotification; extern FKNotificationName const FKTaskDidFinishNotication; extern FKNotificationName const FKTaskErrorNotication; extern FKNotificationName const FKTaskWillSuspendNotication; extern FKNotificationName const FKTaskDidSuspendNotication; extern FKNotificationName const FKTaskWillCancelldNotication; extern FKNotificationName const FKTaskDidCancelldNotication; extern FKNotificationName const FKTaskSpeedInfoNotication;Copy the code
- That needs to be called in the AppDelegate
- (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier CompletionHandler :(void (^)(void))completionHandler {// saves system blocks needed for background downloads, differentiating identifiers to prevent collisions with other third partiesif([identifier isEqualToString:[FKDownloadManager manager].configure.sessionIdentifier]) { [FKDownloadManager manager].configure.backgroundHandler = completionHandler; }}Copy the code
Some details of FKDownloader handling
- ResumeData
There is a format error in the recovered data in iOS 10.0/10.1. The official fix is successful in iOS 10.2, but it is still needed for compatibility. The specific solution is in thehere.
In iOS 11, there are moreNSURLSessionResumeByteRange
The field causes some strange problems that can be usedFKResumeHelper
FKDownloader (FKDownloader, FKDownloader, FKDownloader, FKDownloader)
There was no error, but in iOS 12, the ResumeData packet format changed,Available nowIs now available+[NSKeyedUnarchiver unarchiveObjectWithData:]
Unpack the package directly-[NSKeyedUnarchiver decodeTopLevelObjectForKey:error:]
Method,key
为NSKeyedArchiveRootObjectKey
To unpack (and the system defaultkey
是root
, Apple I don’t know much about you 😂), but previous versions need to be used+[NSPropertyListSerialization propertyListWithData:roptions:format:error:]
When unpacking packets, pay attention to distinguish between packets.
In iOS 8, becauseNSURLSessionResumeInfoVersion
Version too old, new versionNSURLSessionResumeInfoTempFileName
Will beNSURLSessionResumeInfoLocalPath
Instead, the cache file path will no longer just be the file name, but the file path, which needs to be noticed, but it doesn’t affect much, and runs fine.
-
Download file check in some big file, the file is required to secure file integrity check, check whether FKDownloader can be configured to open file. Among them, the initialized using NSDataReadingMappedIfSafe option NSData, According to the test, it takes 4 to 5 seconds to calculate MD5 for a 6 GB file, and the memory usage is less than 1 MB. However, the CPU usage is more than 90% because the Hash operation is computation-intensive. Therefore, file verification can be enabled when downloading small files. But large files please handle accordingly.
-
NSURLSessionDownloadTask – the call [NSURLSessionDownloadTask cancelByProducingResumeData:], Although the task status change for NSURLSessionTaskStateCanceling, but after the agent – [URLSession URLSession: task: didCompleteWithError:], Status of NSURLSessionTaskStateCompleted, almost is the pit not light, so the current state management FKTask status attributes hands completely.
-
Network Reachability uses official files to detect and monitor Network status. The official mode is only suitable for real machines. In VMS, only the status of the lost Network can be monitored. Therefore, use the real machine test to test the network status.
FKDownloader best practices
See running Demo