Created
November 23, 2015 03:57
-
-
Save jemmons/b1c84130a7dcf0fc1f11 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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