这里每天分享一个 iOS 的新知识,快来关注我吧
前言
在前几篇文章中,我们探讨了 URLSession
的介绍、基本使用和安全性等等。
看到有小伙伴留言让讲讲后台下载相关的内容,那本篇文章就来重点介绍 URLSession
的后台传输任务,帮助你在应用处于后台时继续进行文件下载和上传操作。
后台传输任务对于需要处理大文件传输的应用非常重要,尤其是在需要保障传输过程不中断的情况下。
在阅读这篇文章之前,建议你先阅读我们前几篇关于 URLSession
的文章,这篇文章的代码都是基于前几篇的,看完你会更容易理解本文的内容。
URLSession 系列第一篇:iOS 开发者必须掌握的 URLSession 使用指南
URLSession 系列第二篇:手把手教你封装超实用的网络工具类
URLSession 系列第三篇:告别回调地狱,掌握 Completion Handlers 和 async/await!
URLSession 系列第四篇:彻底防止中间人攻击,确保网络安全!
URLSession 系列第五篇:如何精准监控 HTTPS 请求性能,优化必备!
什么是后台传输任务?
后台传输任务是指在应用处于后台状态时,仍然可以继续进行的网络传输任务(如文件下载和上传)。iOS 提供了 URLSessionConfiguration
的 background
选项,允许我们创建支持后台传输的 URLSession
。
后台传输任务有哪些具体的应用场景:
-
下载大文件(如视频、音频、PDF 等),如果只支持前台下载,用户需要一直保持在前台,体验会很差
-
上传大文件(如用户生成的内容)
-
保证传输任务即使在应用被挂起或终止时也能继续进行
创建后台传输任务
要使用 URLSession 的后台传输任务,我们需要完成以下几个步骤:
-
创建一个支持后台传输的
URLSessionConfiguration
。 -
实现
URLSessionDelegate
以处理后台任务完成的回调。 -
创建和管理后台传输任务。
-
申请 App 在后台运行,以确保下载任务不会被终止。
实现步骤
1. 创建后台传输的 URLSessionConfiguration
我们首先需要创建一个 URLSessionConfiguration
并设置其 identifier
,它的作用是在后台任务完成时能够正确地恢复会话。
swift
import Foundation
class BackgroundSessionManager: NSObject {
static let shared = BackgroundSessionManager()
private var session: URLSession!
private override init() {
super.init()
let configuration = URLSessionConfiguration.background(withIdentifier: "com.yourapp.backgroundSession")
session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
}
func startDownload(url: URL) {
let downloadTask = session.downloadTask(with: url)
downloadTask.resume()
}
func startUpload(fileURL: URL, to serverURL: URL) {
var request = URLRequest(url: serverURL)
request.httpMethod = "POST"
let uploadTask = session.uploadTask(with: request, fromFile: fileURL)
uploadTask.resume()
}
}
2. 实现 URLSessionDelegate 处理后台任务完成的回调
接下来,我们需要实现 URLSessionDelegate
的相关方法,处理后台任务完成的回调。
swift
extension BackgroundSessionManager: URLSessionDelegate, URLSessionDownloadDelegate, URLSessionTaskDelegate {
// 处理下载任务完成的回调
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
// 将下载的文件从临时位置移动到目标位置
let fileManager = FileManager.default
let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0]
let destinationURL = documentsURL.appendingPathComponent(location.lastPathComponent)
do {
try fileManager.moveItem(at: location, to: destinationURL)
print("File downloaded to: \(destinationURL)")
} catch {
print("Error moving file: \(error)")
}
}
// 处理上传任务完成的回调
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let error = error {
print("Task completed with error: \(error)")
} else {
print("Task completed successfully")
}
}
// 处理后台任务完成后的回调
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
DispatchQueue.main.async {
if let appDelegate = UIApplication.shared.delegate as? AppDelegate,
let backgroundCompletionHandler = appDelegate.backgroundCompletionHandler {
appDelegate.backgroundCompletionHandler = nil
backgroundCompletionHandler()
}
}
}
}
3. 在 AppDelegate 中处理后台事件
我们还需要在 AppDelegate
中处理后台任务完成后的事件,确保应用在后台任务完成后能够正确恢复。
less
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var backgroundCompletionHandler: (() -> Void)?
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
backgroundCompletionHandler = completionHandler
}
}
做完这一步,我们的后台传输任务就可以正常工作了。当下载或上传任务完成时,我们会收到相应的回调通知,可以在回调中处理下载的文件或上传的结果。
如果这时 App 退到后台,我们的任务会继续进行,一直到完成。
但如果 App 被杀掉,就无法继续下载了,如果你想要让 App 能够一直在后台保持下载,还需要申请后台运行的权限,可以继续往下看。
4. 申请 App 在后台运行
为了确保下载任务在应用被挂起时不会被系统杀掉,我们需要申请一些后台模式。你可以在 Xcode 的 Capabilities
中打开 Background Modes
,并勾选 Background fetch
。
同时,我们还可以在 AppDelegate 中使用 beginBackgroundTask
来申请更多的后台执行时间。
swift
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var backgroundCompletionHandler: (() -> Void)?
var backgroundTask: UIBackgroundTaskIdentifier = .invalid
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
backgroundCompletionHandler = completionHandler
}
func applicationDidEnterBackground(_ application: UIApplication) {
backgroundTask = application.beginBackgroundTask(withName: "DownloadTask") {
// 当后台任务结束时调用
application.endBackgroundTask(self.backgroundTask)
self.backgroundTask = .invalid
}
}
func applicationWillEnterForeground(_ application: UIApplication) {
if backgroundTask != .invalid {
application.endBackgroundTask(backgroundTask)
backgroundTask = .invalid
}
}
}
使用示例
以下是如何使用 BackgroundSessionManager
进行后台文件下载和上传的示例:
typescript
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// 启动文件下载
let downloadURL = URL(string: "https://github.com/onevcat/Kingfisher/releases/download/7.12.0/Kingfisher-7.12.0.zip")! // 一个可用的文件下载地址
BackgroundSessionManager.shared.startDownload(url: downloadURL)
// 启动文件上传
if let fileURL = Bundle.main.url(forResource: "sample", withExtension: "txt") {
let serverURL = URL(string: "https://example.com/upload")!
BackgroundSessionManager.shared.startUpload(fileURL: fileURL, to: serverURL)
}
}
}
在这个示例中,我们启动了一个文件下载任务和一个文件上传任务。即使应用被挂起或终止,这些任务也会继续进行,并在完成后通过回调通知应用。
总结
在这篇文章中,我们介绍了如何使用 URLSession
的后台传输任务。通过创建支持后台传输的 URLSessionConfiguration
,并实现相应的回调处理,我们可以确保网络传输任务即使在应用处于后台状态时也能继续进行。
这篇文章比较简单,主要是介绍了后台传输任务的基本使用方法,如果想做完善的后台传输任务,还需要考虑一些其他因素,比如:
-
如何处理后台任务完成后的恢复
-
如何处理后台任务的失败重试
-
如何处理后台任务的优先级
-
如何处理后台任务的并发数
我们还讨论了如何申请 App 在后台运行,以确保下载任务不会被终止。希望这篇文章对你有所帮助,能够在实际项目中应用这些技术来实现更稳定和可靠的文件传输。下一篇文章将继续探讨 URLSession
的其他高级用法,敬请期待!
这里每天分享一个 iOS 的新知识,快来关注我吧
本文同步自微信公众号 "iOS新知",每天准时分享一个新知识,这里只是同步,想要及时学到就来关注我吧!