CompletionRequirement
public class CompletionRequirement<T>
A type that handles completion callbacks with safety checks and semantics that reduce the risks of callers forgetting to call the completion handler.
The type is not concurrency safe (see notes in Threading) and it will always call the completion
handler
synchronously, using the supplied completionQueue
if available, or on whatever the current queue thread is if
currentQueue
is nil.
To use, define a typealias for this type, with T the type of the completion function’s argument (use a tuple if your completion requires multiple arguments).
Then make your function that requires a completion handler take an instance of this type instead of the closure type, and make
the function expect a return value of the nested Status
type:
protocol MyCoordinator {
typealias DoSomethingCompletion = CompletionRequirement<Bool>
func doSomething(input: Any, completionRequirement: DoSomethingCompletion) -> DoSomethingCompletion.Status
}
Now, when calling this function on the protocol, you construct the requirement instance, pass it and verify the result:
let coordinator: MyCoordinator = ...
let completion = MyCoordinator.DoSomethingCompletion( { (shouldCancel: Bool, completedAsync: Bool) in
print("Cancel? \(shouldCancel)")
})
The block takes one argument of type `T`, in this case a boolean, and a second `Bool` argument that indicates
if the completion block has been called asynchronously.
// Call the function that requires completion
let status = coordinator.doSomething(input: x, completionRequirement: completion)
// Make sure one of the valid statuses was returned.
// This safety test ensures that the completion from the correct completion requirement instance was returned.
precondition(completion.verify(status))
// If the result does not return true for `isCompletingAsync`, the completion callback will have already been called by now.
if !status.isCompletingAsync {
print("Completed synchronously: \(status.value)")
} else {
print("Completing asynchronously... see you later")
}
When implementing such a function requiring a completion handler, you return one of two statuses returned by either
the CompletionRequirement.completed(_ arg: T)
or CompletionRequirement.willCompleteAsync()
.
The CompletionRequirement
will take care of calling the completion block as appropriate.
func doSomething(input: Any, completionRequirement: DoSomethingCompletion) -> DoSomethingCompletion.Status {
return completionRequirement.completedSync(false)
}
// or for async completion, you retain the result and later call `completed(value)`
func doSomething(input: Any, completionRequirement: DoSomethingCompletion) -> DoSomethingCompletion.Status {
// Capture the async status
let result = completionRequirement.willCompleteAsync()
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
// Use the retained status to indicate completion later
result.completed(false)
}
return result
}
## Threading
A `CompletionRequirement` is not concurrency safe. You must not change any properties or call any methods
after calling `completedSync` or `willCompleteAsync`. State of the object will not change asynchronously once
you have called either of these.
-
An
See moreabstract
base for the status result type. By design it must not be possible to instantiate this type in apps, so we can trust the instance handed back to the app matches our semantics.Declaration
Swift
public class Status
-
Undocumented
Declaration
Swift
public typealias Handler = (T, _ completedAsync: Bool) -> Void
-
Undocumented
Declaration
Swift
public typealias ProxyHandler = (T, _ completedAsync: Bool) -> T
-
Undocumented
Declaration
Swift
public let completionQueue: SmartDispatchQueue?
-
Instantiate a new completion requirement that calls the supplied completion handler, either synchronously or asynchronously.
Declaration
Swift
public init(smartQueue: SmartDispatchQueue?, completionHandler: @escaping Handler)
-
Instantiate a new completion requirement that calls the supplied completion handler, either synchronously or asynchronously.
Declaration
Swift
public convenience init(queue: DispatchQueue?, completionHandler: @escaping Handler)
-
Call to verify that the result belongs to this completion instance and there hasn’t been a mistake
Declaration
Swift
public func verify(_ status: Status) -> Bool
-
Call to indicate that completion will be called later, asynchronously by code that has a reference to the deferred status.
Declaration
Swift
public func willCompleteAsync() -> DeferredStatus
-
Call to indicate that completion is to be called immediately, synchronously
Declaration
Swift
public func completedSync(_ result: T) -> SyncCompletionStatus
-
Undocumented
Declaration
Swift
public func addProxyCompletionHandler(_ proxyCompletion: @escaping ProxyHandler)