Skip to content

Commit f5fa6e4

Browse files
author
Ruben Nine
committed
Synchronize access to task data and completion handler.
Using regular completion handler on non-background uploads. Properly capture response and response error in `SubmitChunkUploadOperation`. Allow commit operation to be always executed after all chunk operations are finished.
1 parent 84fd468 commit f5fa6e4

File tree

3 files changed

+57
-22
lines changed

3 files changed

+57
-22
lines changed

Sources/FilestackSDK/Internal/Operations/SubmitChunkUploadOperation.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,12 @@ private extension SubmitChunkUploadOperation {
109109
uploadTask = UploadService.shared.upload(data: data, to: url, method: "PUT", headers: headers, uploadProgress: { (progress) in
110110
self.progress.totalUnitCount = Int64(self.data.size ?? 0)
111111
self.progress.completedUnitCount = Int64(Double(self.progress.totalUnitCount) * progress.fractionCompleted)
112-
}) { (request, data, error) in
112+
}) { (_, response, error) in
113113
self.uploadTask = nil
114114

115-
if let error = response.error {
115+
if let error = error {
116116
self.finish(with: .failure(.wrapped(error)))
117-
} else if let httpURLResponse = response.response, httpURLResponse.statusCode == 200 {
117+
} else if let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200 {
118118
self.progress.completedUnitCount = self.progress.totalUnitCount
119119
self.finish(with: .success(httpURLResponse))
120120
} else {

Sources/FilestackSDK/Internal/Operations/SubmitPartIntelligentUploadOperation.swift

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,21 +60,22 @@ private extension SubmitPartIntelligentUploadOperation {
6060
func upload() {
6161
let chunkSize = Defaults.resumableMobileChunkSize
6262
var chunkOffset: UInt64 = 0
63-
let finishOperation = BlockOperation { self.executeCommit() }
6463

6564
while !isCancelled, chunkOffset < UInt64(size) {
6665
// Guard against EOF
6766
guard let chunkOperation = submitChunk(chunkOffset: chunkOffset, chunkSize: chunkSize) else { break }
6867

69-
finishOperation.addDependency(chunkOperation)
70-
7168
let actualChunkSize = chunkOperation.progress.totalUnitCount
7269

7370
progress.addChild(chunkOperation.progress, withPendingUnitCount: Int64(actualChunkSize))
7471
chunkOffset += UInt64(actualChunkSize)
7572
}
7673

77-
chunkUploadOperationQueue.addOperation(finishOperation)
74+
chunkUploadOperationQueue.waitUntilAllOperationsAreFinished()
75+
76+
if !isCancelled {
77+
executeCommit()
78+
}
7879
}
7980

8081
func submitChunk(chunkOffset: UInt64, chunkSize: Int, retries: Int = Defaults.maxRetries) -> SubmitChunkUploadOperation? {

Sources/FilestackSDK/Internal/Services/UploadService.swift

Lines changed: 49 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,39 @@ import os.log
1111

1212
private let Shared = UploadService()
1313

14+
// Provides synchronized access to task data and completion handlers.
15+
struct TaskState {
16+
typealias CompletionHandler = (Data?, URLResponse?, Swift.Error?) -> Void
17+
18+
private var taskData: [URLSessionDataTask: Data] = [:]
19+
private var taskCompletion: [URLSessionDataTask: CompletionHandler] = [:]
20+
private let syncQueue = DispatchQueue(label: "io.filestack.TaskState-sync-queue")
21+
22+
func getData(for task: URLSessionDataTask) -> Data? {
23+
syncQueue.sync { taskData[task] }
24+
}
25+
26+
mutating func setData(for task: URLSessionDataTask, data: Data) {
27+
syncQueue.sync { taskData[task] = data }
28+
}
29+
30+
func getCompletion(for task: URLSessionDataTask) -> CompletionHandler? {
31+
syncQueue.sync { taskCompletion[task] }
32+
}
33+
34+
mutating func setCompletionHandler(for task: URLSessionDataTask, completionHandler: @escaping CompletionHandler) {
35+
syncQueue.sync { taskCompletion[task] = completionHandler }
36+
}
37+
38+
mutating func forget(task: URLSessionDataTask) {
39+
syncQueue.sync {
40+
taskData.removeValue(forKey: task)
41+
taskCompletion.removeValue(forKey: task)
42+
}
43+
}
44+
}
45+
46+
1447
/// Service used for uploading files.
1548
@objc(FSUploadService)
1649
public final class UploadService: NSObject, NetworkingService {
@@ -20,8 +53,7 @@ public final class UploadService: NSObject, NetworkingService {
2053

2154
// MARK: - Private Properties
2255

23-
private var taskData: [URLSessionDataTask: Data] = [:]
24-
private var taskCompletion: [URLSessionDataTask: (Data?, URLResponse?, Swift.Error?) -> Void] = [:]
56+
private var taskState = TaskState()
2557

2658
// MARK: - Public Properties
2759

@@ -52,10 +84,9 @@ extension UploadService: URLSessionDataDelegate {
5284
completionHandler(URLSession.ResponseDisposition .allow)
5385

5486
if response.expectedContentLength == 0 || dataTask.error != nil {
55-
let completionHandler = taskCompletion[dataTask]
87+
let completionHandler = taskState.getCompletion(for: dataTask)
5688

57-
taskData.removeValue(forKey: dataTask)
58-
taskCompletion.removeValue(forKey: dataTask)
89+
taskState.forget(task: dataTask)
5990

6091
DispatchQueue.main.async {
6192
completionHandler?(nil, response, dataTask.error)
@@ -64,19 +95,18 @@ extension UploadService: URLSessionDataDelegate {
6495
}
6596

6697
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
67-
if var existingData = taskData[dataTask] {
98+
if var existingData = taskState.getData(for: dataTask) {
6899
existingData.append(data)
69-
taskData[dataTask] = existingData
100+
taskState.setData(for: dataTask, data: existingData)
70101
} else {
71-
taskData[dataTask] = data
102+
taskState.setData(for: dataTask, data: data)
72103
}
73104

74105
if let response = dataTask.response {
75-
let data = taskData[dataTask]
76-
let completionHandler = taskCompletion[dataTask]
106+
let data = taskState.getData(for: dataTask)
107+
let completionHandler = taskState.getCompletion(for: dataTask)
77108

78-
taskData.removeValue(forKey: dataTask)
79-
taskCompletion.removeValue(forKey: dataTask)
109+
taskState.forget(task: dataTask)
80110

81111
DispatchQueue.main.async {
82112
completionHandler?(data, response, dataTask.error)
@@ -107,12 +137,16 @@ extension UploadService {
107137
defer { try? FileManager.default.removeItem(at: dataURL) }
108138

109139
task = session.uploadTask(with: request, fromFile: dataURL)
140+
141+
taskState.setCompletionHandler(for: task, completionHandler: completionHandler)
110142
} else {
111-
task = session.uploadTask(with: request, from: data)
143+
task = session.uploadTask(with: request, from: data) { (data, response, error) in
144+
DispatchQueue.main.async {
145+
completionHandler(data, response, error)
146+
}
147+
}
112148
}
113149

114-
taskCompletion[task] = completionHandler
115-
116150
if let uploadProgress = uploadProgress {
117151
var progressObservers: [NSKeyValueObservation] = []
118152

0 commit comments

Comments
 (0)