The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .github
    ├── FUNDING.yml
    ├── ISSUE_TEMPLATE.md
    ├── LinuxMain.stencil
    ├── codecov.yml
    ├── jazzy.yml
    ├── ranger.yml
    ├── sourcery.yml
    └── workflows
    │   ├── cd.yml
    │   ├── ci-podspec.yml
    │   ├── ci.yml
    │   └── publish.yml
├── .gitignore
├── .gitmodules
├── .tidelift.yml
├── .travis.yml
├── Documentation
    ├── Appendix.md
    ├── CommonPatterns.md
    ├── Examples
    │   ├── ImageCache.md
    │   ├── URLSession+BadResponseErrors.swift
    │   └── detweet.swift
    ├── FAQ.md
    ├── GettingStarted.md
    ├── Installation.md
    ├── ObjectiveC.md
    ├── README.md
    └── Troubleshooting.md
├── LICENSE
├── Package.swift
├── Package@swift-4.2.swift
├── Package@swift-5.0.swift
├── Package@swift-5.3.swift
├── PromiseKit.playground
    ├── Contents.swift
    ├── contents.xcplayground
    └── playground.xcworkspace
    │   └── contents.xcworkspacedata
├── PromiseKit.podspec
├── PromiseKit.xcodeproj
    ├── project.pbxproj
    ├── project.xcworkspace
    │   ├── contents.xcworkspacedata
    │   └── xcshareddata
    │   │   ├── IDEWorkspaceChecks.plist
    │   │   └── WorkspaceSettings.xcsettings
    └── xcshareddata
    │   └── xcschemes
    │       └── PromiseKit.xcscheme
├── README.md
├── Sources
    ├── AnyPromise+Private.h
    ├── AnyPromise.h
    ├── AnyPromise.m
    ├── AnyPromise.swift
    ├── Async.swift
    ├── Box.swift
    ├── Catchable.swift
    ├── Combine.swift
    ├── Configuration.swift
    ├── CustomStringConvertible.swift
    ├── Deprecations.swift
    ├── Error.swift
    ├── Guarantee.swift
    ├── Info.plist
    ├── LogEvent.swift
    ├── NSMethodSignatureForBlock.m
    ├── PMKCallVariadicBlock.m
    ├── Promise.swift
    ├── PromiseKit.h
    ├── Resolver.swift
    ├── Resources
    │   └── PrivacyInfo.xcprivacy
    ├── Thenable.swift
    ├── after.m
    ├── after.swift
    ├── dispatch_promise.m
    ├── firstly.swift
    ├── fwd.h
    ├── hang.m
    ├── hang.swift
    ├── join.m
    ├── race.m
    ├── race.swift
    ├── when.m
    └── when.swift
├── Tests
    ├── A+
    │   ├── 0.0.0.swift
    │   ├── 2.1.2.swift
    │   ├── 2.1.3.swift
    │   ├── 2.2.2.swift
    │   ├── 2.2.3.swift
    │   ├── 2.2.4.swift
    │   ├── 2.2.6.swift
    │   ├── 2.2.7.swift
    │   ├── 2.3.1.swift
    │   ├── 2.3.2.swift
    │   ├── 2.3.4.swift
    │   ├── README.md
    │   └── XCTestManifests.swift
    ├── Bridging
    │   ├── BridgingTests.m
    │   ├── BridgingTests.swift
    │   ├── Infrastructure.h
    │   ├── Infrastructure.m
    │   └── Infrastructure.swift
    ├── CoreObjC
    │   ├── AnyPromiseTests.m
    │   ├── AnyPromiseTests.swift
    │   ├── HangTests.m
    │   ├── JoinTests.m
    │   ├── PMKManifoldTests.m
    │   ├── RaceTests.m
    │   └── WhenTests.m
    ├── CorePromise
    │   ├── AfterTests.swift
    │   ├── AsyncTests.swift
    │   ├── CancellableErrorTests.swift
    │   ├── CatchableTests.swift
    │   ├── CombineTests.swift
    │   ├── DefaultDispatchQueueTests.swift
    │   ├── ErrorTests.swift
    │   ├── GuaranteeTests.swift
    │   ├── HangTests.swift
    │   ├── LoggingTests.swift
    │   ├── PromiseTests.swift
    │   ├── RaceTests.swift
    │   ├── RegressionTests.swift
    │   ├── ResolverTests.swift
    │   ├── StressTests.swift
    │   ├── ThenableTests.swift
    │   ├── Utilities.swift
    │   ├── WhenConcurrentTests.swift
    │   ├── WhenResolvedTests.swift
    │   ├── WhenTests.swift
    │   ├── XCTestManifests.swift
    │   └── ZalgoTests.swift
    ├── DeprecationTests.swift
    ├── JS-A+
    │   ├── .gitignore
    │   ├── AllTests.swift
    │   ├── JSAdapter.swift
    │   ├── JSPromise.swift
    │   ├── JSUtils.swift
    │   ├── MockNodeEnvironment.swift
    │   ├── README.md
    │   ├── index.js
    │   ├── package-lock.json
    │   ├── package.json
    │   └── webpack.config.js
    └── LinuxMain.swift
└── tea.yaml


/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | tidelift: "cocoapods/PromiseKit"
2 | patreon: "mxcl"
3 | github: mxcl
4 | 


--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
 1 | [PLEASE READ THE TROUBLESHOOTING GUIDE](https://github.com/mxcl/PromiseKit/blob/master/Documentation/Troubleshooting.md).
 2 | 
 3 | ---
 4 | 
 5 | You read the guide but it didn’t help? OK, we’re here to help.
 6 | 
 7 | * Please specify the PromiseKit major version you are using
 8 | * [Please format your code in triple backticks and ensure readable indentation](https://help.github.com/articles/creating-and-highlighting-code-blocks/)
 9 | * Please specify how you installed PromiseKit, ie. Carthage, CocoaPods, SwiftPM or other. If other provide DETAILED information about how you are integrating PromiseKit.
10 | 
11 | If you ignore this template we will close your ticket and link to this template until you provide this necessary information. We cannot help you without it.
12 | 


--------------------------------------------------------------------------------
/.github/LinuxMain.stencil:
--------------------------------------------------------------------------------
 1 | @testable import Core
 2 | @testable import A_
 3 | import XCTest
 4 | 
 5 | //TODO get this to run on CI and don’t have it committed
 6 | //NOTE problem is Sourcery doesn’t support Linux currently
 7 | //USAGE: cd PromiseKit/Sources/.. && sourcery --config .github/sourcery.yml
 8 | 
 9 | {% for type in types.classes|based:"XCTestCase" %}
10 | extension {{ type.name }} {
11 |     static var allTests = [
12 |     {% for method in type.methods %}{% if method.parameters.count == 0 and method.shortName|hasPrefix:"test" %}    ("{{ method.shortName }}", {{type.name}}.{{ method.shortName }}),
13 |     {% endif %}{% endfor %}]
14 | }
15 | 
16 | {% endfor %}
17 | XCTMain([
18 | {% for type in types.classes|based:"XCTestCase" %}{% if not type.annotations.excludeFromLinuxMain %}    testCase({{ type.name }}.allTests),
19 | {% endif %}{% endfor %}])
20 | 


--------------------------------------------------------------------------------
/.github/codecov.yml:
--------------------------------------------------------------------------------
 1 | ignore:
 2 |   - "Tests"
 3 |   - "README.md"
 4 |   - "Documentation"
 5 |   - ".travis.yml"
 6 | 
 7 | codecov:
 8 |   notify:
 9 |     require_ci_to_pass: yes
10 | 
11 | coverage:
12 |   precision: 1
13 |   round: up
14 |   range: "70...100"
15 | 
16 |   status:
17 |     project: yes
18 |     patch:
19 |       default:
20 |         threshold: 0.2%
21 |     changes: no
22 | 
23 | parsers:
24 |   gcov:
25 |     branch_detection:
26 |       conditional: yes
27 |       loop: yes
28 |       method: no
29 |       macro: no
30 | 
31 | comment: off
32 | 


--------------------------------------------------------------------------------
/.github/jazzy.yml:
--------------------------------------------------------------------------------
 1 | module: PromiseKit
 2 | custom_categories:
 3 |   - name: Core Components
 4 |     children:
 5 |       - Promise
 6 |       - Guarantee
 7 |       - Thenable
 8 |       - CatchMixin
 9 |       - Resolver
10 | xcodebuild_arguments:
11 |   - UseModernBuildSystem=NO
12 | output:
13 |   ../output
14 |   # output directory is relative to config file… ugh
15 | readme:
16 |   Documentation/README.md
17 | theme:
18 |   fullwidth
19 | 


--------------------------------------------------------------------------------
/.github/ranger.yml:
--------------------------------------------------------------------------------
1 | merges:
2 |   - action: delete_branch
3 | 


--------------------------------------------------------------------------------
/.github/sourcery.yml:
--------------------------------------------------------------------------------
 1 | sources:
 2 |   include:
 3 |     - ../Tests/A+
 4 |     - ../Tests/CorePromise
 5 |   exclude:
 6 |     - ../Tests/A+/0.0.0.swift
 7 |     - ../Tests/CorePromise/Utilities.swift
 8 | templates:
 9 |   include:
10 |     - LinuxMain.stencil
11 | output:
12 |   ../Tests/LinuxMain.swift
13 | 


--------------------------------------------------------------------------------
/.github/workflows/cd.yml:
--------------------------------------------------------------------------------
  1 | name: CD
  2 | on:
  3 |   workflow_dispatch:
  4 |     inputs:
  5 |       version:
  6 |         required: true
  7 | jobs:
  8 |   pods:
  9 |     runs-on: macos-latest
 10 |     steps:
 11 | 
 12 |     - name: Start Deployment
 13 |       uses: bobheadxi/deployments@v0.5.2
 14 |       id: deployment
 15 |       with:
 16 |         step: start
 17 |         token: ${{ secrets.GITHUB_TOKEN }}
 18 |         env: pods
 19 | 
 20 |     - uses: actions/checkout@v3
 21 |       with:
 22 |         submodules: true
 23 | 
 24 |     - run: pod trunk push --allow-warnings --skip-tests --skip-import-validation
 25 |       env:
 26 |         COCOAPODS_TRUNK_TOKEN: ${{ secrets.COCOAPODS_TRUNK_TOKEN }}
 27 | 
 28 |     - name: Seal Deployment
 29 |       uses: bobheadxi/deployments@v0.5.2
 30 |       if: always()
 31 |       with:
 32 |         step: finish
 33 |         token: ${{ secrets.GITHUB_TOKEN }}
 34 |         status: ${{ job.status }}
 35 |         deployment_id: ${{ steps.deployment.outputs.deployment_id }}
 36 | 
 37 |   # carthage:
 38 |   #   runs-on: macos-latest
 39 |   #   steps:
 40 | 
 41 |   #   - name: Start Deployment
 42 |   #     uses: bobheadxi/deployments@v0.5.2
 43 |   #     id: deployment
 44 |   #     with:
 45 |   #       step: start
 46 |   #       token: ${{ secrets.GITHUB_TOKEN }}
 47 |   #       env: carthage
 48 | 
 49 |   #   - uses: maxim-lobanov/setup-xcode@v1
 50 |   #     with:
 51 |   #       xcode-version: ^11
 52 |   #       # Waiting on https://github.com/Carthage/Carthage/issues/3103 for Xcode 12
 53 | 
 54 |   #   - uses: joutvhu/get-release@v1
 55 |   #     id: release
 56 |   #     with:
 57 |   #       tag_name: ${{ github.event.inputs.version }}
 58 |   #     env:
 59 |   #       GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 60 | 
 61 |   #   - uses: actions/checkout@v2
 62 |   #   - run: carthage build --no-skip-current --platform macOS,iOS,watchOS,tvOS --archive
 63 |   #   - run: mv PromiseKit.framework.zip PromiseKit-$v.framework.zip
 64 | 
 65 |   #   - uses: actions/upload-release-asset@v1
 66 |   #     with:
 67 |   #       upload_url: ${{ steps.release.outputs.upload_url }}
 68 |   #       asset_path: ./PromiseKit-${{ github.event.inputs.version }}.framework.zip
 69 |   #       asset_name: PromiseKit-${{ github.event.inputs.version }}.framework.zip
 70 |   #       asset_content_type: application/zip
 71 |   #     env:
 72 |   #       GITHUB_TOKEN: ${{ github.token }}
 73 | 
 74 |   #   - name: Seal Deployment
 75 |   #     uses: bobheadxi/deployments@v0.5.2
 76 |   #     if: always()
 77 |   #     with:
 78 |   #       step: finish
 79 |   #       token: ${{ secrets.GITHUB_TOKEN }}
 80 |   #       status: ${{ job.status }}
 81 |   #       deployment_id: ${{ steps.deployment.outputs.deployment_id }}
 82 | 
 83 |   docs:
 84 |     runs-on: macos-latest
 85 |     steps:
 86 | 
 87 |     - name: Start Deployment
 88 |       uses: bobheadxi/deployments@v0.5.2
 89 |       id: deployment
 90 |       with:
 91 |         step: start
 92 |         token: ${{ secrets.GITHUB_TOKEN }}
 93 |         env: docs
 94 | 
 95 |     - uses: actions/checkout@v2
 96 |     - run: gem install jazzy
 97 |     - run: |
 98 |         jazzy --config .github/jazzy.yml \
 99 |           --github_url 'https://github.com/mxcl/PromiseKit' \
100 |           --module-version ${{ github.event.inputs.version }}
101 |     - run: git remote update
102 |     - run: git checkout gh-pages
103 |     - run: rm -rf reference/v6
104 |     - run: mv output reference/v6
105 |     - run: git add reference/v6
106 |     - run: git config user.name github-actions
107 |     - run: git config user.email github-actions@github.com
108 |     - run: git commit -m 'Updated docs for v${{ github.event.inputs.version }}'
109 |     - run: git remote add secure-origin https://${{ secrets.JAZZY_PAT }}@github.com/mxcl/PromiseKit.git
110 |     - run: git push secure-origin gh-pages
111 | 
112 |     - name: Seal Deployment
113 |       uses: bobheadxi/deployments@v0.5.2
114 |       if: always()
115 |       with:
116 |         step: finish
117 |         token: ${{ secrets.GITHUB_TOKEN }}
118 |         status: ${{ job.status }}
119 |         deployment_id: ${{ steps.deployment.outputs.deployment_id }}
120 | 


--------------------------------------------------------------------------------
/.github/workflows/ci-podspec.yml:
--------------------------------------------------------------------------------
 1 | on:
 2 |   workflow_dispatch:
 3 |   pull_request:
 4 |     paths:
 5 |       - PromiseKit.podspec
 6 | jobs:
 7 |   lint:
 8 |     runs-on: macos-15
 9 |     steps:
10 |     - uses: maxim-lobanov/setup-xcode@v1
11 |       with:
12 |         xcode-version: 16
13 |     - uses: maxim-lobanov/setup-cocoapods@v1
14 |       with:
15 |         version: 1.16.2
16 |     - uses: actions/checkout@v2
17 |       with:
18 |         submodules: true
19 |     - run: pod lib lint --fail-fast --allow-warnings
20 | 


--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
  1 | name: CI
  2 | 
  3 | on:
  4 |   workflow_dispatch:
  5 |   workflow_call:
  6 |   pull_request:
  7 |     paths:
  8 |       - Sources/**
  9 |       - Tests/**
 10 |       - .github/workflows/ci.yml
 11 |       - PromiseKit.xcodeproj/**
 12 | 
 13 | concurrency:
 14 |   group: ${{ github.ref }}
 15 |   cancel-in-progress: true
 16 | 
 17 | jobs:
 18 |   linux-build:
 19 |     name: Linux (Swift ${{ matrix.swift }})
 20 |     runs-on: ubuntu-20.04
 21 |     strategy:
 22 |       matrix:
 23 |         swift:
 24 |         - '4.0'
 25 |         - '4.1'
 26 |         - '4.2'
 27 |     container:
 28 |       image: swift:${{ matrix.swift }}
 29 |     steps:
 30 |     - uses: actions/checkout@v1 # DO NOT UPDATE ∵ failure on older containers due to old glibc
 31 |     - run: swift build -Xswiftc -warnings-as-errors -Xswiftc -swift-version -Xswiftc 3
 32 |     - run: swift build  # can’t test ∵ generated linuxmain requires Swift 5
 33 | 
 34 |   linux-test:
 35 |     name: Linux (Swift ${{ matrix.swift }})
 36 |     runs-on: ubuntu-latest
 37 |     continue-on-error: true
 38 |     strategy:
 39 |       matrix:
 40 |         swift:
 41 |         - '5.0'
 42 |         - '5.1'
 43 |         - '5.2'
 44 |         - '5.3'
 45 |         - '5.4'
 46 |         - '5.5'
 47 |         - '5.6'
 48 |         - '5.7'
 49 |         - '5.8'
 50 |         - '5.9'
 51 |         - '5.10'
 52 |         - '6.0'
 53 |     container:
 54 |       image: swift:${{ matrix.swift }}
 55 |     steps:
 56 |     - uses: actions/checkout@v1 # DO NOT UPDATE ∵ failure on older containers due to old glibc
 57 |     - run: swift build -Xswiftc -warnings-as-errors -Xswiftc -swift-version -Xswiftc 4
 58 |       if: ${{ matrix.swift < 6 }}
 59 |     - run: swift build -Xswiftc -warnings-as-errors -Xswiftc -swift-version -Xswiftc 4.2
 60 |       if: ${{ matrix.swift < 6 }}
 61 |     - run: swift test --enable-code-coverage --parallel
 62 | 
 63 |     - name: Generate Coverage Report
 64 |       if: ${{ matrix.swift >= 6 }}
 65 |       run: |
 66 |         apt-get -qq update
 67 |         apt-get -qq install llvm-18 curl
 68 |         export b=$(swift build --show-bin-path) && llvm-cov-18 \
 69 |           export -format lcov \
 70 |           -instr-profile=$b/codecov/default.profdata \
 71 |           --ignore-filename-regex='\.build/' \
 72 |           $b/*.xctest \
 73 |           > info.lcov
 74 | 
 75 |     - uses: codecov/codecov-action@v1
 76 |       with:
 77 |         file: ./info.lcov
 78 |       if: ${{ matrix.swift >= 6 }}
 79 | 
 80 |   test:
 81 |     runs-on: macos-latest
 82 |     name: ${{ matrix.platform }}
 83 |     continue-on-error: true
 84 |     strategy:
 85 |       matrix:
 86 |         platform:
 87 |           - macOS
 88 |           - watchOS
 89 |           - tvOS
 90 |           - iOS
 91 |           # - mac-catalyst
 92 |           # - visionOS
 93 |           # ^^ both are unavailable in the github-hosted runners
 94 |     steps:
 95 |     - uses: actions/checkout@v4
 96 |     - uses: mxcl/xcodebuild@v3
 97 |       with:
 98 |         platform: ${{ matrix.platform }}
 99 |         code-coverage: true
100 |     - uses: codecov/codecov-action@v1
101 | 
102 |   test-android:
103 |     name: Android
104 |     runs-on: ubuntu-latest
105 |     steps:
106 |     - uses: actions/checkout@v4
107 |     - uses: skiptools/swift-android-action@v2
108 | 


--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
 1 | name: Publish
 2 | 
 3 | on:
 4 |   workflow_dispatch:
 5 |     inputs:
 6 |       version:
 7 |         description: semantic version to publish
 8 |         required: true
 9 | 
10 | concurrency:
11 |   group: publish-{{github.event.inputs.version}}
12 |   cancel-in-progress: true
13 | 
14 | jobs:
15 |   ci:
16 |     uses: ./.github/workflows/ci.yml
17 | 
18 |   lint:
19 |     runs-on: macos-latest
20 |     strategy:
21 |       matrix:
22 |         xcode:
23 |           - ^16
24 |     steps:
25 |     - uses: maxim-lobanov/setup-xcode@v1
26 |       with:
27 |         xcode-version: ${{ matrix.xcode }}
28 |     - uses: actions/checkout@v3
29 |       with:
30 |         submodules: true
31 |     - run: pod lib lint --fail-fast --allow-warnings
32 | 
33 |   create-release:
34 |     runs-on: ubuntu-latest
35 |     needs: [ci, lint]
36 |     env:
37 |       v: ${{ github.event.inputs.version }}
38 |     steps:
39 |     - uses: actions/checkout@v3
40 |       with:
41 |         fetch-depth: 0  # zero means “all” (or push fails)
42 |     - name: Update committed versions
43 |       run: |
44 |         ruby -i -pe "sub(/CURRENT_PROJECT_VERSION = [0-9.]+/, 'CURRENT_PROJECT_VERSION = $v')" PromiseKit.xcodeproj/project.pbxproj
45 |         ruby -i -pe "sub(/s.version = '[0-9.]+'/, 's.version = \'$v\'')" PromiseKit.podspec
46 |     - run: |
47 |         ! (git diff --quiet)
48 |     - name: Commit
49 |       run: |
50 |         git config user.name github-actions
51 |         git config user.email github-actions@github.com
52 |         git commit -am "PromiseKit $v"
53 |         git push
54 |     - uses: softprops/action-gh-release@v1
55 |       env:
56 |         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
57 |       with:
58 |         tag_name: ${{ github.event.inputs.version }}
59 |         name: ${{ github.event.inputs.version }}
60 | 
61 |   cd:
62 |     needs: create-release
63 |     runs-on: ubuntu-latest
64 |     steps:
65 |     - uses: aurelien-baudet/workflow-dispatch@v2
66 |       with:
67 |         workflow: CD
68 |         token: ${{ secrets.JAZZY_PAT }}
69 |         inputs: "{\"version\": \"${{ github.event.inputs.version }}\"}"
70 |         ref: ${{ env.GITHUB_REF_NAME }}  # or uses the SHA rather than branch and thus the above commit is not used
71 |         wait-for-completion: true
72 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
 1 | *.xcodeproj/**/xcuserdata/
 2 | *.xcscmblueprint
 3 | /Carthage
 4 | /.build
 5 | .DS_Store
 6 | DerivedData
 7 | /Extensions/Carthage
 8 | /Tests/JS-A+/build
 9 | .swiftpm/
10 | 


--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
 1 | [submodule "Extensions/Foundation"]
 2 | 	path = Extensions/Foundation
 3 | 	url = https://github.com/PromiseKit/Foundation.git
 4 | [submodule "Extensions/UIKit"]
 5 | 	path = Extensions/UIKit
 6 | 	url = https://github.com/PromiseKit/UIKit.git
 7 | [submodule "Extensions/Accounts"]
 8 | 	path = Extensions/Accounts
 9 | 	url = https://github.com/PromiseKit/Accounts.git
10 | [submodule "Extensions/MessagesUI"]
11 | 	path = Extensions/MessagesUI
12 | 	url = https://github.com/PromiseKit/MessagesUI.git
13 | [submodule "Extensions/WatchConnectivity"]
14 | 	path = Extensions/WatchConnectivity
15 | 	url = https://github.com/PromiseKit/WatchConnectivity.git
16 | [submodule "Extensions/Photos"]
17 | 	path = Extensions/Photos
18 | 	url = https://github.com/PromiseKit/Photos.git
19 | [submodule "Extensions/MapKit"]
20 | 	path = Extensions/MapKit
21 | 	url = https://github.com/PromiseKit/MapKit.git
22 | [submodule "Extensions/CloudKit"]
23 | 	path = Extensions/CloudKit
24 | 	url = https://github.com/PromiseKit/CloudKit.git
25 | [submodule "Extensions/AddressBook"]
26 | 	path = Extensions/AddressBook
27 | 	url = https://github.com/PromiseKit/AddressBook.git
28 | [submodule "Extensions/AssetsLibrary"]
29 | 	path = Extensions/AssetsLibrary
30 | 	url = https://github.com/PromiseKit/AssetsLibrary.git
31 | [submodule "Extensions/CoreLocation"]
32 | 	path = Extensions/CoreLocation
33 | 	url = https://github.com/PromiseKit/CoreLocation.git
34 | [submodule "Extensions/QuartzCore"]
35 | 	path = Extensions/QuartzCore
36 | 	url = https://github.com/PromiseKit/QuartzCore.git
37 | [submodule "Extensions/Social"]
38 | 	path = Extensions/Social
39 | 	url = https://github.com/PromiseKit/Social.git
40 | [submodule "Extensions/StoreKit"]
41 | 	path = Extensions/StoreKit
42 | 	url = https://github.com/PromiseKit/StoreKit.git
43 | [submodule "Extensions/Bolts"]
44 | 	path = Extensions/Bolts
45 | 	url = https://github.com/PromiseKit/Bolts.git
46 | [submodule "Extensions/CoreBluetooth"]
47 | 	path = Extensions/CoreBluetooth
48 | 	url = https://github.com/PromiseKit/CoreBluetooth.git
49 | [submodule "Extensions/EventKit"]
50 | 	path = Extensions/EventKit
51 | 	url = https://github.com/PromiseKit/EventKit.git
52 | [submodule "Extensions/SystemConfiguration"]
53 | 	path = Extensions/SystemConfiguration
54 | 	url = https://github.com/PromiseKit/SystemConfiguration
55 | [submodule "Extensions/Alamofire"]
56 | 	path = Extensions/Alamofire
57 | 	url = https://github.com/PromiseKit/Alamofire
58 | [submodule "Extensions/OMGHTTPURLRQ"]
59 | 	path = Extensions/OMGHTTPURLRQ
60 | 	url = https://github.com/PromiseKit/OMGHTTPURLRQ
61 | [submodule "Extensions/AVFoundation"]
62 | 	path = Extensions/AVFoundation
63 | 	url = https://github.com/PromiseKit/AVFoundation
64 | [submodule "Extensions/HomeKit"]
65 | 	path = Extensions/HomeKit
66 | 	url = https://github.com/PromiseKit/HomeKit.git
67 | [submodule "Extensions/HealthKit"]
68 | 	path = Extensions/HealthKit
69 | 	url = https://github.com/PromiseKit/PMKHealthKit
70 | 


--------------------------------------------------------------------------------
/.tidelift.yml:
--------------------------------------------------------------------------------
1 | extra_ignore_directories: [ Tests ]
2 | 


--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
 1 | os: osx
 2 | language: swift
 3 | osx_image: xcode9.4
 4 | 
 5 | if: tag =~ /^[0-9]+\.[0-9]+\.[0-9]+/
 6 | 
 7 | stages:
 8 |   - swiftpm
 9 |   - carthage
10 | 
11 | jobs:
12 |   include:
13 |     - &swiftpm
14 |       stage: swiftpm
15 |       osx_image: xcode9.4
16 |       script: swift build
17 |     - <<: *swiftpm
18 |       osx_image: xcode10.3
19 |     - <<: *swiftpm
20 |       osx_image: xcode11.6
21 | 
22 |     - &carthage
23 |       stage: carthage
24 |       osx_image: xcode9.4
25 |       install: sed -i '' "s/SWIFT_TREAT_WARNINGS_AS_ERRORS = NO;/SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;/" *.xcodeproj/project.pbxproj
26 |       script: carthage build --no-skip-current
27 |     - <<: *carthage
28 |       osx_image: xcode10.3
29 |     - <<: *carthage
30 |       osx_image: xcode11.6
31 | 


--------------------------------------------------------------------------------
/Documentation/Appendix.md:
--------------------------------------------------------------------------------
  1 | # Common Misusage
  2 | 
  3 | ## Doubling up Promises
  4 | 
  5 | Don’t do this:
  6 | 
  7 | ```swift
  8 | func toggleNetworkSpinnerWithPromise<T>(funcToCall: () -> Promise<T>) -> Promise<T> {
  9 |     return Promise { seal in
 10 |         firstly {
 11 |             setNetworkActivityIndicatorVisible(true)
 12 |             return funcToCall()
 13 |         }.then { result in
 14 |             seal.fulfill(result)
 15 |         }.always {
 16 |             setNetworkActivityIndicatorVisible(false)
 17 |         }.catch { err in
 18 |             seal.reject(err)
 19 |         }
 20 |     }
 21 | }
 22 | ```
 23 | 
 24 | Do this:
 25 | 
 26 | ```swift
 27 | func toggleNetworkSpinnerWithPromise<T>(funcToCall: () -> Promise<T>) -> Promise<T> {
 28 |     return firstly {
 29 |         setNetworkActivityIndicatorVisible(true)
 30 |         return funcToCall()
 31 |     }.always {
 32 |         setNetworkActivityIndicatorVisible(false)
 33 |     }
 34 | }
 35 | ```
 36 | 
 37 | You already *had* a promise, you don’t need to wrap it in another promise.
 38 | 
 39 | 
 40 | ## Optionals in Promises
 41 | 
 42 | When we see `Promise<Item?>`, it usually implies a misuse of promises. For
 43 | example:
 44 | 
 45 | ```swift
 46 | return firstly {
 47 |     getItems()
 48 | }.then { items -> Promise<[Item]?> in
 49 |     guard !items.isEmpty else {
 50 |         return .value(nil)
 51 |     }
 52 |     return Promise(value: items)
 53 | }
 54 | ```
 55 | 
 56 | The second `then` chooses to return `nil` in some circumstances. This choice
 57 | imposes the need to check for `nil` on the consumer of the promise.
 58 | 
 59 | It's usually better to shunt these sorts of exceptions away from the
 60 | happy path and onto the error path. In this case, we can create a specific
 61 | error type for this condition:
 62 | 
 63 | ```swift
 64 | return firstly {
 65 |     getItems()
 66 | }.map { items -> [Item]> in
 67 |     guard !items.isEmpty else {
 68 |         throw MyError.emptyItems
 69 |     }
 70 |     return items
 71 | }
 72 | ```
 73 | 
 74 | > *Note*: Use `compactMap` when an API outside your control returns an Optional and you want to generate an error instead of propagating `nil`.
 75 | 
 76 | # Tips n’ Tricks
 77 | 
 78 | ## Background-Loaded Member Variables
 79 | 
 80 | ```swift
 81 | class MyViewController: UIViewController {
 82 |     private let ambience: Promise<AVAudioPlayer> = DispatchQueue.global().async(.promise) {
 83 |         guard let asset = NSDataAsset(name: "CreepyPad") else { throw PMKError.badInput }
 84 |         let player =  try AVAudioPlayer(data: asset.data)
 85 |         player.prepareToPlay()
 86 |         return player
 87 |     }
 88 | }
 89 | ```
 90 | 
 91 | ## Chaining Animations
 92 | 
 93 | ```swift
 94 | firstly {
 95 |     UIView.animate(.promise, duration: 0.3) {
 96 |         self.button1.alpha = 0
 97 |     }
 98 | }.then {
 99 |     UIView.animate(.promise, duration: 0.3) {
100 |         self.button2.alpha = 1
101 |     }
102 | }.then {
103 |     UIView.animate(.promise, duration: 0.3) {
104 |         adjustConstraints()
105 |         self.view.layoutIfNeeded()
106 |     }
107 | }
108 | ```
109 | 
110 | 
111 | ## Voiding Promises
112 | 
113 | It is often convenient to erase the type of a promise to facilitate chaining.
114 | For example, `UIView.animate(.promise)` returns `Guarantee<Bool>` because UIKit’s
115 | completion API supplies a `Bool`. However, we usually don’t need this value and 
116 | can chain more simply if it is discarded (that is, converted to `Void`). We can use
117 | `asVoid()` to achieve this conversion:
118 | 
119 | ```swift
120 | UIView.animate(.promise, duration: 0.3) {
121 |     self.button1.alpha = 0
122 | }.asVoid().done(self.nextStep)
123 | ```
124 | 
125 | For situations in which we are combining many promises into a `when`, `asVoid()`
126 | becomes essential:
127 | 
128 | ```swift
129 | let p1 = foo()
130 | let p2 = bar()
131 | let p3 = baz()
132 | //…
133 | let p10 = fluff()
134 | 
135 | when(fulfilled: p1.asVoid(), p2.asVoid(), /*…*/, p10.asVoid()).then {
136 |     let value1 = p1.value!  // safe bang since all the promises fulfilled
137 |     // …
138 |     let value10 = p10.value!
139 | }.catch {
140 |     //…
141 | }
142 | ```
143 | 
144 | You normally don't have to do this explicitly because `when` does it for you
145 | for up to 5 parameters.
146 | 
147 | 
148 | ## Blocking (Await)
149 | 
150 | Sometimes you have to block the main thread to await completion of an asynchronous task.
151 | In these cases, you can (with caution) use `wait`:
152 | 
153 | ```swift
154 | public extension UNUserNotificationCenter {
155 |     var wasPushRequested: Bool {
156 |         let settings = Guarantee(resolver: getNotificationSettings).wait()
157 |         return settings != .notDetermined
158 |     }
159 | }
160 | ```
161 | 
162 | The task under the promise **must not** call back onto the current thread or it
163 | will deadlock.
164 | 
165 | ## Starting a Chain on a Background Queue/Thread
166 | 
167 | `firstly` deliberately does not take a queue. A detailed rationale for this choice
168 | can be found in the ticket tracker.
169 | 
170 | So, if you want to start a chain by dispatching to the background, you have to use
171 | `DispatchQueue.async`:
172 | 
173 | ```swift
174 | DispatchQueue.global().async(.promise) {
175 |     return value  
176 | }.done { value in
177 |     //…
178 | }
179 | ```
180 | 
181 | However, this function cannot return a promise because of Swift compiler ambiguity
182 | issues. Thus, if you must start a promise on a background queue, you need to
183 | do something like this:
184 | 
185 | 
186 | ```swift
187 | Promise { seal in
188 |     DispatchQueue.global().async {
189 |         seal(value)
190 |     }  
191 | }.done { value in
192 |     //…
193 | }
194 | ```
195 | 
196 | Or more simply (though with caveats; see the documentation for `wait`):
197 | 
198 | ```swift
199 | DispatchQueue.global().async(.promise) {
200 |     return try fetch().wait()
201 | }.done { value in
202 |     //…
203 | }
204 | ```
205 | 
206 | However, you shouldn't need to do this often. If you find yourself wanting to use
207 | this technique, perhaps you should instead modify the code for `fetch` to make it do
208 | its work on a background thread.
209 | 
210 | Promises abstract asynchronicity, so exploit and support that model. Design your
211 | APIs so that consumers don’t have to care what queue your functions run on.
212 | 


--------------------------------------------------------------------------------
/Documentation/Examples/ImageCache.md:
--------------------------------------------------------------------------------
  1 | # Image Cache with Promises
  2 | 
  3 | Here is an example of a simple image cache that uses promises to simplify the
  4 | state machine:
  5 | 
  6 | ```swift
  7 | import Foundation
  8 | import PromiseKit
  9 | 
 10 | /**
 11 |  * Small (10 images)
 12 |  * Thread-safe
 13 |  * Consolidates multiple requests to the same URLs
 14 |  * Removes stale entries (FIXME well, strictly we may delete while fetching from cache, but this is unlikely and non-fatal)
 15 |  * Completely _ignores_ server caching headers!
 16 |  */
 17 | 
 18 | private let q = DispatchQueue(label: "org.promisekit.cache.image", attributes: .concurrent)
 19 | private var active: [URL: Promise<Data>] = [:]
 20 | private var cleanup = Promise()
 21 | 
 22 | 
 23 | public func fetch(image url: URL) -> Promise<Data> {
 24 |     var promise: Promise<Data>?
 25 |     q.sync {
 26 |         promise = active[url]
 27 |     }
 28 |     if let promise = promise {
 29 |         return promise
 30 |     }
 31 | 
 32 |     q.sync {
 33 |         promise = Promise(.start) {
 34 | 
 35 |             let dst = try url.cacheDestination()
 36 | 
 37 |             guard !FileManager.default.isReadableFile(atPath: dst.path) else {
 38 |                 return Promise(dst)
 39 |             }
 40 | 
 41 |             return Promise { seal in
 42 |                 URLSession.shared.downloadTask(with: url) { tmpurl, _, error in
 43 |                     do {
 44 |                         guard let tmpurl = tmpurl else { throw error ?? E.unexpectedError }
 45 |                         try FileManager.default.moveItem(at: tmpurl, to: dst)
 46 |                         seal.fulfill(dst)
 47 |                     } catch {
 48 |                         seal.reject(error)
 49 |                     }
 50 |                 }.resume()
 51 |             }
 52 | 
 53 |         }.then(on: .global(QoS: .userInitiated)) {
 54 |             try Data(contentsOf: $0)
 55 |         }
 56 | 
 57 |         active[url] = promise
 58 | 
 59 |         if cleanup.isFulfilled {
 60 |             cleanup = promise!.asVoid().then(on: .global(QoS: .utility), execute: docleanup)
 61 |         }
 62 |     }
 63 | 
 64 |     return promise!
 65 | }
 66 | 
 67 | public func cached(image url: URL) -> Data? {
 68 |     guard let dst = try? url.cacheDestination() else {
 69 |         return nil
 70 |     }
 71 |     return try? Data(contentsOf: dst)
 72 | }
 73 | 
 74 | 
 75 | public func cache(destination remoteUrl: URL) throws -> URL {
 76 |     return try remoteUrl.cacheDestination()
 77 | }
 78 | 
 79 | private func cache() throws -> URL {
 80 |     guard let dst = FileManager.default.docs?
 81 |         .appendingPathComponent("Library")
 82 |         .appendingPathComponent("Caches")
 83 |         .appendingPathComponent("cache.img")
 84 |     else {
 85 |         throw E.unexpectedError
 86 |     }
 87 | 
 88 |     try FileManager.default.createDirectory(at: dst, withIntermediateDirectories: true, attributes: [:])
 89 | 
 90 |     return dst
 91 | }
 92 | 
 93 | private extension URL {
 94 |     func cacheDestination() throws -> URL {
 95 | 
 96 |         var fn = String(hashValue)
 97 |         let ext = pathExtension
 98 | 
 99 |         // many of Apple's functions don’t recognize file type
100 |         // unless we preserve the file extension
101 |         if !ext.isEmpty {
102 |             fn += ".\(ext)"
103 |         }
104 | 
105 |         return try cache().appendingPathComponent(fn)
106 |     }
107 | }
108 | 
109 | enum E: Error {
110 |     case unexpectedError
111 |     case noCreationTime
112 | }
113 | 
114 | private func docleanup() throws {
115 |     var contents = try FileManager.default
116 |         .contentsOfDirectory(at: try cache(), includingPropertiesForKeys: [.creationDateKey])
117 |         .map { url -> (Date, URL) in
118 |             guard let date = try url.resourceValues(forKeys: [.creationDateKey]).creationDate else {
119 |                 throw E.noCreationTime
120 |             }
121 |             return (date, url)
122 |         }.sorted(by: {
123 |             $0.0 > $1.0
124 |         })
125 | 
126 |     while contents.count > 10 {
127 |         let rm = contents.popLast()!.1
128 |         try FileManager.default.removeItem(at: rm)
129 |     }
130 | }
131 | ````


--------------------------------------------------------------------------------
/Documentation/Examples/URLSession+BadResponseErrors.swift:
--------------------------------------------------------------------------------
 1 | Promise(.pending) { seal in
 2 |     URLSession.shared.dataTask(with: rq, completionHandler: { data, rsp, error in
 3 |         if let data = data {
 4 |             seal.fulfill(data)
 5 |         } else if let error = error {
 6 |             if case URLError.badServerResponse = error, let rsp = rsp as? HTTPURLResponse {
 7 |                 seal.reject(Error.badResponse(rsp.statusCode))
 8 |             } else {
 9 |                 seal.reject(error)
10 |             }
11 |         } else {
12 |             seal.reject(PMKError.invalidCallingConvention)
13 |         }
14 |     })
15 | }
16 | 
17 | enum Error: Swift.Error {
18 |     case badUrl
19 |     case badResponse(Int)
20 | }
21 | 


--------------------------------------------------------------------------------
/Documentation/Examples/detweet.swift:
--------------------------------------------------------------------------------
  1 | #!/usr/bin/swift sh
  2 | import Foundation
  3 | import PromiseKit  // @mxcl ~> 6.5 
  4 | import Swifter     // @mattdonnelly == b27a89
  5 | let swifter = Swifter(
  6 | 	consumerKey: "FILL",
  7 | 	consumerSecret: "ME",
  8 | 	oauthToken: "IN",
  9 | 	oauthTokenSecret: "https://developer.twitter.com/en/docs/basics/apps/overview.html"
 10 | )
 11 | 
 12 | extension JSON {
 13 |     var date: Date? {
 14 |         guard let string = string else { return nil }
 15 | 
 16 |         let formatter = DateFormatter()
 17 |         formatter.dateFormat = "EEE MMM dd HH:mm:ss Z yyyy"
 18 |         return formatter.date(from: string)
 19 |     }
 20 | }
 21 | 
 22 | let twoMonthsAgo = Date() - 24*60*60*30*2
 23 | 
 24 | print("Deleting qualifying tweets before:", twoMonthsAgo)
 25 | 
 26 | func deleteTweets(maxID: String? = nil) -> Promise<Void> {
 27 |     return Promise { seal in
 28 |         swifter.getTimeline(for: "mxcl", count: 200, maxID: maxID, success: { json in
 29 | 
 30 |             if json.array!.count <= 1 {
 31 |                 // if we get one result for a requested maxID, we're done
 32 |                 return seal.fulfill()
 33 |             }
 34 | 
 35 |             for item in json.array! {
 36 |                 let date = item["created_at"].date!
 37 |                 guard date < twoMonthsAgo, item["favorite_count"].integer! < 2 else {
 38 |                     continue
 39 |                 }
 40 |                 swifter.destroyTweet(forID: id, success: { _ in
 41 |                     print("D:", item["text"].string!)
 42 |                 }, failure: seal.reject)
 43 |             }
 44 | 
 45 |             let next = json.array!.last!["id_str"].string!
 46 |             deleteTweets(maxID: next).pipe(to: seal.resolve)
 47 | 
 48 |         }, failure: seal.reject)
 49 |     }
 50 | }
 51 | 
 52 | func deleteFavorites(maxID: String? = nil) -> Promise<Void> {
 53 |     return Promise { seal in
 54 |         swifter.getRecentlyFavoritedTweets(count: 200, maxID: maxID, success: { json in
 55 | 
 56 |             if json.array!.count <= 1 {
 57 |                 return seal.fulfill()
 58 |             }
 59 | 
 60 |             for item in json.array! {
 61 |                 guard item["created_at"].date! < twoMonthsAgo else { continue }
 62 | 
 63 |                 swifter.unfavoriteTweet(forID: item["id_str"].string!, success: { _ in
 64 |                     print("D❤️:", item["text"].string!)
 65 |                 }, failure: seal.reject)
 66 |             }
 67 |             
 68 |             let next = json.array!.last!["id_str"].string!
 69 |             deleteFavorites(maxID: next).pipe(to: seal.resolve)
 70 | 
 71 |         }, failure: seal.reject)
 72 |     }
 73 | }
 74 | 
 75 | func unblockPeople(cursor: String? = nil) -> Promise<Void> {
 76 |     return Promise { seal in
 77 |         swifter.getBlockedUsersIDs(stringifyIDs: "true", cursor: cursor, success: { json, prev, next in
 78 |             for id in json.array! {
 79 |                 print("Unblocking:", id)
 80 |                 swifter.unblockUser(for: .id(id.string!))
 81 |             }
 82 | 
 83 |             if let next = next, !next.isEmpty, next != prev, next != "0" {
 84 |                 unblockPeople(cursor: next).pipe(to: seal.resolve)
 85 |             } else {
 86 |                 seal.fulfill()
 87 |             }
 88 | 
 89 |         }, failure: seal.reject)
 90 |     }
 91 | }
 92 | 
 93 | firstly {
 94 |     when(fulfilled: deleteTweets(), deleteFavorites(), unblockPeople())
 95 | }.done {
 96 |     exit(0)
 97 | }.catch {
 98 |     print("error:", $0)
 99 |     exit(1)
100 | }
101 | 
102 | RunLoop.main.run()
103 | 


--------------------------------------------------------------------------------
/Documentation/README.md:
--------------------------------------------------------------------------------
 1 | # Contents
 2 | 
 3 | * [README](../README.md)
 4 | * Handbook
 5 |   * [Getting Started](GettingStarted.md)
 6 |   * [Promises: Common Patterns](CommonPatterns.md)
 7 |   * [Frequently Asked Questions](FAQ.md)
 8 | * Manual
 9 |   * [Installation Guide](Installation.md)
10 |   * [Objective-C Guide](ObjectiveC.md)
11 |   * [Troubleshooting](Troubleshooting.md)
12 |   * [Appendix](Appendix.md)
13 | * [Examples](Examples)
14 | * [API Reference](https://mxcl.dev/PromiseKit/reference/v6/Classes/Promise.html)
15 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | Copyright 2016-present, Max Howell; mxcl@me.com
 2 | 
 3 | Permission is hereby granted, free of charge, to any person obtaining a
 4 | copy of this software and associated documentation files (the
 5 | "Software"), to deal in the Software without restriction, including
 6 | without limitation the rights to use, copy, modify, merge, publish,
 7 | distribute, sublicense, and/or sell copies of the Software, and to
 8 | permit persons to whom the Software is furnished to do so, subject to
 9 | the following conditions:
10 | 
11 | The above copyright notice and this permission notice shall be included
12 | in all copies or substantial portions of the Software.
13 | 
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 | 


--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
 1 | // swift-tools-version:4.0
 2 | 
 3 | import PackageDescription
 4 | 
 5 | let pkg = Package(name: "PromiseKit")
 6 | pkg.products = [
 7 |     .library(name: "PromiseKit", targets: ["PromiseKit"]),
 8 | ]
 9 | 
10 | let pmk: Target = .target(name: "PromiseKit")
11 | pmk.path = "Sources"
12 | pmk.exclude = [
13 |     "AnyPromise.swift",
14 |     "AnyPromise.m",
15 |     "PMKCallVariadicBlock.m",
16 |     "dispatch_promise.m",
17 |     "join.m",
18 |     "when.m",
19 |     "NSMethodSignatureForBlock.m",
20 |     "after.m",
21 |     "hang.m",
22 |     "race.m",
23 |     "Deprecations.swift"
24 | ]
25 | pkg.swiftLanguageVersions = [3, 4, 5]
26 | pkg.targets = [
27 |     pmk,
28 |     .testTarget(name: "APlus", dependencies: ["PromiseKit"], path: "Tests/A+"),
29 |     .testTarget(name: "CorePromise", dependencies: ["PromiseKit"], path: "Tests/CorePromise"),
30 | ]
31 | 


--------------------------------------------------------------------------------
/Package@swift-4.2.swift:
--------------------------------------------------------------------------------
 1 | // swift-tools-version:4.2
 2 | 
 3 | import PackageDescription
 4 | 
 5 | let pkg = Package(name: "PromiseKit")
 6 | pkg.products = [
 7 |     .library(name: "PromiseKit", targets: ["PromiseKit"]),
 8 | ]
 9 | 
10 | let pmk: Target = .target(name: "PromiseKit")
11 | pmk.path = "Sources"
12 | pmk.exclude = [
13 |     "AnyPromise.swift",
14 |     "AnyPromise.m",
15 |     "PMKCallVariadicBlock.m",
16 |     "dispatch_promise.m",
17 |     "join.m",
18 |     "when.m",
19 |     "NSMethodSignatureForBlock.m",
20 |     "after.m",
21 |     "hang.m",
22 |     "race.m",
23 |     "Deprecations.swift"
24 | ]
25 | pkg.swiftLanguageVersions = [.v3, .v4, .v4_2]
26 | pkg.targets = [
27 |     pmk,
28 |     .testTarget(name: "APlus", dependencies: ["PromiseKit"], path: "Tests/A+"),
29 |     .testTarget(name: "CorePromise", dependencies: ["PromiseKit"], path: "Tests/CorePromise"),
30 | ]
31 | 


--------------------------------------------------------------------------------
/Package@swift-5.0.swift:
--------------------------------------------------------------------------------
 1 | // swift-tools-version:5.0
 2 | 
 3 | import PackageDescription
 4 | 
 5 | let pkg = Package(name: "PromiseKit")
 6 | pkg.platforms = [
 7 |    .macOS(.v10_10), .iOS(.v8), .tvOS(.v9), .watchOS(.v2)
 8 | ]
 9 | pkg.products = [
10 |     .library(name: "PromiseKit", targets: ["PromiseKit"]),
11 | ]
12 | 
13 | let pmk: Target = .target(name: "PromiseKit")
14 | pmk.path = "Sources"
15 | pmk.exclude = [
16 |     "AnyPromise.swift",
17 |     "AnyPromise.m",
18 |     "PMKCallVariadicBlock.m",
19 |     "dispatch_promise.m",
20 |     "join.m",
21 |     "when.m",
22 |     "NSMethodSignatureForBlock.m",
23 |     "after.m",
24 |     "hang.m",
25 |     "race.m",
26 |     "Deprecations.swift"
27 | ]
28 | pkg.swiftLanguageVersions = [.v4, .v4_2, .v5]
29 | pkg.targets = [
30 |     pmk,
31 |     .testTarget(name: "APlus", dependencies: ["PromiseKit"], path: "Tests/A+"),
32 |     .testTarget(name: "CorePromise", dependencies: ["PromiseKit"], path: "Tests/CorePromise"),
33 | ]
34 | 


--------------------------------------------------------------------------------
/Package@swift-5.3.swift:
--------------------------------------------------------------------------------
 1 | // swift-tools-version:5.3
 2 | 
 3 | import PackageDescription
 4 | 
 5 | let pkg = Package(name: "PromiseKit")
 6 | pkg.platforms = [
 7 |    .macOS(.v10_10), .iOS(.v9), .tvOS(.v9), .watchOS(.v2)
 8 | ]
 9 | pkg.products = [
10 |     .library(name: "PromiseKit", targets: ["PromiseKit"]),
11 | ]
12 | 
13 | let pmk: Target = .target(name: "PromiseKit")
14 | pmk.path = "Sources"
15 | pmk.resources = [
16 |     .process("Resources/PrivacyInfo.xcprivacy")
17 | ]
18 | pmk.exclude = [
19 |     "AnyPromise.swift",
20 |     "AnyPromise.m",
21 |     "PMKCallVariadicBlock.m",
22 |     "dispatch_promise.m",
23 |     "join.m",
24 |     "when.m",
25 |     "NSMethodSignatureForBlock.m",
26 |     "after.m",
27 |     "hang.m",
28 |     "race.m",
29 |     "Deprecations.swift",
30 |     "Info.plist"
31 | ]
32 | pkg.swiftLanguageVersions = [.v4, .v4_2, .v5]
33 | pkg.targets = [
34 |     pmk,
35 |     .testTarget(name: "APlus", dependencies: ["PromiseKit"], path: "Tests/A+", exclude: ["README.md"]),
36 |     .testTarget(name: "CorePromise", dependencies: ["PromiseKit"], path: "Tests/CorePromise"),
37 | ]
38 | 


--------------------------------------------------------------------------------
/PromiseKit.playground/Contents.swift:
--------------------------------------------------------------------------------
 1 | import PlaygroundSupport
 2 | 
 3 | // Is this erroring? If so open the `.xcodeproj` and build the
 4 | // framework for a macOS target (usually labeled: “My Mac”).
 5 | // Then select `PromiseKit.playground` from inside Xcode.
 6 | import PromiseKit
 7 | 
 8 | 
 9 | func promise3() -> Promise<Int> {
10 |     return after(.seconds(1)).map{ 3 }
11 | }
12 | 
13 | firstly {
14 |     Promise.value(1)
15 | }.map { _ in
16 |     2
17 | }.then { _ in
18 |     promise3()
19 | }.done {
20 |     print($0)  // => 3
21 | }.catch { error in
22 |     // only happens for errors
23 | }.finally {
24 |     PlaygroundPage.current.finishExecution()
25 | }
26 | 
27 | PlaygroundPage.current.needsIndefiniteExecution = true
28 | 


--------------------------------------------------------------------------------
/PromiseKit.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2 | <playground version='3.0' sdk='macosx' auto-termination-delay='2'>
3 |     <sections>
4 |         <code source-file-name='section-1.swift'/>
5 |     </sections>
6 |     <timeline fileName='timeline.xctimeline'/>
7 | </playground>


--------------------------------------------------------------------------------
/PromiseKit.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="UTF-8"?>
2 | <Workspace
3 |    version = "1.0">
4 |    <FileRef
5 |       location = "self:">
6 |    </FileRef>
7 | </Workspace>
8 | 


--------------------------------------------------------------------------------
/PromiseKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="UTF-8"?>
2 | <Workspace
3 |    version = "1.0">
4 |    <FileRef
5 |       location = "self:">
6 |    </FileRef>
7 | </Workspace>
8 | 


--------------------------------------------------------------------------------
/PromiseKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="UTF-8"?>
2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3 | <plist version="1.0">
4 | <dict>
5 | 	<key>IDEDidComputeMac32BitWarning</key>
6 | 	<true/>
7 | </dict>
8 | </plist>
9 | 


--------------------------------------------------------------------------------
/PromiseKit.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="UTF-8"?>
2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3 | <plist version="1.0">
4 | <dict>
5 | 	<key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
6 | 	<false/>
7 | </dict>
8 | </plist>
9 | 


--------------------------------------------------------------------------------
/PromiseKit.xcodeproj/xcshareddata/xcschemes/PromiseKit.xcscheme:
--------------------------------------------------------------------------------
  1 | <?xml version="1.0" encoding="UTF-8"?>
  2 | <Scheme
  3 |    LastUpgradeVersion = "1250"
  4 |    version = "1.3">
  5 |    <BuildAction
  6 |       parallelizeBuildables = "YES"
  7 |       buildImplicitDependencies = "NO">
  8 |       <BuildActionEntries>
  9 |          <BuildActionEntry
 10 |             buildForTesting = "NO"
 11 |             buildForRunning = "YES"
 12 |             buildForProfiling = "YES"
 13 |             buildForArchiving = "YES"
 14 |             buildForAnalyzing = "YES">
 15 |             <BuildableReference
 16 |                BuildableIdentifier = "primary"
 17 |                BlueprintIdentifier = "63B0AC561D595E1B00FA21D9"
 18 |                BuildableName = "PromiseKit.framework"
 19 |                BlueprintName = "PromiseKit"
 20 |                ReferencedContainer = "container:PromiseKit.xcodeproj">
 21 |             </BuildableReference>
 22 |          </BuildActionEntry>
 23 |       </BuildActionEntries>
 24 |    </BuildAction>
 25 |    <TestAction
 26 |       buildConfiguration = "Debug"
 27 |       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
 28 |       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
 29 |       shouldUseLaunchSchemeArgsEnv = "YES"
 30 |       codeCoverageEnabled = "YES"
 31 |       onlyGenerateCoverageForSpecifiedTargets = "YES">
 32 |       <CodeCoverageTargets>
 33 |          <BuildableReference
 34 |             BuildableIdentifier = "primary"
 35 |             BlueprintIdentifier = "63B0AC561D595E1B00FA21D9"
 36 |             BuildableName = "PromiseKit.framework"
 37 |             BlueprintName = "PromiseKit"
 38 |             ReferencedContainer = "container:PromiseKit.xcodeproj">
 39 |          </BuildableReference>
 40 |       </CodeCoverageTargets>
 41 |       <Testables>
 42 |          <TestableReference
 43 |             skipped = "NO"
 44 |             parallelizable = "YES"
 45 |             testExecutionOrdering = "random">
 46 |             <BuildableReference
 47 |                BuildableIdentifier = "primary"
 48 |                BlueprintIdentifier = "630019011D596292003B4E30"
 49 |                BuildableName = "PMKCoreTests.xctest"
 50 |                BlueprintName = "PMKCoreTests"
 51 |                ReferencedContainer = "container:PromiseKit.xcodeproj">
 52 |             </BuildableReference>
 53 |          </TestableReference>
 54 |          <TestableReference
 55 |             skipped = "NO"
 56 |             parallelizable = "YES"
 57 |             testExecutionOrdering = "random">
 58 |             <BuildableReference
 59 |                BuildableIdentifier = "primary"
 60 |                BlueprintIdentifier = "6317518B1D59766500A9DDDC"
 61 |                BuildableName = "PMKA+Tests.xctest"
 62 |                BlueprintName = "PMKA+Tests"
 63 |                ReferencedContainer = "container:PromiseKit.xcodeproj">
 64 |             </BuildableReference>
 65 |          </TestableReference>
 66 |          <TestableReference
 67 |             skipped = "NO"
 68 |             parallelizable = "YES"
 69 |             testExecutionOrdering = "random">
 70 |             <BuildableReference
 71 |                BuildableIdentifier = "primary"
 72 |                BlueprintIdentifier = "631411321D59795700E24B9E"
 73 |                BuildableName = "PMKBridgeTests.xctest"
 74 |                BlueprintName = "PMKBridgeTests"
 75 |                ReferencedContainer = "container:PromiseKit.xcodeproj">
 76 |             </BuildableReference>
 77 |          </TestableReference>
 78 |          <TestableReference
 79 |             skipped = "NO"
 80 |             parallelizable = "YES"
 81 |             testExecutionOrdering = "random">
 82 |             <BuildableReference
 83 |                BuildableIdentifier = "primary"
 84 |                BlueprintIdentifier = "633027E0203CC0060037E136"
 85 |                BuildableName = "PMKDeprecatedTests.xctest"
 86 |                BlueprintName = "PMKDeprecatedTests"
 87 |                ReferencedContainer = "container:PromiseKit.xcodeproj">
 88 |             </BuildableReference>
 89 |          </TestableReference>
 90 |          <TestableReference
 91 |             skipped = "NO"
 92 |             parallelizable = "YES"
 93 |             testExecutionOrdering = "random">
 94 |             <BuildableReference
 95 |                BuildableIdentifier = "primary"
 96 |                BlueprintIdentifier = "C0244E4E2047A6CB00ACB4AC"
 97 |                BuildableName = "PMKJSA+Tests.xctest"
 98 |                BlueprintName = "PMKJSA+Tests"
 99 |                ReferencedContainer = "container:PromiseKit.xcodeproj">
100 |             </BuildableReference>
101 |          </TestableReference>
102 |       </Testables>
103 |    </TestAction>
104 |    <LaunchAction
105 |       buildConfiguration = "Debug"
106 |       selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
107 |       selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
108 |       launchStyle = "0"
109 |       useCustomWorkingDirectory = "NO"
110 |       ignoresPersistentStateOnLaunch = "NO"
111 |       debugDocumentVersioning = "YES"
112 |       debugServiceExtension = "internal"
113 |       allowLocationSimulation = "YES">
114 |       <MacroExpansion>
115 |          <BuildableReference
116 |             BuildableIdentifier = "primary"
117 |             BlueprintIdentifier = "63B0AC561D595E1B00FA21D9"
118 |             BuildableName = "PromiseKit.framework"
119 |             BlueprintName = "PromiseKit"
120 |             ReferencedContainer = "container:PromiseKit.xcodeproj">
121 |          </BuildableReference>
122 |       </MacroExpansion>
123 |    </LaunchAction>
124 |    <ProfileAction
125 |       buildConfiguration = "Release"
126 |       shouldUseLaunchSchemeArgsEnv = "YES"
127 |       savedToolIdentifier = ""
128 |       useCustomWorkingDirectory = "NO"
129 |       debugDocumentVersioning = "YES">
130 |       <MacroExpansion>
131 |          <BuildableReference
132 |             BuildableIdentifier = "primary"
133 |             BlueprintIdentifier = "63B0AC561D595E1B00FA21D9"
134 |             BuildableName = "PromiseKit.framework"
135 |             BlueprintName = "PromiseKit"
136 |             ReferencedContainer = "container:PromiseKit.xcodeproj">
137 |          </BuildableReference>
138 |       </MacroExpansion>
139 |    </ProfileAction>
140 |    <AnalyzeAction
141 |       buildConfiguration = "Debug">
142 |    </AnalyzeAction>
143 |    <ArchiveAction
144 |       buildConfiguration = "Release"
145 |       revealArchiveInOrganizer = "YES">
146 |    </ArchiveAction>
147 | </Scheme>
148 | 


--------------------------------------------------------------------------------
/Sources/AnyPromise+Private.h:
--------------------------------------------------------------------------------
 1 | @import Foundation.NSError;
 2 | @import Foundation.NSPointerArray;
 3 | 
 4 | #if TARGET_OS_IPHONE
 5 |     #define NSPointerArrayMake(N) ({ \
 6 |         NSPointerArray *aa = [NSPointerArray strongObjectsPointerArray]; \
 7 |         aa.count = N; \
 8 |         aa; \
 9 |     })
10 | #else
11 |     static inline NSPointerArray * __nonnull NSPointerArrayMake(NSUInteger count) {
12 |       #pragma clang diagnostic push
13 |       #pragma clang diagnostic ignored "-Wdeprecated-declarations"
14 |         NSPointerArray *aa = [[NSPointerArray class] respondsToSelector:@selector(strongObjectsPointerArray)]
15 |             ? [NSPointerArray strongObjectsPointerArray]
16 |             : [NSPointerArray pointerArrayWithStrongObjects];
17 |       #pragma clang diagnostic pop
18 |         aa.count = count;
19 |         return aa;
20 |     }
21 | #endif
22 | 
23 | #define IsError(o) [o isKindOfClass:[NSError class]]
24 | #define IsPromise(o) [o isKindOfClass:[AnyPromise class]]
25 | 
26 | #import "AnyPromise.h"
27 | 
28 | @class PMKArray;
29 | 
30 | @interface AnyPromise ()
31 | - (void)__pipe:(void(^ __nonnull)(__nullable id))block NS_REFINED_FOR_SWIFT;
32 | @end
33 | 


--------------------------------------------------------------------------------
/Sources/AnyPromise.m:
--------------------------------------------------------------------------------
  1 | #if __has_include("PromiseKit-Swift.h")
  2 |     #import "PromiseKit-Swift.h"
  3 | #else
  4 |     #import <PromiseKit/PromiseKit-Swift.h>
  5 | #endif
  6 | #import "PMKCallVariadicBlock.m"
  7 | #import "AnyPromise+Private.h"
  8 | #import "AnyPromise.h"
  9 | 
 10 | NSString *const PMKErrorDomain = @"PMKErrorDomain";
 11 | 
 12 | 
 13 | @implementation AnyPromise {
 14 |     __AnyPromise *d;
 15 | }
 16 | 
 17 | - (instancetype)initWith__D:(__AnyPromise *)dd {
 18 |     self = [super init];
 19 |     if (self) self->d = dd;
 20 |     return self;
 21 | }
 22 | 
 23 | - (instancetype)initWithResolver:(PMKResolver __strong *)resolver {
 24 |     self = [super init];
 25 |     if (self)
 26 |         d = [[__AnyPromise alloc] initWithResolver:^(void (^resolve)(id)) {
 27 |             *resolver = resolve;
 28 |         }];
 29 |     return self;
 30 | }
 31 | 
 32 | + (instancetype)promiseWithResolverBlock:(void (^)(PMKResolver _Nonnull))resolveBlock {
 33 |     id d = [[__AnyPromise alloc] initWithResolver:resolveBlock];
 34 |     return [[self alloc] initWith__D:d];
 35 | }
 36 | 
 37 | + (instancetype)promiseWithValue:(id)value {
 38 |     //TODO provide a more efficient route for sealed promises
 39 |     id d = [[__AnyPromise alloc] initWithResolver:^(void (^resolve)(id)) {
 40 |         resolve(value);
 41 |     }];
 42 |     return [[self alloc] initWith__D:d];
 43 | }
 44 | 
 45 | //TODO remove if possible, but used by when.m
 46 | - (void)__pipe:(void (^)(id _Nullable))block {
 47 |     [d __pipe:block];
 48 | }
 49 | 
 50 | //NOTE used by AnyPromise.swift
 51 | - (id)__d {
 52 |     return d;
 53 | }
 54 | 
 55 | - (AnyPromise *(^)(id))then {
 56 |     return ^(id block) {
 57 |         return [self->d __thenOn:dispatch_get_main_queue() execute:^(id obj) {
 58 |             return PMKCallVariadicBlock(block, obj);
 59 |         }];
 60 |     };
 61 | }
 62 | 
 63 | - (AnyPromise *(^)(dispatch_queue_t, id))thenOn {
 64 |     return ^(dispatch_queue_t queue, id block) {
 65 |         return [self->d __thenOn:queue execute:^(id obj) {
 66 |             return PMKCallVariadicBlock(block, obj);
 67 |         }];
 68 |     };
 69 | }
 70 | 
 71 | - (AnyPromise *(^)(id))thenInBackground {
 72 |     return ^(id block) {
 73 |         return [self->d __thenOn:dispatch_get_global_queue(0, 0) execute:^(id obj) {
 74 |             return PMKCallVariadicBlock(block, obj);
 75 |         }];
 76 |     };
 77 | }
 78 | 
 79 | - (AnyPromise *(^)(dispatch_queue_t, id))catchOn {
 80 |     return ^(dispatch_queue_t q, id block) {
 81 |         return [self->d __catchOn:q execute:^(id obj) {
 82 |             return PMKCallVariadicBlock(block, obj);
 83 |         }];
 84 |     };
 85 | }
 86 | 
 87 | - (AnyPromise *(^)(id))catch {
 88 |     return ^(id block) {
 89 |         return [self->d __catchOn:dispatch_get_main_queue() execute:^(id obj) {
 90 |             return PMKCallVariadicBlock(block, obj);
 91 |         }];
 92 |     };
 93 | }
 94 | 
 95 | - (AnyPromise *(^)(id))catchInBackground {
 96 |     return ^(id block) {
 97 |         return [self->d __catchOn:dispatch_get_global_queue(0, 0) execute:^(id obj) {
 98 |             return PMKCallVariadicBlock(block, obj);
 99 |         }];
100 |     };
101 | }
102 | 
103 | - (AnyPromise *(^)(dispatch_block_t))ensure {
104 |     return ^(dispatch_block_t block) {
105 |         return [self->d __ensureOn:dispatch_get_main_queue() execute:block];
106 |     };
107 | }
108 | 
109 | - (AnyPromise *(^)(dispatch_queue_t, dispatch_block_t))ensureOn {
110 |     return ^(dispatch_queue_t queue, dispatch_block_t block) {
111 |         return [self->d __ensureOn:queue execute:block];
112 |     };
113 | }
114 | 
115 | - (AnyPromise *(^)(dispatch_block_t))ensureInBackground {
116 |     return ^(dispatch_block_t block) {
117 |         return [self->d __ensureOn:dispatch_get_global_queue(QOS_CLASS_UNSPECIFIED, 0) execute:block];
118 |     };
119 | }
120 | 
121 | - (id)wait {
122 |     return [d __wait];
123 | }
124 | 
125 | - (BOOL)pending {
126 |     return [[d valueForKey:@"__pending"] boolValue];
127 | }
128 | 
129 | - (BOOL)rejected {
130 |     return IsError([d __value]);
131 | }
132 | 
133 | - (BOOL)fulfilled {
134 |     return !self.rejected;
135 | }
136 | 
137 | - (id)value {
138 |     id obj = [d __value];
139 | 
140 |     if ([obj isKindOfClass:[PMKArray class]]) {
141 |         return obj[0];
142 |     } else {
143 |         return obj;
144 |     }
145 | }
146 | 
147 | @end
148 | 
149 | 
150 | 
151 | @implementation AnyPromise (Adapters)
152 | 
153 | + (instancetype)promiseWithAdapterBlock:(void (^)(PMKAdapter))block {
154 |     return [self promiseWithResolverBlock:^(PMKResolver resolve) {
155 |         block(^(id value, id error){
156 |             resolve(error ?: value);
157 |         });
158 |     }];
159 | }
160 | 
161 | + (instancetype)promiseWithIntegerAdapterBlock:(void (^)(PMKIntegerAdapter))block {
162 |     return [self promiseWithResolverBlock:^(PMKResolver resolve) {
163 |         block(^(NSInteger value, id error){
164 |             if (error) {
165 |                 resolve(error);
166 |             } else {
167 |                 resolve(@(value));
168 |             }
169 |         });
170 |     }];
171 | }
172 | 
173 | + (instancetype)promiseWithBooleanAdapterBlock:(void (^)(PMKBooleanAdapter adapter))block {
174 |     return [self promiseWithResolverBlock:^(PMKResolver resolve) {
175 |         block(^(BOOL value, id error){
176 |             if (error) {
177 |                 resolve(error);
178 |             } else {
179 |                 resolve(@(value));
180 |             }
181 |         });
182 |     }];
183 | }
184 | 
185 | @end
186 | 


--------------------------------------------------------------------------------
/Sources/Async.swift:
--------------------------------------------------------------------------------
 1 | #if swift(>=5.5)
 2 | #if canImport(_Concurrency)
 3 | @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
 4 | public extension Guarantee {
 5 |     func async() async -> T {
 6 |         await withCheckedContinuation { continuation in
 7 |             done { value in
 8 |                 continuation.resume(returning: value)
 9 |             }
10 |         }
11 |     }
12 | }
13 | 
14 | @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
15 | public extension Promise {
16 |     func async() async throws -> T {
17 |         try await withCheckedThrowingContinuation { continuation in
18 |             done { value in
19 |                 continuation.resume(returning: value)
20 |             }.catch(policy: .allErrors) { error in
21 |                 continuation.resume(throwing: error)
22 |             }
23 |         }
24 |     }
25 | }
26 | #endif
27 | #endif
28 | 
29 | 


--------------------------------------------------------------------------------
/Sources/Box.swift:
--------------------------------------------------------------------------------
  1 | import Dispatch
  2 | 
  3 | enum Sealant<R> {
  4 |     case pending(Handlers<R>)
  5 |     case resolved(R)
  6 | }
  7 | 
  8 | final class Handlers<R> {
  9 |     var bodies: [(R) -> Void] = []
 10 |     func append(_ item: @escaping(R) -> Void) { bodies.append(item) }
 11 | }
 12 | 
 13 | /// - Remark: not protocol ∵ http://www.russbishop.net/swift-associated-types-cont
 14 | class Box<T> {
 15 |     func inspect() -> Sealant<T> { fatalError() }
 16 |     func inspect(_: (Sealant<T>) -> Void) { fatalError() }
 17 |     func seal(_: T) {}
 18 | }
 19 | 
 20 | final class SealedBox<T>: Box<T> {
 21 |     let value: T
 22 | 
 23 |     init(value: T) {
 24 |         self.value = value
 25 |     }
 26 | 
 27 |     override func inspect() -> Sealant<T> {
 28 |         return .resolved(value)
 29 |     }
 30 | }
 31 | 
 32 | class EmptyBox<T>: Box<T> {
 33 |     private var sealant = Sealant<T>.pending(.init())
 34 |     private let barrier = DispatchQueue(label: "org.promisekit.barrier", attributes: .concurrent)
 35 | 
 36 |     override func seal(_ value: T) {
 37 |         var handlers: Handlers<T>!
 38 |         barrier.sync(flags: .barrier) {
 39 |             guard case .pending(let _handlers) = self.sealant else {
 40 |                 return  // already fulfilled!
 41 |             }
 42 |             handlers = _handlers
 43 |             self.sealant = .resolved(value)
 44 |         }
 45 | 
 46 |         //FIXME we are resolved so should `pipe(to:)` be called at this instant, “thens are called in order” would be invalid
 47 |         //NOTE we don’t do this in the above `sync` because that could potentially deadlock
 48 |         //THOUGH since `then` etc. typically invoke after a run-loop cycle, this issue is somewhat less severe
 49 | 
 50 |         if let handlers = handlers {
 51 |             handlers.bodies.forEach{ $0(value) }
 52 |         }
 53 | 
 54 |         //TODO solution is an unfortunate third state “sealed” where then's get added
 55 |         // to a separate handler pool for that state
 56 |         // any other solution has potential races
 57 |     }
 58 | 
 59 |     override func inspect() -> Sealant<T> {
 60 |         var rv: Sealant<T>!
 61 |         barrier.sync {
 62 |             rv = self.sealant
 63 |         }
 64 |         return rv
 65 |     }
 66 | 
 67 |     override func inspect(_ body: (Sealant<T>) -> Void) {
 68 |         var sealed = false
 69 |         barrier.sync(flags: .barrier) {
 70 |             switch sealant {
 71 |             case .pending:
 72 |                 // body will append to handlers, so we must stay barrier’d
 73 |                 body(sealant)
 74 |             case .resolved:
 75 |                 sealed = true
 76 |             }
 77 |         }
 78 |         if sealed {
 79 |             // we do this outside the barrier to prevent potential deadlocks
 80 |             // it's safe because we never transition away from this state
 81 |             body(sealant)
 82 |         }
 83 |     }
 84 | }
 85 | 
 86 | 
 87 | extension Optional where Wrapped: DispatchQueue {
 88 |     @inline(__always)
 89 |     func async(flags: DispatchWorkItemFlags?, _ body: @escaping() -> Void) {
 90 |         switch self {
 91 |         case .none:
 92 |             body()
 93 |         case .some(let q):
 94 |             if let flags = flags {
 95 |                 q.async(flags: flags, execute: body)
 96 |             } else {
 97 |                 q.async(execute: body)
 98 |             }
 99 |         }
100 |     }
101 | }
102 | 


--------------------------------------------------------------------------------
/Sources/Combine.swift:
--------------------------------------------------------------------------------
 1 | #if swift(>=4.1)
 2 | #if canImport(Combine)
 3 | import Combine
 4 | 
 5 | @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
 6 | public extension Guarantee {
 7 |     func future() -> Future<T, Never> {
 8 |         .init { [weak self] promise in
 9 |             self?.done { value in
10 |                 promise(.success(value))
11 |             }
12 |         }
13 |     }
14 | }
15 | 
16 | @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
17 | public extension Promise {
18 |     func future() -> Future<T, Error> {
19 |         .init { [weak self] promise in
20 |             self?.done { value in
21 |                 promise(.success(value))
22 |             }.catch { error in
23 |                 promise(.failure(error))
24 |             }
25 |         }
26 |     }
27 | }
28 | 
29 | @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
30 | public extension Future {
31 |     func promise() -> PromiseKit.Promise<Output> {
32 |         return .init { [weak self] resolver in
33 |             var cancellable: AnyCancellable?
34 |             cancellable = self?.sink(receiveCompletion: { completion in
35 |                 cancellable?.cancel()
36 |                 cancellable = nil
37 |                 switch completion {
38 |                 case .failure(let error):
39 |                     resolver.reject(error)
40 |                 case .finished:
41 |                     break
42 |                 }
43 |             }, receiveValue: { value in
44 |                 cancellable?.cancel()
45 |                 cancellable = nil
46 |                 resolver.fulfill(value)
47 |             })
48 |         }
49 |     }
50 | }
51 | 
52 | @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
53 | public extension Future where Failure == Never {
54 |     func guarantee() -> Guarantee<Output> {
55 |         return .init { [weak self] resolver in
56 |             var cancellable: AnyCancellable?
57 |             cancellable = self?.sink(receiveValue: { value in
58 |                 cancellable?.cancel()
59 |                 cancellable = nil
60 |                 resolver(value)
61 |             })
62 |         }
63 |     }
64 | }
65 | #endif
66 | #endif
67 | 


--------------------------------------------------------------------------------
/Sources/Configuration.swift:
--------------------------------------------------------------------------------
 1 | import Dispatch
 2 | 
 3 | /**
 4 |  PromiseKit’s configurable parameters.
 5 | 
 6 |  Do not change these after any Promise machinery executes as the configuration object is not thread-safe.
 7 | 
 8 |  We would like it to be, but sadly `Swift` does not expose `dispatch_once` et al. which is what we used to use in order to make the configuration immutable once first used.
 9 | */
10 | public struct PMKConfiguration {
11 |     /// The default queues that promises handlers dispatch to
12 |     public var Q: (map: DispatchQueue?, return: DispatchQueue?) = (map: DispatchQueue.main, return: DispatchQueue.main)
13 | 
14 |     /// The default catch-policy for all `catch` and `resolve`
15 |     public var catchPolicy = CatchPolicy.allErrorsExceptCancellation
16 |     
17 |     /// The closure used to log PromiseKit events.
18 |     /// Not thread safe; change before processing any promises.
19 |     /// - Note: The default handler calls `print()`
20 |     public var logHandler: (LogEvent) -> Void = { event in
21 |         switch event {
22 |         case .waitOnMainThread:
23 |             print("PromiseKit: warning: `wait()` called on main thread!")
24 |         case .pendingPromiseDeallocated:
25 |             print("PromiseKit: warning: pending promise deallocated")
26 |         case .pendingGuaranteeDeallocated:
27 |             print("PromiseKit: warning: pending guarantee deallocated")
28 |         case .cauterized (let error):
29 |             print("PromiseKit:cauterized-error: \(error)")
30 |         }
31 |     }
32 | }
33 | 
34 | /// Modify this as soon as possible in your application’s lifetime
35 | public var conf = PMKConfiguration()
36 | 


--------------------------------------------------------------------------------
/Sources/CustomStringConvertible.swift:
--------------------------------------------------------------------------------
 1 | 
 2 | extension Promise: CustomStringConvertible {
 3 |     /// - Returns: A description of the state of this promise.
 4 |     public var description: String {
 5 |         switch result {
 6 |         case nil:
 7 |             return "Promise(…\(T.self))"
 8 |         case .rejected(let error)?:
 9 |             return "Promise(\(error))"
10 |         case .fulfilled(let value)?:
11 |             return "Promise(\(value))"
12 |         }
13 |     }
14 | }
15 | 
16 | extension Promise: CustomDebugStringConvertible {
17 |     /// - Returns: A debug-friendly description of the state of this promise.
18 |     public var debugDescription: String {
19 |         switch box.inspect() {
20 |         case .pending(let handlers):
21 |             return "Promise<\(T.self)>.pending(handlers: \(handlers.bodies.count))"
22 |         case .resolved(.rejected(let error)):
23 |             return "Promise<\(T.self)>.rejected(\(type(of: error)).\(error))"
24 |         case .resolved(.fulfilled(let value)):
25 |             return "Promise<\(T.self)>.fulfilled(\(value))"
26 |         }
27 |     }
28 | }
29 | 
30 | #if !SWIFT_PACKAGE
31 | extension AnyPromise {
32 |     /// - Returns: A description of the state of this promise.
33 |     override open var description: String {
34 |         switch box.inspect() {
35 |         case .pending:
36 |             return "AnyPromise(…)"
37 |         case .resolved(let obj?):
38 |             return "AnyPromise(\(obj))"
39 |         case .resolved(nil):
40 |             return "AnyPromise(nil)"
41 |         }
42 |     }
43 | }
44 | #endif
45 | 


--------------------------------------------------------------------------------
/Sources/Deprecations.swift:
--------------------------------------------------------------------------------
 1 | import Dispatch
 2 | 
 3 | @available(*, deprecated, message: "See `init(resolver:)`")
 4 | public func wrap<T>(_ body: (@escaping (T?, Error?) -> Void) throws -> Void) -> Promise<T> {
 5 |     return Promise { seal in
 6 |         try body(seal.resolve)
 7 |     }
 8 | }
 9 | 
10 | @available(*, deprecated, message: "See `init(resolver:)`")
11 | public func wrap<T>(_ body: (@escaping (T, Error?) -> Void) throws -> Void) -> Promise<T>  {
12 |     return Promise { seal in
13 |         try body(seal.resolve)
14 |     }
15 | }
16 | 
17 | @available(*, deprecated, message: "See `init(resolver:)`")
18 | public func wrap<T>(_ body: (@escaping (Error?, T?) -> Void) throws -> Void) -> Promise<T> {
19 |     return Promise { seal in
20 |         try body(seal.resolve)
21 |     }
22 | }
23 | 
24 | @available(*, deprecated, message: "See `init(resolver:)`")
25 | public func wrap(_ body: (@escaping (Error?) -> Void) throws -> Void) -> Promise<Void> {
26 |     return Promise { seal in
27 |         try body(seal.resolve)
28 |     }
29 | }
30 | 
31 | @available(*, deprecated, message: "See `init(resolver:)`")
32 | public func wrap<T>(_ body: (@escaping (T) -> Void) throws -> Void) -> Promise<T> {
33 |     return Promise { seal in
34 |         try body(seal.fulfill)
35 |     }
36 | }
37 | 
38 | public extension Promise {
39 |     @available(*, deprecated, message: "See `ensure`")
40 |     func always(on q: DispatchQueue = .main, execute body: @escaping () -> Void) -> Promise {
41 |         return ensure(on: q, body)
42 |     }
43 | }
44 | 
45 | public extension Thenable {
46 | #if PMKFullDeprecations
47 |     /// disabled due to ambiguity with the other `.flatMap`
48 |     @available(*, deprecated, message: "See: `compactMap`")
49 |     func flatMap<U>(on: DispatchQueue? = conf.Q.map, _ transform: @escaping(T) throws -> U?) -> Promise<U> {
50 |         return compactMap(on: on, transform)
51 |     }
52 | #endif
53 | }
54 | 
55 | public extension Thenable where T: Sequence {
56 | #if PMKFullDeprecations
57 |     /// disabled due to ambiguity with the other `.map`
58 |     @available(*, deprecated, message: "See: `mapValues`")
59 |     func map<U>(on: DispatchQueue? = conf.Q.map, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U]> {
60 |         return mapValues(on: on, transform)
61 |     }
62 | 
63 |     /// disabled due to ambiguity with the other `.flatMap`
64 |     @available(*, deprecated, message: "See: `flatMapValues`")
65 |     func flatMap<U: Sequence>(on: DispatchQueue? = conf.Q.map, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.Iterator.Element]> {
66 |         return flatMapValues(on: on, transform)
67 |     }
68 | #endif
69 | 
70 |     @available(*, deprecated, message: "See: `filterValues`")
71 |     func filter(on: DispatchQueue? = conf.Q.map, test: @escaping (T.Iterator.Element) -> Bool) -> Promise<[T.Iterator.Element]> {
72 |         return filterValues(on: on, test)
73 |     }
74 | }
75 | 
76 | public extension Thenable where T: Collection {
77 |     @available(*, deprecated, message: "See: `firstValue`")
78 |     var first: Promise<T.Iterator.Element> {
79 |         return firstValue
80 |     }
81 | 
82 |     @available(*, deprecated, message: "See: `lastValue`")
83 |     var last: Promise<T.Iterator.Element> {
84 |         return lastValue
85 |     }
86 | }
87 | 
88 | public extension Thenable where T: Sequence, T.Iterator.Element: Comparable {
89 |     @available(*, deprecated, message: "See: `sortedValues`")
90 |     func sorted(on: DispatchQueue? = conf.Q.map) -> Promise<[T.Iterator.Element]> {
91 |         return sortedValues(on: on)
92 |     }
93 | }
94 | 


--------------------------------------------------------------------------------
/Sources/Error.swift:
--------------------------------------------------------------------------------
  1 | import Foundation
  2 | 
  3 | public enum PMKError: Error {
  4 |     /**
  5 |      The completionHandler with form `(T?, Error?)` was called with `(nil, nil)`.
  6 |      This is invalid as per Cocoa/Apple calling conventions.
  7 |      */
  8 |     case invalidCallingConvention
  9 | 
 10 |     /**
 11 |      A handler returned its own promise. 99% of the time, this is likely a 
 12 |      programming error. It is also invalid per Promises/A+.
 13 |      */
 14 |     case returnedSelf
 15 | 
 16 |     /** `when()`, `race()` etc. were called with invalid parameters, eg. an empty array. */
 17 |     case badInput
 18 | 
 19 |     /// The operation was cancelled
 20 |     case cancelled
 21 | 
 22 |     /// `nil` was returned from `flatMap`
 23 |     @available(*, deprecated, message: "See: `compactMap`")
 24 |     case flatMap(Any, Any.Type)
 25 | 
 26 |     /// `nil` was returned from `compactMap`
 27 |     case compactMap(Any, Any.Type)
 28 | 
 29 |     /**
 30 |      The lastValue or firstValue of a sequence was requested but the sequence was empty.
 31 | 
 32 |      Also used if all values of this collection failed the test passed to `firstValue(where:)`.
 33 |      */
 34 |     case emptySequence
 35 | 
 36 |     /// no winner in `race(fulfilled:)`
 37 |     case noWinner
 38 | }
 39 | 
 40 | extension PMKError: CustomDebugStringConvertible {
 41 |     public var debugDescription: String {
 42 |         switch self {
 43 |         case .flatMap(let obj, let type):
 44 |             return "Could not `flatMap<\(type)>`: \(obj)"
 45 |         case .compactMap(let obj, let type):
 46 |             return "Could not `compactMap<\(type)>`: \(obj)"
 47 |         case .invalidCallingConvention:
 48 |             return "A closure was called with an invalid calling convention, probably (nil, nil)"
 49 |         case .returnedSelf:
 50 |             return "A promise handler returned itself"
 51 |         case .badInput:
 52 |             return "Bad input was provided to a PromiseKit function"
 53 |         case .cancelled:
 54 |             return "The asynchronous sequence was cancelled"
 55 |         case .emptySequence:
 56 |             return "The first or last element was requested for an empty sequence"
 57 |         case .noWinner:
 58 |             return "All thenables passed to race(fulfilled:) were rejected"
 59 |         }
 60 |     }
 61 | }
 62 | 
 63 | extension PMKError: LocalizedError {
 64 |     public var errorDescription: String? {
 65 |         return debugDescription
 66 |     }
 67 | }
 68 | 
 69 | 
 70 | //////////////////////////////////////////////////////////// Cancellation
 71 | 
 72 | /// An error that may represent the cancelled condition
 73 | public protocol CancellableError: Error {
 74 |     /// returns true if this Error represents a cancelled condition
 75 |     var isCancelled: Bool { get }
 76 | }
 77 | 
 78 | extension Error {
 79 |     public var isCancelled: Bool {
 80 |         do {
 81 |             throw self
 82 |         } catch PMKError.cancelled {
 83 |             return true
 84 |         } catch let error as CancellableError {
 85 |             return error.isCancelled
 86 |         } catch URLError.cancelled {
 87 |             return true
 88 |         } catch CocoaError.userCancelled {
 89 |             return true
 90 |         } catch let error as NSError {
 91 |             #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
 92 |                 let domain = error.domain
 93 |                 let code = error.code
 94 |                 return ("SKErrorDomain", 2) == (domain, code)
 95 |             #else
 96 |                 return false
 97 |             #endif
 98 |         } catch {
 99 |             return false
100 |         }
101 |     }
102 | }
103 | 
104 | /// Used by `catch` and `recover`
105 | public enum CatchPolicy {
106 |     /// Indicates that `catch` or `recover` handle all error types including cancellable-errors.
107 |     case allErrors
108 | 
109 |     /// Indicates that `catch` or `recover` handle all error except cancellable-errors.
110 |     case allErrorsExceptCancellation
111 | }
112 | 


--------------------------------------------------------------------------------
/Sources/Info.plist:
--------------------------------------------------------------------------------
 1 | <?xml version="1.0" encoding="UTF-8"?>
 2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 3 | <plist version="1.0">
 4 | <dict>
 5 | 	<key>CFBundleDevelopmentRegion</key>
 6 | 	<string>en</string>
 7 | 	<key>CFBundleExecutable</key>
 8 | 	<string>$(EXECUTABLE_NAME)</string>
 9 | 	<key>CFBundleIdentifier</key>
10 | 	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11 | 	<key>CFBundleInfoDictionaryVersion</key>
12 | 	<string>6.0</string>
13 | 	<key>CFBundleName</key>
14 | 	<string>$(PRODUCT_NAME)</string>
15 | 	<key>CFBundlePackageType</key>
16 | 	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
17 | 	<key>CFBundleShortVersionString</key>
18 | 	<string>$(CURRENT_PROJECT_VERSION)</string>
19 | 	<key>CFBundleSignature</key>
20 | 	<string>????</string>
21 | 	<key>CFBundleVersion</key>
22 | 	<string>1</string>
23 | 	<key>UISupportedInterfaceOrientations</key>
24 | 	<array>
25 | 		<string>UIInterfaceOrientationPortrait</string>
26 | 	</array>
27 | </dict>
28 | </plist>
29 | 


--------------------------------------------------------------------------------
/Sources/LogEvent.swift:
--------------------------------------------------------------------------------
 1 | /**
 2 |     The PromiseKit events which may be logged.
 3 |  
 4 |     ````
 5 |     /// A promise or guarantee has blocked the main thread
 6 |     case waitOnMainThread
 7 |  
 8 |     /// A promise has been deallocated without being resolved
 9 |     case pendingPromiseDeallocated
10 |  
11 |     /// An error which occurred while fulfilling a promise was swallowed
12 |     case cauterized(Error)
13 |  
14 |     /// Errors which give a string error message
15 |     case misc (String)
16 |     ````
17 | */
18 | public enum LogEvent {
19 |     /// A promise or guarantee has blocked the main thread
20 |     case waitOnMainThread
21 |     
22 |     /// A promise has been deallocated without being resolved
23 |     case pendingPromiseDeallocated
24 |     
25 |     /// A guarantee has been deallocated without being resolved
26 |     case pendingGuaranteeDeallocated
27 |     
28 |     /// An error which occurred while resolving a promise was swallowed
29 |     case cauterized(Error)
30 | }
31 | 


--------------------------------------------------------------------------------
/Sources/NSMethodSignatureForBlock.m:
--------------------------------------------------------------------------------
 1 | #import <Foundation/NSMethodSignature.h>
 2 | 
 3 | struct PMKBlockLiteral {
 4 |     void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
 5 |     int flags;
 6 |     int reserved;
 7 |     void (*invoke)(void *, ...);
 8 |     struct block_descriptor {
 9 |         unsigned long int reserved;	// NULL
10 |     	unsigned long int size;         // sizeof(struct Block_literal_1)
11 |         // optional helper functions
12 |     	void (*copy_helper)(void *dst, void *src);     // IFF (1<<25)
13 |     	void (*dispose_helper)(void *src);             // IFF (1<<25)
14 |         // required ABI.2010.3.16
15 |         const char *signature;                         // IFF (1<<30)
16 |     } *descriptor;
17 |     // imported variables
18 | };
19 | 
20 | typedef NS_OPTIONS(NSUInteger, PMKBlockDescriptionFlags) {
21 |     PMKBlockDescriptionFlagsHasCopyDispose = (1 << 25),
22 |     PMKBlockDescriptionFlagsHasCtor = (1 << 26), // helpers have C++ code
23 |     PMKBlockDescriptionFlagsIsGlobal = (1 << 28),
24 |     PMKBlockDescriptionFlagsHasStret = (1 << 29), // IFF BLOCK_HAS_SIGNATURE
25 |     PMKBlockDescriptionFlagsHasSignature = (1 << 30)
26 | };
27 | 
28 | // It appears 10.7 doesn't support quotes in method signatures. Remove them
29 | // via @rabovik's method. See https://github.com/OliverLetterer/SLObjectiveCRuntimeAdditions/pull/2
30 | #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_8
31 | NS_INLINE static const char * pmk_removeQuotesFromMethodSignature(const char *str){
32 |     char *result = malloc(strlen(str) + 1);
33 |     BOOL skip = NO;
34 |     char *to = result;
35 |     char c;
36 |     while ((c = *str++)) {
37 |         if ('"' == c) {
38 |             skip = !skip;
39 |             continue;
40 |         }
41 |         if (skip) continue;
42 |         *to++ = c;
43 |     }
44 |     *to = '\0';
45 |     return result;
46 | }
47 | #endif
48 | 
49 | static NSMethodSignature *NSMethodSignatureForBlock(id block) {
50 |     if (!block)
51 |         return nil;
52 | 
53 |     struct PMKBlockLiteral *blockRef = (__bridge struct PMKBlockLiteral *)block;
54 |     PMKBlockDescriptionFlags flags = (PMKBlockDescriptionFlags)blockRef->flags;
55 | 
56 |     if (flags & PMKBlockDescriptionFlagsHasSignature) {
57 |         void *signatureLocation = blockRef->descriptor;
58 |         signatureLocation += sizeof(unsigned long int);
59 |         signatureLocation += sizeof(unsigned long int);
60 | 
61 |         if (flags & PMKBlockDescriptionFlagsHasCopyDispose) {
62 |             signatureLocation += sizeof(void(*)(void *dst, void *src));
63 |             signatureLocation += sizeof(void (*)(void *src));
64 |         }
65 | 
66 |         const char *signature = (*(const char **)signatureLocation);
67 | #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_8
68 |         signature = pmk_removeQuotesFromMethodSignature(signature);
69 |         NSMethodSignature *nsSignature = [NSMethodSignature signatureWithObjCTypes:signature];
70 |         free((void *)signature);
71 | 
72 |         return nsSignature;
73 | #endif
74 |         return [NSMethodSignature signatureWithObjCTypes:signature];
75 |     }
76 |     return 0;
77 | }
78 | 


--------------------------------------------------------------------------------
/Sources/PMKCallVariadicBlock.m:
--------------------------------------------------------------------------------
  1 | #import "NSMethodSignatureForBlock.m"
  2 | #import <Foundation/NSDictionary.h>
  3 | #import <Foundation/NSException.h>
  4 | #import "AnyPromise+Private.h"
  5 | #import <Foundation/NSError.h>
  6 | #import <dispatch/once.h>
  7 | #import <string.h>
  8 | 
  9 | #ifndef PMKLog
 10 | #define PMKLog NSLog
 11 | #endif
 12 | 
 13 | @interface PMKArray : NSObject {
 14 | @public
 15 |     id objs[3];
 16 |     NSUInteger count;
 17 | } @end
 18 | 
 19 | @implementation PMKArray
 20 | 
 21 | - (id)objectAtIndexedSubscript:(NSUInteger)idx {
 22 |     if (count <= idx) {
 23 |         // this check is necessary due to lack of checks in `pmk_safely_call_block`
 24 |         return nil;
 25 |     }
 26 |     return objs[idx];
 27 | }
 28 | 
 29 | @end
 30 | 
 31 | id __PMKArrayWithCount(NSUInteger count, ...) {
 32 |     PMKArray *this = [PMKArray new];
 33 |     this->count = count;
 34 |     va_list args;
 35 |     va_start(args, count);
 36 |     for (NSUInteger x = 0; x < count; ++x)
 37 |         this->objs[x] = va_arg(args, id);
 38 |     va_end(args);
 39 |     return this;
 40 | }
 41 | 
 42 | 
 43 | static inline id _PMKCallVariadicBlock(id frock, id result) {
 44 |     NSCAssert(frock, @"");
 45 | 
 46 |     NSMethodSignature *sig = NSMethodSignatureForBlock(frock);
 47 |     const NSUInteger nargs = sig.numberOfArguments;
 48 |     const char rtype = sig.methodReturnType[0];
 49 | 
 50 |     #define call_block_with_rtype(type) ({^type{ \
 51 |         switch (nargs) { \
 52 |             case 1: \
 53 |                 return ((type(^)(void))frock)(); \
 54 |             case 2: { \
 55 |                 const id arg = [result class] == [PMKArray class] ? result[0] : result; \
 56 |                 return ((type(^)(id))frock)(arg); \
 57 |             } \
 58 |             case 3: { \
 59 |                 type (^block)(id, id) = frock; \
 60 |                 return [result class] == [PMKArray class] \
 61 |                     ? block(result[0], result[1]) \
 62 |                     : block(result, nil); \
 63 |             } \
 64 |             case 4: { \
 65 |                 type (^block)(id, id, id) = frock; \
 66 |                 return [result class] == [PMKArray class] \
 67 |                     ? block(result[0], result[1], result[2]) \
 68 |                     : block(result, nil, nil); \
 69 |             } \
 70 |             default: \
 71 |                 @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"PromiseKit: The provided block’s argument count is unsupported." userInfo:nil]; \
 72 |         }}();})
 73 | 
 74 |     switch (rtype) {
 75 |         case 'v':
 76 |             call_block_with_rtype(void);
 77 |             return nil;
 78 |         case '@':
 79 |             return call_block_with_rtype(id) ?: nil;
 80 |         case '*': {
 81 |             char *str = call_block_with_rtype(char *);
 82 |             return str ? @(str) : nil;
 83 |         }
 84 |         case 'c': return @(call_block_with_rtype(char));
 85 |         case 'i': return @(call_block_with_rtype(int));
 86 |         case 's': return @(call_block_with_rtype(short));
 87 |         case 'l': return @(call_block_with_rtype(long));
 88 |         case 'q': return @(call_block_with_rtype(long long));
 89 |         case 'C': return @(call_block_with_rtype(unsigned char));
 90 |         case 'I': return @(call_block_with_rtype(unsigned int));
 91 |         case 'S': return @(call_block_with_rtype(unsigned short));
 92 |         case 'L': return @(call_block_with_rtype(unsigned long));
 93 |         case 'Q': return @(call_block_with_rtype(unsigned long long));
 94 |         case 'f': return @(call_block_with_rtype(float));
 95 |         case 'd': return @(call_block_with_rtype(double));
 96 |         case 'B': return @(call_block_with_rtype(_Bool));
 97 |         case '^':
 98 |             if (strcmp(sig.methodReturnType, "^v") == 0) {
 99 |                 call_block_with_rtype(void);
100 |                 return nil;
101 |             }
102 |             // else fall through!
103 |         default:
104 |             @throw [NSException exceptionWithName:@"PromiseKit" reason:@"PromiseKit: Unsupported method signature." userInfo:nil];
105 |     }
106 | }
107 | 
108 | static id PMKCallVariadicBlock(id frock, id result) {
109 |     @try {
110 |         return _PMKCallVariadicBlock(frock, result);
111 |     } @catch (id thrown) {
112 |         if ([thrown isKindOfClass:[NSString class]])
113 |             return thrown;
114 |         if ([thrown isKindOfClass:[NSError class]])
115 |             return thrown;
116 | 
117 |         // we don’t catch objc exceptions: they are meant to crash your app
118 |         @throw thrown;
119 |     }
120 | }
121 | 


--------------------------------------------------------------------------------
/Sources/Promise.swift:
--------------------------------------------------------------------------------
  1 | import class Foundation.Thread
  2 | import Dispatch
  3 | 
  4 | /**
  5 |  A `Promise` is a functional abstraction around a failable asynchronous operation.
  6 |  - See: `Thenable`
  7 |  */
  8 | public final class Promise<T>: Thenable, CatchMixin {
  9 |     let box: Box<Result<T>>
 10 | 
 11 |     fileprivate init(box: SealedBox<Result<T>>) {
 12 |         self.box = box
 13 |     }
 14 | 
 15 |     /**
 16 |       Initialize a new fulfilled promise.
 17 | 
 18 |       We do not provide `init(value:)` because Swift is “greedy”
 19 |       and would pick that initializer in cases where it should pick
 20 |       one of the other more specific options leading to Promises with
 21 |       `T` that is eg: `Error` or worse `(T->Void,Error->Void)` for
 22 |       uses of our PMK < 4 pending initializer due to Swift trailing
 23 |       closure syntax (nothing good comes without pain!).
 24 | 
 25 |       Though often easy to detect, sometimes these issues would be
 26 |       hidden by other type inference leading to some nasty bugs in
 27 |       production.
 28 | 
 29 |       In PMK5 we tried to work around this by making the pending
 30 |       initializer take the form `Promise(.pending)` but this led to
 31 |       bad migration errors for PMK4 users. Hence instead we quickly
 32 |       released PMK6 and now only provide this initializer for making
 33 |       sealed & fulfilled promises.
 34 | 
 35 |       Usage is still (usually) good:
 36 | 
 37 |           guard foo else {
 38 |               return .value(bar)
 39 |           }
 40 |      */
 41 |     public static func value(_ value: T) -> Promise<T> {
 42 |         return Promise(box: SealedBox(value: .fulfilled(value)))
 43 |     }
 44 | 
 45 |     /// Initialize a new rejected promise.
 46 |     public init(error: Error) {
 47 |         box = SealedBox(value: .rejected(error))
 48 |     }
 49 | 
 50 |     /// Initialize a new promise bound to the provided `Thenable`.
 51 |     public init<U: Thenable>(_ bridge: U) where U.T == T {
 52 |         box = EmptyBox()
 53 |         bridge.pipe(to: box.seal)
 54 |     }
 55 | 
 56 |     /// Initialize a new promise that can be resolved with the provided `Resolver`.
 57 |     public init(resolver body: (Resolver<T>) throws -> Void) {
 58 |         box = EmptyBox()
 59 |         let resolver = Resolver(box)
 60 |         do {
 61 |             try body(resolver)
 62 |         } catch {
 63 |             resolver.reject(error)
 64 |         }
 65 |     }
 66 | 
 67 |     /// - Returns: a tuple of a new pending promise and its `Resolver`.
 68 |     public class func pending() -> (promise: Promise<T>, resolver: Resolver<T>) {
 69 |         return { ($0, Resolver($0.box)) }(Promise<T>(.pending))
 70 |     }
 71 | 
 72 |     /// - See: `Thenable.pipe`
 73 |     public func pipe(to: @escaping(Result<T>) -> Void) {
 74 |         switch box.inspect() {
 75 |         case .pending:
 76 |             box.inspect {
 77 |                 switch $0 {
 78 |                 case .pending(let handlers):
 79 |                     handlers.append(to)
 80 |                 case .resolved(let value):
 81 |                     to(value)
 82 |                 }
 83 |             }
 84 |         case .resolved(let value):
 85 |             to(value)
 86 |         }
 87 |     }
 88 | 
 89 |     /// - See: `Thenable.result`
 90 |     public var result: Result<T>? {
 91 |         switch box.inspect() {
 92 |         case .pending:
 93 |             return nil
 94 |         case .resolved(let result):
 95 |             return result
 96 |         }
 97 |     }
 98 | 
 99 |     init(_: PMKUnambiguousInitializer) {
100 |         box = EmptyBox()
101 |     }
102 | }
103 | 
104 | public extension Promise {
105 |     /**
106 |      Blocks this thread, so—you know—don’t call this on a serial thread that
107 |      any part of your chain may use. Like the main thread for example.
108 |      */
109 |     func wait() throws -> T {
110 | 
111 |         if Thread.isMainThread {
112 |             conf.logHandler(LogEvent.waitOnMainThread)
113 |         }
114 | 
115 |         var result = self.result
116 | 
117 |         if result == nil {
118 |             let group = DispatchGroup()
119 |             group.enter()
120 |             pipe { result = $0; group.leave() }
121 |             group.wait()
122 |         }
123 | 
124 |         switch result! {
125 |         case .rejected(let error):
126 |             throw error
127 |         case .fulfilled(let value):
128 |             return value
129 |         }
130 |     }
131 | }
132 | 
133 | #if swift(>=3.1)
134 | extension Promise where T == Void {
135 |     /// Initializes a new promise fulfilled with `Void`
136 |     public convenience init() {
137 |         self.init(box: SealedBox(value: .fulfilled(Void())))
138 |     }
139 | 
140 |     /// Returns a new promise fulfilled with `Void`
141 |     public static var value: Promise<Void> {
142 |         return .value(Void())
143 |     }
144 | }
145 | #endif
146 | 
147 | 
148 | public extension DispatchQueue {
149 |     /**
150 |      Asynchronously executes the provided closure on a dispatch queue.
151 | 
152 |          DispatchQueue.global().async(.promise) {
153 |              try md5(input)
154 |          }.done { md5 in
155 |              //…
156 |          }
157 | 
158 |      - Parameter body: The closure that resolves this promise.
159 |      - Returns: A new `Promise` resolved by the result of the provided closure.
160 |      - Note: There is no Promise/Thenable version of this due to Swift compiler ambiguity issues.
161 |      */
162 |     @available(macOS 10.10, iOS 8.0, tvOS 9.0, watchOS 2.0, *)
163 |     final func async<T>(_: PMKNamespacer, group: DispatchGroup? = nil, qos: DispatchQoS = .default, flags: DispatchWorkItemFlags = [], execute body: @escaping () throws -> T) -> Promise<T> {
164 |         let promise = Promise<T>(.pending)
165 |         async(group: group, qos: qos, flags: flags) {
166 |             do {
167 |                 promise.box.seal(.fulfilled(try body()))
168 |             } catch {
169 |                 promise.box.seal(.rejected(error))
170 |             }
171 |         }
172 |         return promise
173 |     }
174 | }
175 | 
176 | 
177 | /// used by our extensions to provide unambiguous functions with the same name as the original function
178 | public enum PMKNamespacer {
179 |     case promise
180 | }
181 | 
182 | enum PMKUnambiguousInitializer {
183 |     case pending
184 | }
185 | 


--------------------------------------------------------------------------------
/Sources/PromiseKit.h:
--------------------------------------------------------------------------------
1 | #import <PromiseKit/fwd.h>
2 | #import <PromiseKit/AnyPromise.h>
3 | 
4 | #import <Foundation/NSObjCRuntime.h>  // `FOUNDATION_EXPORT`
5 | 
6 | FOUNDATION_EXPORT double PromiseKitVersionNumber;
7 | FOUNDATION_EXPORT const unsigned char PromiseKitVersionString[];
8 | 


--------------------------------------------------------------------------------
/Sources/Resolver.swift:
--------------------------------------------------------------------------------
  1 | /// An object for resolving promises
  2 | public final class Resolver<T> {
  3 |     let box: Box<Result<T>>
  4 | 
  5 |     init(_ box: Box<Result<T>>) {
  6 |         self.box = box
  7 |     }
  8 | 
  9 |     deinit {
 10 |         if case .pending = box.inspect() {
 11 |             conf.logHandler(.pendingPromiseDeallocated)
 12 |         }
 13 |     }
 14 | }
 15 | 
 16 | public extension Resolver {
 17 |     /// Fulfills the promise with the provided value
 18 |     func fulfill(_ value: T) {
 19 |         box.seal(.fulfilled(value))
 20 |     }
 21 | 
 22 |     /// Rejects the promise with the provided error
 23 |     func reject(_ error: Error) {
 24 |         box.seal(.rejected(error))
 25 |     }
 26 | 
 27 |     /// Resolves the promise with the provided result
 28 |     func resolve(_ result: Result<T>) {
 29 |         box.seal(result)
 30 |     }
 31 | 
 32 |     /// Resolves the promise with the provided value or error
 33 |     func resolve(_ obj: T?, _ error: Error?) {
 34 |         if let error = error {
 35 |             reject(error)
 36 |         } else if let obj = obj {
 37 |             fulfill(obj)
 38 |         } else {
 39 |             reject(PMKError.invalidCallingConvention)
 40 |         }
 41 |     }
 42 | 
 43 |     /// Fulfills the promise with the provided value unless the provided error is non-nil
 44 |     func resolve(_ obj: T, _ error: Error?) {
 45 |         if let error = error {
 46 |             reject(error)
 47 |         } else {
 48 |             fulfill(obj)
 49 |         }
 50 |     }
 51 | 
 52 |     /// Resolves the promise, provided for non-conventional value-error ordered completion handlers.
 53 |     func resolve(_ error: Error?, _ obj: T?) {
 54 |         resolve(obj, error)
 55 |     }
 56 | }
 57 | 
 58 | #if swift(>=3.1)
 59 | extension Resolver where T == Void {
 60 |     /// Fulfills the promise unless error is non-nil
 61 |     public func resolve(_ error: Error?) {
 62 |         if let error = error {
 63 |             reject(error)
 64 |         } else {
 65 |             fulfill(())
 66 |         }
 67 |     }
 68 | #if false
 69 |     // disabled ∵ https://github.com/mxcl/PromiseKit/issues/990
 70 | 
 71 |     /// Fulfills the promise
 72 |     public func fulfill() {
 73 |         self.fulfill(())
 74 |     }
 75 | #else
 76 |     /// Fulfills the promise
 77 |     /// - Note: underscore is present due to: https://github.com/mxcl/PromiseKit/issues/990
 78 |     public func fulfill_() {
 79 |         self.fulfill(())
 80 |     }
 81 | #endif
 82 | }
 83 | #endif
 84 | 
 85 | #if swift(>=5.0)
 86 | extension Resolver {
 87 |     /// Resolves the promise with the provided result
 88 |     public func resolve<E: Error>(_ result: Swift.Result<T, E>) {
 89 |         switch result {
 90 |         case .failure(let error): self.reject(error)
 91 |         case .success(let value): self.fulfill(value)
 92 |         }
 93 |     }
 94 | }
 95 | #endif
 96 | 
 97 | public enum Result<T> {
 98 |     case fulfilled(T)
 99 |     case rejected(Error)
100 | }
101 | 
102 | public extension PromiseKit.Result {
103 |     var isFulfilled: Bool {
104 |         switch self {
105 |         case .fulfilled:
106 |             return true
107 |         case .rejected:
108 |             return false
109 |         }
110 |     }
111 | }
112 | 


--------------------------------------------------------------------------------
/Sources/Resources/PrivacyInfo.xcprivacy:
--------------------------------------------------------------------------------
1 | <?xml version="1.0" encoding="UTF-8"?>
2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3 | <plist version="1.0">
4 | <dict>
5 | 	<key>NSPrivacyTracking</key>
6 | 	<false/>
7 | </dict>
8 | </plist>
9 | 


--------------------------------------------------------------------------------
/Sources/after.m:
--------------------------------------------------------------------------------
 1 | #import "AnyPromise.h"
 2 | @import Dispatch;
 3 | @import Foundation.NSDate;
 4 | @import Foundation.NSValue;
 5 | 
 6 | /// @return A promise that fulfills after the specified duration.
 7 | AnyPromise *PMKAfter(NSTimeInterval duration) {
 8 |     return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
 9 |         dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC));
10 |         dispatch_after(time, dispatch_get_global_queue(QOS_CLASS_UNSPECIFIED, 0), ^{
11 |             resolve(@(duration));
12 |         });
13 |     }];
14 | }
15 | 


--------------------------------------------------------------------------------
/Sources/after.swift:
--------------------------------------------------------------------------------
 1 | import struct Foundation.TimeInterval
 2 | import Dispatch
 3 | 
 4 | /**
 5 |      after(seconds: 1.5).then {
 6 |          //…
 7 |      }
 8 | 
 9 | - Returns: A guarantee that resolves after the specified duration.
10 | */
11 | public func after(seconds: TimeInterval) -> Guarantee<Void> {
12 |     let (rg, seal) = Guarantee<Void>.pending()
13 |     let when = DispatchTime.now() + seconds
14 | #if swift(>=4.0)
15 |     q.asyncAfter(deadline: when) { seal(()) }
16 | #else
17 |     q.asyncAfter(deadline: when, execute: seal)
18 | #endif
19 |     return rg
20 | }
21 | 
22 | /**
23 |      after(.seconds(2)).then {
24 |          //…
25 |      }
26 | 
27 |  - Returns: A guarantee that resolves after the specified duration.
28 | */
29 | public func after(_ interval: DispatchTimeInterval) -> Guarantee<Void> {
30 |     let (rg, seal) = Guarantee<Void>.pending()
31 |     let when = DispatchTime.now() + interval
32 | #if swift(>=4.0)
33 |     q.asyncAfter(deadline: when) { seal(()) }
34 | #else
35 |     q.asyncAfter(deadline: when, execute: seal)
36 | #endif
37 |     return rg
38 | }
39 | 
40 | private var q: DispatchQueue {
41 |     if #available(macOS 10.10, iOS 8.0, tvOS 9.0, watchOS 2.0, *) {
42 |         return DispatchQueue.global(qos: .default)
43 |     } else {
44 |         return DispatchQueue.global(priority: .default)
45 |     }
46 | }
47 | 


--------------------------------------------------------------------------------
/Sources/dispatch_promise.m:
--------------------------------------------------------------------------------
 1 | #import "AnyPromise.h"
 2 | @import Dispatch;
 3 | 
 4 | AnyPromise *dispatch_promise_on(dispatch_queue_t queue, id block) {
 5 |     return [AnyPromise promiseWithValue:nil].thenOn(queue, block);
 6 | }
 7 | 
 8 | AnyPromise *dispatch_promise(id block) {
 9 |     return dispatch_promise_on(dispatch_get_global_queue(QOS_CLASS_UNSPECIFIED, 0), block);
10 | }
11 | 


--------------------------------------------------------------------------------
/Sources/firstly.swift:
--------------------------------------------------------------------------------
 1 | import Dispatch
 2 | 
 3 | /**
 4 |  Judicious use of `firstly` *may* make chains more readable.
 5 | 
 6 |  Compare:
 7 | 
 8 |      URLSession.shared.dataTask(url: url1).then {
 9 |          URLSession.shared.dataTask(url: url2)
10 |      }.then {
11 |          URLSession.shared.dataTask(url: url3)
12 |      }
13 | 
14 |  With:
15 | 
16 |      firstly {
17 |          URLSession.shared.dataTask(url: url1)
18 |      }.then {
19 |          URLSession.shared.dataTask(url: url2)
20 |      }.then {
21 |          URLSession.shared.dataTask(url: url3)
22 |      }
23 | 
24 |  - Note: the block you pass executes immediately on the current thread/queue.
25 |  */
26 | public func firstly<U: Thenable>(execute body: () throws -> U) -> Promise<U.T> {
27 |     do {
28 |         let rp = Promise<U.T>(.pending)
29 |         try body().pipe(to: rp.box.seal)
30 |         return rp
31 |     } catch {
32 |         return Promise(error: error)
33 |     }
34 | }
35 | 
36 | /// - See: firstly()
37 | public func firstly<T>(execute body: () -> Guarantee<T>) -> Guarantee<T> {
38 |     return body()
39 | }
40 | 


--------------------------------------------------------------------------------
/Sources/hang.m:
--------------------------------------------------------------------------------
 1 | #import "AnyPromise.h"
 2 | #import "AnyPromise+Private.h"
 3 | @import CoreFoundation.CFRunLoop;
 4 | 
 5 | /**
 6 |  Suspends the active thread waiting on the provided promise.
 7 | 
 8 |  @return The value of the provided promise once resolved. 
 9 |  */
10 | id PMKHang(AnyPromise *promise) {
11 |     if (promise.pending) {
12 |         static CFRunLoopSourceContext context;
13 | 
14 |         CFRunLoopRef runLoop = CFRunLoopGetCurrent();
15 |         CFRunLoopSourceRef runLoopSource = CFRunLoopSourceCreate(NULL, 0, &context);
16 |         CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopDefaultMode);
17 | 
18 |         promise.ensure(^{
19 |             CFRunLoopStop(runLoop);
20 |         });
21 |         while (promise.pending) {
22 |             CFRunLoopRun();
23 |         }
24 |         CFRunLoopRemoveSource(runLoop, runLoopSource, kCFRunLoopDefaultMode);
25 |         CFRelease(runLoopSource);
26 |     }
27 | 
28 |     return promise.value;
29 | }
30 | 


--------------------------------------------------------------------------------
/Sources/hang.swift:
--------------------------------------------------------------------------------
 1 | import Foundation
 2 | import CoreFoundation
 3 | 
 4 | /**
 5 |  Runs the active run-loop until the provided promise resolves.
 6 | 
 7 |  This is for debug and is not a generally safe function to use in your applications. We mostly provide it for use in testing environments.
 8 | 
 9 |  Still if you like, study how it works (by reading the sources!) and use at your own risk.
10 | 
11 |  - Returns: The value of the resolved promise
12 |  - Throws: An error, should the promise be rejected
13 |  - See: `wait()`
14 | */
15 | public func hang<T>(_ promise: Promise<T>) throws -> T {
16 | #if os(Linux) || os(Android)
17 | #if swift(>=4)
18 |     let runLoopMode: CFRunLoopMode = kCFRunLoopDefaultMode
19 | #else
20 |     // isMainThread is not yet implemented on Linux.
21 |     let runLoopModeRaw = RunLoopMode.defaultRunLoopMode.rawValue._bridgeToObjectiveC()
22 |     let runLoopMode: CFString = unsafeBitCast(runLoopModeRaw, to: CFString.self)
23 | #endif
24 | #else
25 |     guard Thread.isMainThread else {
26 |         // hang doesn't make sense on threads that aren't the main thread.
27 |         // use `.wait()` on those threads.
28 |         fatalError("Only call hang() on the main thread.")
29 |     }
30 |     let runLoopMode: CFRunLoopMode = CFRunLoopMode.defaultMode
31 | #endif
32 | 
33 |     if promise.isPending {
34 |         var context = CFRunLoopSourceContext()
35 |         let runLoop = CFRunLoopGetCurrent()
36 |         let runLoopSource = CFRunLoopSourceCreate(nil, 0, &context)
37 |         CFRunLoopAddSource(runLoop, runLoopSource, runLoopMode)
38 | 
39 |         _ = promise.ensure {
40 |             CFRunLoopStop(runLoop)
41 |         }
42 | 
43 |         while promise.isPending {
44 |             CFRunLoopRun()
45 |         }
46 |         CFRunLoopRemoveSource(runLoop, runLoopSource, runLoopMode)
47 |     }
48 | 
49 |     switch promise.result! {
50 |     case .rejected(let error):
51 |         throw error
52 |     case .fulfilled(let value):
53 |         return value
54 |     }
55 | }
56 | 


--------------------------------------------------------------------------------
/Sources/join.m:
--------------------------------------------------------------------------------
 1 | @import Foundation.NSDictionary;
 2 | #import "AnyPromise+Private.h"
 3 | #import <libkern/OSAtomic.h>
 4 | @import Foundation.NSError;
 5 | @import Foundation.NSNull;
 6 | #import "PromiseKit.h"
 7 | #import <stdatomic.h>
 8 | 
 9 | /**
10 |  Waits on all provided promises.
11 | 
12 |  `PMKWhen` rejects as soon as one of the provided promises rejects. `PMKJoin` waits on all provided promises, then rejects if any of those promises rejects, otherwise it fulfills with values from the provided promises.
13 | 
14 |  - Returns: A new promise that resolves once all the provided promises resolve.
15 | */
16 | AnyPromise *PMKJoin(NSArray *promises) {
17 |     if (promises == nil)
18 |         return [AnyPromise promiseWithValue:[NSError errorWithDomain:PMKErrorDomain code:PMKInvalidUsageError userInfo:@{NSLocalizedDescriptionKey: @"PMKJoin(nil)"}]];
19 | 
20 |     if (promises.count == 0)
21 |         return [AnyPromise promiseWithValue:promises];
22 | 
23 |     return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
24 |         NSPointerArray *results = NSPointerArrayMake(promises.count);
25 |         __block atomic_int countdown = promises.count;
26 |         __block BOOL rejected = NO;
27 | 
28 |         [promises enumerateObjectsUsingBlock:^(AnyPromise *promise, NSUInteger ii, BOOL *stop) {
29 |             [promise __pipe:^(id value) {
30 | 
31 |                 if (IsError(value)) {
32 |                     rejected = YES;
33 |                 }
34 | 
35 |                 //FIXME surely this isn't thread safe on multiple cores?
36 |                 [results replacePointerAtIndex:ii withPointer:(__bridge void *)(value ?: [NSNull null])];
37 | 
38 |                 atomic_fetch_sub_explicit(&countdown, 1, memory_order_relaxed);
39 | 
40 |                 if (countdown == 0) {
41 |                     if (!rejected) {
42 |                         resolve(results.allObjects);
43 |                     } else {
44 |                         id userInfo = @{PMKJoinPromisesKey: promises};
45 |                         id err = [NSError errorWithDomain:PMKErrorDomain code:PMKJoinError userInfo:userInfo];
46 |                         resolve(err);
47 |                     }
48 |                 }
49 |             }];
50 | 
51 |             (void) stop;
52 |         }];
53 |     }];
54 | }
55 | 


--------------------------------------------------------------------------------
/Sources/race.m:
--------------------------------------------------------------------------------
 1 | #import "AnyPromise+Private.h"
 2 | #import <libkern/OSAtomic.h>
 3 | 
 4 | #pragma clang diagnostic push
 5 | #pragma clang diagnostic ignored "-Wdeprecated-declarations"
 6 | // ^^ OSAtomicDecrement32 is deprecated on watchOS
 7 | 
 8 | AnyPromise *PMKRace(NSArray *promises) {
 9 |     if (promises == nil || promises.count == 0)
10 |         return [AnyPromise promiseWithValue:[NSError errorWithDomain:PMKErrorDomain code:PMKInvalidUsageError userInfo:@{NSLocalizedDescriptionKey: @"PMKRace(nil)"}]];
11 | 
12 |     return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
13 |         for (AnyPromise *promise in promises) {
14 |             [promise __pipe:resolve];
15 |         }
16 |     }];
17 | }
18 | 
19 | /**
20 |  Waits for one promise to fulfill
21 | 
22 |  @note If there are no fulfilled promises, the returned promise is rejected with `PMKNoWinnerError`.
23 |  @param promises The promises to fulfill.
24 |  @return The promise that was fulfilled first.
25 | */
26 | AnyPromise *PMKRaceFulfilled(NSArray *promises) {
27 |     if (promises == nil || promises.count == 0)
28 |         return [AnyPromise promiseWithValue:[NSError errorWithDomain:PMKErrorDomain code:PMKInvalidUsageError userInfo:@{NSLocalizedDescriptionKey: @"PMKRaceFulfilled(nil)"}]];
29 | 
30 |     __block int32_t countdown = (int32_t)[promises count];
31 | 
32 |     return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
33 |         for (__strong AnyPromise* promise in promises) {
34 |             [promise __pipe:^(id value) {
35 |                 if (IsError(value)) {
36 |                     if (OSAtomicDecrement32(&countdown) == 0) {
37 |                         id err = [NSError errorWithDomain:PMKErrorDomain code:PMKNoWinnerError userInfo:@{NSLocalizedDescriptionKey: @"PMKRaceFulfilled(nil)"}];
38 |                         resolve(err);
39 |                     }
40 |                 } else {
41 |                     resolve(value);
42 |                 }
43 |             }];
44 |         }
45 |     }];
46 | }
47 | 
48 | #pragma GCC diagnostic pop
49 | 


--------------------------------------------------------------------------------
/Sources/race.swift:
--------------------------------------------------------------------------------
  1 | import Dispatch
  2 | 
  3 | @inline(__always)
  4 | private func _race<U: Thenable>(_ thenables: [U]) -> Promise<U.T> {
  5 |     let rp = Promise<U.T>(.pending)
  6 |     for thenable in thenables {
  7 |         thenable.pipe(to: rp.box.seal)
  8 |     }
  9 |     return rp
 10 | }
 11 | 
 12 | /**
 13 |  Waits for one promise to resolve
 14 | 
 15 |      race(promise1, promise2, promise3).then { winner in
 16 |          //…
 17 |      }
 18 | 
 19 |  - Returns: The promise that resolves first
 20 |  - Warning: If the first resolution is a rejection, the returned promise is rejected
 21 | */
 22 | public func race<U: Thenable>(_ thenables: U...) -> Promise<U.T> {
 23 |     return _race(thenables)
 24 | }
 25 | 
 26 | /**
 27 |  Waits for one promise to resolve
 28 | 
 29 |      race(promise1, promise2, promise3).then { winner in
 30 |          //…
 31 |      }
 32 | 
 33 |  - Returns: The promise that resolves first
 34 |  - Warning: If the first resolution is a rejection, the returned promise is rejected
 35 |  - Remark: If the provided array is empty the returned promise is rejected with PMKError.badInput
 36 | */
 37 | public func race<U: Thenable>(_ thenables: [U]) -> Promise<U.T> {
 38 |     guard !thenables.isEmpty else {
 39 |         return Promise(error: PMKError.badInput)
 40 |     }
 41 |     return _race(thenables)
 42 | }
 43 | 
 44 | /**
 45 |  Waits for one guarantee to resolve
 46 | 
 47 |      race(promise1, promise2, promise3).then { winner in
 48 |          //…
 49 |      }
 50 | 
 51 |  - Returns: The guarantee that resolves first
 52 | */
 53 | public func race<T>(_ guarantees: Guarantee<T>...) -> Guarantee<T> {
 54 |     let rg = Guarantee<T>(.pending)
 55 |     for guarantee in guarantees {
 56 |         guarantee.pipe(to: rg.box.seal)
 57 |     }
 58 |     return rg
 59 | }
 60 | 
 61 | /**
 62 |  Waits for one promise to fulfill
 63 | 
 64 |      race(fulfilled: [promise1, promise2, promise3]).then { winner in
 65 |          //…
 66 |      }
 67 | 
 68 |  - Returns: The promise that was fulfilled first.
 69 |  - Warning: Skips all rejected promises.
 70 |  - Remark: If the provided array is empty, the returned promise is rejected with `PMKError.badInput`. If there are no fulfilled promises, the returned promise is rejected with `PMKError.noWinner`.
 71 | */
 72 | public func race<U: Thenable>(fulfilled thenables: [U]) -> Promise<U.T> {
 73 |     var countdown = thenables.count
 74 |     guard countdown > 0 else {
 75 |         return Promise(error: PMKError.badInput)
 76 |     }
 77 | 
 78 |     let rp = Promise<U.T>(.pending)
 79 | 
 80 |     let barrier = DispatchQueue(label: "org.promisekit.barrier.race", attributes: .concurrent)
 81 | 
 82 |     for promise in thenables {
 83 |         promise.pipe { result in
 84 |             barrier.sync(flags: .barrier) {
 85 |                 switch result {
 86 |                 case .rejected:
 87 |                     guard rp.isPending else { return }
 88 |                     countdown -= 1
 89 |                     if countdown == 0 {
 90 |                         rp.box.seal(.rejected(PMKError.noWinner))
 91 |                     }
 92 |                 case .fulfilled(let value):
 93 |                     guard rp.isPending else { return }
 94 |                     countdown = 0
 95 |                     rp.box.seal(.fulfilled(value))
 96 |                 }
 97 |             }
 98 |         }
 99 |     }
100 | 
101 |     return rp
102 | }
103 | 


--------------------------------------------------------------------------------
/Sources/when.m:
--------------------------------------------------------------------------------
  1 | @import Foundation.NSDictionary;
  2 | #import "AnyPromise+Private.h"
  3 | @import Foundation.NSProgress;
  4 | #import <libkern/OSAtomic.h>
  5 | @import Foundation.NSError;
  6 | @import Foundation.NSNull;
  7 | #import "PromiseKit.h"
  8 | 
  9 | #pragma clang diagnostic push
 10 | #pragma clang diagnostic ignored "-Wdeprecated-declarations"
 11 | // ^^ OSAtomicDecrement32 is deprecated on watchOS
 12 | 
 13 | 
 14 | // NSProgress resources:
 15 | //  * https://robots.thoughtbot.com/asynchronous-nsprogress
 16 | //  * http://oleb.net/blog/2014/03/nsprogress/
 17 | // NSProgress! Beware!
 18 | //  * https://github.com/AFNetworking/AFNetworking/issues/2261
 19 | 
 20 | /**
 21 |  Wait for all promises in a set to resolve.
 22 | 
 23 |  @note If *any* of the provided promises reject, the returned promise is immediately rejected with that error.
 24 |  @warning In the event of rejection the other promises will continue to resolve and, as per any other promise, will either fulfill or reject. This is the right pattern for `getter` style asynchronous tasks, but often for `setter` tasks (eg. storing data on a server), you most likely will need to wait on all tasks and then act based on which have succeeded and which have failed, in such situations use `when(resolved:)`.
 25 |  @param promises The promises upon which to wait before the returned promise resolves.
 26 |  @note PMKWhen provides NSProgress.
 27 |  @return A new promise that resolves when all the provided promises fulfill or one of the provided promises rejects.
 28 | */
 29 | AnyPromise *PMKWhen(id promises) {
 30 |     if (promises == nil)
 31 |         return [AnyPromise promiseWithValue:[NSError errorWithDomain:PMKErrorDomain code:PMKInvalidUsageError userInfo:@{NSLocalizedDescriptionKey: @"PMKWhen(nil)"}]];
 32 | 
 33 |     if ([promises isKindOfClass:[NSArray class]] || [promises isKindOfClass:[NSDictionary class]]) {
 34 |         if ([promises count] == 0)
 35 |             return [AnyPromise promiseWithValue:promises];
 36 |     } else if ([promises isKindOfClass:[AnyPromise class]]) {
 37 |         promises = @[promises];
 38 |     } else {
 39 |         return [AnyPromise promiseWithValue:promises];
 40 |     }
 41 | 
 42 | #ifndef PMKDisableProgress
 43 |     NSProgress *progress = [NSProgress progressWithTotalUnitCount:(int64_t)[promises count]];
 44 |     progress.pausable = NO;
 45 |     progress.cancellable = NO;
 46 | #else
 47 |     struct PMKProgress {
 48 |         int completedUnitCount;
 49 |         int totalUnitCount;
 50 |         double fractionCompleted;
 51 |     };
 52 |     __block struct PMKProgress progress;
 53 | #endif
 54 | 
 55 |     __block int32_t countdown = (int32_t)[promises count];
 56 |     BOOL const isdict = [promises isKindOfClass:[NSDictionary class]];
 57 | 
 58 |     return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
 59 |         NSInteger index = 0;
 60 | 
 61 |         for (__strong id key in promises) {
 62 |             AnyPromise *promise = isdict ? promises[key] : key;
 63 |             if (!isdict) key = @(index);
 64 | 
 65 |             if (![promise isKindOfClass:[AnyPromise class]])
 66 |                 promise = [AnyPromise promiseWithValue:promise];
 67 | 
 68 |             [promise __pipe:^(id value){
 69 |                 if (progress.fractionCompleted >= 1)
 70 |                     return;
 71 | 
 72 |                 if (IsError(value)) {
 73 |                     progress.completedUnitCount = progress.totalUnitCount;
 74 | 
 75 |                     NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:[(NSError *)value userInfo] ?: @{}];
 76 |                     userInfo[PMKFailingPromiseIndexKey] = key;
 77 |                     [userInfo setObject:value forKey:NSUnderlyingErrorKey];
 78 |                     id err = [[NSError alloc] initWithDomain:[value domain] code:[value code] userInfo:userInfo];
 79 |                     resolve(err);
 80 |                 }
 81 |                 else if (OSAtomicDecrement32(&countdown) == 0) {
 82 |                     progress.completedUnitCount = progress.totalUnitCount;
 83 | 
 84 |                     id results;
 85 |                     if (isdict) {
 86 |                         results = [NSMutableDictionary new];
 87 |                         for (id key in promises) {
 88 |                             id promise = promises[key];
 89 |                             results[key] = IsPromise(promise) ? ((AnyPromise *)promise).value : promise;
 90 |                         }
 91 |                     } else {
 92 |                         results = [NSMutableArray new];
 93 |                         for (AnyPromise *promise in promises) {
 94 |                             id value = IsPromise(promise) ? (promise.value ?: [NSNull null]) : promise;
 95 |                             [results addObject:value];
 96 |                         }
 97 |                     }
 98 |                     resolve(results);
 99 |                 } else {
100 |                     progress.completedUnitCount++;
101 |                 }
102 |             }];
103 |         }
104 |     }];
105 | }
106 | 
107 | #pragma GCC diagnostic pop
108 | 


--------------------------------------------------------------------------------
/Tests/A+/2.1.2.swift:
--------------------------------------------------------------------------------
 1 | import PromiseKit
 2 | import XCTest
 3 | 
 4 | class Test212: XCTestCase {
 5 |     func test() {
 6 |         describe("2.1.2.1: When fulfilled, a promise: must not transition to any other state.") {
 7 |             testFulfilled { promise, expectation, _ in
 8 |                 promise.test(onFulfilled: expectation.fulfill, onRejected: { XCTFail() })
 9 |             }
10 | 
11 |             specify("trying to fulfill then immediately reject") { d, expectation in
12 |                 d.promise.test(onFulfilled: expectation.fulfill, onRejected: { XCTFail() })
13 |                 d.fulfill()
14 |                 d.reject(Error.dummy)
15 |             }
16 | 
17 |             specify("trying to fulfill then reject, delayed") { d, expectation in
18 |                 d.promise.test(onFulfilled: expectation.fulfill, onRejected: { XCTFail() })
19 |                 after(ticks: 1) {
20 |                     d.fulfill()
21 |                     d.reject(Error.dummy)
22 |                 }
23 |             }
24 |         }
25 |     }
26 | }
27 | 


--------------------------------------------------------------------------------
/Tests/A+/2.1.3.swift:
--------------------------------------------------------------------------------
 1 | import PromiseKit
 2 | import XCTest
 3 | 
 4 | class Test213: XCTestCase {
 5 |     func test() {
 6 |         describe("2.1.3.1: When rejected, a promise: must not transition to any other state.") {
 7 |             testRejected { promise, expectation, _ in
 8 |                 promise.test(onFulfilled: { XCTFail() }, onRejected: expectation.fulfill)
 9 |             }
10 | 
11 |             specify("trying to reject then immediately fulfill") { d, expectation in
12 |                 d.promise.test(onFulfilled: { XCTFail() }, onRejected: expectation.fulfill)
13 |                 d.reject(Error.dummy)
14 |                 d.fulfill()
15 |             }
16 | 
17 |             specify("trying to reject then fulfill, delayed") { d, expectation in
18 |                 d.promise.test(onFulfilled: { XCTFail() }, onRejected: expectation.fulfill)
19 |                 after(ticks: 1) {
20 |                     d.reject(Error.dummy)
21 |                     d.fulfill()
22 |                 }
23 |             }
24 | 
25 |             specify("trying to reject immediately then fulfill delayed") { d, expectation in
26 |                 d.promise.test(onFulfilled: { XCTFail() }, onRejected: expectation.fulfill)
27 |                 d.reject(Error.dummy)
28 |                 after(ticks: 1) {
29 |                     d.fulfill()
30 |                 }
31 |             }
32 |         }
33 |     }
34 | }
35 | 


--------------------------------------------------------------------------------
/Tests/A+/2.2.2.swift:
--------------------------------------------------------------------------------
 1 | import PromiseKit
 2 | import XCTest
 3 | 
 4 | class Test222: XCTestCase {
 5 |     func test() {
 6 |         describe("2.2.2: If `onFulfilled` is a function,") {
 7 |             describe("2.2.2.1: it must be called after `promise` is fulfilled, with `promise`’s fulfillment value as its first argument.") {
 8 |                 testFulfilled { promise, expectation, sentinel in
 9 |                     promise.done {
10 |                         XCTAssertEqual(sentinel, $0)
11 |                         expectation.fulfill()
12 |                     }.silenceWarning()
13 |                 }
14 |             }
15 | 
16 |             describe("2.2.2.2: it must not be called before `promise` is fulfilled") {
17 |                 specify("fulfilled after a delay") { d, expectation in
18 |                     var called = false
19 |                     d.promise.done {
20 |                         called = true
21 |                         expectation.fulfill()
22 |                     }.silenceWarning()
23 |                     after(ticks: 5) {
24 |                         XCTAssertFalse(called)
25 |                         d.fulfill()
26 |                     }
27 |                 }
28 |                 specify("never fulfilled") { d, expectation in
29 |                     d.promise.done{ XCTFail() }.silenceWarning()
30 |                     after(ticks: 1000, execute: expectation.fulfill)
31 |                 }
32 |             }
33 | 
34 |             describe("2.2.2.3: it must not be called more than once.") {
35 |                 specify("already-fulfilled") { _, expectation in
36 |                     let ex = (expectation, mkex())
37 |                     Promise().done {
38 |                         ex.0.fulfill()
39 |                     }.silenceWarning()
40 |                     after(ticks: 1000) {
41 |                         ex.1.fulfill()
42 |                     }
43 |                 }
44 |                 specify("trying to fulfill a pending promise more than once, immediately") { d, expectation in
45 |                     d.promise.done(expectation.fulfill).silenceWarning()
46 |                     d.fulfill()
47 |                     d.fulfill()
48 |                 }
49 |                 specify("trying to fulfill a pending promise more than once, delayed") { d, expectation in
50 |                     d.promise.done(expectation.fulfill).silenceWarning()
51 |                     after(ticks: 5) {
52 |                         d.fulfill()
53 |                         d.fulfill()
54 |                     }
55 |                 }
56 |                 specify("trying to fulfill a pending promise more than once, immediately then delayed") { d, expectation in
57 |                     let ex = (expectation, mkex())
58 |                     d.promise.done(ex.0.fulfill).silenceWarning()
59 |                     d.fulfill()
60 |                     after(ticks: 5) {
61 |                         d.fulfill()
62 |                     }
63 |                     after(ticks: 10, execute: ex.1.fulfill)
64 |                 }
65 |                 specify("when multiple `then` calls are made, spaced apart in time") { d, expectation in
66 |                     let ex = (expectation, self.expectation(description: ""), self.expectation(description: ""), self.expectation(description: ""))
67 | 
68 |                     do {
69 |                         d.promise.done(ex.0.fulfill).silenceWarning()
70 |                     }
71 |                     after(ticks: 5) {
72 |                         d.promise.done(ex.1.fulfill).silenceWarning()
73 |                     }
74 |                     after(ticks: 10) {
75 |                         d.promise.done(ex.2.fulfill).silenceWarning()
76 |                     }
77 |                     after(ticks: 15) {
78 |                         d.fulfill()
79 |                         ex.3.fulfill()
80 |                     }
81 |                 }
82 |                 specify("when `then` is interleaved with fulfillment") { d, expectation in
83 |                     let ex = (expectation, self.expectation(description: ""), self)
84 | 
85 |                     d.promise.done(ex.0.fulfill).silenceWarning()
86 |                     d.fulfill()
87 |                     d.promise.done(ex.1.fulfill).silenceWarning()
88 |                 }
89 |             }
90 |         }
91 |     }
92 | }
93 | 


--------------------------------------------------------------------------------
/Tests/A+/2.2.3.swift:
--------------------------------------------------------------------------------
 1 | import PromiseKit
 2 | import XCTest
 3 | 
 4 | class Test223: XCTestCase {
 5 |     func test() {
 6 |         describe("2.2.3: If `onRejected` is a function,") {
 7 |             describe("2.2.3.1: it must be called after `promise` is rejected, with `promise`’s rejection reason as its first argument.") {
 8 |                 testRejected { promise, expectation, sentinel in
 9 |                     promise.catch { error in
10 |                         if case Error.sentinel(let value) = error {
11 |                             XCTAssertEqual(value, sentinel)
12 |                         } else {
13 |                             XCTFail()
14 |                         }
15 |                         expectation.fulfill()
16 |                     }
17 |                 }
18 |             }
19 |             describe("2.2.3.2: it must not be called before `promise` is rejected") {
20 |                 specify("rejected after a delay") { d, expectation in
21 |                     var called = false
22 |                     d.promise.catch { _ in
23 |                         called = true
24 |                         expectation.fulfill()
25 |                     }
26 |                     after(ticks: 1) {
27 |                         XCTAssertFalse(called)
28 |                         d.reject(Error.dummy)
29 |                     }
30 |                 }
31 |                 specify("never rejected") { d, expectation in
32 |                     d.promise.catch { _ in XCTFail() }
33 |                     after(ticks: 1, execute: expectation.fulfill)
34 |                 }
35 |             }
36 |             describe("2.2.3.3: it must not be called more than once.") {
37 |                 specify("already-rejected") { d, expectation in
38 |                     var timesCalled = 0
39 |                     Promise<Int>(error: Error.dummy).catch { _ in
40 |                         XCTAssertEqual(++timesCalled, 1)
41 |                     }
42 |                     after(ticks: 2) {
43 |                         XCTAssertEqual(timesCalled, 1)
44 |                         expectation.fulfill()
45 |                     }
46 |                 }
47 |                 specify("trying to reject a pending promise more than once, immediately") { d, expectation in
48 |                     d.promise.catch{_ in expectation.fulfill() }
49 |                     d.reject(Error.dummy)
50 |                     d.reject(Error.dummy)
51 |                 }
52 |                 specify("trying to reject a pending promise more than once, delayed") { d, expectation in
53 |                     d.promise.catch{_ in expectation.fulfill() }
54 |                     after(ticks: 1) {
55 |                         d.reject(Error.dummy)
56 |                         d.reject(Error.dummy)
57 |                     }
58 |                 }
59 |                 specify("trying to reject a pending promise more than once, immediately then delayed") { d, expectation in
60 |                     d.promise.catch{_ in expectation.fulfill() }
61 |                     d.reject(Error.dummy)
62 |                     after(ticks: 1) {
63 |                         d.reject(Error.dummy)
64 |                     }
65 |                 }
66 |                 specify("when multiple `then` calls are made, spaced apart in time") { d, expectation in
67 |                     let mk = { self.expectation(description: "") }
68 |                     let ex = (expectation, mk(), mk(), mk())
69 | 
70 |                     do {
71 |                         d.promise.catch{ _ in ex.0.fulfill() }
72 |                     }
73 |                     after(ticks: 1) {
74 |                         d.promise.catch{ _ in ex.1.fulfill() }
75 |                     }
76 |                     after(ticks: 2) {
77 |                         d.promise.catch{ _ in ex.2.fulfill() }
78 |                     }
79 |                     after(ticks: 3) {
80 |                         d.reject(Error.dummy)
81 |                         ex.3.fulfill()
82 |                     }
83 |                 }
84 |                 specify("when `then` is interleaved with rejection") { d, expectation in
85 |                     let ex = (expectation, self.expectation(description: ""))
86 |                     d.promise.catch{ _ in ex.0.fulfill() }
87 |                     d.reject(Error.dummy)
88 |                     d.promise.catch{ _ in ex.1.fulfill() }
89 |                 }
90 |             }
91 |         }
92 |     }
93 | }
94 | 


--------------------------------------------------------------------------------
/Tests/A+/2.2.7.swift:
--------------------------------------------------------------------------------
 1 | import PromiseKit
 2 | import XCTest
 3 | 
 4 | class Test227: XCTestCase {
 5 |     func test() {
 6 |         describe("2.2.7: `then` must return a promise: `promise2 = promise1.then(onFulfilled, onRejected)") {
 7 |             describe("2.2.7.2: If either `onFulfilled` or `onRejected` throws an exception `e`, `promise2` must be rejected with `e` as the reason.") {
 8 | 
 9 |                 testFulfilled { promise1, expectation, _ in
10 |                     let sentinel = arc4random()
11 |                     let promise2 = promise1.done { _ in throw Error.sentinel(sentinel) }
12 | 
13 |                     promise2.catch {
14 |                         if case Error.sentinel(let x) = $0, x == sentinel {
15 |                             expectation.fulfill()
16 |                         }
17 |                     }
18 |                 }
19 | 
20 |                 testRejected { promise1, expectation, _ in
21 |                     let sentinel = arc4random()
22 |                     let promise2 = promise1.recover { _ -> Promise<UInt32> in throw Error.sentinel(sentinel) }
23 | 
24 |                     promise2.catch { error in
25 |                         if case Error.sentinel(let x) = error, x == sentinel {
26 |                             expectation.fulfill()
27 |                         }
28 |                     }
29 |                 }
30 |             }
31 |         }
32 |     }
33 | }
34 | 


--------------------------------------------------------------------------------
/Tests/A+/2.3.1.swift:
--------------------------------------------------------------------------------
 1 | import PromiseKit
 2 | import XCTest
 3 | 
 4 | class Test231: XCTestCase {
 5 |     func test() {
 6 |         describe("2.3.1: If `promise` and `x` refer to the same object, reject `promise` with a `TypeError' as the reason.") {
 7 |             specify("via return from a fulfilled promise") { d, expectation in
 8 |                 var promise: Promise<Void>!
 9 |                 promise = Promise().then { () -> Promise<Void> in
10 |                     return promise
11 |                 }
12 |                 promise.catch { err in
13 |                     if case PMKError.returnedSelf = err {
14 |                         expectation.fulfill()
15 |                     }
16 |                 }
17 |             }
18 |             specify("via return from a rejected promise") { d, expectation in
19 |                 var promise: Promise<Void>!
20 |                 promise = Promise<Void>(error: Error.dummy).recover { _ -> Promise<Void> in
21 |                     return promise
22 |                 }
23 |                 promise.catch { err in
24 |                     if case PMKError.returnedSelf = err {
25 |                         expectation.fulfill()
26 |                     }
27 |                 }
28 |             }
29 |         }
30 |     }
31 | }
32 | 


--------------------------------------------------------------------------------
/Tests/A+/2.3.2.swift:
--------------------------------------------------------------------------------
  1 | import PromiseKit
  2 | import XCTest
  3 | 
  4 | class Test232: XCTestCase {
  5 |     func test() {
  6 |         describe("2.3.2: If `x` is a promise, adopt its state") {
  7 |             describe("2.3.2.1: If `x` is pending, `promise` must remain pending until `x` is fulfilled or rejected.") {
  8 | 
  9 |                 func xFactory() -> Promise<UInt32> {
 10 |                     return Promise.pending().promise
 11 |                 }
 12 | 
 13 |                 testPromiseResolution(factory: xFactory) { promise, expectation in
 14 |                     var wasFulfilled = false;
 15 |                     var wasRejected = false;
 16 | 
 17 |                     promise.test(onFulfilled: { wasFulfilled = true }, onRejected: { wasRejected = true })
 18 | 
 19 |                     after(ticks: 4) {
 20 |                         XCTAssertFalse(wasFulfilled)
 21 |                         XCTAssertFalse(wasRejected)
 22 |                         expectation.fulfill()
 23 |                     }
 24 |                 }
 25 |             }
 26 | 
 27 |             describe("2.3.2.2: If/when `x` is fulfilled, fulfill `promise` with the same value.") {
 28 |                 describe("`x` is already-fulfilled") {
 29 |                     let sentinel = arc4random()
 30 | 
 31 |                     func xFactory() -> Promise<UInt32> {
 32 |                         return .value(sentinel)
 33 |                     }
 34 | 
 35 |                     testPromiseResolution(factory: xFactory) { promise, expectation in
 36 |                         promise.done {
 37 |                             XCTAssertEqual($0, sentinel)
 38 |                             expectation.fulfill()
 39 |                         }.silenceWarning()
 40 |                     }
 41 |                 }
 42 |                 describe("`x` is eventually-fulfilled") {
 43 |                     let sentinel = arc4random()
 44 | 
 45 |                     func xFactory() -> Promise<UInt32> {
 46 |                         return Promise { seal in
 47 |                             after(ticks: 2) {
 48 |                                 seal.fulfill(sentinel)
 49 |                             }
 50 |                         }
 51 |                     }
 52 | 
 53 |                     testPromiseResolution(factory: xFactory) { promise, expectation in
 54 |                         promise.done {
 55 |                             XCTAssertEqual($0, sentinel)
 56 |                             expectation.fulfill()
 57 |                         }.silenceWarning()
 58 |                     }
 59 |                 }
 60 |             }
 61 | 
 62 |             describe("2.3.2.3: If/when `x` is rejected, reject `promise` with the same reason.") {
 63 |                 describe("`x` is already-rejected") {
 64 |                     let sentinel = arc4random()
 65 | 
 66 |                     func xFactory() -> Promise<UInt32> {
 67 |                         return Promise(error: Error.sentinel(sentinel))
 68 |                     }
 69 | 
 70 |                     testPromiseResolution(factory: xFactory) { promise, expectation in
 71 |                         promise.catch { err in
 72 |                             if case Error.sentinel(let value) = err, value == sentinel {
 73 |                                 expectation.fulfill()
 74 |                             }
 75 |                         }
 76 |                     }
 77 |                 }
 78 |                 describe("`x` is eventually-rejected") {
 79 |                     let sentinel = arc4random()
 80 | 
 81 |                     func xFactory() -> Promise<UInt32> {
 82 |                         return Promise { seal in
 83 |                             after(ticks: 2) {
 84 |                                 seal.reject(Error.sentinel(sentinel))
 85 |                             }
 86 |                         }
 87 |                     }
 88 | 
 89 |                     testPromiseResolution(factory: xFactory) { promise, expectation in
 90 |                         promise.catch { err in
 91 |                             if case Error.sentinel(let value) = err, value == sentinel {
 92 |                                 expectation.fulfill()
 93 |                             }
 94 |                         }
 95 |                     }
 96 |                 }
 97 |             }
 98 |         }
 99 |     }
100 | }
101 | 
102 | 
103 | /////////////////////////////////////////////////////////////////////////
104 | 
105 | extension Test232 {
106 |     fileprivate func testPromiseResolution(factory: @escaping () -> Promise<UInt32>, line: UInt = #line, test: (Promise<UInt32>, XCTestExpectation) -> Void) {
107 |         specify("via return from a fulfilled promise", file: #file, line: line) { d, expectation in
108 |             let promise = Promise.value(arc4random()).then { _ in factory() }
109 |             test(promise, expectation)
110 |         }
111 |         specify("via return from a rejected promise", file: #file, line: line) { d, expectation in
112 |             let promise: Promise<UInt32> = Promise(error: Error.dummy).recover { _ in factory() }
113 |             test(promise, expectation)
114 |         }
115 |     }
116 | }
117 | 


--------------------------------------------------------------------------------
/Tests/A+/2.3.4.swift:
--------------------------------------------------------------------------------
 1 | import PromiseKit
 2 | import XCTest
 3 | 
 4 | 
 5 | class Test234: XCTestCase {
 6 |     func test() {
 7 |         describe("2.3.4: If `x` is not an object or function, fulfill `promise` with `x`") {
 8 |             testFulfilled { promise, exception, _ in
 9 |                 promise.map { value -> UInt32 in
10 |                     return 1
11 |                 }.done { value in
12 |                     XCTAssertEqual(value, 1)
13 |                     exception.fulfill()
14 |                 }.silenceWarning()
15 |             }
16 |             testRejected { promise, expectation, _ in
17 |                 promise.recover { _ -> Promise<UInt32> in
18 |                     return .value(UInt32(1))
19 |                 }.done { value in
20 |                     XCTAssertEqual(value, 1)
21 |                     expectation.fulfill()
22 |                 }.silenceWarning()
23 |             }
24 |         }
25 |     }
26 | }
27 | 


--------------------------------------------------------------------------------
/Tests/A+/README.md:
--------------------------------------------------------------------------------
 1 | Resources
 2 | =========
 3 | * https://github.com/promises-aplus/promises-tests
 4 | 
 5 | 
 6 | Skipped
 7 | =======
 8 | * 2.3.3: Otherwise, if x is an object or function.
 9 |   This spec is a NOOP for Swift:
10 |   - We have decided not to interact with other Promises A+ implementations
11 |   - functions cannot have properties
12 | * 2.3.3.4: If then is not a function, fulfill promise with x.
13 |   - See: The 2.3.4 suite.
14 | 


--------------------------------------------------------------------------------
/Tests/A+/XCTestManifests.swift:
--------------------------------------------------------------------------------
  1 | #if !canImport(ObjectiveC)
  2 | import XCTest
  3 | 
  4 | #if os(Android)
  5 | extension XCTestExpectation {
  6 |     func fulfill() {
  7 |         fulfill(#file, line: #line)
  8 |     }
  9 | }
 10 | #endif
 11 | 
 12 | extension Test212 {
 13 |     // DO NOT MODIFY: This is autogenerated, use:
 14 |     //   `swift test --generate-linuxmain`
 15 |     // to regenerate.
 16 |     static let __allTests__Test212 = [
 17 |         ("test", test),
 18 |     ]
 19 | }
 20 | 
 21 | extension Test213 {
 22 |     // DO NOT MODIFY: This is autogenerated, use:
 23 |     //   `swift test --generate-linuxmain`
 24 |     // to regenerate.
 25 |     static let __allTests__Test213 = [
 26 |         ("test", test),
 27 |     ]
 28 | }
 29 | 
 30 | extension Test222 {
 31 |     // DO NOT MODIFY: This is autogenerated, use:
 32 |     //   `swift test --generate-linuxmain`
 33 |     // to regenerate.
 34 |     static let __allTests__Test222 = [
 35 |         ("test", test),
 36 |     ]
 37 | }
 38 | 
 39 | extension Test223 {
 40 |     // DO NOT MODIFY: This is autogenerated, use:
 41 |     //   `swift test --generate-linuxmain`
 42 |     // to regenerate.
 43 |     static let __allTests__Test223 = [
 44 |         ("test", test),
 45 |     ]
 46 | }
 47 | 
 48 | extension Test224 {
 49 |     // DO NOT MODIFY: This is autogenerated, use:
 50 |     //   `swift test --generate-linuxmain`
 51 |     // to regenerate.
 52 |     static let __allTests__Test224 = [
 53 |         ("test", test),
 54 |     ]
 55 | }
 56 | 
 57 | extension Test226 {
 58 |     // DO NOT MODIFY: This is autogenerated, use:
 59 |     //   `swift test --generate-linuxmain`
 60 |     // to regenerate.
 61 |     static let __allTests__Test226 = [
 62 |         ("test", test),
 63 |     ]
 64 | }
 65 | 
 66 | extension Test227 {
 67 |     // DO NOT MODIFY: This is autogenerated, use:
 68 |     //   `swift test --generate-linuxmain`
 69 |     // to regenerate.
 70 |     static let __allTests__Test227 = [
 71 |         ("test", test),
 72 |     ]
 73 | }
 74 | 
 75 | extension Test231 {
 76 |     // DO NOT MODIFY: This is autogenerated, use:
 77 |     //   `swift test --generate-linuxmain`
 78 |     // to regenerate.
 79 |     static let __allTests__Test231 = [
 80 |         ("test", test),
 81 |     ]
 82 | }
 83 | 
 84 | extension Test232 {
 85 |     // DO NOT MODIFY: This is autogenerated, use:
 86 |     //   `swift test --generate-linuxmain`
 87 |     // to regenerate.
 88 |     static let __allTests__Test232 = [
 89 |         ("test", test),
 90 |     ]
 91 | }
 92 | 
 93 | extension Test234 {
 94 |     // DO NOT MODIFY: This is autogenerated, use:
 95 |     //   `swift test --generate-linuxmain`
 96 |     // to regenerate.
 97 |     static let __allTests__Test234 = [
 98 |         ("test", test),
 99 |     ]
100 | }
101 | 
102 | public func __allTests() -> [XCTestCaseEntry] {
103 |     return [
104 |         testCase(Test212.__allTests__Test212),
105 |         testCase(Test213.__allTests__Test213),
106 |         testCase(Test222.__allTests__Test222),
107 |         testCase(Test223.__allTests__Test223),
108 |         testCase(Test224.__allTests__Test224),
109 |         testCase(Test226.__allTests__Test226),
110 |         testCase(Test227.__allTests__Test227),
111 |         testCase(Test231.__allTests__Test231),
112 |         testCase(Test232.__allTests__Test232),
113 |         testCase(Test234.__allTests__Test234),
114 |     ]
115 | }
116 | #endif
117 | 


--------------------------------------------------------------------------------
/Tests/Bridging/BridgingTests.m:
--------------------------------------------------------------------------------
 1 | @import PromiseKit;
 2 | @import XCTest;
 3 | #import "Infrastructure.h"
 4 | 
 5 | 
 6 | @interface BridgingTests: XCTestCase @end @implementation BridgingTests
 7 | 
 8 | - (void)testChainAnyPromiseFromSwiftCode {
 9 |     XCTestExpectation *ex = [self expectationWithDescription:@""];
10 |     AnyPromise *promise = PMKAfter(0.02);
11 |     for (int x = 0; x < 100; ++x) {
12 |         promise = promise.then(^{
13 |             return [[[PromiseBridgeHelper alloc] init] bridge1];
14 |         });
15 |     }
16 |     promise.then(^{
17 |         [ex fulfill];
18 |     });
19 |     [self waitForExpectationsWithTimeout:20 handler:nil];
20 | }
21 | 
22 | - (void)test626 {
23 |     XCTestExpectation *ex = [self expectationWithDescription:@""];
24 | 
25 |     testCase626().then(^{
26 |         XCTFail();
27 |     }).ensure(^{
28 |         [ex fulfill];
29 |     });
30 | 
31 |     [self waitForExpectationsWithTimeout:20 handler:nil];
32 | }
33 | 
34 | @end
35 | 


--------------------------------------------------------------------------------
/Tests/Bridging/Infrastructure.h:
--------------------------------------------------------------------------------
 1 | @import Foundation;
 2 | @class AnyPromise;
 3 | 
 4 | AnyPromise *PMKDummyAnyPromise_YES(void);
 5 | AnyPromise *PMKDummyAnyPromise_Manifold(void);
 6 | AnyPromise *PMKDummyAnyPromise_Error(void);
 7 | 
 8 | __attribute__((objc_runtime_name("PMKPromiseBridgeHelper")))
 9 | __attribute__((objc_subclassing_restricted))
10 | @interface PromiseBridgeHelper: NSObject
11 | - (AnyPromise *)bridge1;
12 | @end
13 | 
14 | AnyPromise *testCase626(void);
15 | 


--------------------------------------------------------------------------------
/Tests/Bridging/Infrastructure.m:
--------------------------------------------------------------------------------
 1 | @import Foundation;
 2 | @import PromiseKit;
 3 | #import "Infrastructure.h"
 4 | 
 5 | AnyPromise *PMKDummyAnyPromise_YES(void) {
 6 |     return [AnyPromise promiseWithValue:@YES];
 7 | }
 8 | 
 9 | AnyPromise *PMKDummyAnyPromise_Manifold(void) {
10 |     return [AnyPromise promiseWithValue:PMKManifold(@YES, @NO, @NO)];
11 | }
12 | 
13 | AnyPromise *PMKDummyAnyPromise_Error(void) {
14 |     return [AnyPromise promiseWithValue:[NSError errorWithDomain:@"a" code:1 userInfo:nil]];
15 | }
16 | 
17 | @implementation PromiseBridgeHelper (objc)
18 | 
19 | - (AnyPromise *)bridge2 {
20 |     return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
21 |         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
22 |             resolve(@123);
23 |         });
24 |     }];
25 | }
26 | 
27 | @end
28 | 
29 | #import "PMKBridgeTests-Swift.h"
30 | 
31 | AnyPromise *testCase626(void) {
32 |     return PMKWhen(@[[TestPromise626 promise], [TestPromise626 promise]]).then(^(id value){
33 |         NSLog(@"Success: %@", value);
34 |     }).catch(^(NSError *error) {
35 |         NSLog(@"Error: %@", error);
36 |         @throw error;
37 |     });
38 | }
39 | 


--------------------------------------------------------------------------------
/Tests/Bridging/Infrastructure.swift:
--------------------------------------------------------------------------------
 1 | import PromiseKit
 2 | 
 3 | // for BridgingTests.m
 4 | @objc(PMKPromiseBridgeHelper) class PromiseBridgeHelper: NSObject {
 5 |     @objc func bridge1() -> AnyPromise {
 6 |         let p = after(.milliseconds(10))
 7 |         return AnyPromise(p)
 8 |     }
 9 | }
10 | 
11 | enum MyError: Error {
12 |     case PromiseError
13 | }
14 | 
15 | @objc class TestPromise626: NSObject {
16 | 
17 |     @objc class func promise() -> AnyPromise {
18 |         let promise: Promise<String> = Promise { seal in
19 |             seal.reject(MyError.PromiseError)
20 |         }
21 | 
22 |         return AnyPromise(promise)
23 |     }
24 | }
25 | 


--------------------------------------------------------------------------------
/Tests/CoreObjC/AnyPromiseTests.swift:
--------------------------------------------------------------------------------
 1 | import PromiseKit
 2 | import XCTest
 3 | 
 4 | class AnyPromiseTests: XCTestCase {
 5 |     func testFulfilledResult() {
 6 |         switch AnyPromise(Promise.value(true)).result {
 7 |         case .fulfilled(let obj as Bool)? where obj:
 8 |             break
 9 |         default:
10 |             XCTFail()
11 |         }
12 |     }
13 | 
14 |     func testRejectedResult() {
15 |         switch AnyPromise(Promise<Int>(error: PMKError.badInput)).result {
16 |         case .rejected(let err)?:
17 |             print(err)
18 |             break
19 |         default:
20 |             XCTFail()
21 |         }
22 |     }
23 | 
24 |     func testPendingResult() {
25 |         switch AnyPromise(Promise<Int>.pending().promise).result {
26 |         case nil:
27 |             break
28 |         default:
29 |             XCTFail()
30 |         }
31 |     }
32 | 
33 |     func testCustomStringConvertible() {
34 |         XCTAssertEqual("\(AnyPromise(Promise<Int>.pending().promise))", "AnyPromise(…)")
35 |         XCTAssertEqual("\(AnyPromise(Promise.value(1)))", "AnyPromise(1)")
36 |         XCTAssertEqual("\(AnyPromise(Promise<Int?>.value(nil)))", "AnyPromise(nil)")
37 |     }
38 | }
39 | 


--------------------------------------------------------------------------------
/Tests/CoreObjC/HangTests.m:
--------------------------------------------------------------------------------
 1 | @import PromiseKit;
 2 | @import XCTest;
 3 | 
 4 | @interface HangTests: XCTestCase @end @implementation HangTests
 5 | 
 6 | - (void)test {
 7 |     __block int x = 0;
 8 |     id value = PMKHang(PMKAfter(0.02).then(^{ x++; return 1; }));
 9 |     XCTAssertEqual(x, 1);
10 |     XCTAssertEqualObjects(value, @1);
11 | }
12 | 
13 | @end
14 | 


--------------------------------------------------------------------------------
/Tests/CoreObjC/JoinTests.m:
--------------------------------------------------------------------------------
 1 | @import Foundation;
 2 | @import PromiseKit;
 3 | @import XCTest;
 4 | 
 5 | 
 6 | @interface JoinTests: XCTestCase @end @implementation JoinTests
 7 | 
 8 | - (void)test_73_join {
 9 |     XCTestExpectation *ex1 = [self expectationWithDescription:@""];
10 | 
11 |     __block void (^fulfiller)(id) = nil;
12 |     AnyPromise *promise = [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
13 |         fulfiller = resolve;
14 |     }];
15 | 
16 |     PMKJoin(@[
17 |         [AnyPromise promiseWithValue:[NSError errorWithDomain:@"dom" code:1 userInfo:nil]],
18 |         promise,
19 |         [AnyPromise promiseWithValue:[NSError errorWithDomain:@"dom" code:2 userInfo:nil]]
20 |     ]).then(^{
21 |         XCTFail();
22 |     }).catch(^(NSError *error){
23 |         id promises = error.userInfo[PMKJoinPromisesKey];
24 | 
25 |         int cume = 0, cumv = 0;
26 | 
27 |         for (AnyPromise *promise in promises) {
28 |             if ([promise.value isKindOfClass:[NSError class]]) {
29 |                 cume |= [promise.value code];
30 |             } else {
31 |                 cumv |= [promise.value unsignedIntValue];
32 |             }
33 |         }
34 | 
35 |         XCTAssertTrue(cumv == 4);
36 |         XCTAssertTrue(cume == 3);
37 | 
38 |         [ex1 fulfill];
39 |     });
40 |     fulfiller(@4);
41 |     [self waitForExpectationsWithTimeout:1 handler:nil];
42 | }
43 | 
44 | - (void)test_74_join_no_errors {
45 |     XCTestExpectation *ex1 = [self expectationWithDescription:@""];
46 |     PMKJoin(@[
47 |               [AnyPromise promiseWithValue:@1],
48 |               [AnyPromise promiseWithValue:@2]
49 |               ]).then(^(NSArray *values, id errors) {
50 |         XCTAssertEqualObjects(values, (@[@1, @2]));
51 |         XCTAssertNil(errors);
52 |         [ex1 fulfill];
53 |     });
54 |     [self waitForExpectationsWithTimeout:1 handler:nil];
55 | }
56 | 
57 | 
58 | - (void)test_75_join_no_success {
59 |     XCTestExpectation *ex1 = [self expectationWithDescription:@""];
60 |     PMKJoin(@[
61 |               [AnyPromise promiseWithValue:[NSError errorWithDomain:@"dom" code:1 userInfo:nil]],
62 |               [AnyPromise promiseWithValue:[NSError errorWithDomain:@"dom" code:2 userInfo:nil]],
63 |               ]).then(^{
64 |         XCTFail();
65 |     }).catch(^(NSError *error){
66 |         XCTAssertNotNil(error.userInfo[PMKJoinPromisesKey]);
67 |         [ex1 fulfill];
68 |     });
69 |     [self waitForExpectationsWithTimeout:1 handler:nil];
70 | }
71 | 
72 | - (void)test_76_join_fulfills_if_empty_input {
73 |     XCTestExpectation *ex1 = [self expectationWithDescription:@""];
74 |     PMKJoin(@[]).then(^(id a, id b, id c){
75 |         XCTAssertEqualObjects(@[], a);
76 |         XCTAssertNil(b);
77 |         XCTAssertNil(c);
78 |         [ex1 fulfill];
79 |     });
80 |     [self waitForExpectationsWithTimeout:1 handler:nil];
81 | }
82 | 
83 | - (void)test_join_nil {
84 |     NSArray *foo = nil;
85 |     NSError *err = PMKJoin(foo).value;
86 |     XCTAssertEqual(err.domain, PMKErrorDomain);
87 |     XCTAssertEqual(err.code, PMKInvalidUsageError);
88 | }
89 | 
90 | @end
91 | 


--------------------------------------------------------------------------------
/Tests/CoreObjC/PMKManifoldTests.m:
--------------------------------------------------------------------------------
 1 | @import PromiseKit;
 2 | @import XCTest;
 3 | 
 4 | @interface PMKManifoldTests: XCTestCase @end @implementation PMKManifoldTests
 5 | 
 6 | - (void)test_62_access_extra_elements {
 7 |     id ex1 = [self expectationWithDescription:@""];
 8 | 
 9 |     [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
10 |         resolve(PMKManifold(@1));
11 |     }].then(^(id o, id m, id n){
12 |         XCTAssertNil(m, @"Accessing extra elements should not crash");
13 |         XCTAssertNil(n, @"Accessing extra elements should not crash");
14 |         XCTAssertEqualObjects(o, @1);
15 |         [ex1 fulfill];
16 |     });
17 | 
18 |     [self waitForExpectationsWithTimeout:1 handler:nil];
19 | }
20 | 
21 | - (void)test_63_then_manifold {
22 |     id ex1 = [self expectationWithDescription:@""];
23 | 
24 |     [AnyPromise promiseWithValue:@0].then(^{
25 |         return PMKManifold(@1, @2, @3);
26 |     }).then(^(id o1, id o2, id o3){
27 |         XCTAssertEqualObjects(o1, @1);
28 |         XCTAssertEqualObjects(o2, @2);
29 |         XCTAssertEqualObjects(o3, @3);
30 |         [ex1 fulfill];
31 |     });
32 | 
33 |     [self waitForExpectationsWithTimeout:1 handler:nil];
34 | }
35 | 
36 | - (void)test_63_then_manifold_with_nil {
37 |     id ex1 = [self expectationWithDescription:@""];
38 | 
39 |     [AnyPromise promiseWithValue:@0].then(^{
40 |         return PMKManifold(@1, nil, @3);
41 |     }).then(^(id o1, id o2, id o3){
42 |         XCTAssertEqualObjects(o1, @1);
43 |         XCTAssertEqualObjects(o2, nil);
44 |         XCTAssertEqualObjects(o3, @3);
45 |         [ex1 fulfill];
46 |     });
47 | 
48 |     [self waitForExpectationsWithTimeout:1 handler:nil];
49 | }
50 | 
51 | - (void)test_65_manifold_fulfill_value {
52 |     id ex1 = [self expectationWithDescription:@""];
53 | 
54 |     AnyPromise *promise = [AnyPromise promiseWithValue:@1].then(^{
55 |         return PMKManifold(@123, @2);
56 |     });
57 | 
58 |     promise.then(^(id a, id b){
59 |         XCTAssertNotNil(a);
60 |         XCTAssertNotNil(b);
61 |         [ex1 fulfill];
62 |     });
63 | 
64 |     [self waitForExpectationsWithTimeout:1 handler:nil];
65 | 
66 |     XCTAssertEqualObjects(promise.value, @123);
67 | }
68 | 
69 | - (void)test_37_PMKMany_2 {
70 |     id ex1 = [self expectationWithDescription:@""];
71 | 
72 |     PMKAfter(0.02).then(^{
73 |         return PMKManifold(@1, @2);
74 |     }).then(^(id a, id b){
75 |         XCTAssertEqualObjects(a, @1);
76 |         XCTAssertEqualObjects(b, @2);
77 |         [ex1 fulfill];
78 |     });
79 | 
80 |     [self waitForExpectationsWithTimeout:1 handler:nil];
81 | }
82 | 
83 | @end
84 | 


--------------------------------------------------------------------------------
/Tests/CoreObjC/RaceTests.m:
--------------------------------------------------------------------------------
 1 | @import Foundation;
 2 | @import PromiseKit;
 3 | @import XCTest;
 4 | #define PMKTestErrorDomain @"PMKTestErrorDomain"
 5 | 
 6 | static inline NSError *dummyWithCode(NSInteger code) {
 7 |     return [NSError errorWithDomain:PMKTestErrorDomain code:rand() userInfo:@{NSLocalizedDescriptionKey: @(code).stringValue}];
 8 | }
 9 | 
10 | @interface RaceTests : XCTestCase @end @implementation RaceTests
11 | 
12 | - (void)test_race {
13 |     id ex = [self expectationWithDescription:@""];
14 |     id p = PMKAfter(0.1).then(^{ return @2; });
15 |     PMKRace(@[PMKAfter(10), PMKAfter(20), p]).then(^(id obj){
16 |         XCTAssertEqual(2, [obj integerValue]);
17 |         [ex fulfill];
18 |     });
19 |     [self waitForExpectationsWithTimeout:1 handler:nil];
20 | }
21 | 
22 | - (void)test_race_empty {
23 |     id ex = [self expectationWithDescription:@""];
24 |     PMKRace(@[]).then(^(NSArray* array){
25 |         XCTFail();
26 |         [ex fulfill];
27 |     }).catch(^(NSError *e){
28 |         XCTAssertEqual(e.domain, PMKErrorDomain);
29 |         XCTAssertEqual(e.code, PMKInvalidUsageError);
30 |         XCTAssertEqualObjects(e.userInfo[NSLocalizedDescriptionKey], @"PMKRace(nil)");
31 |         [ex fulfill];
32 |     });
33 |     [self waitForExpectationsWithTimeout:1 handler:nil];
34 | }
35 | 
36 | - (void)test_race_fullfilled {
37 |     id ex = [self expectationWithDescription:@""];
38 |     NSArray* promises = @[
39 |         PMKAfter(1).then(^{ return dummyWithCode(1); }),
40 |         PMKAfter(2).then(^{ return dummyWithCode(2); }),
41 |         PMKAfter(5).then(^{ return @1; }),
42 |         PMKAfter(4).then(^{ return @2; }),
43 |         PMKAfter(3).then(^{ return dummyWithCode(3); })
44 |     ];
45 |     PMKRaceFulfilled(promises).then(^(id obj){
46 |         XCTAssertEqual(2, [obj integerValue]);
47 |         [ex fulfill];
48 |     }).catch(^{
49 |         XCTFail();
50 |         [ex fulfill];
51 |     });
52 |     [self waitForExpectationsWithTimeout:10 handler:nil];
53 | }
54 | 
55 | - (void)test_race_fulfilled_empty {
56 |     id ex = [self expectationWithDescription:@""];
57 |     PMKRaceFulfilled(@[]).then(^(NSArray* array){
58 |         XCTFail();
59 |         [ex fulfill];
60 |     }).catch(^(NSError *e){
61 |         XCTAssertEqual(e.domain, PMKErrorDomain);
62 |         XCTAssertEqual(e.code, PMKInvalidUsageError);
63 |         XCTAssertEqualObjects(e.userInfo[NSLocalizedDescriptionKey], @"PMKRaceFulfilled(nil)");
64 |         [ex fulfill];
65 |     });
66 |     [self waitForExpectationsWithTimeout:1 handler:nil];
67 | }
68 | 
69 | - (void)test_race_fullfilled_with_no_winner {
70 |     id ex = [self expectationWithDescription:@""];
71 |     NSArray* promises = @[
72 |         PMKAfter(1).then(^{ return dummyWithCode(1); }),
73 |         PMKAfter(2).then(^{ return dummyWithCode(2); }),
74 |         PMKAfter(3).then(^{ return dummyWithCode(3); })
75 |     ];
76 |     PMKRaceFulfilled(promises).then(^(id obj){
77 |         XCTFail();
78 |         [ex fulfill];
79 |     }).catch(^(NSError *e){
80 |         XCTAssertEqual(e.domain, PMKErrorDomain);
81 |         XCTAssertEqual(e.code, PMKNoWinnerError);
82 |         XCTAssertEqualObjects(e.userInfo[NSLocalizedDescriptionKey], @"PMKRaceFulfilled(nil)");
83 |         [ex fulfill];
84 |     });
85 |     [self waitForExpectationsWithTimeout:10 handler:nil];
86 | }
87 | 
88 | @end
89 | 


--------------------------------------------------------------------------------
/Tests/CorePromise/AfterTests.swift:
--------------------------------------------------------------------------------
 1 | import PromiseKit
 2 | import XCTest
 3 | 
 4 | class AfterTests: XCTestCase {
 5 |     func testZero() {
 6 |         let ex2 = expectation(description: "")
 7 |         after(seconds: 0).done(ex2.fulfill)
 8 |         waitForExpectations(timeout: 2, handler: nil)
 9 | 
10 |         let ex3 = expectation(description: "")
11 |         after(.seconds(0)).done(ex3.fulfill)
12 |         waitForExpectations(timeout: 2, handler: nil)
13 | 
14 |     #if !SWIFT_PACKAGE
15 |         let ex4 = expectation(description: "")
16 |         __PMKAfter(0).done{ _ in ex4.fulfill() }.silenceWarning()
17 |         waitForExpectations(timeout: 2, handler: nil)
18 |     #endif
19 |     }
20 | 
21 |     func testNegative() {
22 |         let ex2 = expectation(description: "")
23 |         after(seconds: -1).done(ex2.fulfill)
24 |         waitForExpectations(timeout: 2, handler: nil)
25 | 
26 |         let ex3 = expectation(description: "")
27 |         after(.seconds(-1)).done(ex3.fulfill)
28 |         waitForExpectations(timeout: 2, handler: nil)
29 | 
30 |     #if !SWIFT_PACKAGE
31 |         let ex4 = expectation(description: "")
32 |         __PMKAfter(-1).done{ _ in ex4.fulfill() }.silenceWarning()
33 |         waitForExpectations(timeout: 2, handler: nil)
34 |     #endif
35 |     }
36 | 
37 |     func testPositive() {
38 |         let ex2 = expectation(description: "")
39 |         after(seconds: 1).done(ex2.fulfill)
40 |         waitForExpectations(timeout: 2, handler: nil)
41 | 
42 |         let ex3 = expectation(description: "")
43 |         after(.seconds(1)).done(ex3.fulfill)
44 |         waitForExpectations(timeout: 2, handler: nil)
45 | 
46 |     #if !SWIFT_PACKAGE
47 |         let ex4 = expectation(description: "")
48 |         __PMKAfter(1).done{ _ in ex4.fulfill() }.silenceWarning()
49 |         waitForExpectations(timeout: 2, handler: nil)
50 |     #endif
51 |     }
52 | }
53 | 


--------------------------------------------------------------------------------
/Tests/CorePromise/AsyncTests.swift:
--------------------------------------------------------------------------------
  1 | import PromiseKit
  2 | import XCTest
  3 | 
  4 | private enum Error: Swift.Error { case dummy }
  5 | 
  6 | class AsyncTests: XCTestCase {
  7 |     
  8 |     #if swift(>=5.5)
  9 |     #if canImport(_Concurrency)
 10 |     @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
 11 |     func testAsyncPromiseValue() async throws {
 12 |         let promise = after(.milliseconds(100)).then(on: nil){ Promise.value(1) }
 13 |         let value = try await promise.async()
 14 |         XCTAssertEqual(value, 1)
 15 |     }
 16 |     
 17 |     @available(iOS, deprecated: 13.0)
 18 |     @available(macOS, deprecated: 10.15)
 19 |     @available(tvOS, deprecated: 13.0)
 20 |     @available(watchOS, deprecated: 6.0)
 21 |     func testAsyncPromiseValue() {
 22 | 
 23 |     }
 24 |     #else
 25 |     func testAsyncPromiseValue() {
 26 | 
 27 |     }
 28 |     #endif
 29 |     #else
 30 |     func testAsyncPromiseValue() {
 31 | 
 32 |     }
 33 |     #endif
 34 |     
 35 |     #if swift(>=5.5)
 36 |     #if canImport(_Concurrency)
 37 |     @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
 38 |     func testAsyncGuaranteeValue() async {
 39 |         let guarantee = after(.milliseconds(100)).then(on: nil){ Guarantee.value(1) }
 40 |         let value = await guarantee.async()
 41 |         XCTAssertEqual(value, 1)
 42 |     }
 43 |     
 44 |     @available(iOS, deprecated: 13.0)
 45 |     @available(macOS, deprecated: 10.15)
 46 |     @available(tvOS, deprecated: 13.0)
 47 |     @available(watchOS, deprecated: 6.0)
 48 |     func testAsyncGuaranteeValue() {
 49 | 
 50 |     }
 51 |     #else
 52 |     func testAsyncGuaranteeValue() {
 53 | 
 54 |     }
 55 |     #endif
 56 |     #else
 57 |     func testAsyncGuaranteeValue() {
 58 | 
 59 |     }
 60 |     #endif
 61 |     
 62 |     #if swift(>=5.5)
 63 |     #if canImport(_Concurrency)
 64 |     @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
 65 |     func testAsyncPromiseThrow() async throws {
 66 |         do {
 67 |             let promise = after(.milliseconds(100)).then(on: nil){ Promise(error: Error.dummy) }.then(on: nil){ Promise.value(1) }
 68 |             try await _ = promise.async()
 69 |             XCTAssert(false)
 70 |         } catch {
 71 |             switch error as? Error {
 72 |             case .dummy:
 73 |                 XCTAssert(true)
 74 |             default:
 75 |                 XCTAssert(false)
 76 |             }
 77 |         }
 78 |     }
 79 |     
 80 |     @available(iOS, deprecated: 13.0)
 81 |     @available(macOS, deprecated: 10.15)
 82 |     @available(tvOS, deprecated: 13.0)
 83 |     @available(watchOS, deprecated: 6.0)
 84 |     func testAsyncPromiseThrow() {
 85 | 
 86 |     }
 87 |     #else
 88 |     func testAsyncPromiseThrow() {
 89 | 
 90 |     }
 91 |     #endif
 92 |     #else
 93 |     func testAsyncPromiseThrow() {
 94 | 
 95 |     }
 96 |     #endif
 97 |     
 98 |     #if swift(>=5.5)
 99 |     #if canImport(_Concurrency)
100 |     @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
101 |     func testAsyncPromiseCancel() async throws {
102 |         do {
103 |             let p = after(seconds: 0).done { _ in
104 |                 throw LocalError.cancel
105 |             }.done {
106 |                 XCTFail()
107 |             }
108 |             p.catch { _ in
109 |                 XCTFail()
110 |             }
111 |             try await p.async()
112 |             XCTAssert(false)
113 |         } catch {
114 |             guard let cancellableError = error as? CancellableError else { return XCTFail("Unexpected error type") }
115 |             XCTAssertTrue(cancellableError.isCancelled)
116 |         }
117 |     }
118 |     
119 |     @available(iOS, deprecated: 13.0)
120 |     @available(macOS, deprecated: 10.15)
121 |     @available(tvOS, deprecated: 13.0)
122 |     @available(watchOS, deprecated: 6.0)
123 |     func testAsyncPromiseCancel() {
124 |         
125 |     }
126 |     #else
127 |     func testAsyncPromiseCancel() {
128 |         
129 |     }
130 |     #endif
131 |     #else
132 |     func testAsyncPromiseCancel() {
133 |         
134 |     }
135 |     #endif
136 | }
137 | 
138 | private enum LocalError: CancellableError {
139 |     case notCancel
140 |     case cancel
141 |     
142 |     var isCancelled: Bool {
143 |         switch self {
144 |         case .notCancel: return false
145 |         case .cancel: return true
146 |         }
147 |     }
148 | }
149 | 


--------------------------------------------------------------------------------
/Tests/CorePromise/CancellableErrorTests.swift:
--------------------------------------------------------------------------------
  1 | import Foundation
  2 | import PromiseKit
  3 | import XCTest
  4 | 
  5 | #if canImport(StoreKit)
  6 | import StoreKit
  7 | #endif
  8 | 
  9 | class CancellationTests: XCTestCase {
 10 |     func testCancellation() {
 11 |         let ex1 = expectation(description: "")
 12 | 
 13 |         let p = after(seconds: 0).done { _ in
 14 |             throw LocalError.cancel
 15 |         }.done {
 16 |             XCTFail()
 17 |         }
 18 |         p.catch { _ in
 19 |             XCTFail()
 20 |         }
 21 |         p.catch(policy: .allErrors) {
 22 |             XCTAssertTrue($0.isCancelled)
 23 |             ex1.fulfill()
 24 |         }
 25 | 
 26 |         waitForExpectations(timeout: 60)
 27 |     }
 28 | 
 29 |     func testThrowCancellableErrorThatIsNotCancelled() {
 30 |         let expct = expectation(description: "")
 31 | 
 32 |         after(seconds: 0).done { _ in
 33 |             throw LocalError.notCancel
 34 |         }.done {
 35 |             XCTFail()
 36 |         }.catch {
 37 |             XCTAssertFalse($0.isCancelled)
 38 |             expct.fulfill()
 39 |         }
 40 | 
 41 |         waitForExpectations(timeout: 1)
 42 |     }
 43 | 
 44 |     func testRecoverWithCancellation() {
 45 |         let ex1 = expectation(description: "")
 46 |         let ex2 = expectation(description: "")
 47 | 
 48 |         let p = after(seconds: 0).done { _ in
 49 |             throw CocoaError.cancelled
 50 |         }.recover(policy: .allErrors) { err -> Promise<Void> in
 51 |             ex1.fulfill()
 52 |             XCTAssertTrue(err.isCancelled)
 53 |             throw err
 54 |         }.done { _ in
 55 |             XCTFail()
 56 |         }
 57 |         p.catch { _ in
 58 |             XCTFail()
 59 |         }
 60 |         p.catch(policy: .allErrors) {
 61 |             XCTAssertTrue($0.isCancelled)
 62 |             ex2.fulfill()
 63 |         }
 64 | 
 65 |         waitForExpectations(timeout: 1)
 66 |     }
 67 | 
 68 |     func testFoundationBridging1() {
 69 |         let ex = expectation(description: "")
 70 | 
 71 |         let p = after(seconds: 0).done { _ in
 72 |             throw CocoaError.cancelled
 73 |         }
 74 |         p.catch { _ in
 75 |             XCTFail()
 76 |         }
 77 |         p.catch(policy: .allErrors) {
 78 |             XCTAssertTrue($0.isCancelled)
 79 |             ex.fulfill()
 80 |         }
 81 | 
 82 |         waitForExpectations(timeout: 1)
 83 |     }
 84 | 
 85 |     func testFoundationBridging2() {
 86 |         let ex = expectation(description: "")
 87 | 
 88 |         let p = Promise().done {
 89 |             throw URLError.cancelled
 90 |         }
 91 |         p.catch { _ in
 92 |             XCTFail()
 93 |         }
 94 |         p.catch(policy: .allErrors) {
 95 |             XCTAssertTrue($0.isCancelled)
 96 |             ex.fulfill()
 97 |         }
 98 | 
 99 |         waitForExpectations(timeout: 1)
100 |     }
101 | 
102 |     func testDoesntCrashSwift() {
103 |       #if os(macOS)
104 |         // Previously exposed a bridging crash in Swift
105 |         // NOTE nobody was brave enough or diligent enough to report this to Apple :{
106 |         // NOTE no Linux test since this constructor doesn’t exist there
107 |         XCTAssertFalse(NSError().isCancelled)
108 |       #endif
109 | 
110 |       #if canImport(StoreKit)
111 |         if #available(watchOS 6.2, *) {
112 |             do {
113 |                 let err = SKError(.paymentCancelled)
114 |                 XCTAssertTrue(err.isCancelled)
115 |                 throw err
116 |             } catch {
117 |                 XCTAssertTrue(error.isCancelled)
118 |             }
119 |             
120 |             XCTAssertFalse(SKError(.clientInvalid).isCancelled)
121 |         }
122 |       #endif
123 |     }
124 | 
125 |     func testBridgeToNSError() {
126 |         // Swift.Error types must be cast to NSError for the bridging to occur.
127 |         // The below would throw an expection about an invalid selector without a cast:
128 |         // `(error as AnyObject).value(forKey: "domain")`
129 |         // This simply checks to make sure `isCancelled` is not making that mistake.
130 | 
131 |         class TestingError: Error { }
132 | 
133 |         XCTAssertFalse(TestingError().isCancelled)
134 |     }
135 | 
136 | #if swift(>=3.2)
137 |     func testIsCancelled() {
138 |         XCTAssertTrue(PMKError.cancelled.isCancelled)
139 |         XCTAssertTrue(URLError.cancelled.isCancelled)
140 |         XCTAssertTrue(CocoaError.cancelled.isCancelled)
141 |         XCTAssertFalse(CocoaError(_nsError: NSError(domain: NSCocoaErrorDomain, code: CocoaError.Code.coderInvalidValue.rawValue)).isCancelled)
142 |     }
143 | #endif
144 | }
145 | 
146 | private enum LocalError: CancellableError {
147 |     case notCancel
148 |     case cancel
149 | 
150 |     var isCancelled: Bool {
151 |         switch self {
152 |             case .notCancel: return false
153 |             case .cancel: return true
154 |         }
155 |     }
156 | }
157 | 
158 | private extension URLError {
159 |     static var cancelled: URLError {
160 |         return .init(_nsError: NSError(domain: NSURLErrorDomain, code: URLError.Code.cancelled.rawValue))
161 |     }
162 | }
163 | 
164 | private extension CocoaError {
165 |     static var cancelled: CocoaError {
166 |         return .init(_nsError: NSError(domain: NSCocoaErrorDomain, code: CocoaError.Code.userCancelled.rawValue))
167 |     }
168 | }
169 | 


--------------------------------------------------------------------------------
/Tests/CorePromise/DefaultDispatchQueueTests.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | //  PMKDefaultDispatchQueue.test.swift
 3 | //  PromiseKit
 4 | //
 5 | //  Created by David Rodriguez on 4/14/16.
 6 | //  Copyright © 2016 Max Howell. All rights reserved.
 7 | //
 8 | 
 9 | import class Foundation.Thread
10 | import PromiseKit
11 | import Dispatch
12 | import XCTest
13 | 
14 | private enum Error: Swift.Error { case dummy }
15 | 
16 | 
17 | class PMKDefaultDispatchQueueTest: XCTestCase {
18 | 
19 |     let myQueue = DispatchQueue(label: "myQueue")
20 | 
21 |     override func setUp() {
22 |         // can actually only set the default queue once
23 |         // - See: PMKSetDefaultDispatchQueue
24 |         conf.Q = (myQueue, myQueue)
25 |     }
26 | 
27 |     override func tearDown() {
28 |         conf.Q = (.main, .main)
29 |     }
30 | 
31 |     func testOverrodeDefaultThenQueue() {
32 |         let ex = expectation(description: "resolving")
33 | 
34 |         Promise.value(1).then { _ -> Promise<Void> in
35 |             ex.fulfill()
36 |             XCTAssertFalse(Thread.isMainThread)
37 |             return Promise()
38 |         }.silenceWarning()
39 | 
40 |         XCTAssertTrue(Thread.isMainThread)
41 | 
42 |         waitForExpectations(timeout: 1)
43 |     }
44 | 
45 |     func testOverrodeDefaultCatchQueue() {
46 |         let ex = expectation(description: "resolving")
47 | 
48 |         Promise<Int>(error: Error.dummy).catch { _ in
49 |             ex.fulfill()
50 |             XCTAssertFalse(Thread.isMainThread)
51 |         }
52 | 
53 |         XCTAssertTrue(Thread.isMainThread)
54 | 
55 |         waitForExpectations(timeout: 1)
56 |     }
57 | 
58 |     func testOverrodeDefaultAlwaysQueue() {
59 |         let ex = expectation(description: "resolving")
60 | 
61 |         Promise.value(1).ensure {
62 |             ex.fulfill()
63 |             XCTAssertFalse(Thread.isMainThread)
64 |         }.silenceWarning()
65 | 
66 |         XCTAssertTrue(Thread.isMainThread)
67 | 
68 |         waitForExpectations(timeout: 1)
69 |     }
70 | }
71 | 


--------------------------------------------------------------------------------
/Tests/CorePromise/ErrorTests.swift:
--------------------------------------------------------------------------------
 1 | import PromiseKit
 2 | import XCTest
 3 | 
 4 | class PMKErrorTests: XCTestCase {
 5 |     func testCustomStringConvertible() {
 6 |         XCTAssertNotNil(PMKError.invalidCallingConvention.errorDescription)
 7 |         XCTAssertNotNil(PMKError.returnedSelf.errorDescription)
 8 |         XCTAssertNotNil(PMKError.badInput.errorDescription)
 9 |         XCTAssertNotNil(PMKError.cancelled.errorDescription)
10 |         XCTAssertNotNil(PMKError.compactMap(1, Int.self).errorDescription)
11 |         XCTAssertNotNil(PMKError.emptySequence.errorDescription)
12 |     }
13 | 
14 |     func testCustomDebugStringConvertible() {
15 |         XCTAssertFalse(PMKError.invalidCallingConvention.debugDescription.isEmpty)
16 |         XCTAssertFalse(PMKError.returnedSelf.debugDescription.isEmpty)
17 |         XCTAssertNotNil(PMKError.badInput.debugDescription.isEmpty)
18 |         XCTAssertFalse(PMKError.cancelled.debugDescription.isEmpty)
19 |         XCTAssertFalse(PMKError.compactMap(1, Int.self).debugDescription.isEmpty)
20 |         XCTAssertFalse(PMKError.emptySequence.debugDescription.isEmpty)
21 |     }
22 | }
23 | 


--------------------------------------------------------------------------------
/Tests/CorePromise/GuaranteeTests.swift:
--------------------------------------------------------------------------------
  1 | import PromiseKit
  2 | import XCTest
  3 | 
  4 | class GuaranteeTests: XCTestCase {
  5 |     func testInit() {
  6 |         let ex = expectation(description: "")
  7 |         Guarantee { seal in
  8 |             seal(1)
  9 |         }.done {
 10 |             XCTAssertEqual(1, $0)
 11 |             ex.fulfill()
 12 |         }
 13 |         wait(for: [ex], timeout: 10)
 14 |     }
 15 | 
 16 |     func testMap() {
 17 |         let ex = expectation(description: "")
 18 | 
 19 |         Guarantee.value(1).map {
 20 |             $0 * 2
 21 |         }.done {
 22 |             XCTAssertEqual(2, $0)
 23 |             ex.fulfill()
 24 |         }
 25 | 
 26 |         wait(for: [ex], timeout: 10)
 27 |     }
 28 | 
 29 |     func testMapByKeyPath() {
 30 |         let ex = expectation(description: "")
 31 | 
 32 |         Guarantee.value(Person(name: "Max")).map(\.name).done {
 33 |             XCTAssertEqual("Max", $0)
 34 |             ex.fulfill()
 35 |         }
 36 | 
 37 |         wait(for: [ex], timeout: 10)
 38 |     }
 39 | 
 40 |     func testWait() {
 41 |         XCTAssertEqual(after(.milliseconds(100)).map(on: nil){ 1 }.wait(), 1)
 42 |     }
 43 | 
 44 |     func testMapValues() {
 45 |         let ex = expectation(description: "")
 46 | 
 47 |         Guarantee.value([1, 2, 3])
 48 |             .mapValues { $0 * 2 }
 49 |             .done { values in
 50 |                 XCTAssertEqual([2, 4, 6], values)
 51 |                 ex.fulfill()
 52 |             }
 53 | 
 54 |         wait(for: [ex], timeout: 10)
 55 |     }
 56 | 
 57 |     func testMapValuesByKeyPath() {
 58 |         let ex = expectation(description: "")
 59 | 
 60 |         Guarantee.value([Person(name: "Max"), Person(name: "Roman"), Person(name: "John")])
 61 |             .mapValues(\.name)
 62 |             .done { values in
 63 |                 XCTAssertEqual(["Max", "Roman", "John"], values)
 64 |                 ex.fulfill()
 65 |             }
 66 | 
 67 |         wait(for: [ex], timeout: 10)
 68 |     }
 69 | 
 70 |     func testFlatMapValues() {
 71 |         let ex = expectation(description: "")
 72 | 
 73 |         Guarantee.value([1, 2, 3])
 74 |             .flatMapValues { [$0, $0] }
 75 |             .done { values in
 76 |                 XCTAssertEqual([1, 1, 2, 2, 3, 3], values)
 77 |                 ex.fulfill()
 78 |             }
 79 | 
 80 |         wait(for: [ex], timeout: 10)
 81 |     }
 82 | 
 83 |     func testCompactMapValues() {
 84 |         let ex = expectation(description: "")
 85 | 
 86 |         Guarantee.value(["1","2","a","3"])
 87 |             .compactMapValues { Int($0) }
 88 |             .done { values in
 89 |                 XCTAssertEqual([1, 2, 3], values)
 90 |                 ex.fulfill()
 91 |             }
 92 | 
 93 |         wait(for: [ex], timeout: 10)
 94 |     }
 95 | 
 96 |     func testCompactMapValuesByKeyPath() {
 97 |         let ex = expectation(description: "")
 98 | 
 99 |         Guarantee.value([Person(name: "Max"), Person(name: "Roman", age: 26), Person(name: "John", age: 23)])
100 |             .compactMapValues(\.age)
101 |             .done { values in
102 |                 XCTAssertEqual([26, 23], values)
103 |                 ex.fulfill()
104 |             }
105 | 
106 |         wait(for: [ex], timeout: 10)
107 |     }
108 | 
109 |     func testThenMap() {
110 | 
111 |         let ex = expectation(description: "")
112 | 
113 |         Guarantee.value([1, 2, 3])
114 |             .thenMap { Guarantee.value($0 * 2) }
115 |             .done { values in
116 |                 XCTAssertEqual([2, 4, 6], values)
117 |                 ex.fulfill()
118 |             }
119 | 
120 |         wait(for: [ex], timeout: 10)
121 |     }
122 | 
123 |     func testThenFlatMap() {
124 | 
125 |         let ex = expectation(description: "")
126 | 
127 |         Guarantee.value([1, 2, 3])
128 |             .thenFlatMap { Guarantee.value([$0, $0]) }
129 |             .done { values in
130 |                 XCTAssertEqual([1, 1, 2, 2, 3, 3], values)
131 |                 ex.fulfill()
132 |             }
133 | 
134 |         wait(for: [ex], timeout: 10)
135 |     }
136 | 
137 |     func testFilterValues() {
138 | 
139 |         let ex = expectation(description: "")
140 | 
141 |         Guarantee.value([1, 2, 3])
142 |             .filterValues { $0 > 1 }
143 |             .done { values in
144 |                 XCTAssertEqual([2, 3], values)
145 |                 ex.fulfill()
146 |             }
147 | 
148 |         wait(for: [ex], timeout: 10)
149 |     }
150 | 
151 |     func testFilterValuesByKeyPath() {
152 | 
153 |         let ex = expectation(description: "")
154 | 
155 |         Guarantee.value([Person(name: "Max"), Person(name: "Roman", age: 26, isStudent: false), Person(name: "John", age: 23, isStudent: true)])
156 |             .filterValues(\.isStudent)
157 |             .done { values in
158 |                 XCTAssertEqual([Person(name: "John", age: 23, isStudent: true)], values)
159 |                 ex.fulfill()
160 |             }
161 | 
162 |         wait(for: [ex], timeout: 10)
163 |     }
164 | 
165 |     func testSorted() {
166 | 
167 |         let ex = expectation(description: "")
168 | 
169 |         Guarantee.value([5, 2, 3, 4, 1])
170 |             .sortedValues()
171 |             .done { values in
172 |                 XCTAssertEqual([1, 2, 3, 4, 5], values)
173 |                 ex.fulfill()
174 |             }
175 | 
176 |         wait(for: [ex], timeout: 10)
177 |     }
178 | 
179 |     func testSortedBy() {
180 | 
181 |         let ex = expectation(description: "")
182 | 
183 |         Guarantee.value([5, 2, 3, 4, 1])
184 |             .sortedValues { $0 > $1 }
185 |             .done { values in
186 |                 XCTAssertEqual([5, 4, 3, 2, 1], values)
187 |                 ex.fulfill()
188 |             }
189 | 
190 |         wait(for: [ex], timeout: 10)
191 |     }
192 | 
193 |     #if swift(>=3.1)
194 |     func testNoAmbiguityForValue() {
195 |         let ex = expectation(description: "")
196 |         let a = Guarantee<Void>.value
197 |         let b = Guarantee<Void>.value(Void())
198 |         let c = Guarantee<Void>.value(())
199 |         when(fulfilled: a, b, c).done {
200 |             ex.fulfill()
201 |         }.cauterize()
202 |         wait(for: [ex], timeout: 10)
203 |     }
204 |     #endif
205 | }
206 | 


--------------------------------------------------------------------------------
/Tests/CorePromise/HangTests.swift:
--------------------------------------------------------------------------------
 1 | import PromiseKit
 2 | import XCTest
 3 | 
 4 | class HangTests: XCTestCase {
 5 |     func test() {
 6 |         let ex = expectation(description: "block executed")
 7 |         do {
 8 |             let value = try hang(after(seconds: 0.02).then { _ -> Promise<Int> in
 9 |                 ex.fulfill()
10 |                 return .value(1)
11 |             })
12 |             XCTAssertEqual(value, 1)
13 |         } catch {
14 |             XCTFail("Unexpected error")
15 |         }
16 |         waitForExpectations(timeout: 0)
17 |     }
18 | 
19 |     enum Error: Swift.Error {
20 |         case test
21 |     }
22 | 
23 |     func testError() {
24 |         var value = 0
25 |         do {
26 |             _ = try hang(after(seconds: 0.02).done {
27 |                 value = 1
28 |                 throw Error.test
29 |             })
30 |             XCTAssertEqual(value, 1)
31 |         } catch Error.test {
32 |             return
33 |         } catch {
34 |             XCTFail("Unexpected error (expected Error.test)")
35 |         }
36 |         XCTFail("Expected error but no error was thrown")
37 |     }
38 | }
39 | 


--------------------------------------------------------------------------------
/Tests/CorePromise/PromiseTests.swift:
--------------------------------------------------------------------------------
  1 | import PromiseKit
  2 | import Dispatch
  3 | import XCTest
  4 | 
  5 | class PromiseTests: XCTestCase {
  6 |     func testIsPending() {
  7 |         XCTAssertTrue(Promise<Void>.pending().promise.isPending)
  8 |         XCTAssertFalse(Promise().isPending)
  9 |         XCTAssertFalse(Promise<Void>(error: Error.dummy).isPending)
 10 |     }
 11 | 
 12 |     func testIsResolved() {
 13 |         XCTAssertFalse(Promise<Void>.pending().promise.isResolved)
 14 |         XCTAssertTrue(Promise().isResolved)
 15 |         XCTAssertTrue(Promise<Void>(error: Error.dummy).isResolved)
 16 |     }
 17 | 
 18 |     func testIsFulfilled() {
 19 |         XCTAssertFalse(Promise<Void>.pending().promise.isFulfilled)
 20 |         XCTAssertTrue(Promise().isFulfilled)
 21 |         XCTAssertFalse(Promise<Void>(error: Error.dummy).isFulfilled)
 22 |     }
 23 | 
 24 |     func testIsRejected() {
 25 |         XCTAssertFalse(Promise<Void>.pending().promise.isRejected)
 26 |         XCTAssertTrue(Promise<Void>(error: Error.dummy).isRejected)
 27 |         XCTAssertFalse(Promise().isRejected)
 28 |     }
 29 | 
 30 |     @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *)
 31 |     func testDispatchQueueAsyncExtensionReturnsPromise() {
 32 |         let ex = expectation(description: "")
 33 | 
 34 |         DispatchQueue.global().async(.promise) { () -> Int in
 35 |             XCTAssertFalse(Thread.isMainThread)
 36 |             return 1
 37 |         }.done { one in
 38 |             XCTAssertEqual(one, 1)
 39 |             ex.fulfill()
 40 |         }
 41 | 
 42 |         waitForExpectations(timeout: 1)
 43 |     }
 44 | 
 45 |     @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *)
 46 |     func testDispatchQueueAsyncExtensionCanThrowInBody() {
 47 |         let ex = expectation(description: "")
 48 | 
 49 |         DispatchQueue.global().async(.promise) { () -> Int in
 50 |             throw Error.dummy
 51 |         }.done { _ in
 52 |             XCTFail()
 53 |         }.catch { _ in
 54 |             ex.fulfill()
 55 |         }
 56 | 
 57 |         waitForExpectations(timeout: 1)
 58 |     }
 59 | 
 60 |     func testCustomStringConvertible() {
 61 |         XCTAssertEqual(Promise<Int>.pending().promise.debugDescription, "Promise<Int>.pending(handlers: 0)")
 62 |         XCTAssertEqual(Promise().debugDescription, "Promise<()>.fulfilled(())")
 63 |         XCTAssertEqual(Promise<String>(error: Error.dummy).debugDescription, "Promise<String>.rejected(Error.dummy)")
 64 | 
 65 |         XCTAssertEqual("\(Promise<Int>.pending().promise)", "Promise(…Int)")
 66 |         XCTAssertEqual("\(Promise.value(3))", "Promise(3)")
 67 |         XCTAssertEqual("\(Promise<Void>(error: Error.dummy))", "Promise(dummy)")
 68 |     }
 69 | 
 70 |     func testCannotFulfillWithError() {
 71 | 
 72 |         // sadly this test proves the opposite :(
 73 |         // left here so maybe one day we can prevent instantiation of `Promise<Error>`
 74 | 
 75 |         _ = Promise { seal in
 76 |             seal.fulfill(Error.dummy)
 77 |         }
 78 | 
 79 |         _ = Promise<Error>.pending()
 80 | 
 81 |         _ = Promise.value(Error.dummy)
 82 | 
 83 |         _ = Promise().map { Error.dummy }
 84 |     }
 85 | 
 86 | #if swift(>=3.1)
 87 |     func testCanMakeVoidPromise() {
 88 |         _ = Promise()
 89 |         _ = Guarantee()
 90 |     }
 91 | #endif
 92 | 
 93 |     enum Error: Swift.Error {
 94 |         case dummy
 95 |     }
 96 | 
 97 |     func testThrowInInitializer() {
 98 |         let p = Promise<Void> { _ in
 99 |             throw Error.dummy
100 |         }
101 |         XCTAssertTrue(p.isRejected)
102 |         guard let err = p.error, case Error.dummy = err else { return XCTFail() }
103 |     }
104 | 
105 |     func testThrowInFirstly() {
106 |         let ex = expectation(description: "")
107 | 
108 |         firstly { () -> Promise<Int> in
109 |             throw Error.dummy
110 |         }.catch {
111 |             XCTAssertEqual($0 as? Error, Error.dummy)
112 |             ex.fulfill()
113 |         }
114 | 
115 |         wait(for: [ex], timeout: 10)
116 |     }
117 | 
118 |     func testWait() throws {
119 |         let p = after(.milliseconds(100)).then(on: nil){ Promise.value(1) }
120 |         XCTAssertEqual(try p.wait(), 1)
121 | 
122 |         do {
123 |             let p = after(.milliseconds(100)).map(on: nil){ throw Error.dummy }
124 |             try p.wait()
125 |             XCTFail()
126 |         } catch {
127 |             XCTAssertEqual(error as? Error, Error.dummy)
128 |         }
129 |     }
130 | 
131 |     func testPipeForResolved() {
132 |         let ex = expectation(description: "")
133 |         Promise.value(1).done {
134 |             XCTAssertEqual(1, $0)
135 |             ex.fulfill()
136 |         }.silenceWarning()
137 |         wait(for: [ex], timeout: 10)
138 |     }
139 | 
140 |     #if swift(>=3.1)
141 |     func testNoAmbiguityForValue() {
142 |         let ex = expectation(description: "")
143 |         let a = Promise<Void>.value
144 |         let b = Promise<Void>.value(Void())
145 |         let c = Promise<Void>.value(())
146 |         when(fulfilled: a, b, c).done {
147 |             ex.fulfill()
148 |         }.cauterize()
149 |         wait(for: [ex], timeout: 10)
150 |     }
151 |     #endif
152 | }
153 | 


--------------------------------------------------------------------------------
/Tests/CorePromise/RaceTests.swift:
--------------------------------------------------------------------------------
 1 | import XCTest
 2 | import PromiseKit
 3 | 
 4 | class RaceTests: XCTestCase {
 5 |     func test1() {
 6 |         let ex = expectation(description: "")
 7 |         race(after(.milliseconds(10)).then{ Promise.value(1) }, after(seconds: 1).map{ 2 }).done { index in
 8 |             XCTAssertEqual(index, 1)
 9 |             ex.fulfill()
10 |         }.silenceWarning()
11 |         waitForExpectations(timeout: 1, handler: nil)
12 |     }
13 |     
14 |     func test2() {
15 |         let ex = expectation(description: "")
16 |         race(after(seconds: 1).map{ 1 }, after(.milliseconds(10)).map{ 2 }).done { index in
17 |             XCTAssertEqual(index, 2)
18 |             ex.fulfill()
19 |         }
20 |         waitForExpectations(timeout: 1, handler: nil)
21 |     }
22 | 
23 |     func test1Array() {
24 |         let ex = expectation(description: "")
25 |         let promises = [after(.milliseconds(10)).map{ 1 }, after(seconds: 1).map{ 2 }]
26 |         race(promises).done { index in
27 |             XCTAssertEqual(index, 1)
28 |             ex.fulfill()
29 |         }.silenceWarning()
30 |         waitForExpectations(timeout: 1, handler: nil)
31 |     }
32 |     
33 |     func test2Array() {
34 |         let ex = expectation(description: "")
35 |         race(after(seconds: 1).map{ 1 }, after(.milliseconds(10)).map{ 2 }).done { index in
36 |             XCTAssertEqual(index, 2)
37 |             ex.fulfill()
38 |         }
39 |         waitForExpectations(timeout: 1, handler: nil)
40 |     }
41 | 
42 |     func testEmptyArray() {
43 |         let ex = expectation(description: "")
44 |         let empty = [Promise<Int>]()
45 |         race(empty).catch {
46 |             guard case PMKError.badInput = $0 else { return XCTFail() }
47 |             ex.fulfill()
48 |         }
49 |         wait(for: [ex], timeout: 10)
50 |     }
51 | 
52 |     func testFulfilled() {
53 |         enum Error: Swift.Error { case test1, test2, test3 }
54 |         let ex = expectation(description: "")
55 |         let promises: [Promise<Int>] = [after(seconds: 1).map { _ in throw Error.test1 }, after(seconds: 2).map { _ in throw Error.test2 }, after(seconds: 5).map { 1 }, after(seconds: 4).map { 2 }, after(seconds: 3).map { _ in throw Error.test3 }]
56 |         race(fulfilled: promises).done {
57 |             XCTAssertEqual($0, 2)
58 |             ex.fulfill()
59 |         }.catch { _ in
60 |             XCTFail()
61 |             ex.fulfill()
62 |         }
63 |         wait(for: [ex], timeout: 10)
64 |     }
65 | 
66 |     func testFulfilledEmptyArray() {
67 |         let ex = expectation(description: "")
68 |         let empty = [Promise<Int>]()
69 |         race(fulfilled: empty).catch {
70 |             guard case PMKError.badInput = $0 else { return XCTFail() }
71 |             ex.fulfill()
72 |         }
73 |         wait(for: [ex], timeout: 10)
74 |     }
75 | 
76 |     func testFulfilledWithNoWinner() {
77 |         enum Error: Swift.Error { case test1, test2 }
78 |         let ex = expectation(description: "")
79 |         let promises: [Promise<Int>] = [after(seconds: 1).map { _ in throw Error.test1 }, after(seconds: 2).map { _ in throw Error.test2 }]
80 |         race(fulfilled: promises).done { _ in
81 |             XCTFail()
82 |             ex.fulfill()
83 |         }.catch {
84 |             guard let pmkError = $0 as? PMKError else { return XCTFail() }
85 |             guard case .noWinner = pmkError else { return XCTFail() }
86 |             guard pmkError.debugDescription == "All thenables passed to race(fulfilled:) were rejected" else { return XCTFail() }
87 |             ex.fulfill()
88 |         }
89 |         wait(for: [ex], timeout: 10)
90 |     }
91 | }
92 | 


--------------------------------------------------------------------------------
/Tests/CorePromise/RegressionTests.swift:
--------------------------------------------------------------------------------
 1 | import PromiseKit
 2 | import XCTest
 3 | 
 4 | class RegressionTests: XCTestCase {
 5 |     func testReturningPreviousPromiseWorks() {
 6 | 
 7 |         // regression test because we were doing this wrong
 8 |         // in our A+ tests implementation for spec: 2.3.1
 9 | 
10 |         do {
11 |             let promise1 = Promise()
12 |             let promise2 = promise1.then(on: nil) { promise1 }
13 |             promise2.catch(on: nil) { _ in XCTFail() }
14 |         }
15 |         do {
16 |             enum Error: Swift.Error { case dummy }
17 | 
18 |             let promise1 = Promise<Void>(error: Error.dummy)
19 |             let promise2 = promise1.recover(on: nil) { _ in promise1 }
20 |             promise2.catch(on: nil) { err in
21 |                 if case PMKError.returnedSelf = err {
22 |                     XCTFail()
23 |                 }
24 |             }
25 |         }
26 |     }
27 | }
28 | 


--------------------------------------------------------------------------------
/Tests/CorePromise/StressTests.swift:
--------------------------------------------------------------------------------
 1 | import PromiseKit
 2 | import Dispatch
 3 | import XCTest
 4 | 
 5 | class StressTests: XCTestCase {
 6 |     func testThenDataRace() {
 7 |         let e1 = expectation(description: "")
 8 | 
 9 |         //will crash if then doesn't protect handlers
10 |         stressDataRace(expectation: e1, stressFunction: { promise in
11 |             promise.done { s in
12 |                 XCTAssertEqual("ok", s)
13 |                 return
14 |             }.silenceWarning()
15 |         }, fulfill: { "ok" })
16 | 
17 |         waitForExpectations(timeout: 10, handler: nil)
18 |     }
19 | 
20 |     @available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *)
21 |     func testThensAreSequentialForLongTime() {
22 |         var values = [Int]()
23 |         let ex = expectation(description: "")
24 |         var promise = DispatchQueue.global().async(.promise){ 0 }
25 |         let N = 1000
26 |         for x in 1..<N {
27 |             promise = promise.then { y -> Guarantee<Int> in
28 |                 values.append(y)
29 |                 XCTAssertEqual(x - 1, y)
30 |                 return DispatchQueue.global().async(.promise) { x }
31 |             }
32 |         }
33 |         promise.done { x in
34 |             values.append(x)
35 |             XCTAssertEqual(values, (0..<N).map{ $0 })
36 |             ex.fulfill()
37 |         }
38 |         waitForExpectations(timeout: 10, handler: nil)
39 |     }
40 | 
41 |     func testZalgoDataRace() {
42 |         let e1 = expectation(description: "")
43 | 
44 |         //will crash if zalgo doesn't protect handlers
45 |         stressDataRace(expectation: e1, stressFunction: { promise in
46 |             promise.done(on: nil) { s in
47 |                 XCTAssertEqual("ok", s)
48 |             }.silenceWarning()
49 |         }, fulfill: {
50 |             return "ok"
51 |         })
52 | 
53 |         waitForExpectations(timeout: 10, handler: nil)
54 |     }
55 | }
56 | 
57 | private enum Error: Swift.Error {
58 |     case Dummy
59 | }
60 | 
61 | private func stressDataRace<T: Equatable>(expectation e1: XCTestExpectation, iterations: Int = 1000, stressFactor: Int = 10, stressFunction: @escaping (Promise<T>) -> Void, fulfill f: @escaping () -> T) {
62 |     let group = DispatchGroup()
63 |     let queue = DispatchQueue(label: "the.domain.of.Zalgo", attributes: .concurrent)
64 | 
65 |     for _ in 0..<iterations {
66 |         let (promise, seal) = Promise<T>.pending()
67 | 
68 |         DispatchQueue.concurrentPerform(iterations: stressFactor) { n in
69 |             stressFunction(promise)
70 |         }
71 | 
72 |         queue.async(group: group) {
73 |             seal.fulfill(f())
74 |         }
75 |     }
76 | 
77 |     group.notify(queue: queue, execute: e1.fulfill)
78 | }
79 | 


--------------------------------------------------------------------------------
/Tests/CorePromise/Utilities.swift:
--------------------------------------------------------------------------------
 1 | import PromiseKit
 2 | 
 3 | extension Promise {
 4 |     func silenceWarning() {}
 5 | }
 6 | 
 7 | #if os(Linux) || os(Android)
 8 | import func CoreFoundation._CFIsMainThread
 9 | 
10 | extension Thread {
11 |     // `isMainThread` is not implemented yet in swift-corelibs-foundation.
12 |     static var isMainThread: Bool {
13 |         return _CFIsMainThread()
14 |     }
15 | }
16 | 
17 | import XCTest
18 | 
19 | extension XCTestCase {
20 |     func wait(for: [XCTestExpectation], timeout: TimeInterval, file: StaticString = #file, line: UInt = #line) {
21 |     #if !(swift(>=4.0) && !swift(>=4.1))
22 |         let line = Int(line)
23 |     #endif
24 |         waitForExpectations(timeout: timeout, file: file, line: line)
25 |     }
26 | }
27 | 
28 | extension XCTestExpectation {
29 |     func fulfill() {
30 |         fulfill(#file, line: #line)
31 |     }
32 | }
33 | #endif
34 | 


--------------------------------------------------------------------------------
/Tests/CorePromise/WhenConcurrentTests.swift:
--------------------------------------------------------------------------------
  1 | import XCTest
  2 | import PromiseKit
  3 | 
  4 | class WhenConcurrentTestCase_Swift: XCTestCase {
  5 | 
  6 |     func testWhen() {
  7 |         let e = expectation(description: "")
  8 | 
  9 |         var numbers = (0..<42).makeIterator()
 10 |         let squareNumbers = numbers.map { $0 * $0 }
 11 | 
 12 |         let generator = AnyIterator<Guarantee<Int>> {
 13 |             guard let number = numbers.next() else {
 14 |                 return nil
 15 |             }
 16 | 
 17 |             return after(.milliseconds(10)).map {
 18 |                 return number * number
 19 |             }
 20 |         }
 21 | 
 22 |         when(fulfilled: generator, concurrently: 5).done { numbers in
 23 |             if numbers == squareNumbers {
 24 |                 e.fulfill()
 25 |             }
 26 |         }.silenceWarning()
 27 | 
 28 |         waitForExpectations(timeout: 3, handler: nil)
 29 |     }
 30 | 
 31 |     func testWhenEmptyGenerator() {
 32 |         let e = expectation(description: "")
 33 | 
 34 |         let generator = AnyIterator<Promise<Int>> {
 35 |             return nil
 36 |         }
 37 | 
 38 |         when(fulfilled: generator, concurrently: 5).done { numbers in
 39 |             if numbers.count == 0 {
 40 |                 e.fulfill()
 41 |             }
 42 |         }.silenceWarning()
 43 | 
 44 |         waitForExpectations(timeout: 1, handler: nil)
 45 |     }
 46 | 
 47 |     func testWhenGeneratorError() {
 48 |         enum LocalError: Error {
 49 |             case Unknown
 50 |             case DivisionByZero
 51 |         }
 52 | 
 53 |         let expectedErrorIndex = 42
 54 |         let expectedError = LocalError.DivisionByZero
 55 | 
 56 |         let e = expectation(description: "")
 57 | 
 58 |         var numbers = (-expectedErrorIndex..<expectedErrorIndex).makeIterator()
 59 | 
 60 |         let generator = AnyIterator<Promise<Int>> {
 61 |             guard let number = numbers.next() else {
 62 |                 return nil
 63 |             }
 64 | 
 65 |             return after(.milliseconds(10)).then { _ -> Promise<Int> in
 66 |                 if number != 0 {
 67 |                     return Promise(error: expectedError)
 68 |                 } else {
 69 |                     return .value(100500 / number)
 70 |                 }
 71 |             }
 72 |         }
 73 | 
 74 |         when(fulfilled: generator, concurrently: 3)
 75 |             .catch { error in
 76 |                 guard let error = error as? LocalError else {
 77 |                     return
 78 |                 }
 79 |                 guard case .DivisionByZero = error else {
 80 |                     return
 81 |                 }
 82 |                 e.fulfill()
 83 |             }
 84 | 
 85 |         waitForExpectations(timeout: 3, handler: nil)
 86 |     }
 87 | 
 88 |     func testWhenConcurrency() {
 89 |         let expectedConcurrently = 4
 90 |         var currentConcurrently = 0
 91 |         var maxConcurrently = 0
 92 | 
 93 |         let e = expectation(description: "")
 94 | 
 95 |         var numbers = (0..<42).makeIterator()
 96 | 
 97 |         let generator = AnyIterator<Promise<Int>> {
 98 |             currentConcurrently += 1
 99 |             maxConcurrently = max(maxConcurrently, currentConcurrently)
100 | 
101 |             guard let number = numbers.next() else {
102 |                 return nil
103 |             }
104 | 
105 |             return after(.milliseconds(10)).then(on: .main) { _ -> Promise<Int> in
106 |                 currentConcurrently -= 1
107 |                 return .value(number * number)
108 |             }
109 |         }
110 | 
111 |         when(fulfilled: generator, concurrently: expectedConcurrently).done { _ in
112 |             XCTAssertEqual(expectedConcurrently, maxConcurrently)
113 |             e.fulfill()
114 |         }.silenceWarning()
115 | 
116 |         waitForExpectations(timeout: 3)
117 |     }
118 | 
119 |     func testWhenConcurrencyLessThanZero() {
120 |         let generator = AnyIterator<Promise<Int>> { XCTFail(); return nil }
121 | 
122 |         let p1 = when(fulfilled: generator, concurrently: 0)
123 |         let p2 = when(fulfilled: generator, concurrently: -1)
124 | 
125 |         guard let e1 = p1.error else { return XCTFail() }
126 |         guard let e2 = p2.error else { return XCTFail() }
127 |         guard case PMKError.badInput = e1 else { return XCTFail() }
128 |         guard case PMKError.badInput = e2 else { return XCTFail() }
129 |     }
130 | 
131 |     func testStopsDequeueingOnceRejected() {
132 |         let ex = expectation(description: "")
133 |         enum Error: Swift.Error { case dummy }
134 | 
135 |         var x: UInt = 0
136 |         let generator = AnyIterator<Promise<Void>> {
137 |             x += 1
138 |             switch x {
139 |             case 0:
140 |                 fatalError()
141 |             case 1:
142 |                 return Promise()
143 |             case 2:
144 |                 return Promise(error: Error.dummy)
145 |             case _:
146 |                 XCTFail()
147 |                 return nil
148 |             }
149 |         }
150 | 
151 |         when(fulfilled: generator, concurrently: 1).done {
152 |             XCTFail("\($0)")
153 |         }.catch { error in
154 |             ex.fulfill()
155 |         }
156 | 
157 |         waitForExpectations(timeout: 3)
158 |     }
159 | 
160 |     func testWhenResolvedContinuesWhenRejected() {
161 |     #if swift(>=5.3)
162 |         let ex = expectation(description: "")
163 |         enum Error: Swift.Error { case dummy }
164 | 
165 |         var x: UInt = 0
166 |         let generator = AnyIterator<Promise<Void>> {
167 |             x += 1
168 |             switch x {
169 |             case 0:
170 |                 fatalError()
171 |             case 1:
172 |                 return Promise()
173 |             case 2:
174 |                 return Promise(error: Error.dummy)
175 |             case 3:
176 |                 return Promise()
177 |             case _:
178 |                 return nil
179 |             }
180 |         }
181 | 
182 |         when(resolved: generator, concurrently: 1).done { results in
183 |             XCTAssertEqual(results.count, 3)
184 |             ex.fulfill()
185 |         }
186 | 
187 |         waitForExpectations(timeout: 3)
188 |     #endif
189 |     }
190 | }
191 | 


--------------------------------------------------------------------------------
/Tests/CorePromise/WhenResolvedTests.swift:
--------------------------------------------------------------------------------
 1 | //  Created by Austin Feight on 3/19/16.
 2 | //  Copyright © 2016 Max Howell. All rights reserved.
 3 | 
 4 | import PromiseKit
 5 | import XCTest
 6 | 
 7 | class JoinTests: XCTestCase {
 8 |     func testImmediates() {
 9 |         let successPromise = Promise()
10 | 
11 |         var joinFinished = false
12 |         when(resolved: successPromise).done(on: nil) { _ in joinFinished = true }
13 |         XCTAssert(joinFinished, "Join immediately finishes on fulfilled promise")
14 |         
15 |         let promise2 = Promise.value(2)
16 |         let promise3 = Promise.value(3)
17 |         let promise4 = Promise.value(4)
18 |         var join2Finished = false
19 |         when(resolved: promise2, promise3, promise4).done(on: nil) { _ in join2Finished = true }
20 |         XCTAssert(join2Finished, "Join immediately finishes on fulfilled promises")
21 |     }
22 | 
23 |     func testFulfilledAfterAllResolve() {
24 |         let (promise1, seal1) = Promise<Void>.pending()
25 |         let (promise2, seal2) = Promise<Void>.pending()
26 |         let (promise3, seal3) = Promise<Void>.pending()
27 |         
28 |         var finished = false
29 |         when(resolved: promise1, promise2, promise3).done(on: nil) { _ in finished = true }
30 |         XCTAssertFalse(finished, "Not all promises have resolved")
31 |         
32 |         seal1.fulfill_()
33 |         XCTAssertFalse(finished, "Not all promises have resolved")
34 |         
35 |         seal2.fulfill_()
36 |         XCTAssertFalse(finished, "Not all promises have resolved")
37 |         
38 |         seal3.fulfill_()
39 |         XCTAssert(finished, "All promises have resolved")
40 |     }
41 | }
42 | 


--------------------------------------------------------------------------------
/Tests/CorePromise/ZalgoTests.swift:
--------------------------------------------------------------------------------
 1 | import XCTest
 2 | import PromiseKit
 3 | 
 4 | class ZalgoTests: XCTestCase {
 5 |     func test1() {
 6 |         var resolved = false
 7 |         Promise.value(1).done(on: nil) { _ in
 8 |             resolved = true
 9 |         }.silenceWarning()
10 |         XCTAssertTrue(resolved)
11 |     }
12 | 
13 |     func test2() {
14 |         let p1 = Promise.value(1).map(on: nil) { x in
15 |             return 2
16 |         }
17 |         XCTAssertEqual(p1.value!, 2)
18 |         
19 |         var x = 0
20 |         
21 |         let (p2, seal) = Promise<Int>.pending()
22 |         p2.done(on: nil) { _ in
23 |             x = 1
24 |         }.silenceWarning()
25 |         XCTAssertEqual(x, 0)
26 |         
27 |         seal.fulfill(1)
28 |         XCTAssertEqual(x, 1)
29 |     }
30 | 
31 |     // returning a pending promise from its own zalgo’d then handler doesn’t hang
32 |     func test3() {
33 |         let ex = (expectation(description: ""), expectation(description: ""))
34 | 
35 |         var p1: Promise<Void>!
36 |         p1 = after(.milliseconds(100)).then(on: nil) { _ -> Promise<Void> in
37 |             ex.0.fulfill()
38 |             return p1
39 |         }
40 | 
41 |         p1.catch { err in
42 |             defer{ ex.1.fulfill() }
43 |             guard case PMKError.returnedSelf = err else { return XCTFail() }
44 |         }
45 | 
46 |         waitForExpectations(timeout: 1)
47 |     }
48 | 
49 |     // return a sealed promise from its own zalgo’d then handler doesn’t hang
50 |     func test4() {
51 |         let ex = expectation(description: "")
52 |         let p1 = Promise.value(1)
53 |         p1.then(on: nil) { _ -> Promise<Int> in
54 |             ex.fulfill()
55 |             return p1
56 |         }.silenceWarning()
57 |         waitForExpectations(timeout: 1)
58 |     }
59 | }
60 | 


--------------------------------------------------------------------------------
/Tests/DeprecationTests.swift:
--------------------------------------------------------------------------------
  1 | import PromiseKit
  2 | import XCTest
  3 | 
  4 | class DeprecationTests: XCTestCase {
  5 |     func testWrap1() {
  6 |         let dummy = 10
  7 | 
  8 |         func completion(_ body: (_ a: Int?, _ b: Error?) -> Void) {
  9 |             body(dummy, nil)
 10 |         }
 11 | 
 12 |         let ex = expectation(description: "")
 13 |         wrap(completion).done {
 14 |             XCTAssertEqual($0, dummy)
 15 |             ex.fulfill()
 16 |         }
 17 |         wait(for: [ex], timeout: 10)
 18 |     }
 19 | 
 20 |     func testWrap2() {
 21 |         let dummy = 10
 22 | 
 23 |         func completion(_ body: (_ a: Int, _ b: Error?) -> Void) {
 24 |             body(dummy, nil)
 25 |         }
 26 | 
 27 |         let ex = expectation(description: "")
 28 |         wrap(completion).done {
 29 |             XCTAssertEqual($0, dummy)
 30 |             ex.fulfill()
 31 |         }
 32 |         wait(for: [ex], timeout: 10)
 33 |     }
 34 | 
 35 |     func testWrap3() {
 36 |         let dummy = 10
 37 | 
 38 |         func completion(_ body: (_ a: Error?, _ b: Int?) -> Void) {
 39 |             body(nil, dummy)
 40 |         }
 41 | 
 42 |         let ex = expectation(description: "")
 43 |         wrap(completion).done {
 44 |             XCTAssertEqual($0, dummy)
 45 |             ex.fulfill()
 46 |         }
 47 |         wait(for: [ex], timeout: 10)
 48 |     }
 49 | 
 50 |     func testWrap4() {
 51 |         let dummy = 10
 52 | 
 53 |         func completion(_ body: (_ a: Error?) -> Void) {
 54 |             body(nil)
 55 |         }
 56 | 
 57 |         let ex = expectation(description: "")
 58 |         wrap(completion).done {
 59 |             ex.fulfill()
 60 |         }
 61 |         wait(for: [ex], timeout: 10)
 62 |     }
 63 | 
 64 |     func testWrap5() {
 65 |         let dummy = 10
 66 | 
 67 |         func completion(_ body: (_ a: Int) -> Void) {
 68 |             body(dummy)
 69 |         }
 70 | 
 71 |         let ex = expectation(description: "")
 72 |         wrap(completion).done {
 73 |             XCTAssertEqual($0, dummy)
 74 |             ex.fulfill()
 75 |         }
 76 |         wait(for: [ex], timeout: 10)
 77 |     }
 78 | 
 79 |     func testAlways() {
 80 |         let ex = expectation(description: "")
 81 |         Promise.value(1).always(execute: ex.fulfill)
 82 |         wait(for: [ex], timeout: 10)
 83 |     }
 84 | 
 85 | #if PMKFullDeprecations
 86 |     func testFlatMap() {
 87 |         let ex = expectation(description: "")
 88 |         Promise.value(1).flatMap { _ -> Int? in
 89 |             nil
 90 |         }.catch {
 91 |             //TODO should be `flatMap`, but how to enact that without causing
 92 |             // compiler to warn when building PromiseKit for end-users? LOL
 93 |             guard case PMKError.compactMap = $0 else { return XCTFail() }
 94 |             ex.fulfill()
 95 |         }
 96 |         wait(for: [ex], timeout: 10)
 97 |     }
 98 | 
 99 |     func testSequenceMap() {
100 |         let ex = expectation(description: "")
101 |         Promise.value([1, 2]).map {
102 |             $0 + 1
103 |         }.done {
104 |             XCTAssertEqual($0, [2, 3])
105 |             ex.fulfill()
106 |         }.silenceWarning()
107 |         wait(for: [ex], timeout: 10)
108 |     }
109 | 
110 |     func testSequenceFlatMap() {
111 |         let ex = expectation(description: "")
112 |         Promise.value([1, 2]).flatMap {
113 |             [$0 + 1, $0 + 2]
114 |         }.done {
115 |             XCTAssertEqual($0, [2, 3, 3, 4])
116 |             ex.fulfill()
117 |         }.silenceWarning()
118 |         wait(for: [ex], timeout: 10)
119 |     }
120 | #endif
121 | 
122 |     func testSequenceFilter() {
123 |         let ex = expectation(description: "")
124 |         Promise.value([0, 1, 2, 3]).filter {
125 |             $0 < 2
126 |         }.done {
127 |             XCTAssertEqual($0, [0, 1])
128 |             ex.fulfill()
129 |         }.silenceWarning()
130 |         wait(for: [ex], timeout: 10)
131 |     }
132 | 
133 |     func testSorted() {
134 |         let ex = expectation(description: "")
135 |         Promise.value([5, 2, 1, 8]).sorted().done {
136 |             XCTAssertEqual($0, [1,2,5,8])
137 |             ex.fulfill()
138 |         }
139 |         wait(for: [ex], timeout: 10)
140 |     }
141 | 
142 |     func testFirst() {
143 |         XCTAssertEqual(Promise.value([1,2]).first.value, 1)
144 |     }
145 | 
146 |     func testLast() {
147 |         XCTAssertEqual(Promise.value([1,2]).last.value, 2)
148 |     }
149 | 
150 |     func testPMKErrorFlatMap() {
151 |         XCTAssertNotNil(PMKError.flatMap(1, Int.self).errorDescription)
152 |     }
153 | }
154 | 
155 | 
156 | extension Promise {
157 |     func silenceWarning() {}
158 | }
159 | 


--------------------------------------------------------------------------------
/Tests/JS-A+/.gitignore:
--------------------------------------------------------------------------------
 1 | # From https://github.com/github/gitignore/blob/master/Node.gitignore
 2 | 
 3 | # Logs
 4 | logs
 5 | *.log
 6 | npm-debug.log*
 7 | yarn-debug.log*
 8 | yarn-error.log*
 9 | 
10 | # Runtime data
11 | pids
12 | *.pid
13 | *.seed
14 | *.pid.lock
15 | 
16 | # Directory for instrumented libs generated by jscoverage/JSCover
17 | lib-cov
18 | 
19 | # Coverage directory used by tools like istanbul
20 | coverage
21 | 
22 | # nyc test coverage
23 | .nyc_output
24 | 
25 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
26 | .grunt
27 | 
28 | # Bower dependency directory (https://bower.io/)
29 | bower_components
30 | 
31 | # node-waf configuration
32 | .lock-wscript
33 | 
34 | # Compiled binary addons (https://nodejs.org/api/addons.html)
35 | build/Release
36 | 
37 | # Dependency directories
38 | node_modules/
39 | jspm_packages/
40 | 
41 | # Typescript v1 declaration files
42 | typings/
43 | 
44 | # Optional npm cache directory
45 | .npm
46 | 
47 | # Optional eslint cache
48 | .eslintcache
49 | 
50 | # Optional REPL history
51 | .node_repl_history
52 | 
53 | # Output of 'npm pack'
54 | *.tgz
55 | 
56 | # Yarn Integrity file
57 | .yarn-integrity
58 | 
59 | # dotenv environment variables file
60 | .env
61 | 
62 | # next.js build output
63 | .next
64 | 


--------------------------------------------------------------------------------
/Tests/JS-A+/AllTests.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | //  AllTests.swift
 3 | //  PMKJSA+Tests
 4 | //
 5 | //  Created by Lois Di Qual on 2/28/18.
 6 | //
 7 | 
 8 | #if swift(>=3.2) && !os(watchOS)
 9 | 
10 | import XCTest
11 | import PromiseKit
12 | import JavaScriptCore
13 | 
14 | class AllTests: XCTestCase {
15 |     
16 |     func testAll() {
17 |         
18 |         let scriptPath = URL(fileURLWithPath: #file).deletingLastPathComponent().appendingPathComponent("build/build.js")
19 |         guard FileManager.default.fileExists(atPath: scriptPath.path) else {
20 |             return print("Skipping JS-A+: see README for instructions on how to build")
21 |         }
22 |         
23 |         guard let script = try? String(contentsOf: scriptPath) else {
24 |             return XCTFail("Couldn't read content of test suite JS file")
25 |         }
26 |         
27 |         let context = JSUtils.sharedContext
28 |         
29 |         // Add a global exception handler
30 |         context.exceptionHandler = { context, exception in
31 |             guard let exception = exception else {
32 |                 return XCTFail("Unknown JS exception")
33 |             }
34 |             JSUtils.printStackTrace(exception: exception, includeExceptionDescription: true)
35 |         }
36 |         
37 |         // Setup mock functions (timers, console.log, etc)
38 |         let environment = MockNodeEnvironment()
39 |         environment.setup(with: context)
40 |         
41 |         // Expose JSPromise in the javascript context
42 |         context.setObject(JSPromise.self, forKeyedSubscript: "JSPromise" as NSString)
43 |         
44 |         // Create adapter
45 |         guard let adapter = JSValue(object: NSDictionary(), in: context) else {
46 |             fatalError("Couldn't create adapter")
47 |         }
48 |         adapter.setObject(JSAdapter.resolved, forKeyedSubscript: "resolved" as NSString)
49 |         adapter.setObject(JSAdapter.rejected, forKeyedSubscript: "rejected" as NSString)
50 |         adapter.setObject(JSAdapter.deferred, forKeyedSubscript: "deferred" as NSString)
51 |         
52 |         // Evaluate contents of `build.js`, which exposes `runTests` in the global context
53 |         context.evaluateScript(script)
54 |         guard let runTests = context.objectForKeyedSubscript("runTests") else {
55 |             return XCTFail("Couldn't find `runTests` in JS context")
56 |         }
57 |         
58 |         // Create a callback that's called whenever there's a failure
59 |         let onFail: @convention(block) (JSValue, JSValue) -> Void = { test, error in
60 |             guard let test = test.toString(), let error = error.toString() else {
61 |                 return XCTFail("Unknown test failure")
62 |             }
63 |             XCTFail("\(test) failed: \(error)")
64 |         }
65 |         let onFailValue: JSValue = JSValue(object: onFail, in: context)
66 |         
67 |         // Create a new callback that we'll send to `runTest` so that it notifies when tests are done running.
68 |         let expectation = self.expectation(description: "async")
69 |         let onDone: @convention(block) (JSValue) -> Void = { failures in
70 |             expectation.fulfill()
71 |         }
72 |         let onDoneValue: JSValue = JSValue(object: onDone, in: context)
73 |         
74 |         // If there's a need to only run one specific test, uncomment the next line and comment the one after
75 |         // let testName: JSValue = JSValue(object: "2.3.1", in: context)
76 |         let testName = JSUtils.undefined
77 |         
78 |         // Call `runTests`
79 |         runTests.call(withArguments: [adapter, onFailValue, onDoneValue, testName])
80 |         self.wait(for: [expectation], timeout: 60)
81 |     }
82 | }
83 | 
84 | #endif
85 | 


--------------------------------------------------------------------------------
/Tests/JS-A+/JSAdapter.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSAdapter.swift
 3 | //  PMKJSA+Tests
 4 | //
 5 | //  Created by Lois Di Qual on 3/2/18.
 6 | //
 7 | 
 8 | #if !os(watchOS)
 9 | 
10 | import Foundation
11 | import JavaScriptCore
12 | import PromiseKit
13 | 
14 | enum JSAdapter {
15 |     
16 |     static let resolved: @convention(block) (JSValue) -> JSPromise = { value in
17 |         return JSPromise(promise: .value(value))
18 |     }
19 |     
20 |     static let rejected: @convention(block) (JSValue) -> JSPromise = { reason in
21 |         let error = JSUtils.JSError(reason: reason)
22 |         let promise = Promise<JSValue>(error: error)
23 |         return JSPromise(promise: promise)
24 |     }
25 |     
26 |     static let deferred: @convention(block) () -> JSValue = {
27 |         
28 |         let context = JSContext.current()
29 |         
30 |         guard let object = JSValue(object: NSDictionary(), in: context) else {
31 |             fatalError("Couldn't create object")
32 |         }
33 |         
34 |         let pendingPromise = Promise<JSValue>.pending()
35 |         let jsPromise = JSPromise(promise: pendingPromise.promise)
36 |         
37 |         // promise
38 |         object.setObject(jsPromise, forKeyedSubscript: "promise" as NSString)
39 |         
40 |         // resolve
41 |         let resolve: @convention(block) (JSValue) -> Void = { value in
42 |             pendingPromise.resolver.fulfill(value)
43 |         }
44 |         object.setObject(resolve, forKeyedSubscript: "resolve" as NSString)
45 |         
46 |         // reject
47 |         let reject: @convention(block) (JSValue) -> Void = { reason in
48 |             let error = JSUtils.JSError(reason: reason)
49 |             pendingPromise.resolver.reject(error)
50 |         }
51 |         object.setObject(reject, forKeyedSubscript: "reject" as NSString)
52 |         
53 |         return object
54 |     }
55 | }
56 | 
57 | #endif
58 | 


--------------------------------------------------------------------------------
/Tests/JS-A+/JSPromise.swift:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSPromise.swift
 3 | //  PMKJSA+Tests
 4 | //
 5 | //  Created by Lois Di Qual on 3/1/18.
 6 | //
 7 | 
 8 | #if !os(watchOS)
 9 | 
10 | import Foundation
11 | import XCTest
12 | import PromiseKit
13 | import JavaScriptCore
14 | 
15 | @objc protocol JSPromiseProtocol: JSExport {
16 |     func then(_: JSValue, _: JSValue) -> JSPromise
17 | }
18 | 
19 | class JSPromise: NSObject, JSPromiseProtocol {
20 |     
21 |     let promise: Promise<JSValue>
22 |     
23 |     init(promise: Promise<JSValue>) {
24 |         self.promise = promise
25 |     }
26 |     
27 |     func then(_ onFulfilled: JSValue, _ onRejected: JSValue) -> JSPromise {
28 |         
29 |         // Keep a reference to the returned promise so we can comply to 2.3.1
30 |         var returnedPromiseRef: Promise<JSValue>?
31 |         
32 |         let afterFulfill = promise.then { value -> Promise<JSValue> in
33 |             
34 |             // 2.2.1: ignored if not a function
35 |             guard JSUtils.isFunction(value: onFulfilled) else {
36 |                 return .value(value)
37 |             }
38 |             
39 |             // Call `onFulfilled`
40 |             // 2.2.5: onFulfilled/onRejected must be called as functions (with no `this` value)
41 |             guard let returnValue = try JSUtils.call(function: onFulfilled, arguments: [JSUtils.undefined, value]) else {
42 |                 return .value(value)
43 |             }
44 |             
45 |             // Extract JSPromise.promise if available, or use plain return value
46 |             if let jsPromise = returnValue.toObjectOf(JSPromise.self) as? JSPromise {
47 |                 
48 |                 // 2.3.1: if returned value is the promise that `then` returned, throw TypeError
49 |                 if jsPromise.promise === returnedPromiseRef {
50 |                     throw JSUtils.JSError(reason: JSUtils.typeError(message: "Returned self"))
51 |                 }
52 |                 return jsPromise.promise
53 |             } else {
54 |                 return .value(returnValue)
55 |             }
56 |         }
57 |         
58 |         let afterReject = promise.recover { error -> Promise<JSValue> in
59 |             
60 |             // 2.2.1: ignored if not a function
61 |             guard let jsError = error as? JSUtils.JSError, JSUtils.isFunction(value: onRejected) else {
62 |                 throw error
63 |             }
64 |             
65 |             // Call `onRejected`
66 |             // 2.2.5: onFulfilled/onRejected must be called as functions (with no `this` value)
67 |             guard let returnValue = try JSUtils.call(function: onRejected, arguments: [JSUtils.undefined, jsError.reason]) else {
68 |                 throw error
69 |             }
70 |             
71 |             // Extract JSPromise.promise if available, or use plain return value
72 |             if let jsPromise = returnValue.toObjectOf(JSPromise.self) as? JSPromise {
73 |                 
74 |                 // 2.3.1: if returned value is the promise that `then` returned, throw TypeError
75 |                 if jsPromise.promise === returnedPromiseRef {
76 |                     throw JSUtils.JSError(reason: JSUtils.typeError(message: "Returned self"))
77 |                 }
78 |                 return jsPromise.promise
79 |             } else {
80 |                 return .value(returnValue)
81 |             }
82 |         }
83 |         
84 |         let newPromise = Promise<Result<JSValue>> { resolver in
85 |             _ = promise.tap(resolver.fulfill)
86 |         }.then(on: nil) { result -> Promise<JSValue> in
87 |             switch result {
88 |             case .fulfilled: return afterFulfill
89 |             case .rejected: return afterReject
90 |             }
91 |         }
92 |         returnedPromiseRef = newPromise
93 |         
94 |         return JSPromise(promise: newPromise)
95 |     }
96 | }
97 | 
98 | #endif
99 | 


--------------------------------------------------------------------------------
/Tests/JS-A+/JSUtils.swift:
--------------------------------------------------------------------------------
  1 | //
  2 | //  JSUtils.swift
  3 | //  PMKJSA+Tests
  4 | //
  5 | //  Created by Lois Di Qual on 3/2/18.
  6 | //
  7 | 
  8 | #if !os(watchOS)
  9 | 
 10 | import Foundation
 11 | import JavaScriptCore
 12 | 
 13 | enum JSUtils {
 14 |     
 15 |     class JSError: Error {
 16 |         let reason: JSValue
 17 |         init(reason: JSValue) {
 18 |             self.reason = reason
 19 |         }
 20 |     }
 21 |     
 22 |     static let sharedContext: JSContext = {
 23 |         guard let context = JSContext() else {
 24 |             fatalError("Couldn't create JS context")
 25 |         }
 26 |         return context
 27 |     }()
 28 |     
 29 |     static var undefined: JSValue {
 30 |         guard let undefined = JSValue(undefinedIn: JSUtils.sharedContext) else {
 31 |             fatalError("Couldn't create `undefined` value")
 32 |         }
 33 |         return undefined
 34 |     }
 35 |     
 36 |     static func typeError(message: String) -> JSValue {
 37 |         let message = message.replacingOccurrences(of: "\"", with: "\\\"")
 38 |         let script = "new TypeError(\"\(message)\")"
 39 |         guard let result = sharedContext.evaluateScript(script) else {
 40 |             fatalError("Couldn't create TypeError")
 41 |         }
 42 |         return result
 43 |     }
 44 |     
 45 |     // @warning: relies on lodash to be present
 46 |     static func isFunction(value: JSValue) -> Bool {
 47 |         guard let context = value.context else {
 48 |             return false
 49 |         }
 50 |         guard let lodash = context.objectForKeyedSubscript("_") else {
 51 |             fatalError("Couldn't get lodash in JS context")
 52 |         }
 53 |         guard let result = lodash.invokeMethod("isFunction", withArguments: [value]) else {
 54 |             fatalError("Couldn't invoke _.isFunction")
 55 |         }
 56 |         return result.toBool()
 57 |     }
 58 |     
 59 |     // Calls a JS function using `Function.prototype.call` and throws any potential exception wrapped in a JSError
 60 |     static func call(function: JSValue, arguments: [JSValue]) throws -> JSValue? {
 61 |         
 62 |         let context = JSUtils.sharedContext
 63 |         
 64 |         // Create a new exception handler that will store a potential exception
 65 |         // thrown in the handler. Save the value of the old handler.
 66 |         var caughtException: JSValue?
 67 |         let savedExceptionHandler = context.exceptionHandler
 68 |         context.exceptionHandler = { context, exception in
 69 |             caughtException = exception
 70 |         }
 71 |         
 72 |         // Call the handler
 73 |         let returnValue = function.invokeMethod("call", withArguments: arguments)
 74 |         context.exceptionHandler = savedExceptionHandler
 75 |         
 76 |         // If an exception was caught, throw it
 77 |         if let exception = caughtException {
 78 |             throw JSError(reason: exception)
 79 |         }
 80 |         
 81 |         return returnValue
 82 |     }
 83 |     
 84 |     static func printCurrentStackTrace() {
 85 |         guard let exception = JSUtils.sharedContext.evaluateScript("new Error()") else {
 86 |             return print("Couldn't get current stack trace")
 87 |         }
 88 |         printStackTrace(exception: exception, includeExceptionDescription: false)
 89 |     }
 90 |     
 91 |     static func printStackTrace(exception: JSValue, includeExceptionDescription: Bool) {
 92 |         guard let lineNumber = exception.objectForKeyedSubscript("line"),
 93 |             let column = exception.objectForKeyedSubscript("column"),
 94 |             let message = exception.objectForKeyedSubscript("message"),
 95 |             let stacktrace = exception.objectForKeyedSubscript("stack")?.toString() else {
 96 |                 return print("Couldn't print stack trace")
 97 |         }
 98 |         
 99 |         if includeExceptionDescription {
100 |             print("JS Exception at \(lineNumber):\(column): \(message)")
101 |         }
102 |         
103 |         let lines = stacktrace.split(separator: "\n").map { "\t> \($0)" }.joined(separator: "\n")
104 |         print(lines)
105 |     }
106 | }
107 | 
108 | #if !swift(>=3.2)
109 | extension String {
110 |     func split(separator: Character, omittingEmptySubsequences: Bool = true) -> [String] {
111 |         return characters.split(separator: separator, omittingEmptySubsequences: omittingEmptySubsequences).map(String.init)
112 |     }
113 |     
114 |     var first: Character? {
115 |         return characters.first
116 |     }
117 | }
118 | #endif
119 | 
120 | #endif
121 | 


--------------------------------------------------------------------------------
/Tests/JS-A+/MockNodeEnvironment.swift:
--------------------------------------------------------------------------------
  1 | //
  2 | //  MockNodeEnvironment.swift
  3 | //  PMKJSA+Tests
  4 | //
  5 | //  Created by Lois Di Qual on 3/1/18.
  6 | //
  7 | 
  8 | #if swift(>=3.2) && !os(watchOS)
  9 | 
 10 | import Foundation
 11 | import JavaScriptCore
 12 | 
 13 | class MockNodeEnvironment {
 14 |     
 15 |     private var timers: [UInt32: Timer] = [:]
 16 |     
 17 |     func setup(with context: JSContext) {
 18 |         
 19 |         // console.log / console.error
 20 |         setupConsole(context: context)
 21 |         
 22 |         // setTimeout
 23 |         let setTimeout: @convention(block) (JSValue, Double) -> UInt32 = { function, intervalMs in
 24 |             let timerID = self.addTimer(interval: intervalMs / 1000, repeats: false, function: function)
 25 |             return timerID
 26 |         }
 27 |         context.setObject(setTimeout, forKeyedSubscript: "setTimeout" as NSString)
 28 |         
 29 |         // clearTimeout
 30 |         let clearTimeout: @convention(block) (JSValue) -> Void = { timeoutID in
 31 |             guard timeoutID.isNumber else {
 32 |                 return
 33 |             }
 34 |             self.removeTimer(timerID: timeoutID.toUInt32())
 35 |         }
 36 |         context.setObject(clearTimeout, forKeyedSubscript: "clearTimeout" as NSString)
 37 |         
 38 |         // setInterval
 39 |         let setInterval: @convention(block) (JSValue, Double) -> UInt32 = { function, intervalMs in
 40 |             let timerID = self.addTimer(interval: intervalMs / 1000, repeats: true, function: function)
 41 |             return timerID
 42 |         }
 43 |         context.setObject(setInterval, forKeyedSubscript: "setInterval" as NSString)
 44 |         
 45 |         // clearInterval
 46 |         let clearInterval: @convention(block) (JSValue) -> Void = { intervalID in
 47 |             guard intervalID.isNumber else {
 48 |                 return
 49 |             }
 50 |             self.removeTimer(timerID: intervalID.toUInt32())
 51 |         }
 52 |         context.setObject(clearInterval, forKeyedSubscript: "clearInterval" as NSString)
 53 |     }
 54 |     
 55 |     private func setupConsole(context: JSContext) {
 56 |         
 57 |         guard let console = context.objectForKeyedSubscript("console") else {
 58 |             fatalError("Couldn't get global `console` object")
 59 |         }
 60 |         
 61 |         let consoleLog: @convention(block) () -> Void = {
 62 |             guard let arguments = JSContext.currentArguments(), let format = arguments.first as? JSValue else {
 63 |                 return
 64 |             }
 65 |             
 66 |             let otherArguments = arguments.dropFirst()
 67 |             if otherArguments.count == 0 {
 68 |                 print(format)
 69 |             } else {
 70 |                 
 71 |                 let otherArguments = otherArguments.compactMap { $0 as? JSValue }
 72 |                 let format = format.toString().replacingOccurrences(of: "%s", with: "%@")
 73 |                 let expectedTypes = format.split(separator: "%", omittingEmptySubsequences: false).dropFirst().compactMap { $0.first }.map { String($0) }
 74 |                 
 75 |                 let typedArguments = otherArguments.enumerated().compactMap { index, value -> CVarArg? in
 76 |                     let expectedType = expectedTypes[index]
 77 |                     let converted: CVarArg
 78 |                     switch expectedType {
 79 |                     case "s": converted = value.toString()
 80 |                     case "d": converted = value.toInt32()
 81 |                     case "f": converted = value.toDouble()
 82 |                     default: converted = value.toString()
 83 |                     }
 84 |                     return converted
 85 |                 }
 86 |                 
 87 |                 let output = String(format: format, arguments: typedArguments)
 88 |                 print(output)
 89 |             }
 90 |         }
 91 |         console.setObject(consoleLog, forKeyedSubscript: "log" as NSString)
 92 |         console.setObject(consoleLog, forKeyedSubscript: "error" as NSString)
 93 |     }
 94 |     
 95 |     private func addTimer(interval: TimeInterval, repeats: Bool, function: JSValue) -> UInt32 {
 96 |         let block = BlockOperation {
 97 |             DispatchQueue.main.async {
 98 |                 function.call(withArguments: [])
 99 |             }
100 |         }
101 |         let timer = Timer.scheduledTimer(timeInterval: interval, target: block, selector: #selector(Operation.main), userInfo: nil, repeats: repeats)
102 |         let rawHash = UUID().uuidString.hashValue
103 |     #if swift(>=4.0)
104 |         let hash = UInt32(truncatingIfNeeded: rawHash)
105 |     #else
106 |         let hash = UInt32(truncatingBitPattern: rawHash)
107 |     #endif
108 |         timers[hash] = timer
109 |         return hash
110 |     }
111 |     
112 |     private func removeTimer(timerID: UInt32) {
113 |         guard let timer = timers[timerID] else {
114 |             return print("Couldn't find timer \(timerID)")
115 |         }
116 |         timer.invalidate()
117 |         timers[timerID] = nil
118 |     }
119 | }
120 | 
121 | 
122 | #if swift(>=4.0) && !swift(>=4.1) || !swift(>=3.3)
123 | extension Sequence {
124 |     func compactMap<T>(_ transform: (Self.Element) throws -> T?) rethrows -> [T] {
125 |         return try flatMap(transform)
126 |     }
127 | }
128 | #endif
129 | #endif
130 | 


--------------------------------------------------------------------------------
/Tests/JS-A+/README.md:
--------------------------------------------------------------------------------
 1 | Promises/A+ Compliance Test Suite (JavaScript)
 2 | ==============================================
 3 | 
 4 | What is this?
 5 | -------------
 6 | 
 7 | This contains the necessary Swift and JS files to run the Promises/A+ compliance test suite from PromiseKit's unit tests.
 8 | 
 9 |  - Promise/A+ Spec: <https://promisesaplus.com/>
10 |  - Compliance Test Suite: <https://github.com/promises-aplus/promises-tests>
11 | 
12 | Run tests
13 | ---------
14 | 
15 | ```
16 | $ npm install
17 | $ npm run build
18 | ```
19 | 
20 | then open `PromiseKit.xcodeproj` and run the `PMKJSA+Tests` unit test scheme.
21 | 
22 | Known limitations
23 | -----------------
24 | 
25 | See `ignoredTests` in `index.js`.
26 | 
27 | 
28 |  - 2.3.3 is disabled: Otherwise, if x is an object or function. This spec is a NOOP for Swift:
29 |   - We have decided not to interact with other Promises A+ implementations
30 |   - functions cannot have properties
31 | 
32 | Upgrade the test suite
33 | ----------------------
34 | 
35 | ```
36 | $ npm install --save promises-aplus-tests@latest
37 | $ npm run build
38 | ```
39 | 
40 | Develop
41 | -------
42 | 
43 | JavaScriptCore is a bit tedious to work with so here are a couple tips in case you're trying to debug the test suite.
44 | 
45 | If you're editing JS files, enable live rebuilds:
46 | 
47 | ```
48 | $ npm run watch
49 | ```
50 | 
51 | If you're editing Swift files, a couple things you can do:
52 | 
53 |  - You can adjust `testName` in `AllTests.swift` to only run one test suite
54 |  - You can call `JSUtils.printCurrentStackTrace()` at any time. It won't contain line numbers but some of the frame names might help.
55 | 
56 | How it works
57 | ------------
58 | 
59 | The Promises/A+ test suite is written in JavaScript but PromiseKit is written in Swift/ObjC. For the test suite to run against swift code, we expose a promise wrapper `JSPromise` inside a JavaScriptCore context. This is done in a regular XCTestCase.
60 | 
61 | Since JavaScriptCore doesn't support CommonJS imports, we inline all the JavaScript code into `build/build.js` using webpack. This includes all the npm dependencies (`promises-aplus-tests`, `mocha`, `sinon`, etc) as well as the glue code in `index.js`.
62 | 
63 | `build.js` exposes one global variable `runTests(adapter, onFail, onDone, [testName])`. In our XCTestCase, a shared JavaScriptCore context is created, `build.js` is evaluated and now `runTests` is accessible from the Swift context.
64 | 
65 | In our swift test, we create a JS-bridged `JSPromise` which only has one method `then(onFulfilled, onRejected) -> Promise`. It wraps a swift `Promise` and delegates call `then` calls to it.
66 | 
67 | An [adapter](https://github.com/promises-aplus/promises-tests#adapters) – plain JS object which provides `revoled(value), rejected(reason), and deferred()` – is passed to `runTests` to run the whole JavaScript test suite.
68 | 
69 | Errors and end events are reported back to Swift and piped to `XCTFail()` if necessary.
70 | 
71 | Since JavaScriptCore isn't a node/web environment, there is quite a bit of stubbing necessary for all this to work:
72 | 
73 |  - The `fs` module is stubbed with an empty function
74 |  - `console.log` redirects to `Swift.print` and provides only basic format parsing
75 |  - `setTimeout/setInterval` are implemented with `Swift.Timer` behind the scenes and stored in a `[TimerID: Timer]` map.
76 | 


--------------------------------------------------------------------------------
/Tests/JS-A+/index.js:
--------------------------------------------------------------------------------
 1 | const _ = require('lodash')
 2 | require('mocha')
 3 | 
 4 | // Ignored by design
 5 | const ignoredTests = [
 6 |   '2.3.3'
 7 | ]
 8 | 
 9 | module.exports = function(adapter, onFail, onDone, testName) {
10 |   
11 |   global.adapter = adapter
12 |   const mocha = new Mocha({ ui: 'bdd' })
13 | 
14 |   // Require all tests
15 |   console.log('Loading test files')
16 |   const requireTest = require.context('promises-aplus-tests/lib/tests', false, /\.js$/)    
17 |   requireTest.keys().forEach(file => {
18 |     
19 |     let currentTestName = _.replace(_.replace(file, './', ''), '.js', '')
20 |     if (testName && currentTestName !== testName) {
21 |       return
22 |     }
23 |     
24 |     if (_.includes(ignoredTests, currentTestName)) {
25 |       return
26 |     }
27 |     
28 |     console.log(`\t${currentTestName}`)
29 |     mocha.suite.emit('pre-require', global, file, mocha)
30 |     mocha.suite.emit('require', requireTest(file), file, mocha)
31 |     mocha.suite.emit('post-require', global, file, mocha)
32 |   })
33 | 
34 |   const runner = mocha.run(failures => {
35 |     onDone(failures)
36 |   })
37 |   
38 |   runner.on('fail', (test, err) => {
39 |     console.error(err)
40 |     onFail(test.title, err)
41 |   })
42 | }
43 | 


--------------------------------------------------------------------------------
/Tests/JS-A+/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "scripts": {
 3 |     "build": "webpack-cli",
 4 |     "watch": "webpack-cli --watch --mode development"
 5 |   },
 6 |   "dependencies": {
 7 |     "babel-core": "^6.26.0",
 8 |     "babel-loader": "^7.1.3",
 9 |     "babel-preset-env": "^1.6.1",
10 |     "lodash": "^4.17.21",
11 |     "mocha": "^5.0.1",
12 |     "promises-aplus-tests": "^2.1.2",
13 |     "sinon": "^4.4.2",
14 |     "webpack": "^4.0.1",
15 |     "webpack-cli": "^2.0.9"
16 |   }
17 | }
18 | 


--------------------------------------------------------------------------------
/Tests/JS-A+/webpack.config.js:
--------------------------------------------------------------------------------
 1 | var webpack = require('webpack');
 2 | 
 3 | module.exports = {
 4 |   mode: 'development',
 5 |   context: __dirname,
 6 |   entry: './index.js',
 7 |   output: {
 8 |     path: __dirname + '/build',
 9 |     filename: 'build.js',
10 |     library: 'runTests'
11 |   },
12 |   stats: {
13 |     warnings: false
14 |   },
15 |   module: {
16 |     rules: [
17 |       {
18 |         test: /\.js$/,
19 |         exclude: /(node_modules)/,
20 |         use: {
21 |           loader: 'babel-loader',
22 |           options: {
23 |             presets: ['env']
24 |           }
25 |         }
26 |       }
27 |     ]
28 |   },
29 |   node: {
30 |     fs: 'empty'
31 |   },
32 | };
33 | 


--------------------------------------------------------------------------------
/Tests/LinuxMain.swift:
--------------------------------------------------------------------------------
 1 | import XCTest
 2 | 
 3 | import APlus
 4 | import CorePromise
 5 | 
 6 | var tests = [XCTestCaseEntry]()
 7 | tests += APlus.__allTests()
 8 | tests += CorePromise.__allTests()
 9 | 
10 | XCTMain(tests)
11 | 


--------------------------------------------------------------------------------
/tea.yaml:
--------------------------------------------------------------------------------
1 | # https://tea.xyz/what-is-this-file
2 | # created with https://mash.pkgx.sh/mxcl/tea-register
3 | ---
4 | version: 1.0.0
5 | codeOwners:
6 |   - '0x5E2DE4A68df811AAAD32d71fb065e6946fA5C8d9'  # mxcl
7 | quorum: 1
8 | 


--------------------------------------------------------------------------------