Skip to content

Commit 7822b68

Browse files
authored
Merge pull request #2429 from millenomi/urlsessiontask-progress
Parity: URLSessionTask.progress
2 parents c5c35c1 + c62b093 commit 7822b68

File tree

1 file changed

+70
-10
lines changed

1 file changed

+70
-10
lines changed

Foundation/URLSession/URLSessionTask.swift

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,66 @@ private class Bag<Element> {
3131
open class URLSessionTask : NSObject, NSCopying {
3232

3333
// These properties aren't heeded in swift-corelibs-foundation, but we may heed them in the future. They exist for source compatibility.
34-
open var countOfBytesClientExpectsToReceive: Int64 = NSURLSessionTransferSizeUnknown
35-
open var countOfBytesClientExpectsToSend: Int64 = NSURLSessionTransferSizeUnknown
34+
open var countOfBytesClientExpectsToReceive: Int64 = NSURLSessionTransferSizeUnknown {
35+
didSet { updateProgress() }
36+
}
37+
open var countOfBytesClientExpectsToSend: Int64 = NSURLSessionTransferSizeUnknown {
38+
didSet { updateProgress() }
39+
}
40+
41+
open private(set) var progress = Progress(totalUnitCount: -1)
42+
43+
func updateProgress() {
44+
self.workQueue.async {
45+
let progress = self.progress
46+
47+
switch self.state {
48+
case .canceling: fallthrough
49+
case .completed:
50+
let total = progress.totalUnitCount
51+
let finalTotal = total < 0 ? 1 : total
52+
progress.totalUnitCount = finalTotal
53+
progress.completedUnitCount = finalTotal
54+
55+
default:
56+
let toBeSent: Int64?
57+
if let bodyLength = try? self.body.getBodyLength() {
58+
toBeSent = Int64(clamping: bodyLength)
59+
} else if self.countOfBytesExpectedToSend > 0 {
60+
toBeSent = Int64(clamping: self.countOfBytesExpectedToSend)
61+
} else if self.countOfBytesClientExpectsToSend != NSURLSessionTransferSizeUnknown && self.countOfBytesClientExpectsToSend > 0 {
62+
toBeSent = Int64(clamping: self.countOfBytesClientExpectsToSend)
63+
} else {
64+
toBeSent = nil
65+
}
66+
67+
let sent = self.countOfBytesSent
68+
69+
let toBeReceived: Int64?
70+
if self.countOfBytesExpectedToReceive > 0 {
71+
toBeReceived = Int64(clamping: self.countOfBytesClientExpectsToReceive)
72+
} else if self.countOfBytesClientExpectsToReceive != NSURLSessionTransferSizeUnknown && self.countOfBytesClientExpectsToReceive > 0 {
73+
toBeReceived = Int64(clamping: self.countOfBytesClientExpectsToReceive)
74+
} else {
75+
toBeReceived = nil
76+
}
77+
78+
let received = self.countOfBytesReceived
79+
80+
progress.completedUnitCount = sent.addingReportingOverflow(received).partialValue
81+
82+
if let toBeSent = toBeSent, let toBeReceived = toBeReceived {
83+
progress.totalUnitCount = toBeSent.addingReportingOverflow(toBeReceived).partialValue
84+
} else {
85+
progress.totalUnitCount = -1
86+
}
87+
88+
}
89+
}
90+
}
3691

3792
// We're not going to heed this one. If someone is setting it in Linux code, they may be relying on behavior that isn't there; warn.
38-
@available(*, deprecated, message: "swift-corelibs-foundation does not support URLSession instances, and this property is documented to have no effect when set on tasks created from non-background URLSession instances. Modifying this property has no effect in swift-corelibs-foundation and shouldn't be relied upon; resume tasks at the appropriate time instead.")
93+
@available(*, deprecated, message: "swift-corelibs-foundation does not support background URLSession instances, and this property is documented to have no effect when set on tasks created from non-background URLSession instances. Modifying this property has no effect in swift-corelibs-foundation and shouldn't be relied upon; resume tasks at the appropriate time instead.")
3994
open var earliestBeginDate: Date? = nil
4095

4196
/// How many times the task has been suspended, 0 indicating a running task.
@@ -180,6 +235,9 @@ open class URLSessionTask : NSObject, NSCopying {
180235
self.body = body
181236
super.init()
182237
self.currentRequest = request
238+
self.progress.cancellationHandler = { [weak self] in
239+
self?.cancel()
240+
}
183241
}
184242
deinit {
185243
//TODO: Do we remove the EasyHandle from the session here? This might run on the wrong thread / queue.
@@ -238,6 +296,7 @@ open class URLSessionTask : NSObject, NSCopying {
238296
}
239297
set {
240298
self.syncQ.sync { self._countOfBytesReceived = newValue }
299+
updateProgress()
241300
}
242301
}
243302
fileprivate var _countOfBytesReceived: Int64 = 0
@@ -249,16 +308,21 @@ open class URLSessionTask : NSObject, NSCopying {
249308
}
250309
set {
251310
self.syncQ.sync { self._countOfBytesSent = newValue }
311+
updateProgress()
252312
}
253313
}
254314

255315
fileprivate var _countOfBytesSent: Int64 = 0
256316

257317
/// Number of body bytes we expect to send, derived from the Content-Length of the HTTP request */
258-
open internal(set) var countOfBytesExpectedToSend: Int64 = 0
318+
open internal(set) var countOfBytesExpectedToSend: Int64 = 0 {
319+
didSet { updateProgress() }
320+
}
259321

260322
/// Number of bytes we expect to receive, usually derived from the Content-Length header of an HTTP response. */
261-
open internal(set) var countOfBytesExpectedToReceive: Int64 = 0
323+
open internal(set) var countOfBytesExpectedToReceive: Int64 = 0 {
324+
didSet { updateProgress() }
325+
}
262326

263327
/// The taskDescription property is available for the developer to
264328
/// provide a descriptive label for the task.
@@ -418,11 +482,7 @@ extension URLSessionTask {
418482
}
419483
}
420484

421-
extension URLSessionTask : ProgressReporting {
422-
public var progress: Progress {
423-
NSUnimplemented()
424-
}
425-
}
485+
extension URLSessionTask : ProgressReporting {}
426486

427487
extension URLSessionTask {
428488
/// Updates the (public) state based on private / internal state.

0 commit comments

Comments
 (0)