diff --git a/AlecrimAsyncKit.podspec b/AlecrimAsyncKit.podspec index 0627958..24d6e93 100644 --- a/AlecrimAsyncKit.podspec +++ b/AlecrimAsyncKit.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = "AlecrimAsyncKit" - s.version = "4.0-beta.5" + s.version = "4.0" s.summary = "async and await for Swift." s.homepage = "https://www.alecrim.com/AlecrimAsyncKit" diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000..b648f58 --- /dev/null +++ b/Package.swift @@ -0,0 +1,3 @@ +import PackageDescription + +let package = Package(name: "AlecrimAsyncKit") diff --git a/README.md b/README.md index acd3dc7..541b16e 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ async and await for Swift. ## Usage ### Awaiting the results -I know I am putting the cart before the horse, but... For the three functions in the next section you can await the returning value in the same way: +Maybe I am putting the cart before the horses, but... For all the functions in the next section you can await the returning value in the same way: ```swift func someFuncRunningInBackground() throws { @@ -161,21 +161,19 @@ func someFuncRunningInTheMainThread() { } ``` -All methods (`then`, `catch`, `cancelled` and `finally`) are optional. If specified, the closure related to the `finally` method will always be called regardless whether the task was cancelled or not, whether there was an error or not. +All methods (`then`, `catch`, `cancelled` and `finally`) are optional. When specified, the closure related to the `finally` method will always be called regardless whether the task was cancelled or not, whether there was an error or not. ## Non failable tasks If you read the framework's code you will find the `NonFailableTask` class. This kind of task cannot fail (sort of). In fact it may fail, but it should not. -The main difference from the failable task class is that you do not have to use the `try` keyword when awaiting for non failable task results. A non failable task cannot be cancelled either. +The main difference from the failable task class is that you do not have to `try` when awaiting for non failable task results. A non failable task cannot be cancelled either. -Please only use this type of task when you are sure that it can not fail. If it do, your program will crash. +Please only use this type of task when you are sure that it cannot fail. If it do and the task fail your program will crash. -## Observers and conditions -The previous version had observers and conditions based on Session 226 of WWDC 2015 (“Advanced NSOperations”). This turned the framework unnecessarily complex. +## Dependencies, conditions and observers +The previous version had observers and conditions based on Session 226 of WWDC 2015 (“Advanced NSOperations”). This turned the framework unnecessarily complex. If you need this functionality right now you can use version 3.x of **AlecrimAsyncKit**. -If you need this functionality right now you can use version 3.x of **AlecrimAsyncKit**. - -Observers and conditions may be reimplemented in a future release or as a separated framework. No guarantees, though. +In version 4.0 the framework has the notion of "dependencies" and a new kind of "conditions" was implemented. Observers may reappear in a future release. No guarantees, though. ## Contribute If you have any problems or need more information, please open an issue using the provided GitHub link. diff --git a/Source/AlecrimAsyncKit.xcodeproj/project.pbxproj b/Source/AlecrimAsyncKit.xcodeproj/project.pbxproj index f4128b4..08dd125 100644 --- a/Source/AlecrimAsyncKit.xcodeproj/project.pbxproj +++ b/Source/AlecrimAsyncKit.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ 1466506C205B178F006C70AA /* Sequence+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1466506B205B178F006C70AA /* Sequence+Extensions.swift */; }; 1466506E205B1905006C70AA /* OtherConveniences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1466506D205B1905006C70AA /* OtherConveniences.swift */; }; 14A391242053A03300AD8249 /* Cancellation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14A391232053A03300AD8249 /* Cancellation.swift */; }; + 14E8AD3B209FF6F20035DFA2 /* Mapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14E8AD3A209FF6F20035DFA2 /* Mapping.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -42,6 +43,7 @@ 1466506B205B178F006C70AA /* Sequence+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Sequence+Extensions.swift"; sourceTree = ""; }; 1466506D205B1905006C70AA /* OtherConveniences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OtherConveniences.swift; sourceTree = ""; }; 14A391232053A03300AD8249 /* Cancellation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Cancellation.swift; sourceTree = ""; }; + 14E8AD3A209FF6F20035DFA2 /* Mapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Mapping.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -121,6 +123,7 @@ 14665067205B1727006C70AA /* DispatchQueue+Extensions.swift */, 14665065205B1708006C70AA /* OperationQueue+Extensions.swift */, 1466506D205B1905006C70AA /* OtherConveniences.swift */, + 14E8AD3A209FF6F20035DFA2 /* Mapping.swift */, 1466506B205B178F006C70AA /* Sequence+Extensions.swift */, ); path = Convenience; @@ -215,6 +218,7 @@ 14665068205B1727006C70AA /* DispatchQueue+Extensions.swift in Sources */, 14665066205B1708006C70AA /* OperationQueue+Extensions.swift in Sources */, 143E712520531FBC002F7222 /* Async.swift in Sources */, + 14E8AD3B209FF6F20035DFA2 /* Mapping.swift in Sources */, 143E712320531F9B002F7222 /* Errors.swift in Sources */, 1444522A209E09200016D444 /* TaskDependency.swift in Sources */, 1466506A205B1763006C70AA /* Availability.swift in Sources */, @@ -360,7 +364,7 @@ CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 356; + CURRENT_PROJECT_VERSION = 370; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; @@ -392,7 +396,7 @@ CODE_SIGN_IDENTITY = ""; CODE_SIGN_STYLE = Manual; COMBINE_HIDPI_IMAGES = YES; - CURRENT_PROJECT_VERSION = 356; + CURRENT_PROJECT_VERSION = 370; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; diff --git a/Source/AlecrimAsyncKit/Conditions and Dependencies/TaskCondition.swift b/Source/AlecrimAsyncKit/Conditions and Dependencies/TaskCondition.swift index e269b36..f0b57f9 100644 --- a/Source/AlecrimAsyncKit/Conditions and Dependencies/TaskCondition.swift +++ b/Source/AlecrimAsyncKit/Conditions and Dependencies/TaskCondition.swift @@ -14,32 +14,19 @@ import Foundation // example: // // extension Reachability: TaskCondition { -// func evaluate() -> Task { -// return conditionAsync { conditionTask in -// self.whenReachable { -// conditionTask.finish(with: true) -// } -// } +// public func evaluate() -> Bool { +// return self.connection != .none // } // } // // ... // -// async(condition: Reachability()) { ... } +// async(condition: self.reachability) { ... } // public protocol TaskCondition { - func evaluate() -> Task -} - -// MARK: - - -public func conditionAsync(execute closure: @escaping AsyncTaskClosure) -> Task { - return async(in: Queue.taskConditionOperationQueue, execute: closure) -} - -public func conditionAsync(execute taskClosure: @escaping AsyncTaskFullClosure) -> Task { - return async(in: Queue.taskConditionOperationQueue, execute: taskClosure) + // this will always be executed on a background thread, so the result does not need to me immediately computed + func evaluate() -> Bool } // MARK: - @@ -47,10 +34,13 @@ public func conditionAsync(execute taskClosure: @escaping AsyncTaskFullClosure Task { - return conditionAsync { + public func evaluate() -> Bool { + do { return try self.await() } + catch { + return false + } } } @@ -104,35 +94,33 @@ public final class CompoundTaskCondition: TaskCondition { self.subconditions = subconditions } - public func evaluate() -> Task { - return conditionAsync { - switch self.compoundTaskConditionType { - case .and: - var all = true - for subcondition in self.subconditions { - if !(try await(subcondition.evaluate())) { - all = false - break - } + public func evaluate() -> Bool { + switch self.compoundTaskConditionType { + case .and: + var all = true + for subcondition in self.subconditions { + if !subcondition.evaluate() { + all = false + break } + } - return all + return all - case .or: - var any = false - for subcondition in self.subconditions { - if (try await(subcondition.evaluate())) { - any = true - break - } + case .or: + var any = false + for subcondition in self.subconditions { + if subcondition.evaluate() { + any = true + break } + } - return any + return any - case .not: - let subcondition = self.subconditions.first! - return !(try await(subcondition.evaluate())) - } + case .not: + let subcondition = self.subconditions.first! + return !subcondition.evaluate() } } diff --git a/Source/AlecrimAsyncKit/Convenience/Availability.swift b/Source/AlecrimAsyncKit/Convenience/Availability.swift index 0b56bdd..4d8510c 100644 --- a/Source/AlecrimAsyncKit/Convenience/Availability.swift +++ b/Source/AlecrimAsyncKit/Convenience/Availability.swift @@ -8,33 +8,33 @@ import Foundation -@available(*, deprecated, renamed: "async(in:execute:)") +@available(*, deprecated, renamed: "async(on:execute:)") public func asyncEx(in queue: OperationQueue? = nil, execute closure: @escaping AsyncTaskFullClosure) -> Task { - return async(in: queue, execute: closure) + return async(on: queue, execute: closure) } @available(*, deprecated, renamed: "async(value:)") public func asyncValue(in queue: OperationQueue? = nil, _ value: Value) -> Task { - return async(in: queue, value: value) + return async(on: queue, value: value) } @available(*, deprecated, renamed: "async(error:)") public func asyncError(in queue: OperationQueue? = nil, _ error: Error) -> Task { - return async(in: queue, error: error) + return async(on: queue, error: error) } -@available(*, deprecated, renamed: "async(in:delay:)") +@available(*, deprecated, renamed: "async(on:delay:)") public func asyncDelay(in queue: OperationQueue? = nil, timeInterval: TimeInterval) -> NonFailableTask { - return async(in: queue, delay: timeInterval) + return async(on: queue, delay: timeInterval) } -@available(*, deprecated, renamed: "async(in:sleepForTimeInterval:)") +@available(*, deprecated, renamed: "async(on:sleepForTimeInterval:)") public func asyncSleep(in queue: OperationQueue? = nil, forTimeInterval timeInterval: TimeInterval) -> NonFailableTask { - return async(in: queue, sleepForTimeInterval: timeInterval) + return async(on: queue, sleepForTimeInterval: timeInterval) } -@available(*, deprecated, renamed: "async(in:sleepUntil:)") +@available(*, deprecated, renamed: "async(on:sleepUntil:)") public func asyncSleep(in queue: OperationQueue? = nil, until date: Date) -> NonFailableTask { - return async(in: queue, sleepUntil: date) + return async(on: queue, sleepUntil: date) } diff --git a/Source/AlecrimAsyncKit/Convenience/DispatchQueue+Extensions.swift b/Source/AlecrimAsyncKit/Convenience/DispatchQueue+Extensions.swift index dee0f54..2ee4833 100644 --- a/Source/AlecrimAsyncKit/Convenience/DispatchQueue+Extensions.swift +++ b/Source/AlecrimAsyncKit/Convenience/DispatchQueue+Extensions.swift @@ -11,19 +11,19 @@ import Foundation extension DispatchQueue { public func async(execute closure: @escaping AsyncTaskClosure) -> Task { - return AlecrimAsyncKit.async(in: Queue.operationQueue(for: self), execute: closure) + return AlecrimAsyncKit.async(on: Queue.operationQueue(for: self), execute: closure) } public func async(execute closure: @escaping AsyncNonFailableTaskClosure) -> NonFailableTask { - return AlecrimAsyncKit.async(in: Queue.operationQueue(for: self), execute: closure) + return AlecrimAsyncKit.async(on: Queue.operationQueue(for: self), execute: closure) } public func async(execute taskClosure: @escaping AsyncTaskFullClosure) -> Task { - return AlecrimAsyncKit.async(in: Queue.operationQueue(for: self), execute: taskClosure) + return AlecrimAsyncKit.async(on: Queue.operationQueue(for: self), execute: taskClosure) } public func async(execute taskClosure: @escaping AsyncTaskFullClosure) -> NonFailableTask { - return AlecrimAsyncKit.async(in: Queue.operationQueue(for: self), execute: taskClosure) + return AlecrimAsyncKit.async(on: Queue.operationQueue(for: self), execute: taskClosure) } } diff --git a/Source/AlecrimAsyncKit/Convenience/Mapping.swift b/Source/AlecrimAsyncKit/Convenience/Mapping.swift new file mode 100644 index 0000000..f9ee588 --- /dev/null +++ b/Source/AlecrimAsyncKit/Convenience/Mapping.swift @@ -0,0 +1,68 @@ +// +// Mapping.swift +// AlecrimAsyncKit +// +// Created by Vanderlei Martinelli on 06/05/18. +// Copyright © 2018 Alecrim. All rights reserved. +// + +import Foundation + +// MARK: - + +extension Task { + + public func map(on queue: OperationQueue? = nil, closure: @escaping (Value) throws -> U) -> Task { + return async(on: queue) { + let value = try self.await() + let mappedValue = try closure(value) + + return mappedValue + } + } + + public func map(on queue: OperationQueue? = nil, closure: @escaping (Value) -> U) -> Task { + return async(on: queue) { + let value = try self.await() + let mappedValue = closure(value) + + return mappedValue + } + } + +} + +extension Task { + + public func asNonFailable(on queue: OperationQueue? = nil) -> NonFailableTask { + return async(on: queue) { + return try! self.await() + } + } + +} + +// MARK: - + +extension NonFailableTask { + + public func map(on queue: OperationQueue? = nil, closure: @escaping (Value) -> U) -> NonFailableTask { + return async(on: queue) { + let value = try! self.await() + let mappedValue = closure(value) + + return mappedValue + } + } + +} + +extension NonFailableTask { + + public func asFailable(on queue: OperationQueue? = nil) -> Task { + return async(on: queue) { + return try self.await() + } + } + +} diff --git a/Source/AlecrimAsyncKit/Convenience/OperationQueue+Extensions.swift b/Source/AlecrimAsyncKit/Convenience/OperationQueue+Extensions.swift index 98323c5..36e92ed 100644 --- a/Source/AlecrimAsyncKit/Convenience/OperationQueue+Extensions.swift +++ b/Source/AlecrimAsyncKit/Convenience/OperationQueue+Extensions.swift @@ -11,19 +11,19 @@ import Foundation extension OperationQueue { public func addOperation(_ closure: @escaping AsyncTaskClosure) -> Task { - return AlecrimAsyncKit.async(in: self, execute: closure) + return AlecrimAsyncKit.async(on: self, execute: closure) } public func addOperation(_ closure: @escaping AsyncNonFailableTaskClosure) -> NonFailableTask { - return AlecrimAsyncKit.async(in: self, execute: closure) + return AlecrimAsyncKit.async(on: self, execute: closure) } public func addOperation(_ taskClosure: @escaping AsyncTaskFullClosure) -> Task { - return AlecrimAsyncKit.async(in: self, execute: taskClosure) + return AlecrimAsyncKit.async(on: self, execute: taskClosure) } public func addOperation(_ taskClosure: @escaping AsyncTaskFullClosure) -> NonFailableTask { - return AlecrimAsyncKit.async(in: self, execute: taskClosure) + return AlecrimAsyncKit.async(on: self, execute: taskClosure) } } diff --git a/Source/AlecrimAsyncKit/Convenience/OtherConveniences.swift b/Source/AlecrimAsyncKit/Convenience/OtherConveniences.swift index ef577c1..25cb424 100644 --- a/Source/AlecrimAsyncKit/Convenience/OtherConveniences.swift +++ b/Source/AlecrimAsyncKit/Convenience/OtherConveniences.swift @@ -13,87 +13,87 @@ import Foundation // MARK: - externally controlled tasks /// The task must be retained and the `finish` method shall be called externally when done. -public func manualTask(in queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil) -> Task { - return async(in: queue, dependency: dependency, condition: condition) { _ in } +public func manualTask(on queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil) -> Task { + return async(on: queue, dependency: dependency, condition: condition) { _ in } } /// The task must be retained and the `finish` method shall be called externally when done. -public func manualTask(in queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil) -> NonFailableTask { - return async(in: queue, dependency: dependency, condition: condition) { _ in } +public func manualTask(on queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil) -> NonFailableTask { + return async(on: queue, dependency: dependency, condition: condition) { _ in } } // MARK: - // stubs for Void tasks -public func async(in queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil) -> Task { - return async(in: queue, dependency: dependency, condition: condition) { return () } +public func async(on queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil) -> Task { + return async(on: queue, dependency: dependency, condition: condition) { return () } } -public func async(in queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil) -> NonFailableTask { - return async(in: queue, dependency: dependency, condition: condition) { return () } +public func async(on queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil) -> NonFailableTask { + return async(on: queue, dependency: dependency, condition: condition) { return () } } // shortcuts to simply return a value or throw an error -public func async(in queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, value: Value) -> Task { - return async(in: queue, dependency: dependency, condition: condition) { return value } +public func async(on queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, value: Value) -> Task { + return async(on: queue, dependency: dependency, condition: condition) { return value } } -public func async(in queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, value: Value) -> NonFailableTask { - return async(in: queue, dependency: dependency, condition: condition) { return value } +public func async(on queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, value: Value) -> NonFailableTask { + return async(on: queue, dependency: dependency, condition: condition) { return value } } -public func async(in queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, error: Error) -> Task { - return async(in: queue, dependency: dependency, condition: condition) { throw error } +public func async(on queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, error: Error) -> Task { + return async(on: queue, dependency: dependency, condition: condition) { throw error } } // delay / sleep -public func async(in queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, delay timeInterval: TimeInterval) -> Task { - return async(in: queue, dependency: dependency, condition: condition, sleepForTimeInterval: timeInterval) +public func async(on queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, delay timeInterval: TimeInterval) -> Task { + return async(on: queue, dependency: dependency, condition: condition, sleepForTimeInterval: timeInterval) } -public func async(in queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, delay timeInterval: TimeInterval) -> NonFailableTask { - return async(in: queue, dependency: dependency, condition: condition, sleepForTimeInterval: timeInterval) +public func async(on queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, delay timeInterval: TimeInterval) -> NonFailableTask { + return async(on: queue, dependency: dependency, condition: condition, sleepForTimeInterval: timeInterval) } -public func async(in queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, sleepForTimeInterval timeInterval: TimeInterval) -> Task { - return async(in: queue, dependency: dependency, condition: condition) { task in +public func async(on queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, sleepForTimeInterval timeInterval: TimeInterval) -> Task { + return async(on: queue, dependency: dependency, condition: condition) { task in Queue.delayDispatchQueue.asyncAfter(deadline: .now() + timeInterval) { task.finish() } } } -public func async(in queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, sleepForTimeInterval timeInterval: TimeInterval) -> NonFailableTask { - return async(in: queue, dependency: dependency, condition: condition) { task in +public func async(on queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, sleepForTimeInterval timeInterval: TimeInterval) -> NonFailableTask { + return async(on: queue, dependency: dependency, condition: condition) { task in Queue.delayDispatchQueue.asyncAfter(deadline: .now() + timeInterval) { task.finish() } } } -public func async(in queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, sleepUntil date: Date) -> Task { +public func async(on queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, sleepUntil date: Date) -> Task { let now = Date() if date > now { let timeInterval = date.timeIntervalSince(now) - return async(in: queue, dependency: dependency, condition: condition, sleepForTimeInterval: timeInterval) + return async(on: queue, dependency: dependency, condition: condition, sleepForTimeInterval: timeInterval) } else { - return async(in: queue, dependency: dependency, condition: condition) {} + return async(on: queue, dependency: dependency, condition: condition) {} } } -public func async(in queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, sleepUntil date: Date) -> NonFailableTask { +public func async(on queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, sleepUntil date: Date) -> NonFailableTask { let now = Date() if date > now { let timeInterval = date.timeIntervalSince(now) - return async(in: queue, dependency: dependency, condition: condition, sleepForTimeInterval: timeInterval) + return async(on: queue, dependency: dependency, condition: condition, sleepForTimeInterval: timeInterval) } else { - return async(in: queue, dependency: dependency, condition: condition) {} + return async(on: queue, dependency: dependency, condition: condition) {} } } diff --git a/Source/AlecrimAsyncKit/Core/Async.swift b/Source/AlecrimAsyncKit/Core/Async.swift index 6fe188a..ce4908c 100644 --- a/Source/AlecrimAsyncKit/Core/Async.swift +++ b/Source/AlecrimAsyncKit/Core/Async.swift @@ -17,15 +17,15 @@ public typealias AsyncTaskFullClosure = (BaseTask) -> Void // MARK: - -public func async(in queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, execute closure: @escaping AsyncTaskClosure) -> Task { - return enqueue(in: queue, dependency: dependency, condition: condition, closure: closure) +public func async(on queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, execute closure: @escaping AsyncTaskClosure) -> Task { + return enqueue(on: queue, dependency: dependency, condition: condition, closure: closure) } -public func async(in queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, execute closure: @escaping AsyncNonFailableTaskClosure) -> NonFailableTask { - return enqueue(in: queue, dependency: dependency, condition: condition, closure: closure) +public func async(on queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, execute closure: @escaping AsyncNonFailableTaskClosure) -> NonFailableTask { + return enqueue(on: queue, dependency: dependency, condition: condition, closure: closure) } -fileprivate func enqueue(in queue: OperationQueue?, dependency: TaskDependency?, condition: TaskCondition?, closure: @escaping AsyncTaskClosure) -> Task { +fileprivate func enqueue(on queue: OperationQueue?, dependency: TaskDependency?, condition: TaskCondition?, closure: @escaping AsyncTaskClosure) -> Task { // let taskClosure: AsyncTaskFullClosure = { do { @@ -38,31 +38,31 @@ fileprivate func enqueue(in queue: OperationQueue?, dependency: TaskDepen } // - return enqueue(in: queue, dependency: dependency, condition: condition, closure: taskClosure) + return enqueue(on: queue, dependency: dependency, condition: condition, closure: taskClosure) } -fileprivate func enqueue(in queue: OperationQueue?, dependency: TaskDependency?, condition: TaskCondition?, closure: @escaping AsyncNonFailableTaskClosure) -> NonFailableTask { +fileprivate func enqueue(on queue: OperationQueue?, dependency: TaskDependency?, condition: TaskCondition?, closure: @escaping AsyncNonFailableTaskClosure) -> NonFailableTask { // let taskClosure: AsyncTaskFullClosure = { $0.finish(with: closure()) } // - return enqueue(in: queue, dependency: dependency, condition: condition, closure: taskClosure) + return enqueue(on: queue, dependency: dependency, condition: condition, closure: taskClosure) } // -public func async(in queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, execute taskClosure: @escaping AsyncTaskFullClosure) -> Task { - return enqueue(in: queue, dependency: dependency, condition: condition, closure: taskClosure) +public func async(on queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, execute taskClosure: @escaping AsyncTaskFullClosure) -> Task { + return enqueue(on: queue, dependency: dependency, condition: condition, closure: taskClosure) } -public func async(in queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, execute taskClosure: @escaping AsyncTaskFullClosure) -> NonFailableTask { - return enqueue(in: queue, dependency: dependency, condition: condition, closure: taskClosure) +public func async(on queue: OperationQueue? = nil, dependency: TaskDependency? = nil, condition: TaskCondition? = nil, execute taskClosure: @escaping AsyncTaskFullClosure) -> NonFailableTask { + return enqueue(on: queue, dependency: dependency, condition: condition, closure: taskClosure) } -fileprivate func enqueue(in queue: OperationQueue?, dependency: TaskDependency?, condition: TaskCondition?, closure taskClosure: @escaping AsyncTaskFullClosure) -> Task { +fileprivate func enqueue(on queue: OperationQueue?, dependency: TaskDependency?, condition: TaskCondition?, closure taskClosure: @escaping AsyncTaskFullClosure) -> Task { // let queue = queue ?? Queue.defaultOperationQueue precondition(queue.maxConcurrentOperationCount > 1 || queue.maxConcurrentOperationCount == OperationQueue.defaultMaxConcurrentOperationCount) @@ -94,7 +94,7 @@ fileprivate func enqueue(in queue: OperationQueue?, dependency: TaskDepen return task } -fileprivate func enqueue(in queue: OperationQueue?, dependency: TaskDependency?, condition: TaskCondition?, closure taskClosure: @escaping AsyncTaskFullClosure) -> NonFailableTask { +fileprivate func enqueue(on queue: OperationQueue?, dependency: TaskDependency?, condition: TaskCondition?, closure taskClosure: @escaping AsyncTaskFullClosure) -> NonFailableTask { // let queue = queue ?? Queue.defaultOperationQueue precondition(queue.maxConcurrentOperationCount > 1 || queue.maxConcurrentOperationCount == OperationQueue.defaultMaxConcurrentOperationCount) diff --git a/Source/AlecrimAsyncKit/Core/Cancellation.swift b/Source/AlecrimAsyncKit/Core/Cancellation.swift index bf42069..2953ae5 100644 --- a/Source/AlecrimAsyncKit/Core/Cancellation.swift +++ b/Source/AlecrimAsyncKit/Core/Cancellation.swift @@ -1,5 +1,5 @@ // -// File.swift +// Cancellation.swift // AlecrimAsyncKit // // Created by Vanderlei Martinelli on 10/03/18. @@ -14,7 +14,7 @@ public typealias CancellationHandler = () -> Void // -public protocol CancellableTask: class { +public protocol CancellableTask: AnyObject { var cancellation: Cancellation { get } var isCancelled: Bool { get } diff --git a/Source/AlecrimAsyncKit/Core/Queue.swift b/Source/AlecrimAsyncKit/Core/Queue.swift index 906a413..38b7ac3 100644 --- a/Source/AlecrimAsyncKit/Core/Queue.swift +++ b/Source/AlecrimAsyncKit/Core/Queue.swift @@ -10,11 +10,13 @@ import Foundation internal /* namespace */ enum Queue { + private static let defaultMaxConcurrentOperationCount = OperationQueue.defaultMaxConcurrentOperationCount + internal static let defaultOperationQueue: OperationQueue = { let queue = OperationQueue() queue.name = "com.alecrim.AlecrimAsyncKit.Task" queue.qualityOfService = .utility - queue.maxConcurrentOperationCount = ProcessInfo().activeProcessorCount * 2 + queue.maxConcurrentOperationCount = Queue.defaultMaxConcurrentOperationCount return queue }() @@ -23,14 +25,14 @@ internal /* namespace */ enum Queue { let queue = OperationQueue() queue.name = "com.alecrim.AlecrimAsyncKit.TaskAwaiter" queue.qualityOfService = .utility - queue.maxConcurrentOperationCount = ProcessInfo().activeProcessorCount * 2 + queue.maxConcurrentOperationCount = Queue.defaultMaxConcurrentOperationCount return queue }() internal static func operationQueue(for dispatchQueue: DispatchQueue) -> OperationQueue { let operationQueue = OperationQueue() - operationQueue.maxConcurrentOperationCount = ProcessInfo().activeProcessorCount * 2 + operationQueue.maxConcurrentOperationCount = Queue.defaultMaxConcurrentOperationCount operationQueue.underlyingQueue = dispatchQueue return operationQueue @@ -42,7 +44,7 @@ internal /* namespace */ enum Queue { let queue = OperationQueue() queue.name = "com.alecrim.AlecrimAsyncKit.TaskCondition" queue.qualityOfService = .utility - queue.maxConcurrentOperationCount = ProcessInfo().activeProcessorCount * 2 + queue.maxConcurrentOperationCount = Queue.defaultMaxConcurrentOperationCount return queue }() diff --git a/Source/AlecrimAsyncKit/Core/Task.swift b/Source/AlecrimAsyncKit/Core/Task.swift index b4a5ad0..5229151 100644 --- a/Source/AlecrimAsyncKit/Core/Task.swift +++ b/Source/AlecrimAsyncKit/Core/Task.swift @@ -60,13 +60,7 @@ public class BaseTask { private func _start() { if let condition = self.condition { - var result = false - do { - result = try AlecrimAsyncKit.await(condition.evaluate()) - } - catch {} - - if result { + if condition.evaluate() { self.__start() } else { @@ -93,15 +87,20 @@ public class BaseTask { internal final func await() throws -> Value { self.wait() - guard let value = self.value else { - guard let error = self.error else { - fatalError("Unexpected: error cannot be nil") + do { + self.lock(); defer { self.unlock() } + + if let error = self.error { + self.value = nil // to be sure + throw error } - - throw error + + guard let value = self.value else { + fatalError("Unexpected: value cannot be nil") + } + + return value } - - return value } // diff --git a/Source/AlecrimAsyncKit/Core/TaskAwaiter.swift b/Source/AlecrimAsyncKit/Core/TaskAwaiter.swift index fc35286..43df890 100644 --- a/Source/AlecrimAsyncKit/Core/TaskAwaiter.swift +++ b/Source/AlecrimAsyncKit/Core/TaskAwaiter.swift @@ -95,22 +95,22 @@ public final class TaskAwaiter { extension Task { @discardableResult - public func then(in queue: OperationQueue? = nil, callbackQueue: OperationQueue? = nil, closure: @escaping (Value) -> Void) -> TaskAwaiter { + public func then(on queue: OperationQueue? = nil, callbackQueue: OperationQueue? = nil, closure: @escaping (Value) -> Void) -> TaskAwaiter { return TaskAwaiter(queue: queue ?? Queue.taskAwaiterDefaultOperationQueue, callbackQueue: callbackQueue ?? OperationQueue.main, task: self).then(closure) } @discardableResult - public func `catch`(in queue: OperationQueue? = nil, callbackQueue: OperationQueue? = nil, closure: @escaping (Error) -> Void) -> TaskAwaiter { + public func `catch`(on queue: OperationQueue? = nil, callbackQueue: OperationQueue? = nil, closure: @escaping (Error) -> Void) -> TaskAwaiter { return TaskAwaiter(queue: queue ?? Queue.taskAwaiterDefaultOperationQueue, callbackQueue: callbackQueue ?? OperationQueue.main, task: self).catch(closure) } @discardableResult - public func cancelled(in queue: OperationQueue? = nil, callbackQueue: OperationQueue? = nil, closure: @escaping () -> Void) -> TaskAwaiter { + public func cancelled(on queue: OperationQueue? = nil, callbackQueue: OperationQueue? = nil, closure: @escaping () -> Void) -> TaskAwaiter { return TaskAwaiter(queue: queue ?? Queue.taskAwaiterDefaultOperationQueue, callbackQueue: callbackQueue ?? OperationQueue.main, task: self).cancelled(closure) } @discardableResult - public func finally(in queue: OperationQueue? = nil, callbackQueue: OperationQueue? = nil, closure: @escaping () -> Void) -> TaskAwaiter { + public func finally(on queue: OperationQueue? = nil, callbackQueue: OperationQueue? = nil, closure: @escaping () -> Void) -> TaskAwaiter { return TaskAwaiter(queue: queue ?? Queue.taskAwaiterDefaultOperationQueue, callbackQueue: callbackQueue ?? OperationQueue.main, task: self).finally(closure) } @@ -168,12 +168,12 @@ public final class NonFailableTaskAwaiter { extension NonFailableTask { @discardableResult - public func then(in queue: OperationQueue? = nil, callbackQueue: OperationQueue? = nil, closure: @escaping (Value) -> Void) -> NonFailableTaskAwaiter { + public func then(on queue: OperationQueue? = nil, callbackQueue: OperationQueue? = nil, closure: @escaping (Value) -> Void) -> NonFailableTaskAwaiter { return NonFailableTaskAwaiter(queue: queue ?? Queue.taskAwaiterDefaultOperationQueue, callbackQueue: callbackQueue ?? OperationQueue.main, task: self).then(closure) } @discardableResult - public func finally(in queue: OperationQueue? = nil, callbackQueue: OperationQueue? = nil, closure: @escaping () -> Void) -> NonFailableTaskAwaiter { + public func finally(on queue: OperationQueue? = nil, callbackQueue: OperationQueue? = nil, closure: @escaping () -> Void) -> NonFailableTaskAwaiter { return NonFailableTaskAwaiter(queue: queue ?? Queue.taskAwaiterDefaultOperationQueue, callbackQueue: callbackQueue ?? OperationQueue.main, task: self).finally(closure) }