diff --git a/.gitignore b/.gitignore
index 6d14149ac..d09feee82 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,4 @@
DerivedData
/Extensions/Carthage
/Tests/JS-A+/build
+.swiftpm/
diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
deleted file mode 100644
index 919434a62..000000000
--- a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
diff --git a/PromiseKit.xcodeproj/project.pbxproj b/PromiseKit.xcodeproj/project.pbxproj
index 96dc451f3..24dcc0fee 100644
--- a/PromiseKit.xcodeproj/project.pbxproj
+++ b/PromiseKit.xcodeproj/project.pbxproj
@@ -83,6 +83,8 @@
9E4170F9287D88C900A3B4B5 /* Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4170F8287D88C800A3B4B5 /* Async.swift */; };
9E4170FC287D8DF900A3B4B5 /* AsyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4170FA287D8DBD00A3B4B5 /* AsyncTests.swift */; };
9E66231626FE5A8C00FA25CB /* RaceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E66231526FE5A8C00FA25CB /* RaceTests.m */; };
+ 9EC774272991495C00803027 /* Combine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EC774262991495C00803027 /* Combine.swift */; };
+ 9EC7742A2991498200803027 /* CombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9EC774282991497900803027 /* CombineTests.swift */; };
C013F7382048E3B6006B57B1 /* MockNodeEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = C013F7372048E3B6006B57B1 /* MockNodeEnvironment.swift */; };
C013F73A2049076A006B57B1 /* JSPromise.swift in Sources */ = {isa = PBXBuildFile; fileRef = C013F7392049076A006B57B1 /* JSPromise.swift */; };
C013F73C20494291006B57B1 /* JSAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C013F73B20494291006B57B1 /* JSAdapter.swift */; };
@@ -229,6 +231,8 @@
9E4170F8287D88C800A3B4B5 /* Async.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Async.swift; path = Sources/Async.swift; sourceTree = ""; };
9E4170FA287D8DBD00A3B4B5 /* AsyncTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncTests.swift; sourceTree = ""; };
9E66231526FE5A8C00FA25CB /* RaceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RaceTests.m; sourceTree = ""; };
+ 9EC774262991495C00803027 /* Combine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Combine.swift; path = Sources/Combine.swift; sourceTree = ""; };
+ 9EC774282991497900803027 /* CombineTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombineTests.swift; sourceTree = ""; };
C013F7372048E3B6006B57B1 /* MockNodeEnvironment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MockNodeEnvironment.swift; path = "Tests/JS-A+/MockNodeEnvironment.swift"; sourceTree = ""; };
C013F7392049076A006B57B1 /* JSPromise.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = JSPromise.swift; path = "Tests/JS-A+/JSPromise.swift"; sourceTree = ""; };
C013F73B20494291006B57B1 /* JSAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = JSAdapter.swift; path = "Tests/JS-A+/JSAdapter.swift"; sourceTree = ""; };
@@ -341,6 +345,7 @@
isa = PBXGroup;
children = (
9E4170FA287D8DBD00A3B4B5 /* AsyncTests.swift */,
+ 9EC774282991497900803027 /* CombineTests.swift */,
63CF6D7F203CD19200EC8927 /* ThenableTests.swift */,
632FBBE41F33B338008F8FBB /* CatchableTests.swift */,
635D64081D59635300BC0AF5 /* PromiseTests.swift */,
@@ -440,6 +445,7 @@
isa = PBXGroup;
children = (
9E4170F8287D88C800A3B4B5 /* Async.swift */,
+ 9EC774262991495C00803027 /* Combine.swift */,
636A29191F1C156B001229C2 /* Promise.swift */,
636A29221F1C17A6001229C2 /* Guarantee.swift */,
636A29201F1C1716001229C2 /* Thenable.swift */,
@@ -686,6 +692,7 @@
630A8056203CEF6800D25F23 /* AnyPromiseTests.m in Sources */,
635D64221D59635300BC0AF5 /* ZalgoTests.swift in Sources */,
635D64271D59635300BC0AF5 /* RaceTests.swift in Sources */,
+ 9EC7742A2991498200803027 /* CombineTests.swift in Sources */,
632FBBE51F33B338008F8FBB /* CatchableTests.swift in Sources */,
63CF6D80203CD19200EC8927 /* ThenableTests.swift in Sources */,
635D642B1D59635300BC0AF5 /* StressTests.swift in Sources */,
@@ -773,6 +780,7 @@
63B0AC8B1D595E6300FA21D9 /* join.m in Sources */,
63B0AC891D595E6300FA21D9 /* hang.m in Sources */,
63B0AC831D595E6300FA21D9 /* AnyPromise.swift in Sources */,
+ 9EC774272991495C00803027 /* Combine.swift in Sources */,
63D9B2F120338D5D0075C00B /* Deprecations.swift in Sources */,
63B0AC871D595E6300FA21D9 /* Error.swift in Sources */,
0CC3AF2B1FCF84F7000E98C9 /* hang.swift in Sources */,
diff --git a/Sources/Combine.swift b/Sources/Combine.swift
new file mode 100644
index 000000000..901497eb7
--- /dev/null
+++ b/Sources/Combine.swift
@@ -0,0 +1,29 @@
+#if swift(>=4.1)
+#if canImport(Combine)
+import Combine
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+public extension Guarantee {
+ func future() -> Future {
+ .init { [weak self] promise in
+ self?.done { value in
+ promise(.success(value))
+ }
+ }
+ }
+}
+
+@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
+public extension Promise {
+ func future() -> Future {
+ .init { [weak self] promise in
+ self?.done { value in
+ promise(.success(value))
+ }.catch { error in
+ promise(.failure(error))
+ }
+ }
+ }
+}
+#endif
+#endif
diff --git a/Tests/CorePromise/AsyncTests.swift b/Tests/CorePromise/AsyncTests.swift
index c075aef6b..fa1dd61ac 100644
--- a/Tests/CorePromise/AsyncTests.swift
+++ b/Tests/CorePromise/AsyncTests.swift
@@ -1,5 +1,4 @@
import PromiseKit
-import Dispatch
import XCTest
private enum Error: Swift.Error { case dummy }
diff --git a/Tests/CorePromise/CombineTests.swift b/Tests/CorePromise/CombineTests.swift
new file mode 100644
index 000000000..ea27af45b
--- /dev/null
+++ b/Tests/CorePromise/CombineTests.swift
@@ -0,0 +1,118 @@
+#if swift(>=4.1)
+#if canImport(Combine)
+import Combine
+#endif
+#endif
+import PromiseKit
+import XCTest
+
+private enum Error: Swift.Error { case dummy }
+
+class CombineTests: XCTestCase {
+ private var cancellable: Any?
+
+ override func tearDown() {
+ #if swift(>=4.1)
+ #if canImport(Combine)
+ if #available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) {
+ (cancellable as? AnyCancellable)?.cancel()
+ }
+ #endif
+ #endif
+ }
+
+ func testCombinePromiseValue() {
+ let ex = expectation(description: "")
+ #if swift(>=4.1)
+ #if canImport(Combine)
+ if #available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) {
+ let promise = after(.milliseconds(100)).then(on: nil){ Promise.value(1) }
+ cancellable = promise.future().sink(receiveCompletion: { result in
+ switch result {
+ case .failure:
+ XCTAssert(false)
+ default:
+ XCTAssert(true)
+ }
+ }, receiveValue: {
+ XCTAssertEqual($0, 1)
+ ex.fulfill()
+ })
+ } else {
+ ex.fulfill()
+ }
+ #else
+ ex.fulfill()
+ #endif
+ #else
+ ex.fulfill()
+ #endif
+
+ wait(for: [ex], timeout: 1)
+ }
+
+ func testCombineGuaranteeValue() {
+ let ex = expectation(description: "")
+ #if swift(>=4.1)
+ #if canImport(Combine)
+ if #available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) {
+ let promise = after(.milliseconds(100)).then(on: nil){ Guarantee.value(1) }
+ cancellable = promise.future().sink(receiveCompletion: { result in
+ switch result {
+ case .failure:
+ XCTAssert(false)
+ default:
+ XCTAssert(true)
+ }
+ }, receiveValue: {
+ XCTAssertEqual($0, 1)
+ ex.fulfill()
+ })
+ } else {
+ ex.fulfill()
+ }
+ #else
+ ex.fulfill()
+ #endif
+ #else
+ ex.fulfill()
+ #endif
+
+ wait(for: [ex], timeout: 1)
+ }
+
+ func testCombinePromiseThrow() {
+ let ex = expectation(description: "")
+ #if swift(>=4.1)
+ #if canImport(Combine)
+ if #available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) {
+ let promise = after(.milliseconds(100)).then(on: nil){ Promise(error: Error.dummy) }.then(on: nil){ Promise.value(1) }
+ cancellable = promise.future().sink(receiveCompletion: { result in
+ switch result {
+ case .failure(let error):
+ switch error as? Error {
+ case .dummy:
+ XCTAssert(true)
+ default:
+ XCTAssert(false)
+ }
+ default:
+ XCTAssert(false)
+ }
+ ex.fulfill()
+ }, receiveValue: { _ in
+ XCTAssert(false)
+ })
+ } else {
+ ex.fulfill()
+ }
+ #else
+ ex.fulfill()
+ #endif
+ #else
+ ex.fulfill()
+ #endif
+
+ wait(for: [ex], timeout: 1)
+ }
+}
diff --git a/Tests/CorePromise/XCTestManifests.swift b/Tests/CorePromise/XCTestManifests.swift
index c5e564f96..8785e2c41 100644
--- a/Tests/CorePromise/XCTestManifests.swift
+++ b/Tests/CorePromise/XCTestManifests.swift
@@ -66,6 +66,17 @@ extension CatchableTests {
]
}
+extension CombineTests {
+ // DO NOT MODIFY: This is autogenerated, use:
+ // `swift test --generate-linuxmain`
+ // to regenerate.
+ static let __allTests__CombineTests = [
+ ("testCombineGuaranteeValue", testCombineGuaranteeValue),
+ ("testCombinePromiseThrow", testCombinePromiseThrow),
+ ("testCombinePromiseValue", testCombinePromiseValue),
+ ]
+}
+
extension GuaranteeTests {
// DO NOT MODIFY: This is autogenerated, use:
// `swift test --generate-linuxmain`
@@ -313,6 +324,7 @@ public func __allTests() -> [XCTestCaseEntry] {
testCase(AsyncTests.__allTests__AsyncTests),
testCase(CancellationTests.__allTests__CancellationTests),
testCase(CatchableTests.__allTests__CatchableTests),
+ testCase(CombineTests.__allTests__CombineTests),
testCase(GuaranteeTests.__allTests__GuaranteeTests),
testCase(HangTests.__allTests__HangTests),
testCase(JoinTests.__allTests__JoinTests),