Skip to content

Instantly share code, notes, and snippets.

@jemmons
Created November 23, 2015 03:57
Show Gist options
  • Star 13 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jemmons/b1c84130a7dcf0fc1f11 to your computer and use it in GitHub Desktop.
Save jemmons/b1c84130a7dcf0fc1f11 to your computer and use it in GitHub Desktop.
import UIKit
class AsyncOperation : NSOperation{
enum State{
case Waiting, Executing, Finished
}
var state = State.Waiting{
willSet{
switch(state, newValue){
case (.Waiting, .Executing):
willChangeValueForKey("isExecuting")
case (.Waiting, .Finished):
willChangeValueForKey("isFinished")
case (.Executing, .Finished):
willChangeValueForKey("isExecuting")
willChangeValueForKey("isFinished")
default:
fatalError("Invalid state change in AsyncOperation: \(state) to \(newValue)")
}
}
didSet{
switch(oldValue, state){
case (.Waiting, .Executing):
didChangeValueForKey("isExecuting")
case (.Waiting, .Finished):
didChangeValueForKey("isFinished")
case (.Executing, .Finished):
didChangeValueForKey("isExecuting")
didChangeValueForKey("isFinished")
default:
fatalError("Invalid state change in AsyncOperation: \(oldValue) to \(state)")
}
}
}
override var executing:Bool{
return state == .Executing
}
override var finished:Bool{
return state == .Finished
}
override var asynchronous:Bool{
return true
}
override init() {
super.init()
addObserver(self, forKeyPath: "isCancelled", options: [], context: nil)
}
deinit{
removeObserver(self, forKeyPath:"isCancelled")
}
override internal func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
guard keyPath == "isCancelled" else{
return
}
if cancelled{
didCancel()
}
}
override func start() {
guard NSThread.isMainThread() else{
fatalError("AsyncOperation should only run on the main thread.")
}
guard !hasCancelledDependencies else{
cancel()
return
}
guard !cancelled else{
return
}
state = .Executing
main()
}
/// Where the main work of the operation happens. Subclasses can override this method to do their own work. If they do so, they *must* call `finish()` when the work is complete. Because this is an asynchronous operation, the actual call to `finish()` will usually happen in a delegate or completion block.
override func main() {
finish()
}
/// Gets called whenever the operation becomes cancelled (i.e. `isCancelled` becomes `true`). Subclasses can override this to cancel and tear down any work that's happening in `main()`. If they do so, they *must* call `finish()` when complete.
func didCancel(){
finish()
}
func finish(){
state = .Finished
}
}
private extension AsyncOperation{
var hasCancelledDependencies:Bool{
return dependencies.reduce(false){ $0 || $1.cancelled }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment