Compare commits

...

57 Commits

Author SHA1 Message Date
Max Howell
46451dde7d
Add new test 2020-10-04 19:48:23 -04:00
Max Howell
ef9b9ee63d
Fix Swift 5.x warnings 2020-10-04 12:01:43 -04:00
Max Howell
59997dd6d9
Fix NSError().isCancelled crashing 2020-10-04 12:00:25 -04:00
Max Howell
6c954ab704
Updates for Xcode 12 2020-10-04 12:00:24 -04:00
Max Howell
aea48ea185
Update PMKStoreKit to 3.1.4 2020-08-31 09:09:37 -04:00
Max Howell
f6b15b7626
Merge pull request #1154 from SixFiveSoftware/tech/remove_unused_variables
Remove unused index var in when.swift
2020-06-24 10:22:33 -04:00
BJ Miller
817ea8c3f4 Remove unused index var in when.swift 2020-06-19 09:51:43 -04:00
Bruno Virlet
953665d28b Minor correction to chaining sequence example 2020-05-24 18:22:01 -04:00
Luka Bratos
f9cc0d67dc Fix typo 2020-03-14 09:21:50 -04:00
Cong Liu
f95e7b2614 Fixed a typo
"excecutes" should be "executes".
2020-02-24 09:44:41 -05:00
Max Howell
f14f16cc26
Correct Travis labels 2020-01-16 10:29:09 -05:00
Max Howell
80053b1db5
Update PMKHomeKit to 1.1.0
Also syncs other submodules, but they have no source code changes.
2020-01-16 10:29:09 -05:00
Max Howell
34f74c1b8f
Merge pull request #1121 from RomanPodymov/master
More map by keyPath
2020-01-15 17:01:33 -05:00
Roman Podymov
87a0b0e30b
Tests for the functions that are using KeyPath require !swift(>=5.2) (Thenable) 2020-01-14 22:03:22 +01:00
Roman Podymov
98e3f1cd28
Tests for the functions that are using KeyPath require !swift(>=5.2) (Guarantee) 2020-01-14 22:02:07 +01:00
Roman Podymov
8fd461350f
Functions that are using KeyPath require !swift(>=5.2) (Thenable) 2020-01-14 22:01:01 +01:00
Roman Podymov
6652ad3d07
Functions that are using KeyPath require !swift(>=5.2) (Guarantee) 2020-01-14 21:59:42 +01:00
Roman Podymov
9ea7d3daed
Fixed tests ordering 2020-01-08 22:26:55 +01:00
Roman Podymov
bf293b7395
New items in GuaranteeTests and ThenableTests 2020-01-08 21:17:06 +01:00
Roman Podymov
15a73f95cd
Tests for new mapping functions 2020-01-08 21:15:05 +01:00
Roman Podymov
35379bb8a0
struct Person and tests for new mapping functions 2020-01-08 21:12:41 +01:00
Roman Podymov
5fb0714f47
New compactMap, mapValues, compactMapValues, filterValues 2020-01-08 21:10:42 +01:00
Roman Podymov
10c9e75f81
New mapValues, compactMapValues and filterValues 2020-01-08 21:07:59 +01:00
Roman Podymov
f8a593a320 Map by keyPath (#1118)
* Added func map<U>(_ keyPath: KeyPath<T, U>)

* Added swift version

* Added func map<U>(_ keyPath: KeyPath<T, U>)

* testMap

* testMap for Guarantee

* Added missing __allTests__GuaranteeTests

* Added all missing tests

* Removed #if swift

* Tests ordering

* Fixed tests
2020-01-07 17:02:16 +00:00
Max Howell
8b97eb2599
https://tidelift.com/security 2019-12-05 16:28:31 -05:00
Max Howell
586da3519a
Merge pull request #1113 from jfahrenkrug/clarify_faq_about_when_promises_start
Documentation: Clarify when a promise "starts" in the FAQ
2019-11-15 16:18:20 -05:00
Johannes Fahrenkrug
5e2e7e3a75
Documentation: Clarify when promise body gets executed 2019-11-15 11:57:38 -05:00
Johannes Fahrenkrug
8fbf6985b4
Documentation: Clarify when a promise "starts" in the FAQ
Provide a simpler exlanation and examples about when promises "start".
Make it clear that the promise's body executes after the promise is created,
without the need to call `then` or `done` on it. Also clarify that async
tasks that a being kicked off from a promise's body behave the same way
regardless of whether PromiseKit is being used or not.
2019-11-15 11:39:21 -05:00
Max Howell
e135aab767
Merge pull request #1112 from emrcftci/master
refactor(*): Change empty tuples to `Void`
2019-11-13 09:58:38 -05:00
emrcftci
c864814c1d refactor(*): Change empty tuples to Void 2019-11-13 08:54:47 +03:00
Max Howell
e36de47eac
Merge pull request #1097 from mxcl/swift-5.1-linux
Swift 5.1 release travis linux
2019-10-18 15:09:57 -04:00
Max Howell
f7e53175f2
Swift 5.1 release travis linux 2019-09-25 13:26:24 -04:00
Roman Podymov
10a10ed8bd Promise.value and Guarantee.value when T == Void (#1096)
* Promise.value when T == Void

* Guarantee.value where T == Void

* Replace class with static because Promise is final

* Optimised Guarantee.value where Value == Void

* Add comment

* Implementation with Void()

* Tests for Promise<Void>.value

* Tests for Guarantee<Void>.value

* Added tests for Promise<Void> and Guarantee<Void>
2019-09-25 13:24:39 -04:00
Max Howell
08fb076071
Update CommonPatterns.md 2019-09-04 14:01:49 -04:00
Max Howell
4d8d1287d2
Merge pull request #1090 from mxcl/xcode10.3
[ci] Xcode 10.2 -> 10.3
2019-08-23 10:04:30 -04:00
Max Howell
2136f01c21
Tidelift can ignore node 2019-08-23 08:58:05 -04:00
Max Howell
2305c38634
Only enable errors as warnings in Travis 2019-08-22 09:42:56 -04:00
Max Howell
b8c2dff1a7
Fix git submodules
Cannot push to CoreBluetooth since I archived it
2019-08-21 13:21:58 -04:00
Max Howell
346dd2c98d
[ci] Xcode 10.2 -> 10.3 2019-08-21 13:10:54 -04:00
Max Howell
bee0497eb9
Merge pull request #1086 from Skoti/feature/correcting_descriptions
correcting and aligning documentation regarding `resolve` and `fulfil…
2019-08-13 07:27:29 -04:00
Skoti
93a098f33b correcting and aligning documentation regarding resolve and fulfill usages 2019-08-10 16:56:26 +02:00
Max Howell
8818af27ba
Update Foundation extension with fix (ee06d95) 2019-07-24 10:50:13 -04:00
Max Howell
951993371a
Update gitsubmodules to LICENSE added extensions 2019-07-24 10:39:13 -04:00
Max Howell
8e5f5d0945
Merge pull request #1079 from mxcl/xcode11
Xcode 11 / Swift 5.1 support
2019-07-09 17:22:17 -04:00
Max Howell
c394b5231e
Xcode 11 / Swift 5.1 support 2019-07-09 15:07:50 -04:00
Max Howell
2fdb8c64a8
Merge pull request #1078 from RomanPodymov/master
Add AnyPromise.wait
2019-06-28 10:23:21 -04:00
Roman Podymov
6303e93089
Update AnyPromiseTests.m 2019-06-28 12:58:15 +02:00
Roman Podymov
9cd56c237d
Update AnyPromiseTests.m 2019-06-28 12:33:21 +02:00
Roman Podymov
ee961e1f59
Update AnyPromise.swift 2019-06-28 12:03:32 +02:00
Roman Podymov
0452552590
Update AnyPromise.m 2019-06-28 12:02:03 +02:00
Roman Podymov
fbe6decc6e
Update AnyPromise.h 2019-06-28 12:00:52 +02:00
Max Howell
a2cc02d3ac
Create FUNDING.yml 2019-06-18 10:32:14 -04:00
Max Howell
cfea84ff08
Merge pull request #1074 from Igor-Palaguta/GuaranteeSequenceOperators
Add guarantee sequence utils
2019-06-18 07:57:08 -04:00
Igor Palaguta
7d3028e0ee update test manifest 2019-06-17 23:36:58 +02:00
Igor Palaguta
9f9f1f9e89 Add guarantee sequence utils 2019-06-17 17:45:55 +02:00
Max Howell
bed5229ba7 Remove Xcode 11 warnings 2019-06-11 12:49:24 -04:00
Max Howell
1c296a8637
[travis] Use CocoaPods ~> 1.7.0; update submodules 2019-06-11 10:40:35 -04:00
49 changed files with 821 additions and 119 deletions

2
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,2 @@
tidelift: "cocoapods/PromiseKit"
patreon: "mxcl"

View File

@ -19,7 +19,7 @@ Pod::Spec.new do |s|
s.default_subspecs = 'CorePromise', 'UIKit', 'Foundation'
s.requires_arc = true
s.swift_versions = ['3.1', '3.2', '3.3', '3.4', '4.0', '4.1', '4.2', '4.3', '4.4', '5.0']
s.swift_versions = ['3.1', '3.2', '3.3', '3.4', '4.0', '4.1', '4.2', '4.3', '4.4', '5.0', '5.1']
# CocoaPods requires us to specify the root deployment targets
# even though for us it is nonsense. Our root spec has no

1
.tidelift.yml Normal file
View File

@ -0,0 +1 @@
extra_ignore_directories: [ Tests ]

View File

@ -1,5 +1,5 @@
os: osx
osx_image: xcode10.2
osx_image: xcode10.3
language: swift
branches:
@ -32,7 +32,9 @@ jobs:
- &carthage
stage: carthage
osx_image: xcode9
before_script: sed -i '' "s/SWIFT_TREAT_WARNINGS_AS_ERRORS = NO;/SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;/" *.xcodeproj/project.pbxproj
before_script: |
sed -i '' "s/SWIFT_TREAT_WARNINGS_AS_ERRORS = NO;/SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;/" *.xcodeproj/project.pbxproj
sed -i '' "s/GCC_TREAT_WARNINGS_AS_ERRORS = NO;/GCC_TREAT_WARNINGS_AS_ERRORS = YES;/" *.xcodeproj/project.pbxproj
script: carthage build --no-skip-current --configuration Release
name: Swift 4.0.0 / Xcode 9.0
- <<: *carthage
@ -51,8 +53,11 @@ jobs:
osx_image: xcode10.1
name: Swift 4.1.50 / Xcode 10.1
- <<: *carthage
osx_image: xcode10.2
name: Swift 4.1.51 / Xcode 10.2
osx_image: xcode10.3
name: Swift 4.1.51 / Xcode 10.3
- <<: *carthage
osx_image: xcode11
name: Swift 4.1.52 / Xcode 11
- &pod
stage: lint
@ -60,7 +65,7 @@ jobs:
env: SWIFT=3.1
cache: cocoapods
before_install: mv .github/PromiseKit.podspec .
install: gem install cocoapods --pre -v 1.7.0.beta.3
install: gem install cocoapods -v '~> 1.7.0'
script: pod lib lint --subspec=PromiseKit/CorePromise --fail-fast --swift-version=$SWIFT
- <<: *pod
osx_image: xcode9.2
@ -81,11 +86,14 @@ jobs:
osx_image: xcode10.1
env: SWIFT=4.2
- <<: *pod
osx_image: xcode10.2
osx_image: xcode10.3
env: SWIFT=4.3
- <<: *pod
osx_image: xcode10.2
osx_image: xcode10.3
env: SWIFT=5.0
- <<: *pod
osx_image: xcode11
env: SWIFT=5.1
- &linux
stage: swiftpm
@ -124,6 +132,9 @@ jobs:
env: SWIFT_BUILD_VERSION=5 SWIFT_VERSION=5.0
name: 'Linux / Swift 5.0 / +Tests'
script: swift test -Xswiftc -swift-version -Xswiftc $SWIFT_BUILD_VERSION
- <<: *linux
env: SWIFT_BUILD_VERSION=5 SWIFT_VERSION=5.1
name: Linux / Swift 5.1
- &test
stage: test
@ -161,12 +172,15 @@ jobs:
osx_image: xcode10.1
name: 'macOS / swift-tools-version: 4.2 / Swift 4.2.1'
- <<: *swiftpm
osx_image: xcode10.2
osx_image: xcode10.3
name: 'macOS / swift-tools-version: 5.0 / Swift 5.0.0'
- <<: *swiftpm
osx_image: xcode11
name: 'macOS / swift-tools-version: 5.0 / Swift 5.1.0'
- name: '`pod trunk push`'
stage: deploy
install: gem install cocoapods --pre -v 1.7.0.beta.3
install: gem install cocoapods -v '~> 1.7.0'
before_script: |
mv .github/PromiseKit.podspec .
sed -i '' "s/s.version = '0.0.1'/s.version = '$TRAVIS_TAG'/g" PromiseKit.podspec

View File

@ -173,7 +173,7 @@ When you have a series of tasks to perform on an array of data:
```swift
// fade all visible table cells one by one in a “cascading” effect
let fade = Guarantee()
var fade = Guarantee()
for cell in tableView.visibleCells {
fade = fade.then {
UIView.animate(.promise, duration: 0.1) {
@ -186,12 +186,14 @@ fade.done {
}
```
Or if you have an array of promises:
Or if you have an array of closures that return promises:
```swift
var foo = Promise()
for nextPromise in arrayOfPromises {
foo = foo.then { nextPromise }
for nextPromise in arrayOfClosuresThatReturnPromises {
foo = foo.then(nextPromise)
// ^^ you rarely would want an array of promises instead, since then
// they have all already started, you may as well use `when()`
}
foo.done {
// finish

View File

@ -256,25 +256,35 @@ You want `recover`.
## When do promises “start”?
Often people are confused about when Promises “start”. Is it immediately? Is it
later? Is it when you call then?
The answer is: promises do not choose when the underlying task they represent
starts. That is up to that task. For example here is the code for a simple
promise that wraps Alamofire:
later? Is it when you call `then`?
The answer is: The promise **body** executes during initialization of the promise, on the current thread.
As an example, `"Executing the promise body"` will be printed to the console right after the promise is created,
without having to call `then` on the promise.
```swift
func foo() -> Promise<Any>
return Promise { seal in
Alamofire.request(rq).responseJSON { rsp in
seal.resolve(rsp.value, rsp.error)
}
let testPromise = Promise<Bool> {
print("Executing the promise body.")
return $0.fulfill(true)
}
```
But what about asynchronous tasks that you create in your promise's body? They behave the same way as they would
without using PromiseKit. Here's a simple example:
```swift
let testPromise = Promise<Bool> { seal in
print("Executing the promise body.")
DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) {
print("Executing asyncAfter.")
return seal.fulfill(true)
}
}
```
Who chooses when this promise starts? The answer is: Alamofire does, and in this
case, it “starts” immediately when `foo()` is called.
The message `"Executing the promise body."` is being logged right away, but the message `"Executing asyncAfter."`
is only logged three seconds later. In this case `DispatchQueue` is responsible for deciding when to execute
the task you pass to it, PromiseKit has nothing to do with it.
## What is a good way to use Firebase with PromiseKit

@ -1 +1 @@
Subproject commit 6e6d184768c0d383e5ca8d7a1ff9710351e17dfd
Subproject commit 911b257783efc0aa705481d556c8ecdfcdc055a9

@ -1 +1 @@
Subproject commit 83ac281446b90a76357085e79616466cfc71a375
Subproject commit 6c01f5719ff08bbbf81d42b7e20c5188cab4a81d

@ -1 +1 @@
Subproject commit 675857283337f0c83dae6e0abeeebfbcbd84f6c5
Subproject commit c694fa0628a96951c6e2db113dc4eb6dd4e47ebc

@ -1 +1 @@
Subproject commit 7c6c6692fec51834128a7a8eb0f6a15bd93bfcf3
Subproject commit a41a97d3d3aa6efb575d0f42f3b8aeec5a756ead

@ -1 +1 @@
Subproject commit 8492e28b521930227523b8a1b00e6f9134a9e6f5
Subproject commit e4317d5279db7ecfc5bc565e1dd88fcabc02c352

@ -1 +1 @@
Subproject commit a14d7a64cccbf777c0e4bd1de212a56c7ccddba6
Subproject commit fbc9941219c9bbdfe6d664ca6b8d10b5e2f8fb70

@ -1 +1 @@
Subproject commit 06514c39bf57e2a96a501948940ddb43f6bdd708
Subproject commit 9586f3c06c4934e776c2b96c128f332a168c2c91

@ -1 +1 @@
Subproject commit f3f3d8525e9ea9fbc19ac269c80413a5454e9a8e
Subproject commit c4ea4dba6e0516986b3cb1d93c86f6991ce46861

@ -1 +1 @@
Subproject commit cf9095526ba0efc0903626ffab3e26c71cc11dec
Subproject commit 1f90d67538047087428d82550976c2e43aba0961

@ -1 +1 @@
Subproject commit e5c5d8471da5b28ad436b3a80fac2aea1f9785eb
Subproject commit 1a276e598dac59489ed904887e0740fa75e571e0

@ -1 +1 @@
Subproject commit b7ad74638b2831b8488f2ccfae2aebe1f7b71f21
Subproject commit b185822cb1fcf5297347498b783b4e21b2e55282

@ -1 +1 @@
Subproject commit 4372659d7704b1d3c395511451e014ee44a12271
Subproject commit 668abe78a52a23bd276c8924dba91592ac0ca45a

@ -1 +1 @@
Subproject commit 4199cb79e9af1f8741250ea53e1fbb5c35432fad
Subproject commit d1d4ebdd6ceac78d734e2ae27e8ab429906d6929

@ -1 +1 @@
Subproject commit ae1a282c3b29bc1115a7089437e2e75f2f74d222
Subproject commit 2a93ce737502e13a3eaedc444b9ecb5abb28ec79

@ -1 +1 @@
Subproject commit a20dddd6a8a36534e123edc251adef5b3de16989
Subproject commit 48f801454b01c69a1553873cb1d95e90ae2ec4cc

@ -1 +1 @@
Subproject commit c034f528da3309fa5f8b3f4a746ab100d6468389
Subproject commit b22f187b6b3f82f102aa309b256bf24570a73d1f

@ -1 +1 @@
Subproject commit 95b086ce353be7258ff5bcf8628cbe06186346fc
Subproject commit 378912a47a206183b931d2008e0f396e9fc390e8

@ -1 +1 @@
Subproject commit 6af1143636fd1f50afe533ead4f7a863d91c11ba
Subproject commit e26f6a55921ea671855093754686991b08ef97cc

@ -1 +1 @@
Subproject commit f39adf82d349e175bddd99b748146d361b856328
Subproject commit 6b009f906fc489346a73759b5668b263a320c51c

@ -1 +1 @@
Subproject commit a7a236cf81e8397aab37780af8d6624ab31fea06
Subproject commit baf104ba29ff94d9d41e90e8ec557a56b87fecd4

@ -1 +1 @@
Subproject commit bdc99552c392c04849f06b77b2afdf0df5cb5a50
Subproject commit b5b3fca958a7296f71313b6c172584bc91d6da8b

View File

@ -620,36 +620,35 @@
6399A3721D595D9100D65233 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0930;
LastUpgradeCheck = 1200;
TargetAttributes = {
630019011D596292003B4E30 = {
LastSwiftMigration = 0920;
LastSwiftMigration = 1100;
};
631411321D59795700E24B9E = {
LastSwiftMigration = 0920;
LastSwiftMigration = 1100;
};
6317518B1D59766500A9DDDC = {
LastSwiftMigration = 0920;
LastSwiftMigration = 1100;
};
633027E0203CC0060037E136 = {
LastSwiftMigration = 0920;
LastSwiftMigration = 1100;
};
63B0AC561D595E1B00FA21D9 = {
CreatedOnToolsVersion = 8.0;
LastSwiftMigration = 0920;
LastSwiftMigration = 1100;
ProvisioningStyle = Automatic;
};
C0244E4E2047A6CB00ACB4AC = {
LastSwiftMigration = 0920;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 6399A3751D595D9100D65233 /* Build configuration list for PBXProject "PromiseKit" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
Base,
);
@ -816,12 +815,12 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.12;
SWIFT_INSTALL_OBJC_HEADER = NO;
TVOS_DEPLOYMENT_TARGET = 10.0;
TVOS_DEPLOYMENT_TARGET = 12.0;
};
name = Debug;
};
@ -829,12 +828,12 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
MACOSX_DEPLOYMENT_TARGET = 10.12;
SWIFT_INSTALL_OBJC_HEADER = NO;
TVOS_DEPLOYMENT_TARGET = 10.0;
TVOS_DEPLOYMENT_TARGET = 12.0;
};
name = Release;
};
@ -908,6 +907,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@ -925,6 +925,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@ -940,7 +941,6 @@
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1";
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
@ -948,7 +948,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_DYLIB_INSTALL_NAME = "@rpath";
MACOSX_DEPLOYMENT_TARGET = 10.10;
ONLY_ACTIVE_ARCH = YES;
@ -959,7 +959,7 @@
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 4.0;
TVOS_DEPLOYMENT_TARGET = 9.0;
TVOS_DEPLOYMENT_TARGET = 12.0;
VERSIONING_SYSTEM = "apple-generic";
WATCHOS_DEPLOYMENT_TARGET = 2.0;
};
@ -969,6 +969,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@ -986,6 +987,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
@ -999,7 +1001,6 @@
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_NO_COMMON_BLOCKS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES;
@ -1007,7 +1008,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist";
IPHONEOS_DEPLOYMENT_TARGET = 8.0;
IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_DYLIB_INSTALL_NAME = "@rpath";
MACOSX_DEPLOYMENT_TARGET = 10.10;
PRODUCT_BUNDLE_IDENTIFIER = org.promisekit;
@ -1016,7 +1017,7 @@
SUPPORTED_PLATFORMS = "macosx appletvsimulator appletvos watchsimulator iphonesimulator watchos iphoneos";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 4.0;
TVOS_DEPLOYMENT_TARGET = 9.0;
TVOS_DEPLOYMENT_TARGET = 12.0;
VERSIONING_SYSTEM = "apple-generic";
WATCHOS_DEPLOYMENT_TARGET = 2.0;
};
@ -1050,6 +1051,7 @@
ENABLE_TESTABILITY = YES;
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
@ -1104,6 +1106,7 @@
DYLIB_INSTALL_NAME_BASE = "@rpath";
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
GCC_TREAT_WARNINGS_AS_ERRORS = NO;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0930"
LastUpgradeVersion = "1200"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@ -26,9 +26,9 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES"
shouldUseLaunchSchemeArgsEnv = "YES">
onlyGenerateCoverageForSpecifiedTargets = "YES">
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
@ -90,17 +90,6 @@
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "63B0AC561D595E1B00FA21D9"
BuildableName = "PromiseKit.framework"
BlueprintName = "PromiseKit"
ReferencedContainer = "container:PromiseKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
@ -121,8 +110,6 @@
ReferencedContainer = "container:PromiseKit.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"

View File

@ -188,6 +188,10 @@ became true, but nowadays it isnt really necessary.
Please check our [Troubleshooting Guide](Documentation/Troubleshooting.md), and
if after that you still have a question, ask at our [Gitter chat channel] or on [our bug tracker].
## Security & Vulnerability Reporting or Disclosure
https://tidelift.com/security
[badge-pod]: https://img.shields.io/cocoapods/v/PromiseKit.svg?label=version
[badge-pms]: https://img.shields.io/badge/supports-CocoaPods%20%7C%20Carthage%20%7C%20Accio%20%7C%20SwiftPM-green.svg

View File

@ -1,6 +1,6 @@
#import <Foundation/Foundation.h>
#import <dispatch/dispatch.h>
#import "fwd.h"
#import <PromiseKit/fwd.h>
/// INTERNAL DO NOT USE
@class __AnyPromise;
@ -71,7 +71,7 @@ typedef void (^PMKResolver)(id __nullable) NS_REFINED_FOR_SWIFT;
/**
The provided block is executed when its receiver is resolved.
The provided block is executed when its receiver is fulfilled.
If you provide a block that takes a parameter, the value of the receiver will be passed as that parameter.
@ -182,6 +182,13 @@ typedef void (^PMKResolver)(id __nullable) NS_REFINED_FOR_SWIFT;
*/
- (AnyPromise * __nonnull(^ __nonnull)(dispatch_queue_t __nonnull, dispatch_block_t __nonnull))ensureOn NS_REFINED_FOR_SWIFT;
/**
Wait until the promise is resolved.
@return Value if fulfilled or error if rejected.
*/
- (id __nullable)wait NS_REFINED_FOR_SWIFT;
/**
Create a new promise with an associated resolver.

View File

@ -112,6 +112,10 @@ NSString *const PMKErrorDomain = @"PMKErrorDomain";
};
}
- (id)wait {
return [d __wait];
}
- (BOOL)pending {
return [[d valueForKey:@"__pending"] boolValue];
}

View File

@ -3,7 +3,7 @@ import Foundation
/**
__AnyPromise is an implementation detail.
Because of how ObjC/Swift compatability work we have to compose our AnyPromise
Because of how ObjC/Swift compatibility work we have to compose our AnyPromise
with this internal object, however this is still part of the public interface.
Sadly. Please dont use it.
*/
@ -61,6 +61,26 @@ import Foundation
}))
}
@objc public func __wait() -> Any? {
if Thread.isMainThread {
conf.logHandler(.waitOnMainThread)
}
var result = __value
if result == nil {
let group = DispatchGroup()
group.enter()
self.__pipe { obj in
result = obj
group.leave()
}
group.wait()
}
return result
}
/// Internal, do not use! Some behaviors undefined.
@objc public func __pipe(_ to: @escaping (Any?) -> Void) {
let to = { (obj: Any?) -> Void in

View File

@ -17,7 +17,7 @@ public struct PMKConfiguration {
/// The closure used to log PromiseKit events.
/// Not thread safe; change before processing any promises.
/// - Note: The default handler calls `print()`
public var logHandler: (LogEvent) -> () = { event in
public var logHandler: (LogEvent) -> Void = { event in
switch event {
case .waitOnMainThread:
print("PromiseKit: warning: `wait()` called on main thread!")

View File

@ -83,9 +83,10 @@ extension Error {
} catch CocoaError.userCancelled {
return true
} catch {
#if os(macOS) || os(iOS) || os(tvOS)
let pair = { ($0.domain, $0.code) }(error as NSError)
return ("SKErrorDomain", 2) == pair
#if canImport(StoreKit)
let domain = (error as AnyObject).value(forKey: "domain") as? String
let code = (error as AnyObject).value(forKey: "code") as? Int
return ("SKErrorDomain", 2) == (domain, code)
#else
return false
#endif

View File

@ -105,7 +105,19 @@ public extension Guarantee {
return rg
}
@discardableResult
#if swift(>=4) && !swift(>=5.2)
func map<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath<T, U>) -> Guarantee<U> {
let rg = Guarantee<U>(.pending)
pipe { value in
on.async(flags: flags) {
rg.box.seal(value[keyPath: keyPath])
}
}
return rg
}
#endif
@discardableResult
func then<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> Guarantee<U>) -> Guarantee<U> {
let rg = Guarantee<U>(.pending)
pipe { value in
@ -144,17 +156,97 @@ public extension Guarantee {
}
public extension Guarantee where T: Sequence {
/**
`Guarantee<[T]>` => `T` -> `U` => `Guarantee<[U]>`
Guarantee.value([1,2,3])
.mapValues { integer in integer * 2 }
.done {
// $0 => [2,4,6]
}
*/
func mapValues<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) -> U) -> Guarantee<[U]> {
return map(on: on, flags: flags) { $0.map(transform) }
}
#if swift(>=4) && !swift(>=5.2)
/**
`Guarantee<[T]>` => `KeyPath<T, U>` => `Guarantee<[U]>`
Guarantee.value([Person(name: "Max"), Person(name: "Roman"), Person(name: "John")])
.mapValues(\.name)
.done {
// $0 => ["Max", "Roman", "John"]
}
*/
func mapValues<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath<T.Iterator.Element, U>) -> Guarantee<[U]> {
return map(on: on, flags: flags) { $0.map { $0[keyPath: keyPath] } }
}
#endif
/**
`Guarantee<[T]>` => `T` -> `[U]` => `Guarantee<[U]>`
Guarantee.value([1,2,3])
.flatMapValues { integer in [integer, integer] }
.done {
// $0 => [1,1,2,2,3,3]
}
*/
func flatMapValues<U: Sequence>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) -> U) -> Guarantee<[U.Iterator.Element]> {
return map(on: on, flags: flags) { (foo: T) in
foo.flatMap { transform($0) }
}
}
/**
`Guarantee<[T]>` => `T` -> `U?` => `Guarantee<[U]>`
Guarantee.value(["1","2","a","3"])
.compactMapValues { Int($0) }
.done {
// $0 => [1,2,3]
}
*/
func compactMapValues<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) -> U?) -> Guarantee<[U]> {
return map(on: on, flags: flags) { foo -> [U] in
#if !swift(>=3.3) || (swift(>=4) && !swift(>=4.1))
return foo.flatMap(transform)
#else
return foo.compactMap(transform)
#endif
}
}
#if swift(>=4) && !swift(>=5.2)
/**
`Guarantee<[T]>` => `KeyPath<T, U?>` => `Guarantee<[U]>`
Guarantee.value([Person(name: "Max"), Person(name: "Roman", age: 26), Person(name: "John", age: 23)])
.compactMapValues(\.age)
.done {
// $0 => [26, 23]
}
*/
func compactMapValues<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath<T.Iterator.Element, U?>) -> Guarantee<[U]> {
return map(on: on, flags: flags) { foo -> [U] in
#if !swift(>=4.1)
return foo.flatMap { $0[keyPath: keyPath] }
#else
return foo.compactMap { $0[keyPath: keyPath] }
#endif
}
}
#endif
/**
`Guarantee<[T]>` => `T` -> `Guarantee<U>` => `Guaranetee<[U]>`
firstly {
.value([1,2,3])
}.thenMap {
.value($0 * 2)
}.done {
// $0 => [2,4,6]
}
Guarantee.value([1,2,3])
.thenMap { .value($0 * 2) }
.done {
// $0 => [2,4,6]
}
*/
func thenMap<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) -> Guarantee<U>) -> Guarantee<[U]> {
return then(on: on, flags: flags) {
@ -164,6 +256,88 @@ public extension Guarantee where T: Sequence {
fatalError(String(describing: $0))
}
}
/**
`Guarantee<[T]>` => `T` -> `Guarantee<[U]>` => `Guarantee<[U]>`
Guarantee.value([1,2,3])
.thenFlatMap { integer in .value([integer, integer]) }
.done {
// $0 => [1,1,2,2,3,3]
}
*/
func thenFlatMap<U: Thenable>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) -> U) -> Guarantee<[U.T.Iterator.Element]> where U.T: Sequence {
return then(on: on, flags: flags) {
when(fulfilled: $0.map(transform))
}.map(on: nil) {
$0.flatMap { $0 }
}.recover {
// if happens then is bug inside PromiseKit
fatalError(String(describing: $0))
}
}
/**
`Guarantee<[T]>` => `T` -> Bool => `Guarantee<[T]>`
Guarantee.value([1,2,3])
.filterValues { $0 > 1 }
.done {
// $0 => [2,3]
}
*/
func filterValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ isIncluded: @escaping(T.Iterator.Element) -> Bool) -> Guarantee<[T.Iterator.Element]> {
return map(on: on, flags: flags) {
$0.filter(isIncluded)
}
}
#if swift(>=4) && !swift(>=5.2)
/**
`Guarantee<[T]>` => `KeyPath<T, Bool>` => `Guarantee<[T]>`
Guarantee.value([Person(name: "Max"), Person(name: "Roman", age: 26, isStudent: false), Person(name: "John", age: 23, isStudent: true)])
.filterValues(\.isStudent)
.done {
// $0 => [Person(name: "John", age: 23, isStudent: true)]
}
*/
func filterValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath<T.Iterator.Element, Bool>) -> Guarantee<[T.Iterator.Element]> {
return map(on: on, flags: flags) {
$0.filter { $0[keyPath: keyPath] }
}
}
#endif
/**
`Guarantee<[T]>` => (`T`, `T`) -> Bool => `Guarantee<[T]>`
Guarantee.value([5,2,3,4,1])
.sortedValues { $0 > $1 }
.done {
// $0 => [5,4,3,2,1]
}
*/
func sortedValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ areInIncreasingOrder: @escaping(T.Iterator.Element, T.Iterator.Element) -> Bool) -> Guarantee<[T.Iterator.Element]> {
return map(on: on, flags: flags) {
$0.sorted(by: areInIncreasingOrder)
}
}
}
public extension Guarantee where T: Sequence, T.Iterator.Element: Comparable {
/**
`Guarantee<[T]>` => `Guarantee<[T]>`
Guarantee.value([5,2,3,4,1])
.sortedValues()
.done {
// $0 => [1,2,3,4,5]
}
*/
func sortedValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil) -> Guarantee<[T.Iterator.Element]> {
return map(on: on, flags: flags) { $0.sorted() }
}
}
#if swift(>=3.1)
@ -171,6 +345,10 @@ public extension Guarantee where T == Void {
convenience init() {
self.init(box: SealedBox(value: Void()))
}
static var value: Guarantee<Void> {
return .value(Void())
}
}
#endif

View File

@ -38,7 +38,7 @@ public final class Promise<T>: Thenable, CatchMixin {
return .value(bar)
}
*/
public class func value(_ value: T) -> Promise<T> {
public static func value(_ value: T) -> Promise<T> {
return Promise(box: SealedBox(value: .fulfilled(value)))
}
@ -136,6 +136,11 @@ extension Promise where T == Void {
public convenience init() {
self.init(box: SealedBox(value: .fulfilled(Void())))
}
/// Returns a new promise fulfilled with `Void`
public static var value: Promise<Void> {
return .value(Void())
}
}
#endif

View File

@ -1,5 +1,5 @@
#import "fwd.h"
#import "AnyPromise.h"
#import <PromiseKit/fwd.h>
#import <PromiseKit/AnyPromise.h>
#import <Foundation/NSObjCRuntime.h> // `FOUNDATION_EXPORT`

View File

@ -14,12 +14,12 @@ public protocol Thenable: class {
public extension Thenable {
/**
The provided closure executes when this promise resolves.
The provided closure executes when this promise is fulfilled.
This allows chaining promises. The promise returned by the provided closure is resolved before the promise returned by this closure resolves.
- Parameter on: The queue to which the provided closure dispatches.
- Parameter body: The closure that executes when this promise fulfills. It must return a promise.
- Parameter body: The closure that executes when this promise is fulfilled. It must return a promise.
- Returns: A new promise that resolves when the promise returned from the provided closure resolves. For example:
firstly {
@ -52,13 +52,13 @@ public extension Thenable {
}
/**
The provided closure is executed when this promise is resolved.
The provided closure is executed when this promise is fulfilled.
This is like `then` but it requires the closure to return a non-promise.
- Parameter on: The queue to which the provided closure dispatches.
- Parameter transform: The closure that is executed when this Promise is fulfilled. It must return a non-promise.
- Returns: A new promise that is resolved with the value returned from the provided closure. For example:
- Returns: A new promise that is fulfilled with the value returned from the provided closure or rejected if the provided closure throws. For example:
firstly {
URLSession.shared.dataTask(.promise, with: url1)
@ -87,8 +87,32 @@ public extension Thenable {
return rp
}
#if swift(>=4) && !swift(>=5.2)
/**
The provided closure is executed when this promise is resolved.
Similar to func `map<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U) -> Promise<U>`, but accepts a key path instead of a closure.
- Parameter on: The queue to which the provided key path for value dispatches.
- Parameter keyPath: The key path to the value that is using when this Promise is fulfilled.
- Returns: A new promise that is fulfilled with the value for the provided key path.
*/
func map<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath<T, U>) -> Promise<U> {
let rp = Promise<U>(.pending)
pipe {
switch $0 {
case .fulfilled(let value):
on.async(flags: flags) {
rp.box.seal(.fulfilled(value[keyPath: keyPath]))
}
case .rejected(let error):
rp.box.seal(.rejected(error))
}
}
return rp
}
#endif
/**
The provided closure is executed when this promise is fulfilled.
In your closure return an `Optional`, if you return `nil` the resulting promise is rejected with `PMKError.compactMap`, otherwise the promise is fulfilled with the unwrapped value.
@ -125,15 +149,47 @@ public extension Thenable {
return rp
}
#if swift(>=4) && !swift(>=5.2)
/**
The provided closure is executed when this promise is resolved.
Similar to func `compactMap<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U?) -> Promise<U>`, but accepts a key path instead of a closure.
- Parameter on: The queue to which the provided key path for value dispatches.
- Parameter keyPath: The key path to the value that is using when this Promise is fulfilled. If the value for `keyPath` is `nil` the resulting promise is rejected with `PMKError.compactMap`.
- Returns: A new promise that is fulfilled with the value for the provided key path.
*/
func compactMap<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath<T, U?>) -> Promise<U> {
let rp = Promise<U>(.pending)
pipe {
switch $0 {
case .fulfilled(let value):
on.async(flags: flags) {
do {
if let rv = value[keyPath: keyPath] {
rp.box.seal(.fulfilled(rv))
} else {
throw PMKError.compactMap(value, U.self)
}
} catch {
rp.box.seal(.rejected(error))
}
}
case .rejected(let error):
rp.box.seal(.rejected(error))
}
}
return rp
}
#endif
/**
The provided closure is executed when this promise is fulfilled.
Equivalent to `map { x -> Void in`, but since we force the `Void` return Swift
is happier and gives you less hassle about your closures qualification.
- Parameter on: The queue to which the provided closure dispatches.
- Parameter body: The closure that is executed when this Promise is fulfilled.
- Returns: A new promise fulfilled as `Void`.
- Returns: A new promise fulfilled as `Void` or rejected if the provided closure throws.
firstly {
URLSession.shared.dataTask(.promise, with: url)
@ -162,14 +218,14 @@ public extension Thenable {
}
/**
The provided closure is executed when this promise is resolved.
The provided closure is executed when this promise is fulfilled.
This is like `done` but it returns the same value that the handler is fed.
`get` immutably accesses the fulfilled value; the returned Promise maintains that value.
- Parameter on: The queue to which the provided closure dispatches.
- Parameter body: The closure that is executed when this Promise is fulfilled.
- Returns: A new promise that is resolved with the value that the handler is fed. For example:
- Returns: A new promise that is fulfilled with the value that the handler is fed or rejected if the provided closure throws. For example:
firstly {
.value(1)
@ -290,6 +346,21 @@ public extension Thenable where T: Sequence {
return map(on: on, flags: flags){ try $0.map(transform) }
}
#if swift(>=4) && !swift(>=5.2)
/**
`Promise<[T]>` => `KeyPath<T, U>` => `Promise<[U]>`
firstly {
.value([Person(name: "Max"), Person(name: "Roman"), Person(name: "John")])
}.mapValues(\.name).done {
// $0 => ["Max", "Roman", "John"]
}
*/
func mapValues<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath<T.Iterator.Element, U>) -> Promise<[U]> {
return map(on: on, flags: flags){ $0.map { $0[keyPath: keyPath] } }
}
#endif
/**
`Promise<[T]>` => `T` -> `[U]` => `Promise<[U]>`
@ -328,6 +399,27 @@ public extension Thenable where T: Sequence {
}
}
#if swift(>=4) && !swift(>=5.2)
/**
`Promise<[T]>` => `KeyPath<T, U?>` => `Promise<[U]>`
firstly {
.value([Person(name: "Max"), Person(name: "Roman", age: 26), Person(name: "John", age: 23)])
}.compactMapValues(\.age).done {
// $0 => [26, 23]
}
*/
func compactMapValues<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath<T.Iterator.Element, U?>) -> Promise<[U]> {
return map(on: on, flags: flags) { foo -> [U] in
#if !swift(>=4.1)
return foo.flatMap { $0[keyPath: keyPath] }
#else
return foo.compactMap { $0[keyPath: keyPath] }
#endif
}
}
#endif
/**
`Promise<[T]>` => `T` -> `Promise<U>` => `Promise<[U]>`
@ -365,7 +457,7 @@ public extension Thenable where T: Sequence {
}
/**
`Promise<[T]>` => `T` -> Bool => `Promise<[U]>`
`Promise<[T]>` => `T` -> Bool => `Promise<[T]>`
firstly {
.value([1,2,3])
@ -380,6 +472,23 @@ public extension Thenable where T: Sequence {
$0.filter(isIncluded)
}
}
#if swift(>=4) && !swift(>=5.2)
/**
`Promise<[T]>` => `KeyPath<T, Bool>` => `Promise<[T]>`
firstly {
.value([Person(name: "Max"), Person(name: "Roman", age: 26, isStudent: false), Person(name: "John", age: 23, isStudent: true)])
}.filterValues(\.isStudent).done {
// $0 => [Person(name: "John", age: 23, isStudent: true)]
}
*/
func filterValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath<T.Iterator.Element, Bool>) -> Promise<[T.Iterator.Element]> {
return map(on: on, flags: flags) {
$0.filter { $0[keyPath: keyPath] }
}
}
#endif
}
public extension Thenable where T: Collection {

View File

@ -21,7 +21,7 @@ import Dispatch
URLSession.shared.dataTask(url: url3)
}
- Note: the block you pass excecutes immediately on the current thread/queue.
- Note: the block you pass executes immediately on the current thread/queue.
*/
public func firstly<U: Thenable>(execute body: () throws -> U) -> Promise<U.T> {
do {

View File

@ -139,7 +139,7 @@ public func when<It: IteratorProtocol>(fulfilled promiseIterator: It, concurrent
}
var generator = promiseIterator
var root = Promise<[It.Element.T]>.pending()
let root = Promise<[It.Element.T]>.pending()
var pendingPromises = 0
var promises: [It.Element] = []
@ -154,15 +154,11 @@ public func when<It: IteratorProtocol>(fulfilled promiseIterator: It, concurrent
}
guard shouldDequeue else { return }
var index: Int!
var promise: It.Element!
barrier.sync(flags: .barrier) {
guard let next = generator.next() else { return }
promise = next
index = promises.count
pendingPromises += 1
promises.append(next)
}

View File

@ -6,7 +6,7 @@ class Test226: XCTestCase {
describe("2.2.6: `then` may be called multiple times on the same promise.") {
describe("2.2.6.1: If/when `promise` is fulfilled, all respective `onFulfilled` callbacks must execute in the order of their originating calls to `then`.") {
describe("multiple boring fulfillment handlers") {
testFulfilled(withExpectationCount: 4) { promise, exes, sentinel -> () in
testFulfilled(withExpectationCount: 4) { promise, exes, sentinel -> Void in
var orderValidator = 0
promise.done {
XCTAssertEqual($0, sentinel)

View File

@ -757,6 +757,22 @@ static inline AnyPromise *fulfillLater() {
[self waitForExpectationsWithTimeout:1 handler:nil];
}
- (void)test_61_wait_for_value {
id o = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
resolve(@1);
}].wait;
XCTAssertEqualObjects(o, @1);
}
- (void)test_62_wait_for_error {
NSError* err = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
resolve([NSError errorWithDomain:@"a" code:123 userInfo:nil]);
}].wait;
XCTAssertEqual(err.code, 123);
}
- (void)test_properties {
XCTAssertEqualObjects([AnyPromise promiseWithValue:@1].value, @1);
XCTAssertEqualObjects([[AnyPromise promiseWithValue:dummyWithCode(2)].value localizedDescription], @"2");

View File

@ -2,6 +2,10 @@ import Foundation
import PromiseKit
import XCTest
#if canImport(StoreKit)
import StoreKit
#endif
class CancellationTests: XCTestCase {
func testCancellation() {
let ex1 = expectation(description: "")
@ -95,6 +99,25 @@ class CancellationTests: XCTestCase {
waitForExpectations(timeout: 1)
}
func testDoesntCrashSwift() {
// Previously exposed a bridging crash in Swift
// NOTE nobody was brave enough or diligent enough to report this to Apple :{
XCTAssertFalse(NSError().isCancelled)
#if canImport(StoreKit)
do {
let err = SKError(.paymentCancelled)
XCTAssertTrue(err.isCancelled)
throw err
} catch {
XCTAssertTrue(error.isCancelled)
}
XCTAssertFalse(SKError(.clientInvalid).isCancelled)
#endif
}
#if swift(>=3.2)
func testIsCancelled() {
XCTAssertTrue(PMKError.cancelled.isCancelled)

View File

@ -13,10 +13,105 @@ class GuaranteeTests: XCTestCase {
wait(for: [ex], timeout: 10)
}
func testMap() {
let ex = expectation(description: "")
Guarantee.value(1).map {
$0 * 2
}.done {
XCTAssertEqual(2, $0)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
#if swift(>=4) && !swift(>=5.2)
func testMapByKeyPath() {
let ex = expectation(description: "")
Guarantee.value(Person(name: "Max")).map(\.name).done {
XCTAssertEqual("Max", $0)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
#endif
func testWait() {
XCTAssertEqual(after(.milliseconds(100)).map(on: nil){ 1 }.wait(), 1)
}
func testMapValues() {
let ex = expectation(description: "")
Guarantee.value([1, 2, 3])
.mapValues { $0 * 2 }
.done { values in
XCTAssertEqual([2, 4, 6], values)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
#if swift(>=4) && !swift(>=5.2)
func testMapValuesByKeyPath() {
let ex = expectation(description: "")
Guarantee.value([Person(name: "Max"), Person(name: "Roman"), Person(name: "John")])
.mapValues(\.name)
.done { values in
XCTAssertEqual(["Max", "Roman", "John"], values)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
#endif
func testFlatMapValues() {
let ex = expectation(description: "")
Guarantee.value([1, 2, 3])
.flatMapValues { [$0, $0] }
.done { values in
XCTAssertEqual([1, 1, 2, 2, 3, 3], values)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
func testCompactMapValues() {
let ex = expectation(description: "")
Guarantee.value(["1","2","a","3"])
.compactMapValues { Int($0) }
.done { values in
XCTAssertEqual([1, 2, 3], values)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
#if swift(>=4) && !swift(>=5.2)
func testCompactMapValuesByKeyPath() {
let ex = expectation(description: "")
Guarantee.value([Person(name: "Max"), Person(name: "Roman", age: 26), Person(name: "John", age: 23)])
.compactMapValues(\.age)
.done { values in
XCTAssertEqual([26, 23], values)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
#endif
func testThenMap() {
let ex = expectation(description: "")
@ -26,8 +121,93 @@ class GuaranteeTests: XCTestCase {
.done { values in
XCTAssertEqual([2, 4, 6], values)
ex.fulfill()
}
}
wait(for: [ex], timeout: 10)
}
func testThenFlatMap() {
let ex = expectation(description: "")
Guarantee.value([1, 2, 3])
.thenFlatMap { Guarantee.value([$0, $0]) }
.done { values in
XCTAssertEqual([1, 1, 2, 2, 3, 3], values)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
func testFilterValues() {
let ex = expectation(description: "")
Guarantee.value([1, 2, 3])
.filterValues { $0 > 1 }
.done { values in
XCTAssertEqual([2, 3], values)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
#if swift(>=4) && !swift(>=5.2)
func testFilterValuesByKeyPath() {
let ex = expectation(description: "")
Guarantee.value([Person(name: "Max"), Person(name: "Roman", age: 26, isStudent: false), Person(name: "John", age: 23, isStudent: true)])
.filterValues(\.isStudent)
.done { values in
XCTAssertEqual([Person(name: "John", age: 23, isStudent: true)], values)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
#endif
func testSorted() {
let ex = expectation(description: "")
Guarantee.value([5, 2, 3, 4, 1])
.sortedValues()
.done { values in
XCTAssertEqual([1, 2, 3, 4, 5], values)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
func testSortedBy() {
let ex = expectation(description: "")
Guarantee.value([5, 2, 3, 4, 1])
.sortedValues { $0 > $1 }
.done { values in
XCTAssertEqual([5, 4, 3, 2, 1], values)
ex.fulfill()
}
wait(for: [ex], timeout: 10)
}
#if swift(>=3.1)
func testNoAmbiguityForValue() {
let ex = expectation(description: "")
let a = Guarantee<Void>.value
let b = Guarantee<Void>.value(Void())
let c = Guarantee<Void>.value(())
when(fulfilled: a, b, c).done {
ex.fulfill()
}.cauterize()
wait(for: [ex], timeout: 10)
}
#endif
}

View File

@ -178,7 +178,7 @@ class LoggingTests: XCTestCase {
func testPendingGuaranteeDeallocatedIsLogged() {
var logOutput: String? = nil
let loggingClosure: (PromiseKit.LogEvent) -> () = { event in
let loggingClosure: (PromiseKit.LogEvent) -> Void = { event in
switch event {
case .waitOnMainThread, .pendingPromiseDeallocated, .pendingGuaranteeDeallocated:
logOutput = "\(event)"

View File

@ -136,4 +136,17 @@ class PromiseTests: XCTestCase {
}.silenceWarning()
wait(for: [ex], timeout: 10)
}
#if swift(>=3.1)
func testNoAmbiguityForValue() {
let ex = expectation(description: "")
let a = Promise<Void>.value
let b = Promise<Void>.value(Void())
let c = Promise<Void>.value(())
when(fulfilled: a, b, c).done {
ex.fulfill()
}.cauterize()
wait(for: [ex], timeout: 10)
}
#endif
}

View File

@ -2,6 +2,22 @@ import PromiseKit
import Dispatch
import XCTest
struct Person: Equatable {
let name: String
let age: Int?
let isStudent: Bool
init(
name: String = "",
age: Int? = nil,
isStudent: Bool = false
) {
self.name = name
self.age = age
self.isStudent = isStudent
}
}
class ThenableTests: XCTestCase {
func testGet() {
let ex1 = expectation(description: "")
@ -16,6 +32,28 @@ class ThenableTests: XCTestCase {
wait(for: [ex1, ex2], timeout: 10)
}
func testMap() {
let ex = expectation(description: "")
Promise.value(1).map {
$0 * 2
}.done {
XCTAssertEqual($0, 2)
ex.fulfill()
}.silenceWarning()
wait(for: [ex], timeout: 10)
}
#if swift(>=4) && !swift(>=5.2)
func testMapByKeyPath() {
let ex = expectation(description: "")
Promise.value(Person(name: "Max")).map(\.name).done {
XCTAssertEqual($0, "Max")
ex.fulfill()
}.silenceWarning()
wait(for: [ex], timeout: 10)
}
#endif
func testCompactMap() {
let ex = expectation(description: "")
Promise.value(1.0).compactMap {
@ -72,6 +110,39 @@ class ThenableTests: XCTestCase {
wait(for: [ex], timeout: 10)
}
#if swift(>=4) && !swift(>=5.2)
func testCompactMapByKeyPath() {
let ex = expectation(description: "")
Promise.value(Person(name: "Roman", age: 26)).compactMap(\.age).done {
XCTAssertEqual($0, 26)
ex.fulfill()
}.silenceWarning()
wait(for: [ex], timeout: 10)
}
#endif
func testMapValues() {
let ex = expectation(description: "")
Promise.value([14, 20, 45]).mapValues {
$0 * 2
}.done {
XCTAssertEqual([28, 40, 90], $0)
ex.fulfill()
}.silenceWarning()
wait(for: [ex], timeout: 10)
}
#if swift(>=4) && !swift(>=5.2)
func testMapValuesByKeyPath() {
let ex = expectation(description: "")
Promise.value([Person(name: "Max"), Person(name: "Roman"), Person(name: "John")]).mapValues(\.name).done {
XCTAssertEqual(["Max", "Roman", "John"], $0)
ex.fulfill()
}.silenceWarning()
wait(for: [ex], timeout: 10)
}
#endif
func testCompactMapValues() {
let ex = expectation(description: "")
Promise.value(["1","2","a","4"]).compactMapValues {
@ -83,6 +154,17 @@ class ThenableTests: XCTestCase {
wait(for: [ex], timeout: 10)
}
#if swift(>=4) && !swift(>=5.2)
func testCompactMapValuesByKeyPath() {
let ex = expectation(description: "")
Promise.value([Person(name: "Max"), Person(name: "Roman", age: 26), Person(name: "John", age: 23)]).compactMapValues(\.age).done {
XCTAssertEqual([26, 23], $0)
ex.fulfill()
}.silenceWarning()
wait(for: [ex], timeout: 10)
}
#endif
func testThenMap() {
let ex = expectation(description: "")
Promise.value([1,2,3,4]).thenMap {
@ -105,6 +187,28 @@ class ThenableTests: XCTestCase {
wait(for: [ex], timeout: 10)
}
func testFilterValues() {
let ex = expectation(description: "")
Promise.value([Person(name: "Max"), Person(name: "Roman", age: 26, isStudent: false), Person(name: "John", age: 23, isStudent: true)]).filterValues {
$0.isStudent
}.done {
XCTAssertEqual([Person(name: "John", age: 23, isStudent: true)], $0)
ex.fulfill()
}.silenceWarning()
wait(for: [ex], timeout: 10)
}
#if swift(>=4) && !swift(>=5.2)
func testFilterValuesByKeyPath() {
let ex = expectation(description: "")
Promise.value([Person(name: "Max"), Person(name: "Roman", age: 26, isStudent: false), Person(name: "John", age: 23, isStudent: true)]).filterValues(\.isStudent).done {
XCTAssertEqual([Person(name: "John", age: 23, isStudent: true)], $0)
ex.fulfill()
}.silenceWarning()
wait(for: [ex], timeout: 10)
}
#endif
func testLastValueForEmpty() {
XCTAssertTrue(Promise.value([]).lastValue.isRejected)
}

View File

@ -18,6 +18,7 @@ extension CancellationTests {
// to regenerate.
static let __allTests__CancellationTests = [
("testCancellation", testCancellation),
("testDoesntCrashSwift", testDoesntCrashSwift),
("testFoundationBridging1", testFoundationBridging1),
("testFoundationBridging2", testFoundationBridging2),
("testIsCancelled", testIsCancelled),
@ -55,7 +56,20 @@ extension GuaranteeTests {
// `swift test --generate-linuxmain`
// to regenerate.
static let __allTests__GuaranteeTests = [
("testCompactMapValues", testCompactMapValues),
("testCompactMapValuesByKeyPath", testCompactMapValuesByKeyPath),
("testFilterValues", testFilterValues),
("testFilterValuesByKeyPath", testFilterValuesByKeyPath),
("testFlatMapValues", testFlatMapValues),
("testInit", testInit),
("testMap", testMap),
("testMapByKeyPath", testMapByKeyPath),
("testMapValues", testMapValues),
("testMapValuesByKeyPath", testMapValuesByKeyPath),
("testNoAmbiguityForValue", testNoAmbiguityForValue),
("testSorted", testSorted),
("testSortedBy", testSortedBy),
("testThenFlatMap", testThenFlatMap),
("testThenMap", testThenMap),
("testWait", testWait),
]
@ -130,6 +144,7 @@ extension PromiseTests {
("testIsPending", testIsPending),
("testIsRejected", testIsRejected),
("testIsResolved", testIsResolved),
("testNoAmbiguityForValue", testNoAmbiguityForValue),
("testPipeForResolved", testPipeForResolved),
("testThrowInFirstly", testThrowInFirstly),
("testThrowInInitializer", testThrowInInitializer),
@ -177,12 +192,20 @@ extension ThenableTests {
static let __allTests__ThenableTests = [
("testBarrier", testBarrier),
("testCompactMap", testCompactMap),
("testCompactMapByKeyPath", testCompactMapByKeyPath),
("testCompactMapThrows", testCompactMapThrows),
("testCompactMapValues", testCompactMapValues),
("testCompactMapValuesByKeyPath", testCompactMapValuesByKeyPath),
("testDispatchFlagsSyntax", testDispatchFlagsSyntax),
("testFilterValues", testFilterValues),
("testFilterValuesByKeyPath", testFilterValuesByKeyPath),
("testFirstValueForEmpty", testFirstValueForEmpty),
("testGet", testGet),
("testLastValueForEmpty", testLastValueForEmpty),
("testMap", testMap),
("testMapByKeyPath", testMapByKeyPath),
("testMapValues", testMapValues),
("testMapValuesByKeyPath", testMapValuesByKeyPath),
("testPMKErrorCompactMap", testPMKErrorCompactMap),
("testRejectedPromiseCompactMap", testRejectedPromiseCompactMap),
("testThenFlatMap", testThenFlatMap),