Skip to content

Commit

Permalink
'Cancel' for PromiseKit -- provides the ability to cancel promises an…
Browse files Browse the repository at this point in the history
…d promise chains
  • Loading branch information
dougzilla32 committed Oct 7, 2018
1 parent 1716ee6 commit c6de0ed
Show file tree
Hide file tree
Showing 6 changed files with 222 additions and 49 deletions.
8 changes: 8 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ matrix:
- {osx_image: xcode9.4, env: 'PLAT=iOS SWFT=3.3 DST="OS=11.4,name=iPhone 5s"'}
- {osx_image: xcode9.4, env: 'PLAT=tvOS SWFT=3.3 DST="OS=11.4,name=Apple TV"'}

- {osx_image: xcode10, env: 'PLAT=macOS SWFT=3.4 DST="arch=x86_64"'}
- {osx_image: xcode10, env: 'PLAT=iOS SWFT=3.4 DST="OS=12.0,name=iPhone SE"'}
- {osx_image: xcode10, env: 'PLAT=tvOS SWFT=3.4 DST="OS=12.0,name=Apple TV"'}

- {osx_image: xcode9.2, env: 'PLAT=macOS SWFT=4.0 DST="arch=x86_64"'}
- {osx_image: xcode9.2, env: 'PLAT=iOS SWFT=4.0 DST="OS=11.2,name=iPhone SE"'}
- {osx_image: xcode9.2, env: 'PLAT=tvOS SWFT=4.0 DST="OS=11.2,name=Apple TV"'}
Expand All @@ -25,6 +29,10 @@ matrix:
- {osx_image: xcode9.4, env: 'PLAT=iOS SWFT=4.1 DST="OS=11.4,name=iPhone 5s" TEST=1'}
- {osx_image: xcode9.3, env: 'PLAT=tvOS SWFT=4.1 DST="OS=10.2,name=Apple TV 1080p"'}
- {osx_image: xcode9.4, env: 'PLAT=tvOS SWFT=4.1 DST="OS=11.4,name=Apple TV" TEST=1'}

- {osx_image: xcode10, env: 'PLAT=macOS SWFT=4.2 DST="arch=x86_64"'}
- {osx_image: xcode10, env: 'PLAT=iOS SWFT=4.2 DST="OS=12.0,name=iPhone SE"'}
- {osx_image: xcode10, env: 'PLAT=tvOS SWFT=4.2 DST="OS=12.0,name=Apple TV"'}
cache:
directories:
- Carthage
Expand Down
3 changes: 2 additions & 1 deletion Cartfile
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
github "mxcl/PromiseKit" ~> 6.0
#github "mxcl/PromiseKit" ~> 6.0
github "dougzilla32/PromiseKit" "CoreCancel"
2 changes: 1 addition & 1 deletion Cartfile.resolved
Original file line number Diff line number Diff line change
@@ -1 +1 @@
github "mxcl/PromiseKit" "6.3.3"
github "dougzilla32/PromiseKit" "087b3cf470890ff9ea841212e2f3e285fecf3988"
108 changes: 82 additions & 26 deletions Sources/MKDirections+Promise.swift
Original file line number Diff line number Diff line change
@@ -1,26 +1,82 @@
import MapKit
#if !PMKCocoaPods
import PromiseKit
#endif

/**
To import the `MKDirections` category:

use_frameworks!
pod "PromiseKit/MapKit"

And then in your sources:

import PromiseKit
*/
extension MKDirections {
/// Begins calculating the requested route information asynchronously.
public func calculate() -> Promise<MKDirectionsResponse> {
return Promise { calculate(completionHandler: $0.resolve) }
}

/// Begins calculating the requested travel-time information asynchronously.
public func calculateETA() -> Promise<MKETAResponse> {
return Promise { calculateETA(completionHandler: $0.resolve) }
}
}
import MapKit
#if !PMKCocoaPods
import PromiseKit
#endif

/**
To import the `MKDirections` category:

use_frameworks!
pod "PromiseKit/MapKit"

And then in your sources:

import PromiseKit
*/
extension MKDirections {
#if swift(>=4.2)
/// Begins calculating the requested route information asynchronously.
public func calculate() -> Promise<Response> {
return Promise<Response>(cancellableTask: MKDirectionsTask(self)) { calculate(completionHandler: $0.resolve) }
}

/// Begins calculating the requested travel-time information asynchronously.
public func calculateETA() -> Promise<ETAResponse> {
return Promise<ETAResponse>(cancellableTask: MKDirectionsTask(self)) { calculateETA(completionHandler: $0.resolve) }
}
#else
/// Begins calculating the requested route information asynchronously.
public func calculate() -> Promise<MKDirectionsResponse> {
return Promise<MKDirectionsResponse>(cancellableTask: MKDirectionsTask(self)) { calculate(completionHandler: $0.resolve) }
}

/// Begins calculating the requested travel-time information asynchronously.
public func calculateETA() -> Promise<MKETAResponse> {
return Promise<MKETAResponse>(cancellableTask: MKDirectionsTask(self)) { calculateETA(completionHandler: $0.resolve) }
}
#endif
}

private class MKDirectionsTask: CancellableTask {
let directions: MKDirections
var cancelAttempted = false

init(_ directions: MKDirections) {
self.directions = directions
}

func cancel() {
directions.cancel()
cancelAttempted = true
}

var isCancelled: Bool {
return cancelAttempted && !directions.isCalculating
}
}

//////////////////////////////////////////////////////////// Cancellable wrappers

extension MKDirections {
#if swift(>=4.2)
/// Begins calculating the requested route information asynchronously.
public func cancellableCalculate() -> CancellablePromise<Response> {
return cancellable(calculate())
}

/// Begins calculating the requested travel-time information asynchronously.
public func cancellableCalculateETA() -> CancellablePromise<ETAResponse> {
return cancellable(calculateETA())
}
#else
/// Begins calculating the requested route information asynchronously.
public func cancellableCalculate() -> CancellablePromise<MKDirectionsResponse> {
return cancellable(calculate())
}

/// Begins calculating the requested travel-time information asynchronously.
public func cancellableCalculateETA() -> CancellablePromise<MKETAResponse> {
return cancellable(calculateETA())
}
#endif
}
83 changes: 62 additions & 21 deletions Sources/MKMapSnapshotter+Promise.swift
Original file line number Diff line number Diff line change
@@ -1,21 +1,62 @@
import MapKit
#if !PMKCocoaPods
import PromiseKit
#endif

/**
To import the `MKMapSnapshotter` category:

use_frameworks!
pod "PromiseKit/MapKit"

And then in your sources:

import PromiseKit
*/
extension MKMapSnapshotter {
/// Starts generating the snapshot using the options set in this object.
public func start() -> Promise<MKMapSnapshot> {
return Promise { start(completionHandler: $0.resolve) }
}
}
import MapKit
#if !PMKCocoaPods
import PromiseKit
#endif

/**
To import the `MKMapSnapshotter` category:

use_frameworks!
pod "PromiseKit/MapKit"

And then in your sources:

import PromiseKit
*/
extension MKMapSnapshotter {
#if swift(>=4.2)
/// Starts generating the snapshot using the options set in this object.
public func start() -> Promise<Snapshot> {
return Promise<Snapshot>(cancellableTask: MKMapSnapshotterTask(self)) { start(completionHandler: $0.resolve) }
}
#else
/// Starts generating the snapshot using the options set in this object.
public func start() -> Promise<MKMapSnapshot> {
return Promise<MKMapSnapshot>(cancellableTask: MKMapSnapshotterTask(self)) { start(completionHandler: $0.resolve) }
}
#endif
}

private class MKMapSnapshotterTask: CancellableTask {
let snapshotter: MKMapSnapshotter
var cancelAttempted = false

init(_ snapshotter: MKMapSnapshotter) {
self.snapshotter = snapshotter
}

func cancel() {
snapshotter.cancel()
cancelAttempted = true
}

var isCancelled: Bool {
return cancelAttempted && !snapshotter.isLoading
}
}

//////////////////////////////////////////////////////////// Cancellable wrapper

extension MKMapSnapshotter {
#if swift(>=4.2)
/// Starts generating the snapshot using the options set in this object.
public func cancellableStart() -> CancellablePromise<Snapshot> {
return cancellable(start())
}
#else
/// Starts generating the snapshot using the options set in this object.
public func cancellableStart() -> CancellablePromise<MKMapSnapshot> {
return cancellable(start())
}
#endif
}
67 changes: 67 additions & 0 deletions Tests/TestMapKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,70 @@ class Test_MKSnapshotter_Swift: XCTestCase {
waitForExpectations(timeout: 1, handler: nil)
}
}

//////////////////////////////////////////////////////////// Cancellation

extension Test_MKDirections_Swift {
func test_cancel_directions_response() {
let ex = expectation(description: "")

class MockDirections: MKDirections {
override func calculate(completionHandler: @escaping MKDirectionsHandler) {
completionHandler(MKDirectionsResponse(), nil)
}
}

let rq = MKDirectionsRequest()
let directions = MockDirections(request: rq)

directions.cancellableCalculate().done { _ in
XCTFail()
}.catch(policy: .allErrors) {
$0.isCancelled ? ex.fulfill() : XCTFail()
}.cancel()

waitForExpectations(timeout: 1, handler: nil)
}


func test_cancel_ETA_response() {
let ex = expectation(description: "")

class MockDirections: MKDirections {
override func calculateETA(completionHandler: @escaping MKETAHandler) {
completionHandler(MKETAResponse(), nil)
}
}

let rq = MKDirectionsRequest()
MockDirections(request: rq).cancellableCalculateETA().done { _ in
XCTFail()
}.catch(policy: .allErrors) {
$0.isCancelled ? ex.fulfill() : XCTFail()
}.cancel()

waitForExpectations(timeout: 1, handler: nil)
}

}

extension Test_MKSnapshotter_Swift {
func test_cancel() {
let ex = expectation(description: "")

class MockSnapshotter: MKMapSnapshotter {
override func start(completionHandler: @escaping MKMapSnapshotCompletionHandler) {
completionHandler(MKMapSnapshot(), nil)
}
}

let snapshotter = MockSnapshotter()
snapshotter.cancellableStart().done { _ in
XCTFail()
}.catch(policy: .allErrors) {
$0.isCancelled ? ex.fulfill() : XCTFail()
}.cancel()

waitForExpectations(timeout: 1, handler: nil)
}
}

0 comments on commit c6de0ed

Please sign in to comment.