Skip to content

Instantly share code, notes, and snippets.

@amadeu01
Last active October 10, 2019 02:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amadeu01/ce347f548b9c663b0c54edaa6002f80a to your computer and use it in GitHub Desktop.
Save amadeu01/ce347f548b9c663b0c54edaa6002f80a to your computer and use it in GitHub Desktop.
// 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