Skip to content

Instantly share code, notes, and snippets.

@lubiluk
Last active September 26, 2018 12:59
Show Gist options
  • Save lubiluk/6bdc21feb34433d600d76d1bc5806670 to your computer and use it in GitHub Desktop.
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).
//
// 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