Larme

Untitled

Nov 17th, 2025
974
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Swift 5.28 KB | None | 0 0
  1. //Just to see the time on the logs
  2. struct Logger {
  3.     private static let dateFormatter: DateFormatter = {
  4.         let formatter = DateFormatter()
  5.         formatter.dateFormat = "yyyy-MM-dd hh:mm:ss.SSSSSZ"
  6.         return formatter
  7.     }()
  8.     static func log(_ object: Any) {
  9.         let date = Self.dateFormatter.string(from: Date())
  10.         print("\(date): - \(object)")
  11.     }
  12. }
  13.  
  14. actor FileDownloader {
  15.     static var fileIdCounter = 0
  16.     let fileId: Int
  17.     var onDidDownload: (() -> Void)? //In case we want to trigger something
  18.    
  19.     init(onDidDownload: (() -> Void)? = nil) {
  20.         self.fileId = Self.fileIdCounter
  21.         Self.fileIdCounter += 1
  22.         self.onDidDownload = onDidDownload
  23.     }
  24.    
  25.     func download() async throws {
  26.         let rng = Int.random(in: 5...15)
  27.         try? await Task.sleep(nanoseconds: UInt64(rng * 100_000_000))
  28.         Logger.log("Download finished for fileId: \(fileId)")
  29.     }
  30. }
  31.  
  32. //Handle 5 downloads max concurrently, when on finished, the next one is directly launched.
  33. // This avoid using Groups, that need the full downloads of the group to finish before handling the next one.
  34. //There is currently no Cancel management, just a hard stop
  35. //`add(_:)` could start the whole download process if it isn't started
  36. //States "started, paused etc". could be added.
  37. actor DownloadManager {
  38.     //Whole list of wdw
  39.     var queue: [FileDownloader] = []
  40.     var maxConcurrentDownloads: Int = 5
  41.     var currentDownloads: [FileDownloader] = []
  42.     //In case of downloads that trigger errors, currently just blacklisted withing app life, but could be handled differently ofc
  43.     var blacklist: [FileDownloader] = []
  44.    
  45.     func prioritize(_ fileDownloaders: [FileDownloader]) {
  46.         fileDownloaders.forEach {
  47.             prioritize($0)
  48.         }
  49.     }
  50.    
  51.     func prioritize(_ fileDownloader: FileDownloader) {
  52.         Logger.log("Prioritize \(fileDownloader.fileId)")
  53.         guard !currentDownloads.contains(where: { $0.fileId == fileDownloader.fileId }) else {
  54.             //Already downloading
  55.             Logger.log("Dwd \(fileDownloader.fileId) is already being downloaded")
  56.             return
  57.         }
  58.         guard !blacklist.contains(where: { $0.fileId == fileDownloader.fileId }) else {
  59.             //Already tried to download this time
  60.             Logger.log("Dwd \(fileDownloader.fileId) is blacklisted")
  61.             return
  62.         }
  63.         if let queuedIndex = queue.firstIndex(where: { $0.fileId == fileDownloader.fileId }) {
  64.             queue.remove(at: queuedIndex)
  65.             queue.insert(fileDownloader, at: 0)
  66.             Logger.log("Dwd \(fileDownloader.fileId) is priorized with success")
  67.         }
  68.     }
  69.    
  70.     func add(_ fileDownloader: FileDownloader) {
  71.         queue.append(fileDownloader)
  72.     }
  73.    
  74.     func add(_ fileDownloaders: [FileDownloader]) {
  75.         queue.append(contentsOf: fileDownloaders)
  76.     }
  77.    
  78.     func download() async {
  79.         guard !queue.isEmpty else {
  80.             Logger.log("Nothing to download, will set a timer to check in X minutes")
  81.             return
  82.         }
  83.        
  84.         let freeQueuesCount  = maxConcurrentDownloads - currentDownloads.count
  85.         guard freeQueuesCount > 0 else {
  86.             Logger.log("Max concurrent reached with \(currentDownloads.map { "\($0.fileId)" }.joined(separator: ", "))")
  87.             return
  88.         }
  89.         for _ in 0..<freeQueuesCount {
  90.             let fileDownloader = queue.removeFirst()
  91.             currentDownloads.append(fileDownloader)
  92.             //Detached launch, that way we can launch in parralels
  93.             Task {
  94.                 await downloadNext(fileDownloader)
  95.             }
  96.         }
  97.     }
  98.    
  99.     func downloadNext(_ fileDownloader: FileDownloader) async {
  100.         Logger.log("Launching: \(fileDownloader.fileId)")
  101.         Logger.log("Current downloads: \(currentDownloads.map { "\($0.fileId)" }.joined(separator: ", "))")
  102.         do {
  103.             try await fileDownloader.download()
  104.         } catch {
  105.             print("File download failed for \(fileDownloader.fileId)")
  106.             blacklist.append(fileDownloader)
  107.         }
  108.         await fileDownloader.onDidDownload?()
  109.         currentDownloads.removeAll(where: { $0.fileId == fileDownloader.fileId })
  110.         Task { await download() }
  111.     }
  112.    
  113.     func stop() {
  114.         Logger.log("STOP")
  115.         queue.removeAll()
  116.     }
  117. }
  118.  
  119.  
  120.  
  121. //Code play
  122. // Justed added
  123. /*
  124. Task {
  125.     await dwdManagement()
  126. }
  127.  
  128. in `func application(_ application:didFinishLaunchingWithOptions:)` in a new sample app to test and play
  129. */
  130.  
  131. func dwdManagement() async {
  132.     let manager = DownloadManager()
  133.  
  134.     var dwds: [FileDownloader] = []
  135.     for _ in 0...49 {
  136.         dwds.append(FileDownloader())
  137.     }
  138.     dwds.append(FileDownloader(onDidDownload: {
  139.         guard let last = dwds.last else { return }
  140.         Logger.log("Did Finished Special one -> Prioritize last!")
  141.         //It's also check if we can mess with the order
  142.         Task { await manager.prioritize(last) }
  143.     }))
  144.     for _ in 0...49 {
  145.         dwds.append(FileDownloader())
  146.     }
  147.    
  148.     await manager.add(dwds)
  149.    
  150.     Task {
  151.         //Quick test on "Stop"
  152.         try? await Task.sleep(nanoseconds: 25_000_000_000)
  153.         await manager.stop()
  154.     }
  155.     await manager.download()
  156. }
Advertisement
Add Comment
Please, Sign In to add comment