diff --git a/Cartfile b/Cartfile index 2bfea98..cdd4145 100644 --- a/Cartfile +++ b/Cartfile @@ -1 +1,4 @@ -github "mxcl/PromiseKit" ~> 6.0 +#github "mxcl/PromiseKit" ~> 6.0 +github "dougzilla32/PromiseKit" "PMKCancel" +#github "PromiseKit/Cancel" ~> 1.0 +github "dougzilla32/Cancel" ~> 1.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index a1be206..e1cadd5 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1 +1,2 @@ -github "mxcl/PromiseKit" "6.3.3" +github "dougzilla32/Cancel" "1.0.0" +github "dougzilla32/PromiseKit" "a0217bd7b69af68237dcdeee0197e63259b9d445" diff --git a/PMKMapKit.xcodeproj/project.pbxproj b/PMKMapKit.xcodeproj/project.pbxproj index 0e1b3d9..62865f6 100644 --- a/PMKMapKit.xcodeproj/project.pbxproj +++ b/PMKMapKit.xcodeproj/project.pbxproj @@ -205,6 +205,7 @@ ); inputPaths = ( PromiseKit, + PMKCancel, ); name = "Embed Carthage Frameworks"; outputPaths = ( diff --git a/Sources/MKDirections+Promise.swift b/Sources/MKDirections+Promise.swift index 04b3ff9..8cd2961 100644 --- a/Sources/MKDirections+Promise.swift +++ b/Sources/MKDirections+Promise.swift @@ -1,5 +1,6 @@ import MapKit #if !PMKCocoaPods +import PMKCancel import PromiseKit #endif @@ -24,3 +25,35 @@ extension MKDirections { return Promise { calculateETA(completionHandler: $0.resolve) } } } + +//////////////////////////////////////////////////////////// Cancellation + +fileprivate 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 + } +} + +extension MKDirections { + /// Begins calculating the requested route information asynchronously. + public func calculateCC() -> CancellablePromise { + return CancellablePromise(task: MKDirectionsTask(self)) { calculate(completionHandler: $0.resolve) } + } + + /// Begins calculating the requested travel-time information asynchronously. + public func calculateETACC() -> CancellablePromise { + return CancellablePromise(task: MKDirectionsTask(self)) { calculateETA(completionHandler: $0.resolve) } + } +} diff --git a/Sources/MKMapSnapshotter+Promise.swift b/Sources/MKMapSnapshotter+Promise.swift index 45d590a..d874195 100644 --- a/Sources/MKMapSnapshotter+Promise.swift +++ b/Sources/MKMapSnapshotter+Promise.swift @@ -1,5 +1,6 @@ import MapKit #if !PMKCocoaPods +import PMKCancel import PromiseKit #endif @@ -19,3 +20,30 @@ extension MKMapSnapshotter { return Promise { start(completionHandler: $0.resolve) } } } + +//////////////////////////////////////////////////////////// Cancellation + +fileprivate 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 + } +} + +extension MKMapSnapshotter { + /// Starts generating the snapshot using the options set in this object. + public func startCC() -> CancellablePromise { + return CancellablePromise(task: MKMapSnapshotterTask(self)) { start(completionHandler: $0.resolve) } + } +} diff --git a/Tests/TestMapKit.swift b/Tests/TestMapKit.swift index 41cbdd6..84651ae 100644 --- a/Tests/TestMapKit.swift +++ b/Tests/TestMapKit.swift @@ -1,4 +1,5 @@ import PromiseKit +import PMKCancel import PMKMapKit import MapKit import XCTest @@ -61,3 +62,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.calculateCC().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).calculateETACC().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.startCC().done { _ in + XCTFail() + }.catch(policy: .allErrors) { + $0.isCancelled ? ex.fulfill() : XCTFail() + }.cancel() + + waitForExpectations(timeout: 1, handler: nil) + } +}