Forked from CanTheAlmighty/AsynchronousBlockOperation.swift
Last active
September 29, 2023 17:37
-
-
Save SergLam/7c1fdd2ac53c70d78b76e3af736e91fa to your computer and use it in GitHub Desktop.
Subclass of (NS)Operation to make it asynchronous in Swift 5, also providing a Block-based alternative.
This file contains hidden or 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
// | |
// AsynchronousBlockOperation.swift | |
// | |
// Created by Jose Canepa on 12/13/17. | |
// Copyright © 2017 Jose Canepa. All rights reserved. | |
// | |
import Foundation | |
/// Block based version of the AsynchronousOperation | |
/// | |
/// Has the same qualities as the AsynchronousOperation, but with an implementation | |
/// closer to the one of a BlockOperation. | |
/// | |
/// Once the inner asynchronous operation is finished, call the finish() | |
/// on the block reference passed. Do not retain the passed block. | |
/// | |
final class AsynchronousBlockOperation : AsynchronousOperation | |
{ | |
private var block : (AsynchronousBlockOperation) -> () | |
init(_ block : @escaping (AsynchronousBlockOperation) -> ()) | |
{ | |
self.block = block | |
} | |
override func main() | |
{ | |
// If we were executing already | |
let wasExecuting = (state == .executing) | |
// Call our main to ensure we are not cancelled | |
super.main() | |
if !wasExecuting | |
{ | |
block(self) | |
} | |
} | |
} |
This file contains hidden or 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 call `finish()` | |
open class AsynchronousOperation: Operation | |
{ | |
public enum State : String | |
{ | |
case ready = "Ready" | |
case executing = "Executing" | |
case finished = "Finished" | |
fileprivate var keyPath: String { return "is" + self.rawValue } | |
} | |
override open var isAsynchronous : Bool { return true } | |
override open var isExecuting : Bool { return state == .executing } | |
override open var isFinished : Bool { return state == .finished } | |
public private(set) var state = State.ready | |
{ | |
willSet(n) | |
{ | |
willChangeValue(forKey: state.keyPath) | |
willChangeValue(forKey: n.keyPath) | |
} | |
didSet(o) | |
{ | |
didChangeValue(forKey: state.keyPath) | |
didChangeValue(forKey: o.keyPath) | |
} | |
} | |
override open func start() | |
{ | |
if self.isCancelled | |
{ | |
state = .finished | |
} | |
else | |
{ | |
state = .ready | |
main() | |
} | |
} | |
override open func main() | |
{ | |
if self.isCancelled | |
{ | |
state = .finished | |
} | |
else | |
{ | |
state = .executing | |
} | |
} | |
public func finish() | |
{ | |
guard state != .finished else { return } | |
state = .finished | |
} | |
} |
This file contains hidden or 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
public typealias DataTaskCompletion = (Data?, URLResponse?, Error?) -> Void | |
public typealias DownloadTaskCompletion = (URL?, URLResponse?, Error?) -> Void | |
import Foundation | |
public final class NetworkOperation: Operation { | |
public enum State: String { | |
case ready = "Ready" | |
case executing = "Executing" | |
case finished = "Finished" | |
fileprivate var keyPath: String { | |
return "is" + self.rawValue | |
} | |
} | |
/// Identifier that is used for URLSessionTask `taskDescription` property setup. | |
/// Helps to identify a network request among other requests in URLSesion. | |
public var identifier: UUID = UUID() | |
/// Use this completion handler for a correct data parsing and error handling. | |
/// Avoid system-provided `competionBlock` usage. | |
public var taskCompletion: DataTaskCompletion? | |
override public var isAsynchronous : Bool { return true } | |
override public var isExecuting : Bool { return state == .executing } | |
override public var isFinished : Bool { return state == .finished } | |
public private(set) var state = State.ready { | |
willSet(newValue) { | |
willChangeValue(forKey: state.keyPath) | |
willChangeValue(forKey: newValue.keyPath) | |
} | |
didSet(oldValue) { | |
didChangeValue(forKey: state.keyPath) | |
didChangeValue(forKey: oldValue.keyPath) | |
} | |
} | |
// MARK: - Request data | |
private let urlSession: URLSession | |
private(set) var request: URLRequest | |
private let uploadData: Data? | |
private var networkTask: URLSessionTask? | |
// MARK: - Response data | |
private(set) var responseData: Data? | |
private(set) var response: URLResponse? | |
private(set) var responseError: Error? | |
init(urlSession: URLSession, | |
request: URLRequest, | |
uploadData: Data? = nil) { | |
self.urlSession = urlSession | |
self.request = request | |
self.uploadData = uploadData | |
super.init() | |
name = identifier.uuidString | |
} | |
override public func start() { | |
if self.isCancelled { | |
state = .finished | |
} else { | |
state = .ready | |
main() | |
} | |
} | |
override public func main() { | |
if self.isCancelled { | |
state = .finished | |
} else { | |
state = .executing | |
if let data = uploadData { | |
networkTask = urlSession.uploadTask(with: request, from: data) { [weak self] (data, response, error) in | |
self?.responseData = data | |
self?.response = response | |
self?.responseError = error | |
self?.taskCompletion?(data, response, error) | |
self?.finish() | |
} | |
} else { | |
networkTask = urlSession.dataTask(with: request, completionHandler: { [weak self] (data, response, error) in | |
self?.responseData = data | |
self?.response = response | |
self?.responseError = error | |
self?.taskCompletion?(data, response, error) | |
self?.finish() | |
}) | |
} | |
networkTask?.resume() | |
} | |
} | |
override public func cancel() { | |
networkTask?.cancel() | |
networkTask = nil | |
state = .finished | |
} | |
public func finish() { | |
guard state != .finished else { | |
return | |
} | |
state = .finished | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment