OperationPlus is a set of NSOperation subclasses and extensions on NSOperation/NSOperationQueue. Its goal is to fill in the API’s missing pieces. You don’t need to learn anything new to use it.
NSOperation has been around for a long time, and there are now two potential first-party alternatives, Combine and Swift concurrency. OperationPlus includes some facilities to help Combine and NSOperation interoperate conveniently.
BaseOperation: provides core functionality for easier NSOperation subclassing
AsyncOperation: convenience wrapper around BaseOperation for async support
AsyncBlockOperation: convenience class for inline async support
(Async)ProducerOperation: produces an output
(Async)ConsumerOperation: accepts an input from a ProducerOperation
(Async)ConsumerProducerOperation: accepts an input from a ProducerOperation and also produces an output
BaseOperation
This is a simple NSOperation subclass built for easier extensibility. It features:
Thread-safety
Timeout support
Easier cancellation handling
Stricter state checking
Built-in asynchrous support
Straight-foward customization
let a = BaseOperation(timeout: 5.0)
// NSOperation will happily allow you do this even
// if `a` has finished. `BaseOperation` will not.
a.addDependency(another)
// ...
public override func main() {
// This will return true if your operation is cancelled, timed out,
// or prematurely finished. ProducerOperation subclass state will be
// handled correctly as well.
if self.checkForCancellation() {
return
}
}
// ...
AsyncOperation
A BaseOperation subclass that can be used for your asynchronous operations. These are any operations that need to extend their lifetime past the main method.
import Foundation
import OperationPlus
class MyAsyncOperation: AsyncOperation {
public override func main() {
DispatchQueue.global().async {
if self.checkForCancellation() {
return
}
// do stuff
self.finish()
}
}
}
There’s also nothing special about this class at all – it’s there just for convenience. If you want, you can just subclass BaseOperation directly and override one method.
import Foundation
import OperationPlus
class MyAsyncOperation: BaseOperation {
override open var isAsynchronous: Bool {
return true
}
}
ProducerOperation
A BaseOperation subclass that yields a value. Includes a completion handler to access the value.
import Foundation
import OperationPlus
class MyValueOperation: ProducerOperation<Int> {
public override func main() {
// do your computation
self.finish(with: 42)
}
}
// ...
let op = MyValueOperation()
op.resultCompletionBlock = { (value) in
// use value here
}
AsyncProducerOperation
A variant of ProducerOperation that may produce a value after the main method has completed executing.
import Foundation
import OperationPlus
class MyAsyncOperation: AsyncProducerOperation<Int> {
public override func main() {
DispatchQueue.global().async {
if self.checkForCancellation() {
return
}
// do stuff
self.finish(with: 42)
}
}
}
ConsumerOperation and AsyncConsumerOperation
A BaseOperation sublass that accepts the input of a ProducerOperation.
import Foundation
import OperationPlus
class MyConsumerOperation: ConsumerOperation<Int> {
override func main() {
guard let value = producerValue else {
// handle failure in some way
}
}
override func main(with value: Int) {
// make use of value here, or automatically
// fail if it wasn't successfully produced
}
}
let op = MyConsumerOperation(producerOp: myIntProducerOperation)
AsyncBlockOperation
A play on NSBlockOperation, but makes it possible to support asynchronous completion without making an Operation subclass. Great for quick, inline work.
let op = AsyncBlockOperation { (completionBlock) in
DispatchQueue.global().async {
// do some async work here, just be certain to call
// the completionBlock when done
completionBlock()
}
}
NSOperation/NSOperationQueue Extensions
Queue creation conveniences:
let a = OperationQueue(name: "myqueue")
let b = OperationQueue(name: "myqueue", maxConcurrentOperations: 1)
let c = OperationQueue.serialQueue()
let d = OperationQueue.serialQueue(named: "myqueue")
publisher.operation() // PublisherOperation
publisher.execute(on: queue) // subscribes and executes chain on queue and returns a publisher for result
XCTest Support
OperationTestingPlus is an optional micro-framework to help make your XCTest-based tests a little nicer. When using Carthage, it is built as a static framework to help ease integration with your testing targets.
FulfillExpectationOperation
A simple NSOperation that will fulfill an XCTestExpectation when complete. Super-useful when used with dependencies on your other operations.
NeverFinishingOperation
A great way to test out your Operations’ timeout behaviors.
OperationExpectation
An XCTestExpectation sublass to make testing async operations a little more XCTest-like.
let op = NeverFinishingOperation()
let expectation = OperationExpectation(operation: op)
expectation.isInverted = true
wait(for: [expectation], timeout: 1.0)
Suggestions or Feedback
We’d love to hear from you! Get in touch via an issue or pull request.
Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.
OperationPlus
OperationPlus is a set of
NSOperation
subclasses and extensions onNSOperation
/NSOperationQueue
. Its goal is to fill in the API’s missing pieces. You don’t need to learn anything new to use it.NSOperation has been around for a long time, and there are now two potential first-party alternatives, Combine and Swift concurrency. OperationPlus includes some facilities to help Combine and NSOperation interoperate conveniently.
Integration
Swift Package Manager:
NSOperation Subclasses
BaseOperation
: provides core functionality for easierNSOperation
subclassingAsyncOperation
: convenience wrapper aroundBaseOperation
for async supportAsyncBlockOperation
: convenience class for inline async support(Async)ProducerOperation
: produces an output(Async)ConsumerOperation
: accepts an input from aProducerOperation
(Async)ConsumerProducerOperation
: accepts an input from aProducerOperation
and also produces an outputBaseOperation
This is a simple
NSOperation
subclass built for easier extensibility. It features:AsyncOperation
A
BaseOperation
subclass that can be used for your asynchronous operations. These are any operations that need to extend their lifetime past themain
method.There’s also nothing special about this class at all – it’s there just for convenience. If you want, you can just subclass
BaseOperation
directly and override one method.ProducerOperation
A
BaseOperation
subclass that yields a value. Includes a completion handler to access the value.AsyncProducerOperation
A variant of
ProducerOperation
that may produce a value after themain
method has completed executing.ConsumerOperation and AsyncConsumerOperation
A
BaseOperation
sublass that accepts the input of aProducerOperation
.AsyncBlockOperation
A play on
NSBlockOperation
, but makes it possible to support asynchronous completion without making anOperation
subclass. Great for quick, inline work.NSOperation/NSOperationQueue Extensions
Queue creation conveniences:
Enforcing runtime constraints on queue execution:
Consise dependencies:
Queueing work when a queue’s current operations are complete:
Convenient inline functions:
Async integration:
Delays:
Combine Integration
PublisherOperation
This
ProducerOperation
subclass takes a publisher. When executed, it creates a subscription and outputs the results.XCTest Support
OperationTestingPlus is an optional micro-framework to help make your XCTest-based tests a little nicer. When using Carthage, it is built as a static framework to help ease integration with your testing targets.
FulfillExpectationOperation
A simple NSOperation that will fulfill an
XCTestExpectation
when complete. Super-useful when used with dependencies on your other operations.NeverFinishingOperation
A great way to test out your Operations’ timeout behaviors.
OperationExpectation
An
XCTestExpectation
sublass to make testing async operations a little more XCTest-like.Suggestions or Feedback
We’d love to hear from you! Get in touch via an issue or pull request.
Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.