Last active
September 26, 2018 12:59
-
-
Save lubiluk/6bdc21feb34433d600d76d1bc5806670 to your computer and use it in GitHub Desktop.
Simple async operation implementation. Based on hints from Apple documentation and ideas from Advanced NSOperations session of WWDC 2015 (https://developer.apple.com/videos/wwdc/2015/?id=226).
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
// | |
// AsyncOperation.swift | |
// | |
// Created by Paweł Gajewski on 26/09/2018. | |
// Copyright © 2018. All rights reserved. | |
// | |
import Foundation | |
/** | |
A subclass of Operation for performing asynchronious tasks. | |
This class is intended to be subclassed, not used directly. | |
*/ | |
class AsyncOperation: Operation { | |
enum State { | |
case initialized | |
case executing | |
case finished | |
} | |
private var stateLock = NSLock() | |
// Hidden state, has to be thread safe | |
private var _state = State.initialized | |
var state: State { | |
get { | |
stateLock.lock() | |
let value = _state | |
stateLock.unlock() | |
return value | |
} | |
set(newState) { | |
willChangeValue(forKey: "state") | |
stateLock.lock() | |
if _state != .finished { | |
_state = newState | |
} | |
stateLock.unlock() | |
didChangeValue(forKey: "state") | |
} | |
} | |
var error: Error? | |
// MARK: State overrides | |
override var isExecuting: Bool { | |
return state == .executing | |
} | |
override var isFinished: Bool { | |
return state == .finished | |
} | |
override var isAsynchronous: Bool { | |
return true | |
} | |
// MARK: KVO setup | |
@objc class func keyPathsForValuesAffectingIsReady() -> Set<NSObject> { | |
return ["state" as NSObject] | |
} | |
@objc class func keyPathsForValuesAffectingIsExecuting() -> Set<NSObject> { | |
return ["state" as NSObject] | |
} | |
@objc class func keyPathsForValuesAffectingIsFinished() -> Set<NSObject> { | |
return ["state" as NSObject] | |
} | |
// MARK: Running | |
override func start() { | |
assert(_state == .initialized) | |
state = .executing | |
if isCancelled { | |
let error = NSError(domain: NSCocoaErrorDomain, code: NSUserCancelledError, userInfo: nil) | |
finish(withError: error) | |
} | |
operationDidStart() | |
} | |
func finish(withError error: Error? = nil) { | |
if self.error == nil { | |
self.error = error | |
} | |
operationWillFinish() | |
state = .finished | |
} | |
// MARK: Subclassing | |
/** | |
(Required) Override this method to perform operation's objective. | |
Use `finish()` or `finish(withError:)` to indicate that the task is complete. | |
*/ | |
func operationDidStart() { | |
} | |
/** | |
(Optional) Called just before the operation completes. | |
Can be used to notify other objects about completion and/or pass them results. | |
*/ | |
func operationWillFinish() { | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment