Last active
February 13, 2024 10:05
-
-
Save AvdLee/6c7353fab031f11f6c9e47594ee9cfa8 to your computer and use it in GitHub Desktop.
Adding children to a Progress instance is easy, but removing is not possible by default. This could have been useful when you want to use a single Progress instance which can have different children over time. Using this custom class MutableProgress makes this possible. See: https://www.avanderlee.com/general/controlling-progress-children/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
/// An additional class build on top of `Progress` to make it possible to also remove children from progress. | |
public final class MutableProgress: Progress { | |
public override var totalUnitCount: Int64 { | |
get { | |
return Int64(children.count) | |
} | |
set { | |
fatalError("Setting the total unit count is not supported for MutableProgress") | |
} | |
} | |
public override var completedUnitCount: Int64 { | |
get { | |
return Int64(children.filter { $0.key.isCompleted }.count) | |
} | |
set { | |
fatalError("Setting the completed unit count is not supported for MutableProgress") | |
} | |
} | |
public override var fractionCompleted: Double { | |
return children.map { $0.key.fractionCompleted }.reduce(0, +) / Double(totalUnitCount) | |
} | |
/// All the current tracked children. | |
private var children: [Progress: NSKeyValueObservation] = [:] | |
/// Adds a new child. Will always use a pending unit count of 1. | |
/// | |
/// - Parameter child: The child to add. | |
func addChild(_ child: Progress) { | |
willChangeValue(for: \.totalUnitCount) | |
children[child] = child.observe(\.fractionCompleted) { [weak self] (progress, _) in | |
self?.willChangeValue(for: \.fractionCompleted) | |
self?.didChangeValue(for: \.fractionCompleted) | |
if progress.isCompleted { | |
self?.willChangeValue(for: \.completedUnitCount) | |
self?.didChangeValue(for: \.completedUnitCount) | |
} | |
} | |
didChangeValue(for: \.totalUnitCount) | |
} | |
/// Removes the given child from the progress reporting. | |
/// | |
/// - Parameter child: The child to remove. | |
func removeChild(_ child: Progress) { | |
willChangeValue(for: \.fractionCompleted) | |
willChangeValue(for: \.completedUnitCount) | |
willChangeValue(for: \.totalUnitCount) | |
children.removeValue(forKey: child)?.invalidate() | |
didChangeValue(for: \.totalUnitCount) | |
didChangeValue(for: \.completedUnitCount) | |
didChangeValue(for: \.fractionCompleted) | |
} | |
// MARK: Overriding methods to make sure this class is used correctly. | |
public override func addChild(_ child: Progress, withPendingUnitCount inUnitCount: Int64) { | |
assert(inUnitCount == 1, "Unit count is ignored and is fixed to 1 for MutableProgress") | |
addChild(child) | |
} | |
} | |
private extension Progress { | |
var isCompleted: Bool { | |
guard totalUnitCount > 0 else { return true } | |
return completedUnitCount >= totalUnitCount | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
When testing with your beautiful
MutableProgress
, I realized that it does not respond to the deletion of processes (for example,totalUploadProgress.cancel()
): can you explain why? How can I modify yourMutableProgress
to make also work.cancel()
,.pause()
and.resume()
.Thank you.
Cesare Piersigilli