Skip to content

Commit c62b093

Browse files
committed
Parity: URLSessionTask.progress
Implement the progress object to return a non-indeterminate progress based on current expectations and reporting where possible.
1 parent b834853 commit c62b093

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.
@@ -179,6 +234,9 @@ open class URLSessionTask : NSObject, NSCopying {
179234
self.body = body
180235
super.init()
181236
self.currentRequest = request
237+
self.progress.cancellationHandler = { [weak self] in
238+
self?.cancel()
239+
}
182240
}
183241
deinit {
184242
//TODO: Do we remove the EasyHandle from the session here? This might run on the wrong thread / queue.
@@ -237,6 +295,7 @@ open class URLSessionTask : NSObject, NSCopying {
237295
}
238296
set {
239297
self.syncQ.sync { self._countOfBytesReceived = newValue }
298+
updateProgress()
240299
}
241300
}
242301
fileprivate var _countOfBytesReceived: Int64 = 0
@@ -248,16 +307,21 @@ open class URLSessionTask : NSObject, NSCopying {
248307
}
249308
set {
250309
self.syncQ.sync { self._countOfBytesSent = newValue }
310+
updateProgress()
251311
}
252312
}
253313

254314
fileprivate var _countOfBytesSent: Int64 = 0
255315

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

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

262326
/// The taskDescription property is available for the developer to
263327
/// provide a descriptive label for the task.
@@ -415,11 +479,7 @@ extension URLSessionTask {
415479
}
416480
}
417481

418-
extension URLSessionTask : ProgressReporting {
419-
public var progress: Progress {
420-
NSUnimplemented()
421-
}
422-
}
482+
extension URLSessionTask : ProgressReporting {}
423483

424484
extension URLSessionTask {
425485
/// Updates the (public) state based on private / internal state.

0 commit comments

Comments
 (0)