forked from swiftlang/swift-corelibs-foundation
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathProgress.swift
557 lines (457 loc) · 31.7 KB
/
Progress.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
#if canImport(Dispatch)
import Dispatch
/**
`Progress` is used to report the amount of work done, and provides a way to allow the user to cancel that work.
Since work is often split up into several parts, progress objects can form a tree where children represent part of the overall total work. Each parent may have as many children as required, but each child only has one parent. The top level progress object in this tree is typically the one that you would display to a user. The leaf objects are updated as work completes, and the updates propagate up the tree.
The work that a `Progress` does is tracked via a "unit count." There are two unit count values: total and completed. In its leaf form, a `Progress` is created with a total unit count and its completed unit count is updated by setting `completedUnitCount` until it matches the `totalUnitCount`. The progress is then considered finished.
When progress objects form nodes in trees, they are still created with a total unit count. Portions of the total are then handed out to children as a "pending unit count." The total amount handed out to children should add up to the parent's `totalUnitCount`. When those children become finished, the pending unit count assigned to that child is added to the parent's `completedUnitCount`. Therefore, when all children are finished, the parent's `completedUnitCount` is equal to its `totalUnitCount` and it becomes finished itself.
Children `Progress` objects can be added implicitly or by calling `addChild(withPendingUnitCount)` on the parent. Implicitly added children are attached to a parent progress between a call to `becomeCurrent(withPendingUnitCount)` and a call to `resignCurrent`. The implicit child is created with the `Progress(totalUnitCount:)` initializer, or by passing the result of `Progress.currentProgress` to the `Progress(parent:userInfo:)` initializer. Both kinds of children can be attached to the same parent progress object. If you have an idea in advance that some portions of the work will take more or less time than the others, you can use different values of pending unit count for each child.
If you are designing an interface of an object that reports progress, then the recommended approach is to vend a `Progress` property and adopt the `ProgressReporting` protocol. The progress should be created with the `Progress.discreteProgress(withTotalUnitCount:)` class function. You can then either update the progress object directly or set it up to have children of its own. Users of your object can compose your progress into their tree by using the `addChild(withPendingUnitCount)` function.
If you want to provide progress reporting for a single method, then the recommended approach is to implicitly attach to a current `Progress` by creating an `Progress` object at the very beginning of your method using `Progress(withTotalUnitCount)`. This progress object will consume the pending unit count, and then you can set up the progress object with children of its own.
The `localizedDescription` and `localizedAdditionalDescription` properties are meant to be observed as well as set. So are the `cancellable` and `pausable` properties. `totalUnitCount` and `completedUnitCount`, on the other hand, are often not the best properties to observe when presenting progress to the user. You should observe `fractionCompleted` instead of observing `totalUnitCount` and `completedUnitCount` and doing your own calculation. `Progress`' default implementation of `fractionCompleted` does fairly sophisticated things like taking child `Progress` into account.
- note: In swift-corelibs-foundation, Key Value Observing is not yet available.
*/
open class Progress : NSObject, @unchecked Sendable {
private weak var _parent : Progress?
private var _children : Set<Progress>
private var _selfFraction : _ProgressFraction
private var _childFraction : _ProgressFraction
private var _userInfo : [ProgressUserInfoKey : Any]
// This is set once, but after initialization
private var _portionOfParent : Int64
static private let _tsdKey = "_Foundation_CurrentProgressKey"
/// The instance of `Progress` associated with the current thread by a previous invocation of `becomeCurrent(withPendingUnitCount:)`, if any.
///
/// The purpose of this per-thread value is to allow code that does work to usefully report progress even when it is widely separated from the code that actually presents progress to the user, without requiring layers of intervening code to pass the instance of `Progress` through. Using the result of invoking this directly will often not be the right thing to do, because the invoking code will often not even know what units of work the current progress object deals in. Using `Progress(withTotalUnitCount:)` to create a child `Progress` object and then using that to report progress makes more sense in that situation.
@available(*, noasync)
open class func current() -> Progress? {
return (Thread.current.threadDictionary[Progress._tsdKey] as? _ProgressTSD)?.currentProgress
}
/// Initializes an instance of `Progress` with a parent of `Progress.current()`.
///
/// The value of the `totalUnitCount` property is also set. In many cases you can simply precede code that does a substantial amount of work with an invocation of this method, with repeated setting of the `completedUnitCount` property and synchronous checking of `isCancelled` in the loop that does the work.
public convenience init(totalUnitCount unitCount: Int64) {
self.init(parent: Progress.current())
totalUnitCount = unitCount
}
/// Initializes an instance of `Progress` with a `nil` parent.
///
/// The value of the `totalUnitCount` property is also set. The resulting progress object is not part of an existing progress tree.
open class func discreteProgress(totalUnitCount unitCount: Int64) -> Progress {
let progress = Progress(parent: nil)
progress.totalUnitCount = unitCount
return progress
}
/// Initializes an instance of `Progress` with the specified total unit count, parent, and pending unit count.
public convenience init(totalUnitCount unitCount: Int64, parent: Progress, pendingUnitCount portionOfParentTotalUnitCount: Int64) {
self.init(parent: nil)
totalUnitCount = unitCount
parent.addChild(self, withPendingUnitCount: portionOfParentTotalUnitCount)
}
/// Initializes an instance of `Progress` with the specified parent and user info dictionary.
@available(*, noasync)
public init(parent parentProgress: Progress?, userInfo userInfoOrNil: [ProgressUserInfoKey : Any]? = nil) {
_children = Set()
isCancellable = false
isPausable = false
isCancelled = false
isPaused = false
_selfFraction = _ProgressFraction()
_childFraction = _ProgressFraction()
// It doesn't matter what the units are here as long as the total is non-zero
_childFraction.total = 1
// This is reset later, if this progress becomes a child of some other progress
_portionOfParent = 0
_userInfo = userInfoOrNil ?? [:]
super.init()
if let p = parentProgress {
precondition(p === Progress.current(), "The Parent must be the current progress")
p._addImplicitChild(self)
}
}
/// MARK: -
/// This is called when some other progress becomes an implicit child of this progress.
@available(*, noasync)
private func _addImplicitChild(_ child : Progress) {
guard let tsd = Thread.current.threadDictionary[Progress._tsdKey] as? _ProgressTSD else { preconditionFailure("A child was added without a current progress being set") }
// We only allow one implicit child. More than that and things get confusing (and wrong) real quick.
if !tsd.childAttached {
addChild(child, withPendingUnitCount: tsd.pendingUnitCount)
tsd.childAttached = true
}
}
/// Make this `Progress` the current thread's current progress object, returned by `Progress.currentProgress()`.
///
/// At the same time, record how large a portion of the work represented by the progress will be represented by the next progress object initialized with `Progress(parent:userInfo:)` in the current thread with this `Progress` as the parent. The portion of work will be used when the `completedUnitCount` of the child is set.
///
/// With this mechanism, code that doesn't know anything about its callers can report progress accurately by using `Progress(withTotalUnitCount:)` and `completedUnitCount`. The calling code will account for the fact that the work done is only a portion of the work to be done as part of a larger operation.
///
/// The unit of work in a call to `becomeCurrent(withPendingUnitCount:)` has to be the same unit of work as that used for the value of the `totalUnitCount` property, but the unit of work used by the child can be a completely different one, and often will be.
///
/// You must always balance invocations of this method with invocations of `resignCurrent`.
@available(*, noasync)
open func becomeCurrent(withPendingUnitCount unitCount: Int64) {
let oldTSD = Thread.current.threadDictionary[Progress._tsdKey] as? _ProgressTSD
if let checkedTSD = oldTSD {
precondition(checkedTSD.currentProgress !== self, "This Progress is already current on this thread.")
}
let newTSD = _ProgressTSD(currentProgress: self, nextTSD: oldTSD, pendingUnitCount: unitCount)
Thread.current.threadDictionary[Progress._tsdKey] = newTSD
}
/// Balance the most recent previous invocation of `becomeCurrent(withPendingUnitCount:)` on the same thread.
///
/// This restores the current progress object to what it was before `becomeCurrent(withPendingUnitCount:)` was invoked.
@available(*, noasync)
open func resignCurrent() {
guard let oldTSD = Thread.current.threadDictionary[Progress._tsdKey] as? _ProgressTSD else {
preconditionFailure("This Progress was not the current progress on this thread.")
}
if !oldTSD.childAttached {
// No children attached. Account for the completed unit count now.
_addCompletedUnitCount(oldTSD.pendingUnitCount)
}
// Ok if the following is nil - then we clear the dictionary entry
let newTSD = oldTSD.nextTSD
Thread.current.threadDictionary[Progress._tsdKey] = newTSD
}
/// Directly add a child progress to the receiver, assigning it a portion of the receiver's total unit count.
open func addChild(_ child: Progress, withPendingUnitCount inUnitCount: Int64) {
precondition(child._parent == nil, "The Progress was already the child of another Progress")
_children.insert(child)
child._setParent(self, portion: inUnitCount)
if isCancelled {
child.cancel()
}
if isPaused {
child.pause()
}
}
private func _setParent(_ parent: Progress, portion: Int64) {
_parent = parent
_portionOfParent = portion
// We need to tell the new parent what our fraction completed is
// The previous fraction is the same as if it didn't exist (0/0), but the new one represents our current state.
_parent?._updateChild(self, from: _ProgressFraction(completed: 0, total: 0), to: _overallFraction, portion: portion)
}
/// How much of the job has been completed so far.
///
/// For a `Progress` with a kind of `.file`, the unit of these properties is bytes while the `fileTotalCountKey` and `fileCompletedCountKey` keys in the `userInfo` dictionary are used for the overall count of files. For any other kind of `Progress`, the unit of measurement you use does not matter as long as you are consistent. The values may be reported to the user in the `localizedDescription` and `localizedAdditionalDescription`.
///
/// If the `Progress` object is a "leaf progress" (no children), then the `fractionCompleted` is generally `completedUnitCount / totalUnitCount`. If the receiver `Progress` has children, the `fractionCompleted` will reflect progress made in child objects in addition to its own `completedUnitCount`. As children finish, the `completedUnitCount` of the parent will be updated.
open var totalUnitCount: Int64 {
get {
return _selfFraction.total
}
set {
let previous = _overallFraction
if _selfFraction.total != newValue && _selfFraction.total > 0 {
_childFraction = _childFraction * _ProgressFraction(completed: _selfFraction.total, total: newValue)
}
_selfFraction.total = newValue
_updateFractionCompleted(from: previous, to: _overallFraction)
}
}
/// The size of the job whose progress is being reported.
///
/// For a `Progress` with a kind of `.file`, the unit of these properties is bytes while the `fileTotalCountKey` and `fileCompletedCountKey` keys in the `userInfo` dictionary are used for the overall count of files. For any other kind of `Progress`, the unit of measurement you use does not matter as long as you are consistent. The values may be reported to the user in the `localizedDescription` and `localizedAdditionalDescription`.
///
/// If the `Progress` object is a "leaf progress" (no children), then the `fractionCompleted` is generally `completedUnitCount / totalUnitCount`. If the receiver `Progress` has children, the `fractionCompleted` will reflect progress made in child objects in addition to its own `completedUnitCount`. As children finish, the `completedUnitCount` of the parent will be updated.
open var completedUnitCount: Int64 {
get {
return _selfFraction.completed
}
set {
let previous = _overallFraction
_selfFraction.completed = newValue
_updateFractionCompleted(from: previous, to: _overallFraction)
}
}
/// A description of what progress is being made, fit to present to the user.
///
/// `Progress` is by default KVO-compliant for this property, with the notifications always being sent on thread which updates the property. The default implementation of the getter for this property does not always return the most recently set value of the property. If the most recently set value of this property is nil then `Progress` uses the value of the `kind` property to determine how to use the values of other properties, as well as values in the user info dictionary, to return a computed string. If it fails to do that then it returns an empty string.
///
/// For example, depending on the kind of progress, the completed and total unit counts, and other parameters, these kinds of strings may be generated:
/// Copying 10 files…
/// 30% completed
/// Copying “TextEdit”…
/// - note: In swift-corelibs-foundation, Key Value Observing is not yet available.
open var localizedDescription: String! {
// Unimplemented
return ""
}
/// A more specific description of what progress is being made, fit to present to the user.
///
/// `Progress` is by default KVO-compliant for this property, with the notifications always being sent on thread which updates the property. The default implementation of the getter for this property does not always return the most recently set value of the property. If the most recently set value of this property is nil then `Progress` uses the value of the `kind` property to determine how to use the values of other properties, as well as values in the user info dictionary, to return a computed string. If it fails to do that then it returns an empty string. The difference between this and `localizedDescription` is that this text is meant to be more specific about what work is being done at any particular moment.
///
/// For example, depending on the kind of progress, the completed and total unit counts, and other parameters, these kinds of strings may be generated:
/// 3 of 10 files
/// 123 KB of 789.1 MB
/// 3.3 MB of 103.92 GB — 2 minutes remaining
/// 1.61 GB of 3.22 GB (2 KB/sec) — 2 minutes remaining
/// 1 minute remaining (1 KB/sec)
/// - note: In swift-corelibs-foundation, Key Value Observing is not yet available.
open var localizedAdditionalDescription: String! {
// Unimplemented
return ""
}
/// Whether the work being done can be cancelled.
///
/// By default `Progress` is cancellable.
///
/// This property is for communicating whether controls for cancelling should appear in a progress reporting user interface. `Progress` itself does not do anything with these properties other than help pass their values from progress reporters to progress observers. It is valid for the values of these properties to change in virtually any way during the lifetime of a `Progress`. Of course, if a `Progress` is cancellable you should actually implement cancellability by setting a cancellation handler or by making your code poll the result of invoking `isCancelled`.
open var isCancellable: Bool
/// Whether the work being done can be paused.
///
/// By default `Progress` not pausable.
///
/// This property is for communicating whether controls for pausing should appear in a progress reporting user interface. `Progress` itself does not do anything with these properties other than help pass their values from progress reporters to progress observers. It is valid for the values of these properties to change in virtually any way during the lifetime of a `Progress`. Of course, if a `Progress` is pausable you should actually implement pausibility by setting a pausing handler or by making your code poll the result of invoking `isPaused`.
open var isPausable: Bool
/// Whether the work being done has been cancelled.
///
/// Instances of `Progress` that have parents are at least as cancelled as their parents.
open var isCancelled: Bool
/// Whether the work being done has been paused.
///
/// Instances of `Progress` that have parents are at least as paused as their parents.
open var isPaused: Bool
/// A closure to be called when `cancel` is called.
///
/// The closure will be called even when the function is called on an ancestor of the receiver. Your closure won't be called on any particular queue. If it must do work on a specific queue then it should schedule that work on that queue.
open var cancellationHandler: (@Sendable () -> Void)? {
didSet {
guard let handler = cancellationHandler else { return }
// If we're already cancelled, then invoke it - asynchronously
if isCancelled {
DispatchQueue.global().async {
handler()
}
}
}
}
/// A closure to be called when pause is called.
///
/// The closure will be called even when the function is called on an ancestor of the receiver. Your closure won't be called on any particular queue. If it must do work on a specific queue then it should schedule that work on that queue.
open var pausingHandler: (@Sendable () -> Void)? {
didSet {
guard let handler = pausingHandler else { return }
// If we're already paused, then invoke it - asynchronously
if isPaused {
DispatchQueue.global().async {
handler()
}
}
}
}
/// A closure to be called when resume is called.
///
/// The closure will be called even when the function is called on an ancestor of the receiver. Your closure won't be called on any particular queue. If it must do work on a specific queue then it should schedule that work on that queue.
open var resumingHandler: (@Sendable () -> Void)?
/// Returns `true` if the progress is indeterminate.
///
/// This returns `true` when the value of the `totalUnitCount` or `completedUnitCount` property is less than zero. Zero values for both of those properties indicates that there turned out to not be any work to do after all; `isIndeterminate` returns `false` and `fractionCompleted` returns `1.0` in that case.
open var isIndeterminate: Bool {
return _selfFraction.isIndeterminate
}
/// Returns `true` if the progress is finished.
///
/// Checking the result of this function is preferred to comparing `fractionCompleted`, as rounding errors can cause that value to appear less than `1.0` in some circumstances.
/// - Experiment: This is a draft API currently under consideration for official import into Foundation
open var isFinished: Bool {
return _selfFraction.isFinished
}
/// The fraction of the overall work completed by this progress object, including work done by any children it may have.
public var fractionCompleted: Double {
// We have to special case one thing to maintain consistency; if _selfFraction.total == 0 then we do not add any children
guard _selfFraction.total > 0 else { return _selfFraction.fractionCompleted }
return (_selfFraction + _childFraction).fractionCompleted
}
/// Calls the closure registered with the `cancellationHandler` property, if there is one, and set the `isCancelled` property to `true`.
///
/// Do this for this `Progress` and any descendants of this `Progress`.
open func cancel() {
isCancelled = true
if let handler = cancellationHandler {
DispatchQueue.global().async {
handler()
}
}
for child in _children {
child.cancel()
}
}
/// Calls the closure registered with the `pausingHandler` property, if there is one, and set the `isPaused` property to `true`.
///
/// Do this for this `Progress` and any descendants of this `Progress`.
open func pause() {
isPaused = true
if let handler = pausingHandler {
DispatchQueue.global().async {
handler()
}
}
for child in _children {
child.pause()
}
}
/// Calls the closure registered with the `resumingHandler` property, if there is one, and set the `isPaused` property to `false`.
///
/// Do this for this `Progress` and any descendants of this `Progress`.
open func resume() {
isPaused = false
if let handler = resumingHandler {
DispatchQueue.global().async {
handler()
}
}
for child in _children {
child.resume()
}
}
/// Set a value in the dictionary returned by `userInfo`, with appropriate KVO notification for properties whose values can depend on values in the user info dictionary, like `localizedDescription`. If a nil value is passed then the dictionary entry is removed.
/// - note: In swift-corelibs-foundation, Key Value Observing is not yet available.
open func setUserInfoObject(_ objectOrNil: Any?, forKey key: ProgressUserInfoKey) {
_userInfo[key] = objectOrNil
}
/// A collection of arbitrary values associated with this `Progress`.
///
/// Returns a KVO-compliant dictionary that changes as `setUserInfoObject(forKey:)` is sent to this `Progress`. The dictionary will send all of its KVO notifications on the thread which updates the property. The result will never be nil, but may be an empty dictionary.
///
/// Some entries have meanings that are recognized by the `Progress` class itself. See also `ProgressUserInfoKey`.
/// - note: In swift-corelibs-foundation, Key Value Observing is not yet available.
open var userInfo: [ProgressUserInfoKey : Any] {
return _userInfo
}
/// Optionally specifies more information about what kind of progress is being reported.
///
/// If the value of the `localizedDescription` property has not been set, then the default implementation of `localizedDescription` uses the progress kind to determine how to use the values of other properties, as well as values in the user info dictionary, to create a string that is presentable to the user.
open var kind: ProgressKind?
public struct FileOperationKind : RawRepresentable, Equatable, Hashable, Sendable {
public let rawValue: String
public init(_ rawValue: String) { self.rawValue = rawValue }
public init(rawValue: String) { self.rawValue = rawValue }
/// Use for indicating the progress represents a download.
public static let downloading = FileOperationKind(rawValue: "NSProgressFileOperationKindDownloading")
/// Use for indicating the progress represents decompressing after downloading.
public static let decompressingAfterDownloading = FileOperationKind(rawValue: "NSProgressFileOperationKindDecompressingAfterDownloading")
/// Use for indicating the progress represents receiving a file in some way.
public static let receiving = FileOperationKind(rawValue: "NSProgressFileOperationKindReceiving")
/// Use for indicating the progress represents copying a file.
public static let copying = FileOperationKind(rawValue: "NSProgressFileOperationKindCopying")
}
// MARK: -
// MARK: Implementation of unit counts
private var _overallFraction : _ProgressFraction {
return _selfFraction + _childFraction
}
private func _addCompletedUnitCount(_ unitCount : Int64) {
let old = _overallFraction
_selfFraction.completed += unitCount
let new = _overallFraction
_updateFractionCompleted(from: old, to: new)
}
private func _updateFractionCompleted(from: _ProgressFraction, to: _ProgressFraction) {
if from != to {
_parent?._updateChild(self, from: from, to: to, portion: _portionOfParent)
}
}
/// A child progress has been updated, which changes our own fraction completed.
private func _updateChild(_ child: Progress, from previous: _ProgressFraction, to next: _ProgressFraction, portion: Int64) {
let previousOverallFraction = _overallFraction
let multiple = _ProgressFraction(completed: portion, total: _selfFraction.total)
let oldFractionOfParent = previous * multiple
// Subtract the previous fraction (multiplied by portion), add new fraction (multiplied by portion). If either is indeterminate, treat as zero.
if !previous.isIndeterminate {
_childFraction = _childFraction - oldFractionOfParent
}
if !next.isIndeterminate {
_childFraction = _childFraction + (next * multiple)
}
if next.isFinished {
_children.remove(child)
if portion != 0 {
// Update our self completed units
_selfFraction.completed += portion
// Subtract the (child's fraction completed * multiple) from our child fraction
_childFraction = _childFraction - (multiple * next)
}
}
_updateFractionCompleted(from: previousOverallFraction, to: _overallFraction)
}
}
/// If your class supports reporting progress, then you can adopt the ProgressReporting protocol.
///
/// Objects that adopt this protocol should typically be "one-shot" -- that is, the progress is setup at initialization of the object and is updated when work is done. The value of the property should not be set to another progress object. Instead, the user of the `ProgressReporting` class should create a new instance to represent a new set of work.
public protocol ProgressReporting : NSObjectProtocol {
var progress: Progress { get }
}
public struct ProgressKind : RawRepresentable, Equatable, Hashable, Sendable {
public let rawValue: String
public init(_ rawValue: String) { self.rawValue = rawValue }
public init(rawValue: String) { self.rawValue = rawValue }
/// Indicates that the progress being performed is related to files.
///
/// Progress of this kind is assumed to use bytes as the unit of work being done and the default implementation of `localizedDescription` takes advantage of that to return more specific text than it could otherwise.
public static let file = ProgressKind(rawValue: "NSProgressKindFile")
}
public struct ProgressUserInfoKey : RawRepresentable, Equatable, Hashable, Sendable {
public let rawValue: String
public init(_ rawValue: String) { self.rawValue = rawValue }
public init(rawValue: String) { self.rawValue = rawValue }
/// How much time is probably left in the operation, as an NSNumber containing a number of seconds.
///
/// If this value is present, then `Progress` can present a more detailed `localizedAdditionalDescription`.
/// Value is an `NSNumber`.
public static let estimatedTimeRemainingKey = ProgressUserInfoKey(rawValue: "NSProgressEstimatedTimeRemainingKey")
/// How fast data is being processed, as an NSNumber containing bytes per second.
///
/// If this value is present, then `Progress` can present a more detailed `localizedAdditionalDescription`.
/// Value is an `NSNumber`.
public static let throughputKey = ProgressUserInfoKey(rawValue: "NSProgressThroughputKey")
/// A description of what "kind" of progress is being made on a file.
///
/// If this value is present, then `Progress` can present a more detailed `localizedAdditionalDescription`.
/// Value is a `Progress.FileOperationKind`.
public static let fileOperationKindKey = ProgressUserInfoKey(rawValue: "NSProgressFileOperationKindKey")
/// A URL for the item on which progress is being made.
///
/// If this value is present, then `Progress` can present a more detailed `localizedAdditionalDescription`.
/// Value is a `URL`.
public static let fileURLKey = ProgressUserInfoKey(rawValue: "NSProgressFileURLKey")
/// The total number of files.
///
/// If this value is present, then `Progress` can present a more detailed `localizedAdditionalDescription`.
/// Value is an `NSNumber`.
public static let fileTotalCountKey = ProgressUserInfoKey(rawValue: "NSProgressFileTotalCountKey")
/// The completed number of files.
///
/// If this value is present, then `Progress` can present a more detailed `localizedAdditionalDescription`.
/// Value is an `NSNumber`.
public static let fileCompletedCountKey = ProgressUserInfoKey(rawValue: "NSProgressFileCompletedCountKey")
}
@available(*, unavailable)
extension _ProgressTSD : @unchecked Sendable { }
fileprivate class _ProgressTSD : NSObject {
/// The thread's default progress.
fileprivate var currentProgress : Progress
/// The instances of this thing that will become current the next time resignCurrent is called
fileprivate var nextTSD : _ProgressTSD?
/// Pending unit count
var pendingUnitCount : Int64
/// True if any children are implicitly attached
var childAttached : Bool
init(currentProgress: Progress, nextTSD: _ProgressTSD?, pendingUnitCount: Int64) {
self.currentProgress = currentProgress
self.pendingUnitCount = pendingUnitCount
self.nextTSD = nextTSD
childAttached = false
}
}
#endif