前提・実現したいこと
iOSとSwiftでURLsessionを利用してネットワークアクセスをするプログラムを作成しています。
サンプルを探すと、Apple公式などでシングルトンパターンで実装しているコードをよく見かけます。
しかし、シングルトンで実装されていると、デリゲートを一つしか指定できません。
これは異なるクラスから同時に利用するときなどに問題になる場合があると思います。
この解決策としては、さらにラッパークラス的なものを追加し、デリゲート先を都度切り替えるように実装するなどの案内も見かけます。
ですが、そもそもURLSessionを利用する場合になぜシングルトンで実装するのが一般的なのか、わかる方がいたら教えていただけないでしょうか。
よろしくお願いします。
該当のソースコード
Swift
1/*URLSessionとURLSessionDelegateをまとめたクラスのサンプル*/ 2import Foundation 3class NetworkDownloader: NSObject { 4 static let shared = NetworkDownloader() 5 var sessionConfiguration = URLSessionConfiguration.default 6 var urlSession: URLSession? 7 var runningURLs: [URL: Int] = [:] 8 var runningTasks: [URL: URLSessionDownloadTask] = [:] 9 weak var delegate: NetworkQueueDelegate? 10 override init() { 11 super.init() 12 sessionConfiguration.httpMaximumConnectionsPerHost = 5 13 urlSession = URLSession(configuration: sessionConfiguration, delegate: self, delegateQueue: .main) 14 } 15 16 func startDownloadTask(with url: String, with opIndex: Int) { 17 print("Starting task: (url)") 18 guard let networkURL = URL(string: url) else { 19 return 20 } 21 runningURLs[networkURL] = opIndex 22 let downloadTask = urlSession?.downloadTask(with: networkURL) 23 runningTasks[networkURL] = downloadTask 24 downloadTask?.resume() 25 } 26 27 func cancelDownloadTask(with url: String, with opIndex: Int) { 28 guard let networkURL = URL(string: url) else { 29 return 30 } 31 let task = runningTasks[networkURL] 32 runningTasks.removeValue(forKey: networkURL) 33 runningURLs.removeValue(forKey: networkURL) 34 task?.cancel() 35 } 36 37} 38 39extension NetworkDownloader: URLSessionDownloadDelegate, URLSessionDelegate { 40 func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) { 41 guard let networkURL = downloadTask.currentRequest?.url, 42 let index = runningURLs[networkURL] else { 43 return 44 } 45 // Remove the running task 46 runningTasks.removeValue(forKey: networkURL) 47 runningURLs.removeValue(forKey: networkURL) 48 self.delegate?.notifyOfNetworkOperationCompletion(with: networkURL.absoluteString, 49 fileURL: location.absoluteString, 50 operationIndex: index, error: nil) 51 } 52 func urlSession(_ session: URLSession, 53 downloadTask: URLSessionDownloadTask, 54 didWriteData bytesWritten: Int64, 55 totalBytesWritten: Int64, 56 totalBytesExpectedToWrite: Int64) { 57 guard let networkURL = downloadTask.currentRequest?.url, 58 let index = runningURLs[networkURL] else { 59 return 60 } 61 62 self.delegate?.notifyOfNetworkProgress(progress: "Bytes written: (totalBytesWritten) out of: (totalBytesExpectedToWrite)", 63 operationIndex: index) 64 } 65 func urlSession(_ session: URLSession, 66 task: URLSessionTask, 67 didCompleteWithError error: Error?) { 68 guard let networkURL = task.currentRequest?.url, 69 let index = runningURLs[networkURL] else { 70 return 71 } 72 // Remove the running task 73 runningTasks.removeValue(forKey: networkURL) 74 runningURLs.removeValue(forKey: networkURL) 75 self.delegate?.notifyOfNetworkOperationCompletion(with: networkURL.absoluteString, 76 fileURL: nil, 77 operation Index: index, error: error) 78 } 79} 80 81/*利用する場合*/ 82class ViewController: UIViewController { 83 @IBOutlet weak var testQueue: UIButton! 84 override func viewDidLoad() { 85 super.viewDidLoad() 86 // Do any additional setup after loading the view. 87 } 88 @IBAction func testOperationQueue() { 89 let urls = [ 90 "https:///xxxx/Test/1.jpg", 91 "https:///xxxx/Test/2.jpg", 92 "https:///xxxx/Test/3.jpg", 93 "https:///xxxx/Test/4.jpg", 94 "https:///xxxx/Test/5.jpg", 95 "https:///xxxx/Test/6.jpg", 96 "https:///xxxx/Test/7.jpg", 97 "https:///xxxx/Test/8.jpg", 98 "https:///xxxx/Test/9.jpg", 99 ] 100 101 NetworkDownloader.shared.delegate = self 102 var index = 1 103 for url in urls { 104 let nonCachedURL = "(url)" 105 NetworkDownloader.shared.startDownloadTask(with: nonCachedURL, with: index) 106 index += 1 107 } 108 } 109} 110extension ViewController: NetworkQueueDelegate { 111 func notifyOfNetworkOperationCompletion(with url: String, fileURL: String?, operationIndex: Int, error: Error?) { 112 guard let filePath = fileURL, error == nil else { 113 print("Error from operation: (error?.localizedDescription ?? "No Error")") 114 return 115 } 116 print("** Operation: (operationIndex) finished with downloaded file: (filePath)") 117 } 118 func notifyOfNetworkProgress(progress: String, operationIndex: Int) { 119 print("-- Operation: (operationIndex) : (progress)") 120 } 121}
補足情報(FW/ツールのバージョンなど)
Swift 5
Xcode 12.5
回答2件
あなたの回答
tips
プレビュー