amplify-swift/Amplify/Core/Support/AsychronousOperation.swift

101 lines
3.2 KiB
Swift

//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//
import Foundation
/// This class is to facilitate executing asychronous requests. The caller can transition the operation to its finished
/// state by calling finish() in the callback of an asychronous request to ensure that the operation is only removed
/// from the OperationQueue after it has completed all its work. This class is not inherently thread safe. Although it
/// is a subclass of Foundation's Operation, it contains private state to support pausing, resuming, and finishing, that
/// must be managed by callers.
open class AsynchronousOperation: Operation {
/// State for this operation.
@objc private enum OperationState: Int {
case notExecuting
case executing
case finished
}
/// Synchronizes access to `state`.
private let stateQueue = DispatchQueue(label: "com.amazonaws.amplify.AsynchronousOperation.state",
target: DispatchQueue.global())
/// Private backing stored property for `state`.
private var _state: OperationState = .notExecuting
/// The state of the operation
@objc private dynamic var state: OperationState {
get { return stateQueue.sync { _state } }
set { stateQueue.sync { self._state = newValue } }
}
// MARK: - Various `Operation` properties
/// `true` if the operation is ready to be executed
open override var isReady: Bool {
return state == .notExecuting && super.isReady
}
/// `true` if the operation is currently executing
public final override var isExecuting: Bool {
return state == .executing
}
/// `true` if the operation has completed executing, either successfully or with an error
public final override var isFinished: Bool {
return state == .finished
}
/// KVN for dependent properties
open override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
if ["isReady", "isFinished", "isExecuting"].contains(key) {
return [#keyPath(state)]
}
return super.keyPathsForValuesAffectingValue(forKey: key)
}
/// Starts the operation
public final override func start() {
if isCancelled {
state = .finished
return
}
state = .executing
main()
}
/// Subclasses must implement this to perform their work and they must not call `super`.
/// The default implementation of this function throws an exception.
open override func main() {
fatalError("Subclasses must implement `main`.")
}
/// Call this function to pause an operation that is currently executing
open func pause() {
if isExecuting {
state = .notExecuting
}
}
/// Call this function to resume an operation that is currently ready
open func resume() {
if isReady {
state = .executing
}
}
/// Call this function to finish an operation that is currently executing
public final func finish() {
if !isFinished {
state = .finished
}
}
}