From 91d7911d03298698c263ad83c58ab81a16d8032c Mon Sep 17 00:00:00 2001 From: Dishit Date: Thu, 7 May 2026 11:28:02 +0530 Subject: [PATCH 1/3] fix(ios): align native download contract and fix downloadId mapping Co-authored-by: Dishit --- ios/DownloadManagerModule.swift | 62 +++++++++++++++++++++-- src/services/backgroundDownloadService.ts | 2 +- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/ios/DownloadManagerModule.swift b/ios/DownloadManagerModule.swift index 9f0c2fb4..8cf34e5f 100644 --- a/ios/DownloadManagerModule.swift +++ b/ios/DownloadManagerModule.swift @@ -59,6 +59,11 @@ class DownloadManagerModule: RCTEventEmitter { var bytesDownloaded: Int64 var status: String // pending, running, paused, completed, failed var startedAt: Double + // v3 fields — mirrors Android WorkManager columns so JS hydration works on both platforms + var modelKey: String? + var modelType: String + var combinedTotalBytes: Int64 + var metadataJson: String? // Single-file download var task: URLSessionDownloadTask? var taskIdentifier: Int? @@ -87,6 +92,10 @@ class DownloadManagerModule: RCTEventEmitter { let bytesDownloaded: Int64 let status: String let startedAt: Double + let modelKey: String? + let modelType: String + let combinedTotalBytes: Int64 + let metadataJson: String? let taskIdentifier: Int? let localUri: String? let multiFileDestDir: String? @@ -95,7 +104,8 @@ class DownloadManagerModule: RCTEventEmitter { enum CodingKeys: String, CodingKey { case downloadId, fileName, modelId, totalBytes, bytesDownloaded, status, - startedAt, taskIdentifier, localUri, multiFileDestDir, isMultiFile, fileTasks + startedAt, modelKey, modelType, combinedTotalBytes, metadataJson, + taskIdentifier, localUri, multiFileDestDir, isMultiFile, fileTasks } init( @@ -106,6 +116,10 @@ class DownloadManagerModule: RCTEventEmitter { bytesDownloaded: Int64, status: String, startedAt: Double, + modelKey: String?, + modelType: String, + combinedTotalBytes: Int64, + metadataJson: String?, taskIdentifier: Int?, localUri: String?, multiFileDestDir: String?, @@ -119,6 +133,10 @@ class DownloadManagerModule: RCTEventEmitter { self.bytesDownloaded = bytesDownloaded self.status = status self.startedAt = startedAt + self.modelKey = modelKey + self.modelType = modelType + self.combinedTotalBytes = combinedTotalBytes + self.metadataJson = metadataJson self.taskIdentifier = taskIdentifier self.localUri = localUri self.multiFileDestDir = multiFileDestDir @@ -135,6 +153,11 @@ class DownloadManagerModule: RCTEventEmitter { bytesDownloaded = try container.decode(Int64.self, forKey: .bytesDownloaded) status = try container.decode(String.self, forKey: .status) startedAt = try container.decode(Double.self, forKey: .startedAt) + // Decode with defaults so old persisted state (pre-v3) deserialises without crashing + modelKey = try container.decodeIfPresent(String.self, forKey: .modelKey) + modelType = (try container.decodeIfPresent(String.self, forKey: .modelType)) ?? "text" + combinedTotalBytes = (try container.decodeIfPresent(Int64.self, forKey: .combinedTotalBytes)) ?? 0 + metadataJson = try container.decodeIfPresent(String.self, forKey: .metadataJson) taskIdentifier = try container.decodeIfPresent(Int.self, forKey: .taskIdentifier) localUri = try container.decodeIfPresent(String.self, forKey: .localUri) multiFileDestDir = try container.decodeIfPresent(String.self, forKey: .multiFileDestDir) @@ -337,6 +360,10 @@ class DownloadManagerModule: RCTEventEmitter { bytesDownloaded: info.bytesDownloaded, status: info.status, startedAt: info.startedAt, + modelKey: info.modelKey, + modelType: info.modelType, + combinedTotalBytes: info.combinedTotalBytes, + metadataJson: info.metadataJson, taskIdentifier: info.taskIdentifier ?? info.task?.taskIdentifier, localUri: info.localUri, multiFileDestDir: info.multiFileDestDir, @@ -368,6 +395,10 @@ class DownloadManagerModule: RCTEventEmitter { bytesDownloaded: persisted.bytesDownloaded, status: persisted.status, startedAt: persisted.startedAt, + modelKey: persisted.modelKey, + modelType: persisted.modelType, + combinedTotalBytes: persisted.combinedTotalBytes, + metadataJson: persisted.metadataJson, task: nil, taskIdentifier: persisted.taskIdentifier, localUri: persisted.localUri, @@ -464,6 +495,10 @@ class DownloadManagerModule: RCTEventEmitter { bytesDownloaded: 0, status: self.statusString(from: downloadTask.state), startedAt: Date().timeIntervalSince1970 * 1000, + modelKey: nil, + modelType: "text", + combinedTotalBytes: 0, + metadataJson: nil, task: nil, taskIdentifier: nil, localUri: nil, @@ -519,11 +554,15 @@ extension DownloadManagerModule { } let totalBytes = (params["totalBytes"] as? NSNumber)?.int64Value ?? 0 + let modelKey = params["modelKey"] as? String + let modelType = (params["modelType"] as? String) ?? "text" + let combinedTotalBytes = (params["combinedTotalBytes"] as? NSNumber)?.int64Value ?? 0 + let metadataJson = params["metadataJson"] as? String let downloadId = String(nextDownloadId) nextDownloadId += 1 - NSLog("[DownloadManager] Starting download #%@: url=%@, fileName=%@, modelId=%@, totalBytes=%lld", - downloadId, urlString, fileName, modelId, totalBytes) + NSLog("[DownloadManager] Starting download #%@: url=%@, fileName=%@, modelId=%@, totalBytes=%lld, modelType=%@", + downloadId, urlString, fileName, modelId, totalBytes, modelType) let task = session.downloadTask(with: url) task.taskDescription = encodeTaskDescription(TaskDescription( @@ -546,6 +585,10 @@ extension DownloadManagerModule { bytesDownloaded: 0, status: "running", startedAt: Date().timeIntervalSince1970 * 1000, + modelKey: modelKey, + modelType: modelType, + combinedTotalBytes: combinedTotalBytes, + metadataJson: metadataJson, task: task, taskIdentifier: task.taskIdentifier, localUri: nil, @@ -647,6 +690,10 @@ extension DownloadManagerModule { bytesDownloaded: 0, status: "running", startedAt: Date().timeIntervalSince1970 * 1000, + modelKey: params["modelKey"] as? String, + modelType: (params["modelType"] as? String) ?? "image", + combinedTotalBytes: (params["combinedTotalBytes"] as? NSNumber)?.int64Value ?? 0, + metadataJson: params["metadataJson"] as? String, task: nil, taskIdentifier: nil, localUri: nil, @@ -714,15 +761,20 @@ extension DownloadManagerModule { let result = downloads.values.map { info -> [String: Any] in NSLog("[DownloadManager] -> #%@: %@ status=%@ bytes=%lld/%lld", info.downloadId, info.fileName, info.status, info.bytesDownloaded, info.totalBytes) - return [ + var entry: [String: Any] = [ "downloadId": info.downloadId, "fileName": info.fileName, "modelId": info.modelId, "status": info.status, "bytesDownloaded": NSNumber(value: info.bytesDownloaded), "totalBytes": NSNumber(value: info.totalBytes), - "startedAt": NSNumber(value: info.startedAt) + "startedAt": NSNumber(value: info.startedAt), + "modelType": info.modelType, + "combinedTotalBytes": NSNumber(value: info.combinedTotalBytes) ] + if let modelKey = info.modelKey { entry["modelKey"] = modelKey } + if let metadataJson = info.metadataJson { entry["metadataJson"] = metadataJson } + return entry } resolve(result) } diff --git a/src/services/backgroundDownloadService.ts b/src/services/backgroundDownloadService.ts index 030580f8..4d622f0a 100644 --- a/src/services/backgroundDownloadService.ts +++ b/src/services/backgroundDownloadService.ts @@ -80,7 +80,7 @@ class BackgroundDownloadService { } const downloads = await DownloadManagerModule.getActiveDownloads(); return downloads.map((d: any) => ({ - downloadId: d.id, + downloadId: d.downloadId ?? d.id, fileName: d.fileName, modelId: d.modelId, status: d.status as BackgroundDownloadStatus, From 1c7571841b64b5d040ee9152edf1107453be02a5 Mon Sep 17 00:00:00 2001 From: Dishit Date: Thu, 7 May 2026 11:29:52 +0530 Subject: [PATCH 2/3] chore(ios): bump llama-rn from rc.5 to rc.9 Co-authored-by: Dishit --- ios/Podfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 3db88fc3..7cedf2d7 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -10,7 +10,7 @@ PODS: - hermes-engine (0.14.0): - hermes-engine/Pre-built (= 0.14.0) - hermes-engine/Pre-built (0.14.0) - - llama-rn (0.12.0-rc.5): + - llama-rn (0.12.0-rc.9): - boost - DoubleConversion - fast_float @@ -3604,7 +3604,7 @@ SPEC CHECKSUMS: fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 hermes-engine: 8c6be38f94b3bf8b864981980e64e55f08e467ec - llama-rn: 3ae5a64b3d08ff41f9e62b214ba5004e475b9561 + llama-rn: 796fa53f37f89e2c77cd6c462ad1172ee96d4c80 lottie-ios: a881093fab623c467d3bce374367755c272bdd59 lottie-react-native: 691b8363e8c591fb78a78254ff2517258891456b op-sqlite: bafff369cecaee4fe65c89eec47deaba26f2db95 From a7861870c7a36e60fe6190206aa7d3c6f65942c0 Mon Sep 17 00:00:00 2001 From: Dishit Date: Thu, 7 May 2026 12:36:46 +0530 Subject: [PATCH 3/3] fix(ios): address Gemini review -thread-safe downloadId, consistent modelType default, add createdAt --- ios/DownloadManagerModule.swift | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/ios/DownloadManagerModule.swift b/ios/DownloadManagerModule.swift index 8cf34e5f..ad9d3d51 100644 --- a/ios/DownloadManagerModule.swift +++ b/ios/DownloadManagerModule.swift @@ -558,8 +558,11 @@ extension DownloadManagerModule { let modelType = (params["modelType"] as? String) ?? "text" let combinedTotalBytes = (params["combinedTotalBytes"] as? NSNumber)?.int64Value ?? 0 let metadataJson = params["metadataJson"] as? String - let downloadId = String(nextDownloadId) - nextDownloadId += 1 + let downloadId = queue.sync(flags: .barrier) { () -> String in + let id = String(nextDownloadId) + nextDownloadId += 1 + return id + } NSLog("[DownloadManager] Starting download #%@: url=%@, fileName=%@, modelId=%@, totalBytes=%lld, modelType=%@", downloadId, urlString, fileName, modelId, totalBytes, modelType) @@ -631,8 +634,11 @@ extension DownloadManagerModule { } let totalBytes = (params["totalBytes"] as? NSNumber)?.int64Value ?? 0 - let downloadId = String(nextDownloadId) - nextDownloadId += 1 + let downloadId = queue.sync(flags: .barrier) { () -> String in + let id = String(nextDownloadId) + nextDownloadId += 1 + return id + } NSLog("[DownloadManager] Starting multi-file download #%@: %d files, totalBytes=%lld, dest=%@", downloadId, filesArray.count, totalBytes, destinationDir) @@ -691,7 +697,7 @@ extension DownloadManagerModule { status: "running", startedAt: Date().timeIntervalSince1970 * 1000, modelKey: params["modelKey"] as? String, - modelType: (params["modelType"] as? String) ?? "image", + modelType: (params["modelType"] as? String) ?? "text", combinedTotalBytes: (params["combinedTotalBytes"] as? NSNumber)?.int64Value ?? 0, metadataJson: params["metadataJson"] as? String, task: nil, @@ -770,7 +776,8 @@ extension DownloadManagerModule { "totalBytes": NSNumber(value: info.totalBytes), "startedAt": NSNumber(value: info.startedAt), "modelType": info.modelType, - "combinedTotalBytes": NSNumber(value: info.combinedTotalBytes) + "combinedTotalBytes": NSNumber(value: info.combinedTotalBytes), + "createdAt": NSNumber(value: info.startedAt) ] if let modelKey = info.modelKey { entry["modelKey"] = modelKey } if let metadataJson = info.metadataJson { entry["metadataJson"] = metadataJson }