Skip to content
This repository has been archived by the owner on Feb 28, 2020. It is now read-only.

Commit

Permalink
Merge branch 'release/1.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
vmartinelli committed Sep 6, 2015
2 parents 674aa41 + 4316d96 commit f520d75
Show file tree
Hide file tree
Showing 23 changed files with 995 additions and 88 deletions.
2 changes: 1 addition & 1 deletion AlecrimAsyncKit.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|

s.name = "AlecrimAsyncKit"
s.version = "1.1.3"
s.version = "1.2"
s.summary = "Bringing async and await to Swift world with some flavouring."
s.homepage = "https://github.com/Alecrim/AlecrimAsyncKit"

Expand Down
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,24 @@ The three main differences in this case: a `task` parameter is used as parameter
import Foundation
import CloudKit

// Some code running in background.

let database: CKDatabase = ...

do {
let records = try await(database.asyncPerformQuery(query, inZoneWithID: zoneID))

for record in records {
// ...
}
}
catch let error {
// do a nice error handling here
}


// A convenience `CKDatabase` extension.

extension CKDatabase {

public func asyncPerformQuery(query: CKQuery, inZoneWithID zoneID: CKRecordZoneID?) -> Task<[CKRecord]> {
Expand Down Expand Up @@ -255,7 +273,7 @@ If you want to contribute, please feel free to fork the repository and send pull
The main areas the framework needs improvement:

- Correct the README, code and examples for English mistakes;
- Write code documentation;
- Write more and better code documentation;
- Write unit tests;
- Write more conditions and observers;
- Replace some pieces of code with more "elegant" ones.
Expand Down
162 changes: 102 additions & 60 deletions Source/AlecrimAsyncKit.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// BooleanTaskCondition.swift
// AlecrimAsyncKit
//
// Created by Vanderlei Martinelli on 2015-09-06.
// Copyright © 2015 Alecrim. All rights reserved.
//

import Foundation

/// A condition that is satisfied according a boolean value.
public final class BooleanTaskCondition: TaskCondition {

/// Initializes a `BooleanTaskCondition` that will be satisfied if the passed closure returns `true`.
///
/// - parameter valueClosure: The closure that will return `true` if the condition is satisfied, `false` otherwise.
///
/// - returns: An initialized `BooleanTaskCondition` that will be satisfied if the passed closure returns `true`
public init(@autoclosure(escaping) _ valueClosure: () -> Bool) {
super.init() { result in
if valueClosure() {
result(.Satisfied)
}
else {
result(.NotSatisfied)
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,15 @@

import Foundation

/// A condition that will simply wait for a given time interval to be satisfied.
public final class DelayTaskCondition: TaskCondition {

/// Initializes a condition that will wait for a given time interval to be satisfied.
///
/// - parameter timeInterval: The time interval to wait.
/// - parameter tolerance: The tolerance time interval (optional, defaults to 0).
///
/// - returns: A condition that will wait for a given time interval to be satisfied.
public init(timeInterval: NSTimeInterval, tolerance: NSTimeInterval = 0) {
super.init() { result in
let queue = dispatch_queue_create(nil, DISPATCH_QUEUE_SERIAL)
Expand All @@ -29,4 +36,4 @@ public final class DelayTaskCondition: TaskCondition {
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@

import Foundation

/// A simple condition that negates the evaluation of another condition.
public final class NegateTaskCondition: TaskCondition {

/// Initializes a condition that negates the evaluation of another condition.
///
/// - parameter otherCondition: The condition to be negated.
///
/// - returns: A condition that negates the evaluation of another condition.
public init(_ otherCondition: TaskCondition) {
super.init(subconditions: otherCondition.subconditions, dependencyTask: otherCondition.dependencyTaskClosure(), evaluationClosure: otherCondition.evaluationClosure)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,14 @@

import Foundation

/// A simple condition that causes another condition to not run its dependency task.
public final class SilentTaskCondition: TaskCondition {

/// Initializes a condition that causes another condition to not run its dependency task.
///
/// - parameter otherCondition: The condition that `dependencyTask` will not run.
///
/// - returns: A condition that causes another condition to not run its dependency task.
public init(_ otherCondition: TaskCondition) {
super.init(subconditions: otherCondition.subconditions, dependencyTask: nil, evaluationClosure: otherCondition.evaluationClosure)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@

import Foundation


/// A task observer that causes the observed failable task to be cancelled if not finished before a specified time interval.
public final class TimeoutTaskObserver: TaskObserver {

/// Initializes a task observer that causes the observed failable task to be cancelled if not finished before a specified time interval.
///
/// - parameter timeout: The timeout time interval.
///
/// - returns: An observer that causes a failable task to be cancelled if not finished before a specified time interval.
public init(timeout: NSTimeInterval) {
super.init()

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//
// EventStorePermissionTaskCondition.swift
// AlecrimAsyncKit
//
// Created by Vanderlei Martinelli on 2015-09-06.
// Copyright © 2015 Alecrim. All rights reserved.
//

import Foundation
import EventKit

// `EKEventStore` takes a while to initialize, so we use a shared instance.
private let _sharedEventStore = EKEventStore()

/// A condition for verifying access to the user's calendar.
public final class EventStorePermissionTaskCondition: TaskCondition {

private static func asyncRequestAuthorization(entityType: EKEntityType) -> Task<Void> {
return asyncEx(condition: MutuallyExclusiveTaskCondition(.Alert)) { task in
let status = EKEventStore.authorizationStatusForEntityType(entityType)

switch status {
case .NotDetermined:
dispatch_async(dispatch_get_main_queue()) {
_sharedEventStore.requestAccessToEntityType(entityType) { _, error in
if let error = error {
task.finishWithError(error)
}
else {
task.finish()
}
}
}

default:
task.finish()
}
}
}

/// Initializes a condition for verifying access to the user's calendar.
///
/// - parameter entityType: The authorization needed (event or reminder).
///
/// - returns: A condition for verifying access to the user's calendar.
public init(entityType: EKEntityType) {
super.init(dependencyTask: EventStorePermissionTaskCondition.asyncRequestAuthorization(entityType)) { result in
switch EKEventStore.authorizationStatusForEntityType(entityType) {
case .Authorized:
result(.Satisfied)

default:
result(.NotSatisfied)
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
//
// LocationPermissionTaskCondition.swift
// AlecrimAsyncKit
//
// Created by Vanderlei Martinelli on 2015-09-05.
// Copyright © 2015 Alecrim. All rights reserved.
//

#if os(iOS)

import Foundation
import CoreLocation

/// A condition for verifying access to the user's location.
public final class LocationPermissionTaskCondition: TaskCondition {

public enum Usage {
case WhenInUse
case Always
}

private static func asyncRequestAuthorizationIfNeededForUsage(usage: LocationPermissionTaskCondition.Usage) -> Task<Void> {
return asyncEx(condition: MutuallyExclusiveTaskCondition(.Alert)) { task in
/*
Not only do we need to handle the "Not Determined" case, but we also
need to handle the "upgrade" (.WhenInUse -> .Always) case.
*/
switch (CLLocationManager.authorizationStatus(), usage) {
case (.NotDetermined, _), (.AuthorizedWhenInUse, .Always):
let locationManager = LocationManager()
locationManager.didChangeAuthorizationStatusClosure = { status in
task.finish()
}

let key: String

switch usage {
case .WhenInUse:
key = "NSLocationWhenInUseUsageDescription"
dispatch_async(dispatch_get_main_queue()) {
locationManager.requestWhenInUseAuthorization()
}

case .Always:
key = "NSLocationAlwaysUsageDescription"
dispatch_async(dispatch_get_main_queue()) {
locationManager.requestAlwaysAuthorization()
}
}

// This is helpful when developing the app.
assert(NSBundle.mainBundle().objectForInfoDictionaryKey(key) != nil, "Requesting location permission requires the \(key) key in your Info.plist")


default:
task.finish()
}
}
}

/// Initializes a condition for verifying access to the user's location.
///
/// - parameter usage: The needed usage (when app is in use only or always).
///
/// - returns: A condition for verifying access to the user's location.
public init(usage: LocationPermissionTaskCondition.Usage) {
super.init(dependencyTask: LocationPermissionTaskCondition.asyncRequestAuthorizationIfNeededForUsage(usage)) { result in
let enabled = CLLocationManager.locationServicesEnabled()
let actual = CLLocationManager.authorizationStatus()

// There are several factors to consider when evaluating this condition
switch (enabled, usage, actual) {
case (true, _, .AuthorizedAlways):
// The service is enabled, and we have "Always" permission -> condition satisfied.
result(.Satisfied)

case (true, .WhenInUse, .AuthorizedWhenInUse):
// The service is enabled, and we have and need "WhenInUse" permission -> condition satisfied.
result(.Satisfied)

default:
/*
Anything else is an error. Maybe location services are disabled,
or maybe we need "Always" permission but only have "WhenInUse",
or maybe access has been restricted or denied,
or maybe access hasn't been request yet.

The last case would happen if this condition were wrapped in a `SilentCondition`.
*/
result(.NotSatisfied)
}
}
}

}

private final class LocationManager: CLLocationManager, CLLocationManagerDelegate {

private var didChangeAuthorizationStatusClosure: ((CLAuthorizationStatus) -> Void)? = nil

private override init() {
super.init()
self.delegate = self
}

@objc private func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
self.didChangeAuthorizationStatusClosure?(status)

self.delegate = nil
self.didChangeAuthorizationStatusClosure = nil
}

}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// PassLibraryAvailableCondition.swift
// AlecrimAsyncKit
//
// Created by Vanderlei Martinelli on 2015-09-05.
// Copyright © 2015 Alecrim. All rights reserved.
//

#if os(iOS)

import Foundation
import PassKit

/// A condition for verifying that Passbook exists and is accessible.
public final class PassLibraryAvailableCondition: TaskCondition {

/// Initializes a condition for verifying that Passbook exists and is accessible.
///
/// - returns: A condition for verifying that Passbook exists and is accessible.
public init() {
super.init() { result in
if PKPassLibrary.isPassLibraryAvailable() {
result(.Satisfied)
}
else {
result(.NotSatisfied)
}
}
}

}

#endif
Loading

0 comments on commit f520d75

Please sign in to comment.