TestsTested | ✓ |
LangLanguage | SwiftSwift |
License | MIT |
ReleasedLast Release | Dec 2016 |
SwiftSwift Version | 2.3 |
SPMSupports SPM | ✓ |
Maintained by Dan Thorpe.
A Swift framework inspired by WWDC 2015 Advanced NSOperations session.
Resource | Where to find it |
---|---|
Session video | developer.apple.com |
Reference documentation | docs.danthorpe.me/operations |
Programming guide | operations.readme.io |
Example projects | danthorpe/Examples |
The programming guide goes into a lot more detail about using this framework. But here are some of the key details.
Operation
is an NSOperation
subclass. It is an abstract class which should be subclassed.
import Operations
class MyFirstOperation: Operation {
override func execute() {
guard !cancelled else { return }
print("Hello World")
finish()
}
}
let queue = OperationQueue()
let myOperation = MyFirstOperation()
queue.addOperation(myOperation)
the key points here are:
Operation
execute
but do not call super.execute()
cancelled
property before starting any work.finish()
after the work is done. This could be done asynchronously.OperationQueue
.Observers are attached to an Operation
. They receive callbacks when operation events occur. In a change from Apple's sample code, Operations defines four observer protocols for the four events: did start, did cancel, did produce operation and did finish. There are block based types which implement these protocols. For example, to observe when an operation starts:
operation.addObserver(StartedObserver { op in
print("Lets go!")
})
The framework also provides BackgroundObserver
, TimeoutObserver
and NetworkObserver
.
See the programming guide on Observers for more information.
Conditions are attached to an Operation
. Before an operation is ready to execute it will asynchronously evaluate all of its conditions. If any condition fails, the operation finishes with an error instead of executing. For example:
operation.addCondition(BlockCondition {
// operation will finish with an error if this is false
return trueOrFalse
}
Conditions can be mutually exclusive. This is akin to a lock being held preventing other operations with the same exclusion being executed.
The framework provides the following conditions: AuthorizedFor
, BlockCondition
, MutuallyExclusive
, NegatedCondition
, NoFailedDependenciesCondition
, SilentCondition
, ReachabilityCondition
, RemoteNotificationCondition
, UserConfirmationCondition
and UserNotificationCondition
.
See the programming guide on Conditions for more information.
CapabilityType
is a protocol which represents the application’s authorization to access device or user account abilities. For example, location services, cloud kit containers, calendars etc. The protocol provides a unified model to:
GetAuthorizationStatus
, Authorize
AuthorizedFor
. For example:
class ReminderOperation: Operation {
override init() {
super.init()
name = "Reminder Operation"
addCondition(AuthorizedFor(Capability.Calendar(.Reminder)))
}
override func execute() {
// do something with EventKit here
finish()
}
}
The framework provides the following capabilities: Capability.Calendar
, Capability.CloudKit
, Capability.Health
, Capability.Location
, Capability.Passbook
and Capability.Photos
.
See the programming guide on Capabilities for more information.
Operation
has its own internal logging functionality exposed via a log
property:
class LogExample: Operation {
override func execute() {
log.info("Hello World!")
finish()
}
}
See the programming guide for more information on logging and supporting 3rd party log frameworks.
State (or data if you prefer) can be seamlessly transitioned between operations automatically. An operation which produces a result can conform to ResultOperationType
and expose state via its result
property. An operation which consumes state, can conform to AutomaticInjectionOperationType
and set its requirement via its requirement
property. Given conformance to these protocols, operations can be chained together:
let getLocation = UserLocationOperation()
let processLocation = ProcessUserLocation()
processLocation.injectResultFromDependency(getLocation)
queue.addOperations(getLocation, processLocation)
See the programming guide on Injecting Results for more information.
See the programming guide for detailed installation instructions.
I want to stress that this code is heavily influenced by Apple. In no way am I attempting to assume any sort of credit for this architecture - that goes to Dave DeLong and his team. My motivations are that I want to adopt this code in my own projects, and so require a solid well tested framework which I can integrate with.
Other developers have created projects based off Apple’a WWDC sample code. Check them out too.