Starting in iOS13, one can monitor the progress of an OperationQueue
using the progress
property. The documentation states that only operations that do not override start()
count when tracking progress. However, asynchronous operations must override start()
and not call super()
according to the documentation.
Does this mean asynchronous
operations and progress
are mutually exclusive (i.e. only synchronous operations can be used with progress)? This seems like a massive limitation if this is the case.
In my own project, I removed my override of start()
and everything appears to work okay (e.g. dependencies are only started when isFinished
is set to true
on the dependent operation internally in my async operation base class). BUT, this seems risky since Operation
explicitly states to override start()
.
Thoughts?
Documentaiton references:
https://developer.apple.com/documentation/foundation/operationqueue/3172535-progress
By default, OperationQueue doesn’t report progress until totalUnitCount is set. When totalUnitCount is set, the queue begins reporting progress. Each operation in the queue contributes one unit of completion to the overall progress of the queue for operations that are finished by the end of main(). Operations that override start() and don’t invoke super don’t contribute to the queue’s progress.
https://developer.apple.com/documentation/foundation/operation/1416837-start
If you are implementing a concurrent operation, you must override this method and use it to initiate your operation. Your custom implementation must not call super at any time. In addition to configuring the execution environment for your task, your implementation of this method must also track the state of the operation and provide appropriate state transitions.
Update: I ended up ditching my AysncOperation
for a simple SyncOperation
that waits until finish()
is called (using a semaphore).
/// A synchronous operation that automatically waits until `finish()` is called.
open class SyncOperation: Operation {
private let waiter = DispatchSemaphore(value: 0)
/// Calls `work()` and waits until `finish()` is called.
public final override func main() {
work()
waiter.wait()
}
/// The work of the operation. Subclasses must override this function and call `finish()` when their work is done.
open func work() {
preconditionFailure("Subclasses must override `work()` and call `finish()`")
}
/// Finishes the operation.
///
/// The work of the operation must be completed when called. Failing to call `finish()` is a programmer error.
final public func finish() {
waiter.signal()
}
}