Last active
October 10, 2019 02:55
-
-
Save amadeu01/ce347f548b9c663b0c54edaa6002f80a to your computer and use it in GitHub Desktop.
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
// Created by Amadeu Cavalcante Filho on 11/05/19. | |
// Copyright © 2019 Amadeu Cavalcante Filho. All rights reserved. | |
// | |
import Foundation | |
public struct Task<T, E: Error> { | |
public typealias Closure = (Controller<T, E>) -> Void | |
private let closure: Closure | |
public init(closure: @escaping Closure) { | |
self.closure = closure | |
} | |
} | |
extension Task { | |
public struct Controller<T, E: Error> { | |
fileprivate let queue: DispatchQueue | |
fileprivate let handler: (Result<T, E>) -> Void | |
public func finish(_ value: T) { | |
handler(.success(value)) | |
} | |
public func fail(with error: E) { | |
handler(.failure(error)) | |
} | |
public func resolve(_ result: Result<T, E>) { | |
handler(result) | |
} | |
} | |
} | |
extension Task { | |
public func perform(on queue: DispatchQueue = .global(), | |
then handler: @escaping (Result<T, E>) -> Void) { | |
queue.async { | |
let controller = Controller( | |
queue: queue, | |
handler: handler | |
) | |
self.closure(controller) | |
} | |
} | |
} | |
extension Task { | |
public func flatMap<NewValue>(_ transform: @escaping (T) -> Result<NewValue, E> ) -> Task<NewValue, E> { | |
return Task<NewValue, E> { controller in | |
self.perform(on: controller.queue) { result in | |
controller.resolve(result.flatMap(transform)) | |
} | |
} | |
} | |
public func map<NewValue>(_ transform: @escaping (T) -> NewValue ) -> Task<NewValue, E> { | |
return Task<NewValue, E> { controller in | |
self.perform(on: controller.queue) { result in | |
controller.resolve(result.map(transform)) | |
} | |
} | |
} | |
} | |
public extension Result { | |
static func zip<A, B>( | |
_ r1: Result<A, Failure>, | |
_ r2: Result<B, Failure> | |
) -> Result<(A, B), Failure> where Failure == Error, Success == (A, B) { | |
return r1.flatMap { (a: A) -> Result<(A, B), Failure> in | |
return Result<(A, B), Failure>.init { | |
let b = try r2.get() | |
return (a, b) | |
} | |
} | |
} | |
static func zip<A, B, C>( | |
_ r1: Result<(A, B), Failure>, | |
_ r2: Result<C, Failure> | |
) -> Result<(A, B, C), Failure> where Failure == Error, Success == (A, B, C) { | |
return r1.flatMap { (a: A, b: B) -> Result<(A, B, C), Failure> in | |
return Result<(A, B, C), Failure>.init { | |
let c = try r2.get() | |
return (a, b, c) | |
} | |
} | |
} | |
static func zip<A, B, C, D>( | |
_ r1: Result<(A, B), Failure>, | |
_ r2: Result<(C, D), Failure> | |
) -> Result<(A, B, C, D), Failure> where Failure == Error, Success == (A, B, C, D) { | |
return r1.flatMap { (a: A, b: B) -> Result<(A, B, C, D), Failure> in | |
return Result<(A, B, C, D), Failure>.init { | |
let (c, d) = try r2.get() | |
return (a, b, c, d) | |
} | |
} | |
} | |
} | |
//swiftlint:disable identifier_name large_tuple type_name | |
public extension Task { | |
static func zip<A, B>( | |
_ t1: Task<A, E>, | |
_ t2: Task<B, E> | |
) -> Task<(A, B), E> where E == Error, T == (A, B) { | |
return Task<(A, B), E> { controller in | |
t1.perform(on: controller.queue) { (t1result: Result<A, Error>) in | |
t2.perform(on: controller.queue) { (t2result: Result<B, Error>) in | |
controller.resolve(.zip(t1result, t2result)) | |
} | |
} | |
} | |
} | |
static func zip<A, B, C>( | |
_ t1: Task<(A, B), E>, | |
_ t2: Task<C, E> | |
) -> Task<(A, B, C), E> where E == Error, T == (A, B, C) { | |
return Task<(A, B, C), E> { controller in | |
t1.perform(on: controller.queue) { t1result in | |
t2.perform(on: controller.queue) { t2result in | |
controller.resolve(.zip(t1result, t2result)) | |
} | |
} | |
} | |
} | |
static func zip<A, B, C>( | |
_ t1: Task<A, E>, | |
_ t2: Task<B, E>, | |
_ t3: Task<C, E> | |
) -> Task<(A, B, C), E> where E == Error, T == (A, B, C) { | |
return .zip(Task<(A, B), E>.zip(t1, t2), t3) | |
} | |
static func zip<A, B, C, D>( | |
_ t1: Task<(A, B), E>, | |
_ t2: Task<(C, D), E> | |
) -> Task<(A, B, C, D), E> where E == Error, T == (A, B, C, D) { | |
return Task<(A, B, C, D), E> { controller in | |
t1.perform(on: controller.queue) { t1result in | |
t2.perform(on: controller.queue) { t2result in | |
controller.resolve(.zip(t1result, t2result)) | |
} | |
} | |
} | |
} | |
static func zip<A, B, C, D>( | |
_ t1: Task<A, E>, | |
_ t2: Task<B, E>, | |
_ t3: Task<C, E>, | |
_ t4: Task<D, E> | |
) -> Task<(A, B, C, D), E> where E == Error, T == (A, B, C, D) { | |
return .zip(.zip(t1, t2), .zip(t3, t4)) | |
} | |
} | |
//swiftlint:enable identifier_name large_tuple type_name | |
public extension Task where T == Void { | |
static func group(_ tasks: [Task]) -> Task { | |
return Task { controller in | |
let group = DispatchGroup() | |
let errorSyncQueue = DispatchQueue(label: "Task.ErrorSync") | |
var anyError: E? | |
for task in tasks { | |
group.enter() | |
task.perform(on: controller.queue) { outcome in | |
switch outcome { | |
case .success: | |
break | |
case .failure(let error): | |
errorSyncQueue.sync { | |
anyError = anyError ?? error | |
} | |
} | |
group.leave() | |
} | |
} | |
group.notify(queue: controller.queue) { | |
if let error = anyError { | |
controller.fail(with: error) | |
} else { | |
controller.finish(()) | |
} | |
} | |
} | |
} | |
} | |
public extension Task { | |
static func group(_ tasks: [Task]) -> Task<[T], E> { | |
return Task<[T], E> { controller in | |
let group = DispatchGroup() | |
let errorSyncQueue = DispatchQueue(label: "Task.ErrorSync") | |
var anyError: E? | |
var ress = [T]() | |
for task in tasks { | |
group.enter() | |
task.perform(on: controller.queue) { outcome in | |
switch outcome { | |
case let .success(res): | |
ress.append(res) | |
case .failure(let error): | |
errorSyncQueue.sync { | |
anyError = anyError ?? error | |
} | |
} | |
group.leave() | |
} | |
} | |
group.notify(queue: controller.queue) { | |
if let error = anyError { | |
controller.fail(with: error) | |
} else { | |
controller.finish(ress) | |
} | |
} | |
} | |
} | |
} | |
public extension Task where T == Void { | |
static func sequence(_ tasks: [Task]) -> Task { | |
var index = 0 | |
func performNext(using controller: Controller<T, E>) { | |
guard index < tasks.count else { | |
controller.finish(()) | |
return | |
} | |
let task = tasks[index] | |
index += 1 | |
task.perform(on: controller.queue) { outcome in | |
switch outcome { | |
case .success: | |
performNext(using: controller) | |
case .failure(let error): | |
controller.fail(with: error) | |
} | |
} | |
} | |
return Task(closure: performNext) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment