Forked from Sorix/AsynchronousOperation.swift
Last active
September 26, 2019 09:01
-
-
Save arashkashi/2f901f83bf1020995ca67e130e560ca4 to your computer and use it in GitHub Desktop.
Subclass of NSOperation to make it asynchronous in Swift 3
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
// | |
// AsynchronousOperation.swift | |
// | |
// Created by Vasily Ulianov on 09.02.17. | |
// Copyright © 2017 Vasily Ulianov. All rights reserved. | |
// | |
import Foundation | |
/// Subclass of `Operation` that add support of asynchronous operations. | |
/// ## How to use: | |
/// 1. Call `super.main()` when override `main` method, call `super.start()` when override `start` method. | |
/// 2. When operation is finished or cancelled set `self.state = .finished` | |
class AsyncOperation: Operation { | |
override var isAsynchronous: Bool { return true } | |
override var isExecuting: Bool { return state == .executing } | |
override var isFinished: Bool { return state == .finished } | |
var state = State.ready { | |
willSet { | |
willChangeValue(forKey: state.keyPath) | |
willChangeValue(forKey: newValue.keyPath) | |
} | |
didSet { | |
didChangeValue(forKey: state.keyPath) | |
didChangeValue(forKey: oldValue.keyPath) | |
} | |
} | |
enum State: String { | |
case ready = "Ready" | |
case executing = "Executing" | |
case finished = "Finished" | |
case canceled = "isCancelled" | |
fileprivate var keyPath: String { return "is" + self.rawValue } | |
} | |
override func start() { | |
if self.isCancelled { | |
state = .finished | |
} else { | |
state = .ready | |
main() | |
} | |
} | |
override func main() { | |
if self.isCancelled { | |
state = .finished | |
} else { | |
state = .executing | |
} | |
} | |
} | |
class TestOperation: AsyncOperation { | |
var delay: Int | |
init(delay: Int) { | |
self.delay = delay | |
} | |
override func main() { | |
if self.isCancelled { | |
state = .finished | |
} else { | |
state = .executing | |
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .milliseconds(self.delay), execute: { | |
print("delay: \(self.delay)") | |
self.state = .finished | |
}) | |
} | |
} | |
} | |
// if cancel op2 right after adding them to the queue the currect implementation would | |
// execute op3 (since it has less delay) first and then op1 . but if we bring the cancel | |
// into logic then only op1 exedcutes. | |
let op1 = TestOperation(delay: 3000) | |
let op2 = TestOperation(delay: 2000) | |
let op3 = TestOperation(delay: 1000) | |
op1.completionBlock = { print("op1 finished") } | |
let queue = OperationQueue() | |
let operations = [op1, op2, op3] | |
for i in stride(from: operations.count - 1, to: 0, by: -1) { | |
print("\(i) depends on \(i-1)") | |
operations[i].addDependency(operations[i-1]) | |
} | |
queue.addOperations(operations, waitUntilFinished: false) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment