|
| 1 | +// The MIT License (MIT) |
| 2 | +// |
| 3 | +// Copyright (c) 2020-2024 Alexander Grebenyuk (github.com/kean). |
| 4 | + |
| 5 | +import Foundation |
| 6 | + |
| 7 | +@available(*, deprecated, renamed: "NetworkLogger.URLSessionProxyDelegate", message: "") |
| 8 | +public typealias URLSessionProxyDelegate = NetworkLogger.URLSessionProxyDelegate |
| 9 | + |
| 10 | +extension NetworkLogger { |
| 11 | + /// Automates URLSession request tracking. |
| 12 | + /// |
| 13 | + /// - important: On iOS 16.0, tvOS 16.0, macOS 13.0, watchOS 9.0, it automatically |
| 14 | + /// tracks new task creation using the `urlSession(_:didCreateTask:)` delegate |
| 15 | + /// method which allows the logger to start tracking network requests right |
| 16 | + /// after their creation. On earlier versions, you can (optionally) call |
| 17 | + /// ``NetworkLogger/logTaskCreated(_:)`` manually. |
| 18 | + public final class URLSessionProxyDelegate: NSObject, URLSessionTaskDelegate, URLSessionDataDelegate, URLSessionDownloadDelegate { |
| 19 | + private let actualDelegate: URLSessionDelegate? |
| 20 | + private let taskDelegate: URLSessionTaskDelegate? |
| 21 | + private let interceptedSelectors: Set<Selector> |
| 22 | + private var logger: NetworkLogger { _logger ?? .shared } |
| 23 | + private let _logger: NetworkLogger? |
| 24 | + |
| 25 | + /// - parameter logger: By default, uses a shared logger |
| 26 | + /// - parameter delegate: The "actual" session delegate, strongly retained. |
| 27 | + public init(logger: NetworkLogger? = nil, delegate: URLSessionDelegate? = nil) { |
| 28 | + self.actualDelegate = delegate |
| 29 | + self.taskDelegate = delegate as? URLSessionTaskDelegate |
| 30 | + self._logger = logger |
| 31 | + |
| 32 | + var interceptedSelectors: Set = [ |
| 33 | + #selector(URLSessionDataDelegate.urlSession(_:dataTask:didReceive:)), |
| 34 | + #selector(URLSessionTaskDelegate.urlSession(_:task:didCompleteWithError:)), |
| 35 | + #selector(URLSessionTaskDelegate.urlSession(_:task:didFinishCollecting:)), |
| 36 | + #selector(URLSessionTaskDelegate.urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)), |
| 37 | + #selector(URLSessionDownloadDelegate.urlSession(_:downloadTask:didFinishDownloadingTo:)), |
| 38 | + #selector(URLSessionDownloadDelegate.urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)) |
| 39 | + ] |
| 40 | + if #available(iOS 16.0, tvOS 16.0, macOS 13.0, watchOS 9.0, *) { |
| 41 | + interceptedSelectors.insert(#selector(URLSessionTaskDelegate.urlSession(_:didCreateTask:))) |
| 42 | + } |
| 43 | + self.interceptedSelectors = interceptedSelectors |
| 44 | + } |
| 45 | + |
| 46 | + // MARK: URLSessionTaskDelegate |
| 47 | + |
| 48 | + var createdTask: URLSessionTask? |
| 49 | + |
| 50 | + public func urlSession(_ session: Foundation.URLSession, didCreateTask task: URLSessionTask) { |
| 51 | + createdTask = task |
| 52 | + logger.logTaskCreated(task) |
| 53 | + if #available(iOS 16.0, tvOS 16.0, macOS 13.0, watchOS 9.0, *) { |
| 54 | + taskDelegate?.urlSession?(session, didCreateTask: task) |
| 55 | + } |
| 56 | + } |
| 57 | + |
| 58 | + public func urlSession(_ session: Foundation.URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { |
| 59 | + logger.logTask(task, didCompleteWithError: error) |
| 60 | + taskDelegate?.urlSession?(session, task: task, didCompleteWithError: error) |
| 61 | + } |
| 62 | + |
| 63 | + public func urlSession(_ session: Foundation.URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) { |
| 64 | + logger.logTask(task, didFinishCollecting: metrics) |
| 65 | + taskDelegate?.urlSession?(session, task: task, didFinishCollecting: metrics) |
| 66 | + } |
| 67 | + |
| 68 | + public func urlSession(_ session: Foundation.URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) { |
| 69 | + if task is URLSessionUploadTask { |
| 70 | + logger.logTask(task, didUpdateProgress: (completed: totalBytesSent, total: totalBytesExpectedToSend)) |
| 71 | + } |
| 72 | + (actualDelegate as? URLSessionTaskDelegate)?.urlSession?(session, task: task, didSendBodyData: bytesSent, totalBytesSent: totalBytesSent, totalBytesExpectedToSend: totalBytesExpectedToSend) |
| 73 | + } |
| 74 | + |
| 75 | + // MARK: URLSessionDataDelegate |
| 76 | + |
| 77 | + public func urlSession(_ session: Foundation.URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { |
| 78 | + logger.logDataTask(dataTask, didReceive: data) |
| 79 | + (actualDelegate as? URLSessionDataDelegate)?.urlSession?(session, dataTask: dataTask, didReceive: data) |
| 80 | + } |
| 81 | + |
| 82 | + // MARK: URLSessionDownloadDelegate |
| 83 | + |
| 84 | + public func urlSession(_ session: Foundation.URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { |
| 85 | + (actualDelegate as? URLSessionDownloadDelegate)?.urlSession(session, downloadTask: downloadTask, didFinishDownloadingTo: location) |
| 86 | + } |
| 87 | + |
| 88 | + public func urlSession(_ session: Foundation.URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { |
| 89 | + logger.logTask(downloadTask, didUpdateProgress: (completed: totalBytesWritten, total: totalBytesExpectedToWrite)) |
| 90 | + (actualDelegate as? URLSessionDownloadDelegate)?.urlSession?(session, downloadTask: downloadTask, didWriteData: bytesWritten, totalBytesWritten: totalBytesWritten, totalBytesExpectedToWrite: totalBytesExpectedToWrite) |
| 91 | + } |
| 92 | + |
| 93 | + // MARK: Proxy |
| 94 | + |
| 95 | + public override func responds(to aSelector: Selector!) -> Bool { |
| 96 | + if interceptedSelectors.contains(aSelector) { |
| 97 | + return true |
| 98 | + } |
| 99 | + return (actualDelegate?.responds(to: aSelector) ?? false) || super.responds(to: aSelector) |
| 100 | + } |
| 101 | + |
| 102 | + public override func forwardingTarget(for selector: Selector!) -> Any? { |
| 103 | + interceptedSelectors.contains(selector) ? nil : actualDelegate |
| 104 | + } |
| 105 | + } |
| 106 | +} |
0 commit comments