Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
Danie1s edited this page Mar 18, 2020 · 6 revisions

基本用法

初始化

SessionManager是 Tiercel 里最重要的类型,负责开启下载任务、管理任务、操作任务。使用 Tiercel 首要就是创建SessionManager实例

因为需要支持原生后台下载,所以需要在AppDelegate.swift文件里初始化,参考以下做法

// 在 AppDelegate.swift 文件里
// 最好成为AppDelegate的属性
// 注意:不能使用懒加载
var sessionManager: SessionManager = {
 var configuration = SessionConfiguration()
 configuration.allowsCellularAccess = true
 let manager = SessionManager("default", configuration: configuration)
 return manager
}()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
 // 如果需要做一些配置,那么必须保证在这个方法结束前完成SessionManager初始化
 
 return true
}
// 必须实现此方法,并且把identifier对应的completionHandler保存起来
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
 if sessionManager.identifier == identifier {
 sessionManager.completionHandler = completionHandler
 }
}

开启下载

初始化完毕后,可以拿到 SessionManager实例直接使用

一行代码开启下载

// 创建下载任务并且开启下载,同时返回可选类型的DownloadTask实例,如果url无效,则返回nil
let task = sessionManager.download("http://dldir1.qq.com/qqfile/QQforMac/QQ_V4.2.4.dmg")
// 批量创建下载任务并且开启下载,返回有效url对应的任务数组,url需要跟fileNames一一对应
let tasks = sessionManager.multiDownload(URLStrings)

开启下载的方法主要接受一个遵守URLConvertible协议的类型参数,在 Tiercel 中默认让 StringURLURLComponents遵守这个协议,也就是可以传入这三种类型的参数来开启下载

public protocol URLConvertible {
 func asURL() throws -> URL
}

状态回调

任务的状态回调

Tiercel 提供了精细的任务状态回调

let task = sessionManager.download("http://dldir1.qq.com/qqfile/QQforMac/QQ_V4.2.4.dmg")
// 回调闭包的参数是DownloadTask实例,可以得到所有相关的信息
// 所有闭包都可以选择是否在主线程上执行,由参数onMainQueue控制,如果onMainQueue传false,则会在sessionManager初始化时指定的队列上执行,onMainQueue默认为true
// progress 闭包:如果任务正在下载,就会触发
// success 闭包:任务已经下载过,或者下载成功,都会触发,这时候task.status == .succeeded
// failure 闭包:只要task.status != .succeeded,就会触发:
// 1. 暂停任务,这时候task.status == .suspended
// 2. 任务下载失败,这时候task.status == .failed
// 3. 取消任务,这时候task.status == .canceled
// 4. 移除任务,这时候task.status == .removed
task?.progress(onMainQueue: true) { (task) in
 let progress = task.progress.fractionCompleted
 print("下载中, 进度:\(progress)")
}.success { (task) in
 print("下载完成")
}.failure { (task) in
 print("下载失败")
}
// 为了让代码更加统一,新增了completion闭包,与success和failure互斥,当使用completion时,success和failure无效
task?.progress(onMainQueue: true) { (task) in
 let progress = task.progress.fractionCompleted
 print("下载中, 进度:\(progress)")
}.completion { (task) in 
 if task.status == .succeeded {
 	// 下载成功
 } else {
 	// 其他状态
 }
}

SessionManager的状态回调

SessionManager作为所有任务的管理者,也可以设置状态回调

sessionManager.progress { [weak self] (manager) in
	// 下载中
}.completion { [weak self] (manager) in
 if manager.status == .succeeded {
 // 下载成功
 } else {
 // 其他状态
 }
}

通知

为了方便使用,Tiercel 还提供了状态通知

public extension DownloadTask {
 static let runningNotification: Notification.Name
 static let didCompleteNotification: Notification.Name
}
public extension SessionManager {
 static let runningNotification: Notification.Name
 static let didCompleteNotification: Notification.Name
}
extension Notification {
 public var downloadTask: DownloadTask? 
 public var sessionManager: SessionManager? 
}

使用方式如下:

// 接受task的通知
NotificationCenter.default.addObserver(forName: DownloadTask.didCompleteNotification, object: nil, queue: nil) { (notification) in
 guard let task = notification.downloadTask else { return }
 print(task.status)
}

操作任务

SessionManager是任务的管理者,任何操作都需要通过SessionManager进行

单个任务

在 Tiercel 中,URL 是下载任务的唯一标识,如果需要对任务进行操作,则传入对应的 URL

let URLString = "http://dldir1.qq.com/qqfile/QQforMac/QQ_V4.2.4.dmg"
// 开始下载
// 如果调用suspend暂停了下载,可以调用这个方法继续下载
sessionManager.start(URLString)
// 暂停下载
sessionManager.suspend(URLString)
// 取消下载,没有下载成功的任务会被移除,不保留缓存,已经下载完成的不受影响
sessionManager.cancel(URLString)
// 移除下载,任何状态的任务都会被移除,没有下载完成的缓存文件会被删除,可以选择是否保留已经下载完成的文件
sessionManager.remove(URLString, completely: false)

也可以直接对任务进行操作

let URLString = "http://dldir1.qq.com/qqfile/QQforMac/QQ_V4.2.4.dmg"
let task = sessionManager.fetchTask(URLString)
sessionManager.start(task)
sessionManager.suspend(task)
sessionManager.cancel(task)
sessionManager.remove(task, completely: false)

批量操作

除了可以对单个任务进行操作,sessionManager也提供了批量操作的 API

Tiercel 对批量操作的 API 进行了优化,性能会比简单地逐个操作(如:for循环、forEach)高出很多倍,如果需要对多个任务同时进行操作,请使用这些 API

sessionManager.multiDownload(URLStrings)
sessionManager.totalStart()
sessionManager.totalSuspend()
sessionManager.totalCancel()
sessionManager.totalRemove(completely: false)

操作回调

暂停下载、取消下载、移除下载的操作可以添加回调,并且可以选择是否在主线程上执行该闭包

注意:这些回调都只会执行一次

// 暂停下载
sessionManager.cancel(task, onMainQueue: true) { [weak self] (task) in
 self?.tableView.reloadData()
}
// 批量操作的回调
sessionManager.totalCancel() { [weak self] _ in
 self?.tableView.reloadData()
}

后台下载

从 Tiercel 2.0 开始支持原生的后台下载,开发者只需按照本文开头的初始化进行操作,就可以完成所需的配置

只要使用 Tiercel 开启了下载任务:

  • 手动Kill App,任务会暂停,重启App后可以恢复进度,继续下载
  • 只要不是手动Kill App,任务都会一直在下载,例如:
    • App退回后台
    • App崩溃或者被系统关闭
    • 重启手机

如果想了解后台下载的细节和注意事项,可以查看:iOS 原生级别后台下载详解

文件校验

Tiercel 提供了文件校验功能,可以根据需要添加,校验结果在回调的task.validation

let task = sessionManager.download("http://dldir1.qq.com/qqfile/QQforMac/QQ_V4.2.4.dmg")
// 回调闭包可以选择是否在主线程上执行
task?.validateFile(code: "9e2a3650530b563da297c9246acaad5c",
 type: .md5,
 onMainQueue: true)
 { (task) in
 if task.validation == .correct {
 // 文件正确
 } else {
 // 文件错误
 }
}

FileChecksumHelper是文件校验的工具类,可以直接使用它对已经存在的文件进行校验

/// 对文件进行校验,是在子线程进行的
///
/// - Parameters:
/// - filePath: 文件路径
/// - verificationCode: 文件的Hash值
/// - verificationType: Hash类型
/// - completion: 完成回调, 在子线程运行
public static func validateFile(_ filePath: String,
 code: String,
 type: FileVerificationType,
 completion: @escaping (Result<Bool, FileVerificationError>) -> ()) {

日志

Tiercel 3.0 默认提供了完善的日志打印,依靠的是SessionManager的属性var logger: Logable,可以设置sessionManager.logger.option = .none来关闭

SessionManager初始化的时候可以传入一个Logable类型参数,开发者可以遵守Logable协议,自定义一个logger,替代默认的日志打印

public enum LogOption {
 case `default`
 case none
}
public enum LogType {
 case sessionManager(_ message: String, manager: SessionManager)
 case downloadTask(_ message: String, task: DownloadTask)
 case error(_ message: String, error: Error)
}
public protocol Logable {
 var identifier: String { get }
 
 var option: LogOption { get set }
 
 func log(_ type: LogType)
}

错误处理

Tiercel 使用过程中产生的错误,默认都会打印出日志,而跟下载任务相关的错误,大多数都同时保留在task.error里面

进阶用法

SessionManager

SessionManager是下载任务的管理者,管理当前模块所有下载任务

⚠️⚠️⚠️ 按照苹果官方文档的要求,SessionManager实例必须在 App 启动的时候创建,即SessionManager实例的生命周期跟 App 几乎一致,为方便使用,最好是作为AppDelegate的属性,或者是全局变量,具体请参照Demo

/// SessionManager的初始化方法
/// - Parameters:
/// - identifier: 设置SessionManager实例的标识,用于区分不同的下载模块,同时也是URLSession实例的标识,原生级别的后台下载必须要有唯一标识
/// - configuration: SessionManager的配置
/// - logger: 用于该SessionManager实例里所有的日志打印
/// - cache: 用于下载任务的持久化
/// - operationQueue: URLSession的代理执行队列,SessionManager中的所有闭包回调如果没有指定在主线程执行,也会在此队列中执行
public init(_ identifier: String,
 configuration: SessionConfiguration,
 logger: Logable? = nil,
 cache: Cache? = nil,
 operationQueue: DispatchQueue = DispatchQueue(label: "com.Tiercel.SessionManager.operationQueue")) {
 // 实现的代码... 
} 

SessionManager回调的详细说明

// 回调闭包的参数是SessionManager实例,可以得到所有相关的信息
// 所有闭包都可以选择是否在主线程上执行,由onMainQueue参数控制,如果onMainQueue传false,则会在sessionManager初始化时指定的队列上执行,onMainQueue默认为true
// progress 闭包:只要有一个任务正在下载,就会触发
// success 闭包:只有一种情况会触发:
// 所有任务都下载成功(取消和移除的任务会被移除然后销毁,不再被manager管理) ,这时候manager.status == .succeeded
// failure 闭包:只要manager.status != .succeeded,就会触发:
// 1. 调用全部暂停的方法,或者没有等待运行的任务,也没有正在运行的任务,这时候manager.status == .suspended
// 2. 所有任务都结束,但有一个或者多个是失败的,这时候manager.status == .failed
// 3. 调用全部取消的方法,或者剩下一个任务的时候把这个任务取消,这时候manager.status == .canceled
// 4. 调用全部移除的方法,或者剩下一个任务的时候把这个任务移除,这时候manager.status == .removed
sessionManager.progress(onMainQueue: true) { (manager) in
 let progress = manager.progress.fractionCompleted
 print("downloadManager运行中, 总进度:\(progress)")
 }.success { (manager) in
 print("所有下载任务都成功了")
 }.failure { (manager) in
 if manager.status == .suspended {
 print("所有下载任务都暂停了")
 } else if manager.status == .failed {
 print("存在下载失败的任务")
 } else if manager.status == .canceled {
 print("所有下载任务都取消了")
 } else if manager.status == .removed {
 print("所有下载任务都移除了")
 }
}

SessionManager的主要属性

// 用于日志打印
public var logger: Logable
// 是否需要对networkActivityIndicator进行管理
public var isControlNetworkActivityIndicator: Bool
// urlSession的代理回调执行队列,SessionManager中的所有闭包回调如果没有指定在主线程执行,也会在此队列中执行
public let operationQueue: DispatchQueue
// SessionManager的状态
public var status: Status
// SessionManager的缓存管理实例
public let cache: Cache
// SessionManager的标识,区分不同的下载模块
public let identifier: String
// SessionManager的进度
public var progress: Progress
// SessionManager的配置,可以设置请求超时时间,最大并发数,是否允许蜂窝网络下载等
public var configuration: SessionConfiguration
// 所有下载中的任务加起来的总速度
public var speed: Int64
public var speedString: String
// 所有下载中的任务需要的剩余时间
public var timeRemaining
public var timeRemainingString: String
// SessionManager管理的下载任务,取消和移除的任务会被销毁,但操作是异步的,在回调闭包里面获取才能保证正确
public var tasks: [Task]

可以通过以下方法进行排序

// 改变单个任务的位置
public func moveTask(at sourceIndex: Int, to destinationIndex: Int) {
 // ...
}
// 对所有任务进行排序
public func tasksSort(by areInIncreasingOrder: (DownloadTask, DownloadTask) throws -> Bool) rethrows {
 // ...
}

SessionConfiguration

SessionConfiguration是 Tiercel 中配置SessionManager的结构体,可配置属性如下:

// 请求超时时间
public var timeoutIntervalForRequest
// 最大并发数
// 支持后台下载的任务,系统会进行最大并发数限制
// 在iOS 11及以上是6,iOS 11以下是3
public var maxConcurrentTasksLimit
// 是否允许蜂窝网络下载
public var allowsCellularAccess = false
// 昂贵网络访问限制,只能在iOS 13及以上系统使用
public var allowsExpensiveNetworkAccess: Bool = true
// 低数据网络访问限制,只能在iOS 13及以上系统使用
public var allowsConstrainedNetworkAccess: Bool = true

更改SessionManager的配置

// 无论是否有下载任务正在运行,都可以更改SessionManager配置
// 如果只是更改某一项,可以直接对SessionManager属性设置
sessionManager.configuration.allowsCellularAccess = true
// 如果是需要更改多项,需要重新创建SessionConfiguration,再进行赋值
let configuration = SessionConfiguration()
configuration.allowsCellularAccess = true
configuration.maxConcurrentTasksLimit = 2
configuration.timeoutIntervalForRequest = 60
sessionManager.configuration = configuration

注意:建议在SessionManager初始化的时候传入已经修改好的SessionConfiguration实例,参考 Demo。Tiercel 也支持在任务下载中修改配置,但是不建议修改configuration后马上开启任务下载,即不要在同一个代码块里修改configuration后开启任务下载,这样很容易造成错误。

// 不要这样操作
sessionManager.configuration.allowsCellularAccess = true
let task = sessionManager.download("http://dldir1.qq.com/qqfile/QQforMac/QQ_V4.2.4.dmg")

如果实在需要进行这种操作,请修改configuration后,设置1秒以上的延迟再开启任务下载。

// 如果实在需要,请延迟开启任务
sessionManager.configuration.allowsCellularAccess = true
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
 let task = sessionManager.download("http://dldir1.qq.com/qqfile/QQforMac/QQ_V4.2.4.dmg")
}

DownloadTask

DownloadTask是 Tiercel 中的下载任务类型,继承自Task<Type>DownloadTask实例都是由SessionManager实例创建,不能单独创建

主要属性

// 保存到沙盒的下载文件的文件名,如果在下载的时候没有设置,则默认为url的md5加上文件扩展名
public var fileName: String
// 下载任务对应的url
public let url: URL
// 下载任务的状态
public var status: Status
// 下载文件的校验状态
public var validation: Validation
// 下载任务的进度
public var progress: Progress = Progress()
// 下载任务的开始日期
public var startDate: TimeInterval
public var startDateString: String	
// 下载任务的结束日期
public var endDate: TimeInterval
public var endDateString: String
// 下载任务的速度
public var speed: Int64
public var speedString: String
// 下载任务的剩余时间
public var timeRemaining: Int64 = 0
public var timeRemainingString: String
// 下载文件路径
public var filePath: String
// 下载文件的扩展名
public var pathExtension: String?
// 下载任务失败的error
public var error: Error?

对下载任务操作,是通过SessionManager实例进行的:

  • 开启
  • 暂停
  • 取消,没有完成的任务从SessionManager实例中的tasks中移除,不保留缓存,已经下载完成的任务不受影响
  • 移除,已经完成的任务也会被移除,没有下载完成的缓存文件会被删除,已经下载完成的文件可以选择是否保留

注意:对下载中的任务进行暂停、取消和移除操作,是异步进行的,这些操作的 API 都有提供回调闭包,在回调闭包里面获取状态才能保证正确

Cache

Cache是 Tiercel 中负责管理缓存下载任务信息和下载文件的类。Cache实例一般作为SessionManager实例的属性来使用。

/// 初始化方法
/// - Parameters:
/// - identifier: 不同的identifier代表不同的下载模块。如果没有自定义下载目录,Cache会提供默认的目录,这些目录跟identifier相关
/// - downloadPath: 存放用于DownloadTask持久化的数据,默认提供的downloadTmpPath、downloadFilePath也是在里面
/// - downloadTmpPath: 存放下载中的临时文件
/// - downloadFilePath: 存放下载完成后的文件
public init(_ identifier: String,
 downloadPath: String? = nil,
 downloadTmpPath: String? = nil,
 downloadFilePath: String? = nil) {
 	// ... 
}

SessionManager初始化的时候可以传入一个Cache类型参数,开发者可以自定义一个Cache,改变缓存目录的位置

主要属性

// 下载模块的目录路径
public let downloadPath: String
// 没有完成的下载文件缓存的目录路径
public let downloadTmpPath: String
// 下载完成的文件的目录路径
public let downloadFilePath: String

对外暴露的 API:

// 传入文件名来返回String、URL,或者判断文件是否存在
public func filePath(fileName: String) -> String?
public func fileURL(fileName: String) -> URL? 
public func fileExists(fileName: String) -> Bool 
// 传入url来返回String、URL,或者判断文件是否存在
public func filePath(url: URLConvertible) -> String? 
public func fileURL(url: URLConvertible) -> URL? 
public func fileExists(url: URLConvertible) -> Bool 
// 清除磁盘缓存
public func clearDiskCache(onMainQueue: Bool = true, handler: Handler<Cache>? = nil)

Clone this wiki locally

AltStyle によって変換されたページ (->オリジナル) /