美文网首页
Kingfisher源码之SessionDelegate

Kingfisher源码之SessionDelegate

作者: Mannyao | 来源:发表于2020-08-24 14:59 被阅读0次

SessionDelegate首先声明一点它的类型是一个类,而不是一个协议,第一次看的时候就把它当做成一个协议,它是一个实现URLSessionDataDelegate的类,按源码的注释,它的行为像是一个下载任务的管理器。不过也确实是这样,因为它实现了SessionDataTask的管理

private var tasks: [URL: SessionDataTask] = [:]

又是熟悉添加获取取消等一系列操作给人感觉思路很清晰

SessionDataTask的管理

1.添加SessionDataTask

func append(
        _ task: SessionDataTask,
        url: URL,
        callback: SessionDataTask.TaskCallback) -> DownloadTask
    {
        let token = task.addCallback(callback)
        return DownloadTask(sessionTask: task, cancelToken: token)
    }

着这里顺便介绍下DownloadTask结构体
DownloadTask结构体可以理解为对SessionDataTask的二次封装

public struct DownloadTask {
    public let sessionTask: SessionDataTask
    public let cancelToken: SessionDataTask.CancelToken
    public func cancel() {
        sessionTask.cancel(token: cancelToken)
    }
}

2.获取SessionDataTask

 private func task(for task: URLSessionTask) -> SessionDataTask? {

        guard let url = task.originalRequest?.url else {
            return nil
        }
        lock.lock()
        defer { lock.unlock() }
        guard let sessionTask = tasks[url] else {
            return nil
        }
        guard sessionTask.task.taskIdentifier == task.taskIdentifier else {
            return nil
        }
        return sessionTask
    }

    func task(for url: URL) -> SessionDataTask? {
        lock.lock()
        defer { lock.unlock() }
        return tasks[url]
    }

3.获取SessionDataTask

func cancel(url: URL) {
        lock.lock()
        let task = tasks[url]
        lock.unlock()
        task?.forceCancel()
    }

URLSessionDataDelegate的实现

extension SessionDelegate: URLSessionDataDelegate {

     func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        guard let task = self.task(for: dataTask) else {
            return
        }
        task.didReceiveData(data)

        if let expectedContentLength = dataTask.response?.expectedContentLength, expectedContentLength != -1 {
            let dataLength = Int64(task.mutableData.count)
            DispatchQueue.main.async {
                task.callbacks.forEach { callback in
                    callback.onProgress?.call((dataLength, expectedContentLength))
                }
            }
        }
    }

    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        guard let sessionTask = self.task(for: task) else { return }

        if let url = task.originalRequest?.url {
            let result: Result<URLResponse, KingfisherError>
            if let error = error {
                result = .failure(KingfisherError.responseError(reason: .URLSessionError(error: error)))
            } else if let response = task.response {
                result = .success(response)
            } else {
                result = .failure(KingfisherError.responseError(reason: .noURLResponse(task: sessionTask)))
            }
            onDownloadingFinished.call((url, result))
        }

        let result: Result<(Data, URLResponse?), KingfisherError>
        if let error = error {
            result = .failure(KingfisherError.responseError(reason: .URLSessionError(error: error)))
        } else {
            if let data = onDidDownloadData.call(sessionTask), let finalData = data {
                result = .success((finalData, task.response))
            } else {
                result = .failure(KingfisherError.responseError(reason: .dataModifyingFailed(task: sessionTask)))
            }
        }
        onCompleted(task: task, result: result)
    }

可以看出通过实现URLSessionDataDelegate完成了进度回调,下载任务完成时的回调。并在任务完成的时候将task移除。

相关文章

网友评论

      本文标题:Kingfisher源码之SessionDelegate

      本文链接:https://www.haomeiwen.com/subject/jtagjktx.html