mirror of
https://github.com/lil-org/wallet.git
synced 2025-01-06 04:13:11 +03:00
Stop using Swift PM, update Pods
This commit is contained in:
parent
4cbed12d92
commit
d97e87042c
@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 52;
|
||||
objectVersion = 51;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
@ -26,7 +26,6 @@
|
||||
2C1995442674C4BA00A8E370 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2C1995432674C4BA00A8E370 /* Assets.xcassets */; };
|
||||
2C1995472674C4BA00A8E370 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2C1995452674C4BA00A8E370 /* Main.storyboard */; };
|
||||
2C1995562674D0F300A8E370 /* Ethereum.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C1995552674D0F300A8E370 /* Ethereum.swift */; };
|
||||
2C19955B2674D54600A8E370 /* WalletConnect in Frameworks */ = {isa = PBXBuildFile; productRef = 2C19955A2674D54600A8E370 /* WalletConnect */; };
|
||||
2C6706A5267A6BFE006AAEF2 /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C6706A4267A6BFE006AAEF2 /* Bundle.swift */; };
|
||||
2C8A09B52675101300993638 /* AccountsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8A09B42675101300993638 /* AccountsService.swift */; };
|
||||
2C8A09C6267513FC00993638 /* Agent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C8A09C5267513FC00993638 /* Agent.swift */; };
|
||||
@ -85,7 +84,6 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2C19955B2674D54600A8E370 /* WalletConnect in Frameworks */,
|
||||
88745B0F4DEE1F60AD0F02C3 /* Pods_Encrypted_Ink.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@ -235,7 +233,6 @@
|
||||
);
|
||||
name = "Encrypted Ink";
|
||||
packageProductDependencies = (
|
||||
2C19955A2674D54600A8E370 /* WalletConnect */,
|
||||
);
|
||||
productName = "Ecrypted Ink";
|
||||
productReference = 2C19953C2674C4B900A8E370 /* Encrypted Ink.app */;
|
||||
@ -265,7 +262,6 @@
|
||||
);
|
||||
mainGroup = 2C1995332674C4B900A8E370;
|
||||
packageReferences = (
|
||||
2C1995592674D54600A8E370 /* XCRemoteSwiftPackageReference "wallet-connect-swift" */,
|
||||
);
|
||||
productRefGroup = 2C19953D2674C4B900A8E370 /* Products */;
|
||||
projectDirPath = "";
|
||||
@ -588,25 +584,6 @@
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
2C1995592674D54600A8E370 /* XCRemoteSwiftPackageReference "wallet-connect-swift" */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/WalletConnect/wallet-connect-swift";
|
||||
requirement = {
|
||||
branch = master;
|
||||
kind = branch;
|
||||
};
|
||||
};
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
2C19955A2674D54600A8E370 /* WalletConnect */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 2C1995592674D54600A8E370 /* XCRemoteSwiftPackageReference "wallet-connect-swift" */;
|
||||
productName = WalletConnect;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
};
|
||||
rootObject = 2C1995342674C4B900A8E370 /* Project object */;
|
||||
}
|
||||
|
@ -1,52 +0,0 @@
|
||||
{
|
||||
"object": {
|
||||
"pins": [
|
||||
{
|
||||
"package": "CryptoSwift",
|
||||
"repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "4e31051c63cc0ddf10a25cf5318856c510cf77f4",
|
||||
"version": "1.4.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "PromiseKit",
|
||||
"repositoryURL": "https://github.com/mxcl/PromiseKit",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "d2f7ba14bcdc45e18f4f60ad9df883fb9055f081",
|
||||
"version": "6.15.3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Starscream",
|
||||
"repositoryURL": "https://github.com/daltoniam/Starscream",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "e6b65c6d9077ea48b4a7bdda8994a1d3c6969c8d",
|
||||
"version": "3.1.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "swift-nio-zlib-support",
|
||||
"repositoryURL": "https://github.com/apple/swift-nio-zlib-support.git",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "37760e9a52030bb9011972c5213c3350fa9d41fd",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "WalletConnect",
|
||||
"repositoryURL": "https://github.com/WalletConnect/wallet-connect-swift",
|
||||
"state": {
|
||||
"branch": "master",
|
||||
"revision": "d4d45e1d94a2b96fb5c2831a1e9d23390f5ac09e",
|
||||
"version": null
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"version": 1
|
||||
}
|
4
Podfile
4
Podfile
@ -2,7 +2,7 @@ platform :osx, '10.14'
|
||||
|
||||
target 'Encrypted Ink' do
|
||||
use_frameworks!
|
||||
pod 'Web3Swift.io', :git => 'https://github.com/grachyov/Web3Swift.git', :branch => 'develop'
|
||||
pod 'Web3Swift.io', :git => 'https://github.com/zeriontech/Web3Swift.git', :branch => 'develop'
|
||||
pod 'BlockiesSwift'
|
||||
|
||||
pod 'WalletConnect', git: 'https://github.com/grachyov/wallet-connect-swift', branch: 'master'
|
||||
end
|
||||
|
34
Podfile.lock
34
Podfile.lock
@ -2,8 +2,20 @@ PODS:
|
||||
- BigInt (5.2.0)
|
||||
- BlockiesSwift (0.1.2)
|
||||
- CryptoSwift (1.4.0)
|
||||
- PromiseKit (6.15.3):
|
||||
- PromiseKit/CorePromise (= 6.15.3)
|
||||
- PromiseKit/Foundation (= 6.15.3)
|
||||
- PromiseKit/UIKit (= 6.15.3)
|
||||
- PromiseKit/CorePromise (6.15.3)
|
||||
- PromiseKit/Foundation (6.15.3):
|
||||
- PromiseKit/CorePromise
|
||||
- secp256k1.swift (0.1.4)
|
||||
- Starscream (4.0.4)
|
||||
- SwiftyJSON (4.3.0)
|
||||
- WalletConnect (0.1.0):
|
||||
- CryptoSwift
|
||||
- PromiseKit
|
||||
- Starscream
|
||||
- Web3Swift.io (0.0.4):
|
||||
- BigInt (~> 5.0)
|
||||
- CryptoSwift (~> 1.0)
|
||||
@ -12,34 +24,46 @@ PODS:
|
||||
|
||||
DEPENDENCIES:
|
||||
- BlockiesSwift
|
||||
- Web3Swift.io (from `https://github.com/grachyov/Web3Swift.git`, branch `develop`)
|
||||
- WalletConnect (from `https://github.com/grachyov/wallet-connect-swift`, branch `master`)
|
||||
- Web3Swift.io (from `https://github.com/zeriontech/Web3Swift.git`, branch `develop`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- BigInt
|
||||
- BlockiesSwift
|
||||
- CryptoSwift
|
||||
- PromiseKit
|
||||
- secp256k1.swift
|
||||
- Starscream
|
||||
- SwiftyJSON
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
WalletConnect:
|
||||
:branch: master
|
||||
:git: https://github.com/grachyov/wallet-connect-swift
|
||||
Web3Swift.io:
|
||||
:branch: develop
|
||||
:git: https://github.com/grachyov/Web3Swift.git
|
||||
:git: https://github.com/zeriontech/Web3Swift.git
|
||||
|
||||
CHECKOUT OPTIONS:
|
||||
WalletConnect:
|
||||
:commit: 689c28f519a615310d493a7e638974bfff6d0ce9
|
||||
:git: https://github.com/grachyov/wallet-connect-swift
|
||||
Web3Swift.io:
|
||||
:commit: a606f5b3c2f4b02ed551dc96b1a1390265b4bb44
|
||||
:git: https://github.com/grachyov/Web3Swift.git
|
||||
:commit: 4e04054ac06a9875137ebbfac5a4b8c9f94d49f6
|
||||
:git: https://github.com/zeriontech/Web3Swift.git
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
BigInt: f668a80089607f521586bbe29513d708491ef2f7
|
||||
BlockiesSwift: 22d8d56dd187e6bfd16cb8c8fbd4fd4896c3e65d
|
||||
CryptoSwift: 7cc902df1784de3b389a387756c7d710f197730c
|
||||
PromiseKit: 3b2b6995e51a954c46dbc550ce3da44fbfb563c5
|
||||
secp256k1.swift: a7e7a214f6db6ce5db32cc6b2b45e5c4dd633634
|
||||
Starscream: 5178aed56b316f13fa3bc55694e583d35dd414d9
|
||||
SwiftyJSON: 6faa0040f8b59dead0ee07436cbf76b73c08fd08
|
||||
WalletConnect: 366f1790394831a768dfc77c8c4f35f8fbdaae31
|
||||
Web3Swift.io: 18fd06aed9d56df9c704f9c6f87b06675bb05b53
|
||||
|
||||
PODFILE CHECKSUM: ac8eefd56a76ec0315ae6ace4dc1c03e89702c78
|
||||
PODFILE CHECKSUM: ade248ecc3a8a618186e3d3d754de44149e11bd9
|
||||
|
||||
COCOAPODS: 1.10.0
|
||||
|
39
Pods/Local Podspecs/WalletConnect.podspec.json
generated
Normal file
39
Pods/Local Podspecs/WalletConnect.podspec.json
generated
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
"name": "WalletConnect",
|
||||
"version": "0.1.0",
|
||||
"summary": "WalletConnect Swift SDK",
|
||||
"description": "WalletConnect Swift SDK",
|
||||
"homepage": "https://github.com/hewigovens/wallet-connect-swift",
|
||||
"license": {
|
||||
"type": "MIT",
|
||||
"file": "LICENSE"
|
||||
},
|
||||
"authors": {
|
||||
"hewigovens": "360470+hewigovens@users.noreply.github.com"
|
||||
},
|
||||
"source": {
|
||||
"git": "https://github.com/hewigovens/WalletConnect.git",
|
||||
"tag": "0.1.0"
|
||||
},
|
||||
"platforms": {
|
||||
"ios": "11.0",
|
||||
"osx": "10.14"
|
||||
},
|
||||
"source_files": "WalletConnect/**/*",
|
||||
"exclude_files": [
|
||||
"WalletConnect/Info.plist"
|
||||
],
|
||||
"swift_versions": "5.0",
|
||||
"dependencies": {
|
||||
"CryptoSwift": [
|
||||
|
||||
],
|
||||
"Starscream": [
|
||||
|
||||
],
|
||||
"PromiseKit": [
|
||||
|
||||
]
|
||||
},
|
||||
"swift_version": "5.0"
|
||||
}
|
34
Pods/Manifest.lock
generated
34
Pods/Manifest.lock
generated
@ -2,8 +2,20 @@ PODS:
|
||||
- BigInt (5.2.0)
|
||||
- BlockiesSwift (0.1.2)
|
||||
- CryptoSwift (1.4.0)
|
||||
- PromiseKit (6.15.3):
|
||||
- PromiseKit/CorePromise (= 6.15.3)
|
||||
- PromiseKit/Foundation (= 6.15.3)
|
||||
- PromiseKit/UIKit (= 6.15.3)
|
||||
- PromiseKit/CorePromise (6.15.3)
|
||||
- PromiseKit/Foundation (6.15.3):
|
||||
- PromiseKit/CorePromise
|
||||
- secp256k1.swift (0.1.4)
|
||||
- Starscream (4.0.4)
|
||||
- SwiftyJSON (4.3.0)
|
||||
- WalletConnect (0.1.0):
|
||||
- CryptoSwift
|
||||
- PromiseKit
|
||||
- Starscream
|
||||
- Web3Swift.io (0.0.4):
|
||||
- BigInt (~> 5.0)
|
||||
- CryptoSwift (~> 1.0)
|
||||
@ -12,34 +24,46 @@ PODS:
|
||||
|
||||
DEPENDENCIES:
|
||||
- BlockiesSwift
|
||||
- Web3Swift.io (from `https://github.com/grachyov/Web3Swift.git`, branch `develop`)
|
||||
- WalletConnect (from `https://github.com/grachyov/wallet-connect-swift`, branch `master`)
|
||||
- Web3Swift.io (from `https://github.com/zeriontech/Web3Swift.git`, branch `develop`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- BigInt
|
||||
- BlockiesSwift
|
||||
- CryptoSwift
|
||||
- PromiseKit
|
||||
- secp256k1.swift
|
||||
- Starscream
|
||||
- SwiftyJSON
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
WalletConnect:
|
||||
:branch: master
|
||||
:git: https://github.com/grachyov/wallet-connect-swift
|
||||
Web3Swift.io:
|
||||
:branch: develop
|
||||
:git: https://github.com/grachyov/Web3Swift.git
|
||||
:git: https://github.com/zeriontech/Web3Swift.git
|
||||
|
||||
CHECKOUT OPTIONS:
|
||||
WalletConnect:
|
||||
:commit: 689c28f519a615310d493a7e638974bfff6d0ce9
|
||||
:git: https://github.com/grachyov/wallet-connect-swift
|
||||
Web3Swift.io:
|
||||
:commit: a606f5b3c2f4b02ed551dc96b1a1390265b4bb44
|
||||
:git: https://github.com/grachyov/Web3Swift.git
|
||||
:commit: 4e04054ac06a9875137ebbfac5a4b8c9f94d49f6
|
||||
:git: https://github.com/zeriontech/Web3Swift.git
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
BigInt: f668a80089607f521586bbe29513d708491ef2f7
|
||||
BlockiesSwift: 22d8d56dd187e6bfd16cb8c8fbd4fd4896c3e65d
|
||||
CryptoSwift: 7cc902df1784de3b389a387756c7d710f197730c
|
||||
PromiseKit: 3b2b6995e51a954c46dbc550ce3da44fbfb563c5
|
||||
secp256k1.swift: a7e7a214f6db6ce5db32cc6b2b45e5c4dd633634
|
||||
Starscream: 5178aed56b316f13fa3bc55694e583d35dd414d9
|
||||
SwiftyJSON: 6faa0040f8b59dead0ee07436cbf76b73c08fd08
|
||||
WalletConnect: 366f1790394831a768dfc77c8c4f35f8fbdaae31
|
||||
Web3Swift.io: 18fd06aed9d56df9c704f9c6f87b06675bb05b53
|
||||
|
||||
PODFILE CHECKSUM: ac8eefd56a76ec0315ae6ace4dc1c03e89702c78
|
||||
PODFILE CHECKSUM: ade248ecc3a8a618186e3d3d754de44149e11bd9
|
||||
|
||||
COCOAPODS: 1.10.0
|
||||
|
4196
Pods/Pods.xcodeproj/project.pbxproj
generated
4196
Pods/Pods.xcodeproj/project.pbxproj
generated
File diff suppressed because it is too large
Load Diff
44
Pods/PromiseKit/Extensions/Foundation/Sources/NSNotificationCenter+AnyPromise.h
generated
Normal file
44
Pods/PromiseKit/Extensions/Foundation/Sources/NSNotificationCenter+AnyPromise.h
generated
Normal file
@ -0,0 +1,44 @@
|
||||
#import <Foundation/NSNotification.h>
|
||||
#import <PromiseKit/AnyPromise.h>
|
||||
|
||||
|
||||
/**
|
||||
To import the `NSNotificationCenter` category:
|
||||
|
||||
use_frameworks!
|
||||
pod "PromiseKit/Foundation"
|
||||
|
||||
Or `NSNotificationCenter` is one of the categories imported by the umbrella pod:
|
||||
|
||||
use_frameworks!
|
||||
pod "PromiseKit"
|
||||
|
||||
And then in your sources:
|
||||
|
||||
#import <PromiseKit/PromiseKit.h>
|
||||
*/
|
||||
@interface NSNotificationCenter (PromiseKit)
|
||||
/**
|
||||
Observe the named notification once.
|
||||
|
||||
[NSNotificationCenter once:UIKeyboardWillShowNotification].then(^(id note, id userInfo){
|
||||
UIViewAnimationCurve curve = [userInfo[UIKeyboardAnimationCurveUserInfoKey] integerValue];
|
||||
CGFloat duration = [userInfo[UIKeyboardAnimationDurationUserInfoKey] floatValue];
|
||||
|
||||
return [UIView promiseWithDuration:duration delay:0.0 options:(curve << 16) animations:^{
|
||||
|
||||
}];
|
||||
});
|
||||
|
||||
@warning *Important* Promises only resolve once. If you need your block to execute more than once then use `-addObserverForName:object:queue:usingBlock:`.
|
||||
|
||||
@param notificationName The name of the notification for which to register the observer.
|
||||
|
||||
@return A promise that fulfills with two parameters:
|
||||
|
||||
1. The NSNotification object.
|
||||
2. The NSNotification’s userInfo property.
|
||||
*/
|
||||
+ (AnyPromise *)once:(NSString *)notificationName NS_REFINED_FOR_SWIFT;
|
||||
|
||||
@end
|
18
Pods/PromiseKit/Extensions/Foundation/Sources/NSNotificationCenter+AnyPromise.m
generated
Normal file
18
Pods/PromiseKit/Extensions/Foundation/Sources/NSNotificationCenter+AnyPromise.m
generated
Normal file
@ -0,0 +1,18 @@
|
||||
#import <Foundation/NSOperation.h>
|
||||
#import <Foundation/NSThread.h>
|
||||
#import "PMKFoundation.h"
|
||||
|
||||
@implementation NSNotificationCenter (PromiseKit)
|
||||
|
||||
+ (AnyPromise *)once:(NSString *)name {
|
||||
return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
|
||||
__block id identifier;
|
||||
identifier = [[NSNotificationCenter defaultCenter] addObserverForName:name object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:identifier name:name object:nil];
|
||||
identifier = nil;
|
||||
resolve(PMKManifold(note, note.userInfo));
|
||||
}];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
33
Pods/PromiseKit/Extensions/Foundation/Sources/NSNotificationCenter+Promise.swift
generated
Normal file
33
Pods/PromiseKit/Extensions/Foundation/Sources/NSNotificationCenter+Promise.swift
generated
Normal file
@ -0,0 +1,33 @@
|
||||
import Foundation
|
||||
#if !PMKCocoaPods
|
||||
import PromiseKit
|
||||
#endif
|
||||
|
||||
/**
|
||||
To import the `NSNotificationCenter` category:
|
||||
|
||||
use_frameworks!
|
||||
pod "PromiseKit/Foundation"
|
||||
|
||||
Or `NSNotificationCenter` is one of the categories imported by the umbrella pod:
|
||||
|
||||
use_frameworks!
|
||||
pod "PromiseKit"
|
||||
|
||||
And then in your sources:
|
||||
|
||||
import PromiseKit
|
||||
*/
|
||||
extension NotificationCenter {
|
||||
/// Observe the named notification once
|
||||
public func observe(once name: Notification.Name, object: Any? = nil) -> Guarantee<Notification> {
|
||||
let (promise, fulfill) = Guarantee<Notification>.pending()
|
||||
#if os(Linux) && ((swift(>=4.0) && !swift(>=4.0.1)) || (swift(>=3.0) && !swift(>=3.2.1)))
|
||||
let id = addObserver(forName: name, object: object, queue: nil, usingBlock: fulfill)
|
||||
#else
|
||||
let id = addObserver(forName: name, object: object, queue: nil, using: fulfill)
|
||||
#endif
|
||||
promise.done { _ in self.removeObserver(id) }
|
||||
return promise
|
||||
}
|
||||
}
|
57
Pods/PromiseKit/Extensions/Foundation/Sources/NSObject+Promise.swift
generated
Normal file
57
Pods/PromiseKit/Extensions/Foundation/Sources/NSObject+Promise.swift
generated
Normal file
@ -0,0 +1,57 @@
|
||||
import Foundation
|
||||
#if !PMKCocoaPods
|
||||
import PromiseKit
|
||||
#endif
|
||||
|
||||
/**
|
||||
To import the `NSObject` category:
|
||||
|
||||
use_frameworks!
|
||||
pod "PromiseKit/Foundation"
|
||||
|
||||
Or `NSObject` is one of the categories imported by the umbrella pod:
|
||||
|
||||
use_frameworks!
|
||||
pod "PromiseKit"
|
||||
|
||||
And then in your sources:
|
||||
|
||||
import PromiseKit
|
||||
*/
|
||||
extension NSObject {
|
||||
/**
|
||||
- Returns: A promise that resolves when the provided keyPath changes.
|
||||
- Warning: *Important* The promise must not outlive the object under observation.
|
||||
- SeeAlso: Apple’s KVO documentation.
|
||||
*/
|
||||
public func observe(_: PMKNamespacer, keyPath: String) -> Guarantee<Any?> {
|
||||
return Guarantee { KVOProxy(observee: self, keyPath: keyPath, resolve: $0) }
|
||||
}
|
||||
}
|
||||
|
||||
private class KVOProxy: NSObject {
|
||||
var retainCycle: KVOProxy?
|
||||
let fulfill: (Any?) -> Void
|
||||
|
||||
@discardableResult
|
||||
init(observee: NSObject, keyPath: String, resolve: @escaping (Any?) -> Void) {
|
||||
fulfill = resolve
|
||||
super.init()
|
||||
observee.addObserver(self, forKeyPath: keyPath, options: NSKeyValueObservingOptions.new, context: pointer)
|
||||
retainCycle = self
|
||||
}
|
||||
|
||||
fileprivate override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
|
||||
if let change = change, context == pointer {
|
||||
defer { retainCycle = nil }
|
||||
fulfill(change[NSKeyValueChangeKey.newKey])
|
||||
if let object = object as? NSObject, let keyPath = keyPath {
|
||||
object.removeObserver(self, forKeyPath: keyPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private lazy var pointer: UnsafeMutableRawPointer = {
|
||||
return Unmanaged<KVOProxy>.passUnretained(self).toOpaque()
|
||||
}()
|
||||
}
|
53
Pods/PromiseKit/Extensions/Foundation/Sources/NSTask+AnyPromise.h
generated
Normal file
53
Pods/PromiseKit/Extensions/Foundation/Sources/NSTask+AnyPromise.h
generated
Normal file
@ -0,0 +1,53 @@
|
||||
#if TARGET_OS_MAC && !TARGET_OS_EMBEDDED && !TARGET_OS_SIMULATOR && !TARGET_OS_UIKITFORMAC
|
||||
|
||||
#import <Foundation/NSTask.h>
|
||||
#import <PromiseKit/AnyPromise.h>
|
||||
|
||||
#define PMKTaskErrorLaunchPathKey @"PMKTaskErrorLaunchPathKey"
|
||||
#define PMKTaskErrorArgumentsKey @"PMKTaskErrorArgumentsKey"
|
||||
#define PMKTaskErrorStandardOutputKey @"PMKTaskErrorStandardOutputKey"
|
||||
#define PMKTaskErrorStandardErrorKey @"PMKTaskErrorStandardErrorKey"
|
||||
#define PMKTaskErrorExitStatusKey @"PMKTaskErrorExitStatusKey"
|
||||
|
||||
/**
|
||||
To import the `NSTask` category:
|
||||
|
||||
use_frameworks!
|
||||
pod "PromiseKit/Foundation"
|
||||
|
||||
Or `NSTask` is one of the categories imported by the umbrella pod:
|
||||
|
||||
use_frameworks!
|
||||
pod "PromiseKit"
|
||||
|
||||
And then in your sources:
|
||||
|
||||
#import <PromiseKit/PromiseKit.h>
|
||||
*/
|
||||
@interface NSTask (PromiseKit)
|
||||
|
||||
/**
|
||||
Launches the receiver and resolves when it exits.
|
||||
|
||||
If the task fails the promise is rejected with code `PMKTaskError`, and
|
||||
`userInfo` keys: `PMKTaskErrorStandardOutputKey`,
|
||||
`PMKTaskErrorStandardErrorKey` and `PMKTaskErrorExitStatusKey`.
|
||||
|
||||
NSTask *task = [NSTask new];
|
||||
task.launchPath = @"/usr/bin/basename";
|
||||
task.arguments = @[@"/usr/bin/sleep"];
|
||||
[task promise].then(^(NSString *stdout){
|
||||
//…
|
||||
});
|
||||
|
||||
@return A promise that fulfills with three parameters:
|
||||
|
||||
1) The stdout interpreted as a UTF8 string.
|
||||
2) The stderr interpreted as a UTF8 string.
|
||||
3) The stdout as `NSData`.
|
||||
*/
|
||||
- (AnyPromise *)promise NS_REFINED_FOR_SWIFT;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
59
Pods/PromiseKit/Extensions/Foundation/Sources/NSTask+AnyPromise.m
generated
Normal file
59
Pods/PromiseKit/Extensions/Foundation/Sources/NSTask+AnyPromise.m
generated
Normal file
@ -0,0 +1,59 @@
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSFileHandle.h>
|
||||
#import <PromiseKit/PromiseKit.h>
|
||||
#import <Foundation/NSString.h>
|
||||
#import <Foundation/NSError.h>
|
||||
|
||||
#if TARGET_OS_MAC && !TARGET_OS_EMBEDDED && !TARGET_OS_SIMULATOR && !TARGET_OS_UIKITFORMAC
|
||||
|
||||
#import "NSTask+AnyPromise.h"
|
||||
|
||||
@implementation NSTask (PromiseKit)
|
||||
|
||||
- (AnyPromise *)promise {
|
||||
return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
|
||||
self.standardOutput = [NSPipe pipe];
|
||||
self.standardError = [NSPipe pipe];
|
||||
self.terminationHandler = ^(NSTask *task){
|
||||
id stdoutData = [[task.standardOutput fileHandleForReading] readDataToEndOfFile];
|
||||
id stdoutString = [[NSString alloc] initWithData:stdoutData encoding:NSUTF8StringEncoding];
|
||||
id stderrData = [[task.standardError fileHandleForReading] readDataToEndOfFile];
|
||||
id stderrString = [[NSString alloc] initWithData:stderrData encoding:NSUTF8StringEncoding];
|
||||
|
||||
if (task.terminationReason == NSTaskTerminationReasonExit && self.terminationStatus == 0) {
|
||||
resolve(PMKManifold(stdoutString, stderrString, stdoutData));
|
||||
} else {
|
||||
id cmd = [NSMutableArray arrayWithObject:task.launchPath];
|
||||
[cmd addObjectsFromArray:task.arguments];
|
||||
cmd = [cmd componentsJoinedByString:@" "];
|
||||
|
||||
id info = @{
|
||||
NSLocalizedDescriptionKey:[NSString stringWithFormat:@"Failed executing: %@.", cmd],
|
||||
PMKTaskErrorStandardOutputKey: stdoutString,
|
||||
PMKTaskErrorStandardErrorKey: stderrString,
|
||||
PMKTaskErrorExitStatusKey: @(task.terminationStatus),
|
||||
};
|
||||
|
||||
resolve([NSError errorWithDomain:PMKErrorDomain code:PMKTaskError userInfo:info]);
|
||||
}
|
||||
};
|
||||
|
||||
#if __clang_major__ >= 9
|
||||
if (@available(macOS 10.13, *)) {
|
||||
NSError *error = nil;
|
||||
|
||||
if (![self launchAndReturnError:&error]) {
|
||||
resolve(error);
|
||||
}
|
||||
} else {
|
||||
[self launch];
|
||||
}
|
||||
#else
|
||||
[self launch]; // might @throw
|
||||
#endif
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
79
Pods/PromiseKit/Extensions/Foundation/Sources/NSURLSession+AnyPromise.h
generated
Normal file
79
Pods/PromiseKit/Extensions/Foundation/Sources/NSURLSession+AnyPromise.h
generated
Normal file
@ -0,0 +1,79 @@
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSURLSession.h>
|
||||
#import <Foundation/NSURLRequest.h>
|
||||
#import <PromiseKit/AnyPromise.h>
|
||||
|
||||
#define PMKURLErrorFailingURLResponseKey @"PMKURLErrorFailingURLResponseKey"
|
||||
#define PMKURLErrorFailingDataKey @"PMKURLErrorFailingDataKey"
|
||||
#define PMKURLErrorFailingStringKey @"PMKURLErrorFailingStringKey"
|
||||
#define PMKJSONErrorJSONObjectKey @"PMKJSONErrorJSONObjectKey"
|
||||
|
||||
/**
|
||||
Really we shouldn’t assume JSON for (application|text)/(x-)javascript,
|
||||
really we should return a String of Javascript. However in practice
|
||||
for the apps we write it *will be* JSON. Thus if you actually want
|
||||
a Javascript String, use the promise variant of our category functions.
|
||||
*/
|
||||
#define PMKHTTPURLResponseIsJSON(rsp) [@[@"application/json", @"text/json", @"text/javascript", @"application/x-javascript", @"application/javascript"] containsObject:[rsp MIMEType]]
|
||||
#define PMKHTTPURLResponseIsImage(rsp) [@[@"image/tiff", @"image/jpeg", @"image/gif", @"image/png", @"image/ico", @"image/x-icon", @"image/bmp", @"image/x-bmp", @"image/x-xbitmap", @"image/x-win-bitmap"] containsObject:[rsp MIMEType]]
|
||||
#define PMKHTTPURLResponseIsText(rsp) [[rsp MIMEType] hasPrefix:@"text/"]
|
||||
|
||||
#define PMKJSONDeserializationOptions ((NSJSONReadingOptions)(NSJSONReadingAllowFragments | NSJSONReadingMutableContainers))
|
||||
|
||||
|
||||
/**
|
||||
To import the `NSURLSession` category:
|
||||
|
||||
use_frameworks!
|
||||
pod "PromiseKit/Foundation"
|
||||
|
||||
Or `NSURLConnection` is one of the categories imported by the umbrella pod:
|
||||
|
||||
use_frameworks!
|
||||
pod "PromiseKit"
|
||||
|
||||
And then in your sources:
|
||||
|
||||
#import <PromiseKit/PromiseKit.h>
|
||||
*/
|
||||
@interface NSURLSession (PromiseKit)
|
||||
|
||||
/**
|
||||
Creates a task that retrieves the contents of a URL based on the
|
||||
specified URL request object.
|
||||
|
||||
PromiseKit automatically deserializes the raw HTTP data response into the
|
||||
appropriate rich data type based on the mime type the server provides.
|
||||
Thus if the response is JSON you will get the deserialized JSON response.
|
||||
PromiseKit supports decoding into strings, JSON and UIImages.
|
||||
|
||||
However if your server does not provide a rich content-type, you will
|
||||
just get `NSData`. This is rare, but a good example we came across was
|
||||
downloading files from Dropbox.
|
||||
|
||||
PromiseKit goes to quite some lengths to provide good `NSError` objects
|
||||
for error conditions at all stages of the HTTP to rich-data type
|
||||
pipeline. We provide the following additional `userInfo` keys as
|
||||
appropriate:
|
||||
|
||||
- `PMKURLErrorFailingDataKey`
|
||||
- `PMKURLErrorFailingStringKey`
|
||||
- `PMKURLErrorFailingURLResponseKey`
|
||||
|
||||
[[NSURLConnection sharedSession] promiseDataTaskWithRequest:rq].then(^(id response){
|
||||
// response is probably an NSDictionary deserialized from JSON
|
||||
});
|
||||
|
||||
@param request The URL request.
|
||||
|
||||
@return A promise that fulfills with three parameters:
|
||||
|
||||
1) The deserialized data response.
|
||||
2) The `NSHTTPURLResponse`.
|
||||
3) The raw `NSData` response.
|
||||
|
||||
@see https://github.com/mxcl/OMGHTTPURLRQ
|
||||
*/
|
||||
- (AnyPromise *)promiseDataTaskWithRequest:(NSURLRequest *)request NS_REFINED_FOR_SWIFT;
|
||||
|
||||
@end
|
113
Pods/PromiseKit/Extensions/Foundation/Sources/NSURLSession+AnyPromise.m
generated
Normal file
113
Pods/PromiseKit/Extensions/Foundation/Sources/NSURLSession+AnyPromise.m
generated
Normal file
@ -0,0 +1,113 @@
|
||||
#import <Foundation/NSJSONSerialization.h>
|
||||
#import <Foundation/NSURLResponse.h>
|
||||
#import <CoreFoundation/CFString.h>
|
||||
#import "NSURLSession+AnyPromise.h"
|
||||
#import <Foundation/NSOperation.h>
|
||||
#import <Foundation/NSURLError.h>
|
||||
#import <PromiseKit/PromiseKit.h>
|
||||
#import <CoreFoundation/CFURL.h>
|
||||
#import <Foundation/NSThread.h>
|
||||
#import <Foundation/NSArray.h>
|
||||
#import <Foundation/NSError.h>
|
||||
#import <Foundation/NSURL.h>
|
||||
|
||||
@implementation NSURLSession (PromiseKit)
|
||||
|
||||
- (AnyPromise *)promiseDataTaskWithRequest:(NSURLRequest *)rq {
|
||||
return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
|
||||
[[self dataTaskWithRequest:rq completionHandler:^(NSData *data, id rsp, NSError *urlError){
|
||||
assert(![NSThread isMainThread]);
|
||||
|
||||
PMKResolver fulfiller = ^(id responseObject){
|
||||
resolve(PMKManifold(responseObject, rsp, data));
|
||||
};
|
||||
PMKResolver rejecter = ^(NSError *error){
|
||||
id userInfo = error.userInfo.mutableCopy ?: [NSMutableDictionary new];
|
||||
if (data) userInfo[PMKURLErrorFailingDataKey] = data;
|
||||
if (rsp) userInfo[PMKURLErrorFailingURLResponseKey] = rsp;
|
||||
error = [NSError errorWithDomain:error.domain code:error.code userInfo:userInfo];
|
||||
resolve(error);
|
||||
};
|
||||
|
||||
NSStringEncoding (^stringEncoding)(void) = ^NSStringEncoding{
|
||||
id encodingName = [rsp textEncodingName];
|
||||
if (encodingName) {
|
||||
CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((CFStringRef)encodingName);
|
||||
if (encoding != kCFStringEncodingInvalidId)
|
||||
return CFStringConvertEncodingToNSStringEncoding(encoding);
|
||||
}
|
||||
return NSUTF8StringEncoding;
|
||||
};
|
||||
|
||||
if (urlError) {
|
||||
rejecter(urlError);
|
||||
} else if (![rsp isKindOfClass:[NSHTTPURLResponse class]]) {
|
||||
fulfiller(data);
|
||||
} else if ([rsp statusCode] < 200 || [rsp statusCode] >= 300) {
|
||||
id info = @{
|
||||
NSLocalizedDescriptionKey: @"The server returned a bad HTTP response code",
|
||||
NSURLErrorFailingURLStringErrorKey: rq.URL.absoluteString,
|
||||
NSURLErrorFailingURLErrorKey: rq.URL
|
||||
};
|
||||
id err = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorBadServerResponse userInfo:info];
|
||||
rejecter(err);
|
||||
} else if (PMKHTTPURLResponseIsJSON(rsp)) {
|
||||
// work around ever-so-common Rails workaround: https://github.com/rails/rails/issues/1742
|
||||
if ([rsp expectedContentLength] == 1 && [data isEqualToData:[NSData dataWithBytes:" " length:1]])
|
||||
return fulfiller(nil);
|
||||
|
||||
NSError *err = nil;
|
||||
id json = [NSJSONSerialization JSONObjectWithData:data options:PMKJSONDeserializationOptions error:&err];
|
||||
if (!err) {
|
||||
fulfiller(json);
|
||||
} else {
|
||||
id userInfo = err.userInfo.mutableCopy;
|
||||
if (data) {
|
||||
NSString *string = [[NSString alloc] initWithData:data encoding:stringEncoding()];
|
||||
if (string)
|
||||
userInfo[PMKURLErrorFailingStringKey] = string;
|
||||
}
|
||||
long long length = [rsp expectedContentLength];
|
||||
id bytes = length <= 0 ? @"" : [NSString stringWithFormat:@"%lld bytes", length];
|
||||
id fmt = @"The server claimed a %@ JSON response, but decoding failed with: %@";
|
||||
userInfo[NSLocalizedDescriptionKey] = [NSString stringWithFormat:fmt, bytes, userInfo[NSLocalizedDescriptionKey]];
|
||||
err = [NSError errorWithDomain:err.domain code:err.code userInfo:userInfo];
|
||||
rejecter(err);
|
||||
}
|
||||
#ifdef UIKIT_EXTERN
|
||||
} else if (PMKHTTPURLResponseIsImage(rsp)) {
|
||||
UIImage *image = [[UIImage alloc] initWithData:data];
|
||||
image = [[UIImage alloc] initWithCGImage:[image CGImage] scale:image.scale orientation:image.imageOrientation];
|
||||
if (image)
|
||||
fulfiller(image);
|
||||
else {
|
||||
id info = @{
|
||||
NSLocalizedDescriptionKey: @"The server returned invalid image data",
|
||||
NSURLErrorFailingURLStringErrorKey: rq.URL.absoluteString,
|
||||
NSURLErrorFailingURLErrorKey: rq.URL
|
||||
};
|
||||
id err = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:info];
|
||||
rejecter(err);
|
||||
}
|
||||
#endif
|
||||
} else if (PMKHTTPURLResponseIsText(rsp)) {
|
||||
id str = [[NSString alloc] initWithData:data encoding:stringEncoding()];
|
||||
if (str)
|
||||
fulfiller(str);
|
||||
else {
|
||||
id info = @{
|
||||
NSLocalizedDescriptionKey: @"The server returned invalid string data",
|
||||
NSURLErrorFailingURLStringErrorKey: rq.URL.absoluteString,
|
||||
NSURLErrorFailingURLErrorKey: rq.URL
|
||||
};
|
||||
id err = [NSError errorWithDomain:NSURLErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:info];
|
||||
rejecter(err);
|
||||
}
|
||||
} else {
|
||||
fulfiller(data);
|
||||
}
|
||||
}] resume];
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
246
Pods/PromiseKit/Extensions/Foundation/Sources/NSURLSession+Promise.swift
generated
Normal file
246
Pods/PromiseKit/Extensions/Foundation/Sources/NSURLSession+Promise.swift
generated
Normal file
@ -0,0 +1,246 @@
|
||||
import Foundation
|
||||
#if !PMKCocoaPods
|
||||
import PromiseKit
|
||||
#endif
|
||||
#if swift(>=4.1)
|
||||
#if canImport(FoundationNetworking)
|
||||
import FoundationNetworking
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/**
|
||||
To import the `NSURLSession` category:
|
||||
|
||||
use_frameworks!
|
||||
pod "PromiseKit/Foundation"
|
||||
|
||||
Or `NSURLSession` is one of the categories imported by the umbrella pod:
|
||||
|
||||
use_frameworks!
|
||||
pod "PromiseKit"
|
||||
|
||||
And then in your sources:
|
||||
|
||||
import PromiseKit
|
||||
*/
|
||||
extension URLSession {
|
||||
/**
|
||||
Example usage:
|
||||
|
||||
firstly {
|
||||
URLSession.shared.dataTask(.promise, with: rq)
|
||||
}.compactMap { data, _ in
|
||||
try JSONSerialization.jsonObject(with: data) as? [String: Any]
|
||||
}.then { json in
|
||||
//…
|
||||
}
|
||||
|
||||
We recommend the use of [OMGHTTPURLRQ] which allows you to construct correct REST requests:
|
||||
|
||||
firstly {
|
||||
let rq = OMGHTTPURLRQ.POST(url, json: parameters)
|
||||
URLSession.shared.dataTask(.promise, with: rq)
|
||||
}.then { data, urlResponse in
|
||||
//…
|
||||
}
|
||||
|
||||
We provide a convenience initializer for `String` specifically for this promise:
|
||||
|
||||
firstly {
|
||||
URLSession.shared.dataTask(.promise, with: rq)
|
||||
}.compactMap(String.init).then { string in
|
||||
// decoded per the string encoding specified by the server
|
||||
}.then { string in
|
||||
print("response: string")
|
||||
}
|
||||
|
||||
Other common types can be easily decoded using compactMap also:
|
||||
|
||||
firstly {
|
||||
URLSession.shared.dataTask(.promise, with: rq)
|
||||
}.compactMap {
|
||||
UIImage(data: $0)
|
||||
}.then {
|
||||
self.imageView.image = $0
|
||||
}
|
||||
|
||||
Though if you do decode the image this way, we recommend inflating it on a background thread
|
||||
first as this will improve main thread performance when rendering the image:
|
||||
|
||||
firstly {
|
||||
URLSession.shared.dataTask(.promise, with: rq)
|
||||
}.compactMap(on: QoS.userInitiated) { data, _ in
|
||||
guard let img = UIImage(data: data) else { return nil }
|
||||
_ = cgImage?.dataProvider?.data
|
||||
return img
|
||||
}.then {
|
||||
self.imageView.image = $0
|
||||
}
|
||||
|
||||
- Parameter convertible: A URL or URLRequest.
|
||||
- Returns: A promise that represents the URL request.
|
||||
- SeeAlso: [OMGHTTPURLRQ]
|
||||
- Remark: We deliberately don’t provide a `URLRequestConvertible` for `String` because in our experience, you should be explicit with this error path to make good apps.
|
||||
|
||||
[OMGHTTPURLRQ]: https://github.com/mxcl/OMGHTTPURLRQ
|
||||
*/
|
||||
public func dataTask(_: PMKNamespacer, with convertible: URLRequestConvertible) -> Promise<(data: Data, response: URLResponse)> {
|
||||
return Promise { dataTask(with: convertible.pmkRequest, completionHandler: adapter($0)).resume() }
|
||||
}
|
||||
|
||||
public func uploadTask(_: PMKNamespacer, with convertible: URLRequestConvertible, from data: Data) -> Promise<(data: Data, response: URLResponse)> {
|
||||
return Promise { uploadTask(with: convertible.pmkRequest, from: data, completionHandler: adapter($0)).resume() }
|
||||
}
|
||||
|
||||
public func uploadTask(_: PMKNamespacer, with convertible: URLRequestConvertible, fromFile file: URL) -> Promise<(data: Data, response: URLResponse)> {
|
||||
return Promise { uploadTask(with: convertible.pmkRequest, fromFile: file, completionHandler: adapter($0)).resume() }
|
||||
}
|
||||
|
||||
/// - Remark: we force a `to` parameter because Apple deletes the downloaded file immediately after the underyling completion handler returns.
|
||||
/// - Note: we do not create the destination directory for you, because we move the file with FileManager.moveItem which changes it behavior depending on the directory status of the URL you provide. So create your own directory first!
|
||||
public func downloadTask(_: PMKNamespacer, with convertible: URLRequestConvertible, to saveLocation: URL) -> Promise<(saveLocation: URL, response: URLResponse)> {
|
||||
return Promise { seal in
|
||||
downloadTask(with: convertible.pmkRequest, completionHandler: { tmp, rsp, err in
|
||||
if let error = err {
|
||||
seal.reject(error)
|
||||
} else if let rsp = rsp, let tmp = tmp {
|
||||
do {
|
||||
try FileManager.default.moveItem(at: tmp, to: saveLocation)
|
||||
seal.fulfill((saveLocation, rsp))
|
||||
} catch {
|
||||
seal.reject(error)
|
||||
}
|
||||
} else {
|
||||
seal.reject(PMKError.invalidCallingConvention)
|
||||
}
|
||||
}).resume()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public protocol URLRequestConvertible {
|
||||
var pmkRequest: URLRequest { get }
|
||||
}
|
||||
extension URLRequest: URLRequestConvertible {
|
||||
public var pmkRequest: URLRequest { return self }
|
||||
}
|
||||
extension URL: URLRequestConvertible {
|
||||
public var pmkRequest: URLRequest { return URLRequest(url: self) }
|
||||
}
|
||||
|
||||
|
||||
#if !os(Linux)
|
||||
public extension String {
|
||||
/**
|
||||
- Remark: useful when converting a `URLSession` response into a `String`
|
||||
|
||||
firstly {
|
||||
URLSession.shared.dataTask(.promise, with: rq)
|
||||
}.map(String.init).done {
|
||||
print($0)
|
||||
}
|
||||
*/
|
||||
init?(data: Data, urlResponse: URLResponse) {
|
||||
guard let str = String(bytes: data, encoding: urlResponse.stringEncoding ?? .utf8) else {
|
||||
return nil
|
||||
}
|
||||
self.init(str)
|
||||
}
|
||||
}
|
||||
|
||||
private extension URLResponse {
|
||||
var stringEncoding: String.Encoding? {
|
||||
guard let encodingName = textEncodingName else { return nil }
|
||||
let encoding = CFStringConvertIANACharSetNameToEncoding(encodingName as CFString)
|
||||
guard encoding != kCFStringEncodingInvalidId else { return nil }
|
||||
return String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(encoding))
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private func adapter<T, U>(_ seal: Resolver<(data: T, response: U)>) -> (T?, U?, Error?) -> Void {
|
||||
return { t, u, e in
|
||||
if let t = t, let u = u {
|
||||
seal.fulfill((t, u))
|
||||
} else if let e = e {
|
||||
seal.reject(e)
|
||||
} else {
|
||||
seal.reject(PMKError.invalidCallingConvention)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if swift(>=3.1)
|
||||
public enum PMKHTTPError: Error, LocalizedError, CustomStringConvertible {
|
||||
case badStatusCode(Int, Data, HTTPURLResponse)
|
||||
|
||||
public var errorDescription: String? {
|
||||
func url(_ rsp: URLResponse) -> String {
|
||||
return rsp.url?.absoluteString ?? "nil"
|
||||
}
|
||||
switch self {
|
||||
case .badStatusCode(401, _, let response):
|
||||
return "Unauthorized (\(url(response))"
|
||||
case .badStatusCode(let code, _, let response):
|
||||
return "Invalid HTTP response (\(code)) for \(url(response))."
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=4.0)
|
||||
public func decodeResponse<T: Decodable>(_ t: T.Type, decoder: JSONDecoder = JSONDecoder()) -> T? {
|
||||
switch self {
|
||||
case .badStatusCode(_, let data, _):
|
||||
return try? decoder.decode(t, from: data)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//TODO rename responseJSON
|
||||
public var jsonDictionary: Any? {
|
||||
switch self {
|
||||
case .badStatusCode(_, let data, _):
|
||||
return try? JSONSerialization.jsonObject(with: data)
|
||||
}
|
||||
}
|
||||
|
||||
var responseBodyString: String? {
|
||||
switch self {
|
||||
case .badStatusCode(_, let data, _):
|
||||
return String(data: data, encoding: .utf8)
|
||||
}
|
||||
}
|
||||
|
||||
public var failureReason: String? {
|
||||
return responseBodyString
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .badStatusCode(let code, let data, let response):
|
||||
var dict: [String: Any] = [
|
||||
"Status Code": code,
|
||||
"Body": String(data: data, encoding: .utf8) ?? "\(data.count) bytes"
|
||||
]
|
||||
dict["URL"] = response.url
|
||||
dict["Headers"] = response.allHeaderFields
|
||||
return "<NSHTTPResponse> \(NSDictionary(dictionary: dict))" // as NSDictionary makes the output look like NSHTTPURLResponse looks
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Promise where T == (data: Data, response: URLResponse) {
|
||||
func validate() -> Promise<T> {
|
||||
return map {
|
||||
guard let response = $0.response as? HTTPURLResponse else { return $0 }
|
||||
switch response.statusCode {
|
||||
case 200..<300:
|
||||
return $0
|
||||
case let code:
|
||||
throw PMKHTTPError.badStatusCode(code, $0.data, response)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
3
Pods/PromiseKit/Extensions/Foundation/Sources/PMKFoundation.h
generated
Normal file
3
Pods/PromiseKit/Extensions/Foundation/Sources/PMKFoundation.h
generated
Normal file
@ -0,0 +1,3 @@
|
||||
#import "NSNotificationCenter+AnyPromise.h"
|
||||
#import "NSURLSession+AnyPromise.h"
|
||||
#import "NSTask+AnyPromise.h"
|
190
Pods/PromiseKit/Extensions/Foundation/Sources/Process+Promise.swift
generated
Normal file
190
Pods/PromiseKit/Extensions/Foundation/Sources/Process+Promise.swift
generated
Normal file
@ -0,0 +1,190 @@
|
||||
import Foundation
|
||||
#if !PMKCocoaPods
|
||||
import PromiseKit
|
||||
#endif
|
||||
|
||||
#if os(macOS)
|
||||
|
||||
/**
|
||||
To import the `Process` category:
|
||||
|
||||
use_frameworks!
|
||||
pod "PromiseKit/Foundation"
|
||||
|
||||
Or `Process` is one of the categories imported by the umbrella pod:
|
||||
|
||||
use_frameworks!
|
||||
pod "PromiseKit"
|
||||
|
||||
And then in your sources:
|
||||
|
||||
import PromiseKit
|
||||
*/
|
||||
extension Process {
|
||||
/**
|
||||
Launches the receiver and resolves when it exits.
|
||||
|
||||
let proc = Process()
|
||||
proc.launchPath = "/bin/ls"
|
||||
proc.arguments = ["/bin"]
|
||||
proc.launch(.promise).compactMap { std in
|
||||
String(data: std.out.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)
|
||||
}.then { stdout in
|
||||
print(str)
|
||||
}
|
||||
*/
|
||||
public func launch(_: PMKNamespacer) -> Promise<(out: Pipe, err: Pipe)> {
|
||||
let (stdout, stderr) = (Pipe(), Pipe())
|
||||
|
||||
do {
|
||||
standardOutput = stdout
|
||||
standardError = stderr
|
||||
|
||||
#if swift(>=4.0)
|
||||
if #available(OSX 10.13, *) {
|
||||
try run()
|
||||
} else if let path = launchPath, FileManager.default.isExecutableFile(atPath: path) {
|
||||
launch()
|
||||
} else {
|
||||
throw PMKError.notExecutable(launchPath)
|
||||
}
|
||||
#else
|
||||
guard let path = launchPath, FileManager.default.isExecutableFile(atPath: path) else {
|
||||
throw PMKError.notExecutable(launchPath)
|
||||
}
|
||||
launch()
|
||||
#endif
|
||||
} catch {
|
||||
return Promise(error: error)
|
||||
}
|
||||
|
||||
|
||||
var q: DispatchQueue {
|
||||
if #available(macOS 10.10, iOS 8.0, tvOS 9.0, watchOS 2.0, *) {
|
||||
return DispatchQueue.global(qos: .default)
|
||||
} else {
|
||||
return DispatchQueue.global(priority: .default)
|
||||
}
|
||||
}
|
||||
|
||||
return Promise { seal in
|
||||
q.async {
|
||||
self.waitUntilExit()
|
||||
|
||||
guard self.terminationReason == .exit, self.terminationStatus == 0 else {
|
||||
let stdoutData = try? self.readDataFromPipe(stdout)
|
||||
let stderrData = try? self.readDataFromPipe(stderr)
|
||||
|
||||
let stdoutString = stdoutData.flatMap { (data: Data) -> String? in String(data: data, encoding: .utf8) }
|
||||
let stderrString = stderrData.flatMap { (data: Data) -> String? in String(data: data, encoding: .utf8) }
|
||||
|
||||
return seal.reject(PMKError.execution(process: self, standardOutput: stdoutString, standardError: stderrString))
|
||||
}
|
||||
seal.fulfill((stdout, stderr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func readDataFromPipe(_ pipe: Pipe) throws -> Data {
|
||||
let handle = pipe.fileHandleForReading
|
||||
defer { handle.closeFile() }
|
||||
|
||||
// Someday, NSFileHandle will probably be updated with throwing equivalents to its read and write methods,
|
||||
// as NSTask has, to avoid raising exceptions and crashing the app.
|
||||
// Unfortunately that day has not yet come, so use the underlying BSD calls for now.
|
||||
|
||||
let fd = handle.fileDescriptor
|
||||
|
||||
let bufsize = 1024 * 8
|
||||
let buf = UnsafeMutablePointer<UInt8>.allocate(capacity: bufsize)
|
||||
|
||||
#if swift(>=4.1)
|
||||
defer { buf.deallocate() }
|
||||
#else
|
||||
defer { buf.deallocate(capacity: bufsize) }
|
||||
#endif
|
||||
|
||||
var data = Data()
|
||||
|
||||
while true {
|
||||
let bytesRead = read(fd, buf, bufsize)
|
||||
|
||||
if bytesRead == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if bytesRead < 0 {
|
||||
throw POSIXError.Code(rawValue: errno).map { POSIXError($0) } ?? CocoaError(.fileReadUnknown)
|
||||
}
|
||||
|
||||
data.append(buf, count: bytesRead)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
/**
|
||||
The error generated by PromiseKit’s `Process` extension
|
||||
*/
|
||||
public enum PMKError {
|
||||
/// NOT AVAILABLE ON 10.13 and above because Apple provide this error handling themselves
|
||||
case notExecutable(String?)
|
||||
case execution(process: Process, standardOutput: String?, standardError: String?)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension Process.PMKError: LocalizedError {
|
||||
public var errorDescription: String? {
|
||||
switch self {
|
||||
case .notExecutable(let path?):
|
||||
return "File not executable: \(path)"
|
||||
case .notExecutable(nil):
|
||||
return "No launch path specified"
|
||||
case .execution(process: let task, standardOutput: _, standardError: _):
|
||||
return "Failed executing: `\(task)` (\(task.terminationStatus))."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Promise where T == (out: Pipe, err: Pipe) {
|
||||
func print() -> Promise<T> {
|
||||
return tap { result in
|
||||
switch result {
|
||||
case .fulfilled(let raw):
|
||||
let stdout = String(data: raw.out.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)
|
||||
let stderr = String(data: raw.err.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8)
|
||||
Swift.print("stdout: `\(stdout ?? "")`")
|
||||
Swift.print("stderr: `\(stderr ?? "")`")
|
||||
case .rejected(let err):
|
||||
Swift.print(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Process {
|
||||
/// Provided because Foundation’s is USELESS
|
||||
open override var description: String {
|
||||
let launchPath = self.launchPath ?? "$0"
|
||||
var args = [launchPath]
|
||||
arguments.flatMap{ args += $0 }
|
||||
return args.map { arg in
|
||||
let contains: Bool
|
||||
#if swift(>=3.2)
|
||||
contains = arg.contains(" ")
|
||||
#else
|
||||
contains = arg.characters.contains(" ")
|
||||
#endif
|
||||
if contains {
|
||||
return "\"\(arg)\""
|
||||
} else if arg == "" {
|
||||
return "\"\""
|
||||
} else {
|
||||
return arg
|
||||
}
|
||||
}.joined(separator: " ")
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
26
Pods/PromiseKit/Extensions/Foundation/Sources/afterlife.swift
generated
Normal file
26
Pods/PromiseKit/Extensions/Foundation/Sources/afterlife.swift
generated
Normal file
@ -0,0 +1,26 @@
|
||||
import Foundation
|
||||
#if !PMKCocoaPods
|
||||
import PromiseKit
|
||||
#endif
|
||||
|
||||
/**
|
||||
- Returns: A promise that resolves when the provided object deallocates
|
||||
- Important: The promise is not guarenteed to resolve immediately when the provided object is deallocated. So you cannot write code that depends on exact timing.
|
||||
*/
|
||||
public func after(life object: NSObject) -> Guarantee<Void> {
|
||||
var reaper = objc_getAssociatedObject(object, &handle) as? GrimReaper
|
||||
if reaper == nil {
|
||||
reaper = GrimReaper()
|
||||
objc_setAssociatedObject(object, &handle, reaper, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
|
||||
}
|
||||
return reaper!.promise
|
||||
}
|
||||
|
||||
private var handle: UInt8 = 0
|
||||
|
||||
private class GrimReaper: NSObject {
|
||||
deinit {
|
||||
fulfill(())
|
||||
}
|
||||
let (promise, fulfill) = Guarantee<Void>.pending()
|
||||
}
|
20
Pods/PromiseKit/LICENSE
generated
Normal file
20
Pods/PromiseKit/LICENSE
generated
Normal file
@ -0,0 +1,20 @@
|
||||
Copyright 2016-present, Max Howell; mxcl@me.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
211
Pods/PromiseKit/README.md
generated
Normal file
211
Pods/PromiseKit/README.md
generated
Normal file
@ -0,0 +1,211 @@
|
||||
![PromiseKit](../gh-pages/public/img/logo-tight.png)
|
||||
|
||||
[![badge-pod][]][cocoapods] ![badge-languages][] ![badge-pms][] ![badge-platforms][] [![badge-travis][]][travis]
|
||||
|
||||
---
|
||||
|
||||
Promises simplify asynchronous programming, freeing you up to focus on the more
|
||||
important things. They are easy to learn, easy to master and result in clearer,
|
||||
more readable code. Your co-workers will thank you.
|
||||
|
||||
```swift
|
||||
UIApplication.shared.isNetworkActivityIndicatorVisible = true
|
||||
|
||||
let fetchImage = URLSession.shared.dataTask(.promise, with: url).compactMap{ UIImage(data: $0.data) }
|
||||
let fetchLocation = CLLocationManager.requestLocation().lastValue
|
||||
|
||||
firstly {
|
||||
when(fulfilled: fetchImage, fetchLocation)
|
||||
}.done { image, location in
|
||||
self.imageView.image = image
|
||||
self.label.text = "\(location)"
|
||||
}.ensure {
|
||||
UIApplication.shared.isNetworkActivityIndicatorVisible = false
|
||||
}.catch { error in
|
||||
self.show(UIAlertController(for: error), sender: self)
|
||||
}
|
||||
```
|
||||
|
||||
PromiseKit is a thoughtful and complete implementation of promises for any
|
||||
platform that has a `swiftc`. It has *excellent* Objective-C bridging and
|
||||
*delightful* specializations for iOS, macOS, tvOS and watchOS. It is a top-100
|
||||
pod used in many of the most popular apps in the world.
|
||||
|
||||
[![codecov](https://codecov.io/gh/mxcl/PromiseKit/branch/master/graph/badge.svg)](https://codecov.io/gh/mxcl/PromiseKit)
|
||||
|
||||
# PromiseKit 7 Alpha
|
||||
|
||||
We are testing PromiseKit 7 alpha, it is Swift 5 only. It is tagged and thus
|
||||
importable in all package managers.
|
||||
|
||||
# PromiseKit 6
|
||||
|
||||
[Release notes and migration guide][PMK6].
|
||||
|
||||
# Quick Start
|
||||
|
||||
In your [Podfile]:
|
||||
|
||||
```ruby
|
||||
use_frameworks!
|
||||
|
||||
target "Change Me!" do
|
||||
pod "PromiseKit", "~> 6.8"
|
||||
end
|
||||
```
|
||||
|
||||
> The above gives an Xcode warning? See our [Installation Guide].
|
||||
|
||||
PromiseKit 6, 5 and 4 support Xcode 8.3, 9.x and 10.0; Swift 3.1,
|
||||
3.2, 3.3, 3.4, 4.0, 4.1, 4.2, 4.3 and 5.0 (development snapshots); iOS, macOS,
|
||||
tvOS, watchOS, Linux and Android; CocoaPods, Carthage and SwiftPM;
|
||||
([CI Matrix](https://travis-ci.org/mxcl/PromiseKit)).
|
||||
|
||||
For Carthage, SwiftPM, Accio, etc., or for instructions when using older Swifts or Xcodes, see our [Installation Guide]. We recommend
|
||||
[Carthage](https://github.com/Carthage/Carthage) or
|
||||
[Accio](https://github.com/JamitLabs/Accio).
|
||||
|
||||
# Professionally Supported PromiseKit is Now Available
|
||||
|
||||
TideLift gives software development teams a single source for purchasing
|
||||
and maintaining their software, with professional grade assurances from
|
||||
the experts who know it best, while seamlessly integrating with existing
|
||||
tools.
|
||||
|
||||
[Get Professional Support for PromiseKit with TideLift](https://tidelift.com/subscription/pkg/cocoapods-promisekit?utm_source=cocoapods-promisekit&utm_medium=referral&utm_campaign=readme).
|
||||
|
||||
# PromiseKit is Thousands of Hours of Work
|
||||
|
||||
Hey there, I’m Max Howell. I’m a prolific producer of open source software and
|
||||
probably you already use some of it (I created [`brew`]). I work full-time on
|
||||
open source and it’s hard; currently *I earn less than minimum wage*. Please
|
||||
help me continue my work, I appreciate it 🙏🏻
|
||||
|
||||
<a href="https://www.patreon.com/mxcl">
|
||||
<img src="https://c5.patreon.com/external/logo/become_a_patron_button@2x.png" width="160">
|
||||
</a>
|
||||
|
||||
[Other ways to say thanks](http://mxcl.dev/#donate).
|
||||
|
||||
[`brew`]: https://brew.sh
|
||||
|
||||
# Documentation
|
||||
|
||||
* Handbook
|
||||
* [Getting Started](Documentation/GettingStarted.md)
|
||||
* [Promises: Common Patterns](Documentation/CommonPatterns.md)
|
||||
* [Frequently Asked Questions](Documentation/FAQ.md)
|
||||
* Manual
|
||||
* [Installation Guide](Documentation/Installation.md)
|
||||
* [Objective-C Guide](Documentation/ObjectiveC.md)
|
||||
* [Troubleshooting](Documentation/Troubleshooting.md) (e.g., solutions to common compile errors)
|
||||
* [Appendix](Documentation/Appendix.md)
|
||||
* [API Reference](https://mxcl.dev/PromiseKit/reference/v6/Classes/Promise.html)
|
||||
|
||||
# Extensions
|
||||
|
||||
Promises are only as useful as the asynchronous tasks they represent. Thus, we
|
||||
have converted (almost) all of Apple’s APIs to promises. The default CocoaPod
|
||||
provides Promises and the extensions for Foundation and UIKit. The other
|
||||
extensions are available by specifying additional subspecs in your `Podfile`,
|
||||
e.g.:
|
||||
|
||||
```ruby
|
||||
pod "PromiseKit/MapKit" # MKDirections().calculate().then { /*…*/ }
|
||||
pod "PromiseKit/CoreLocation" # CLLocationManager.requestLocation().then { /*…*/ }
|
||||
```
|
||||
|
||||
All our extensions are separate repositories at the [PromiseKit organization].
|
||||
|
||||
## I don't want the extensions!
|
||||
|
||||
Then don’t have them:
|
||||
|
||||
```ruby
|
||||
pod "PromiseKit/CorePromise", "~> 6.8"
|
||||
```
|
||||
|
||||
> *Note:* Carthage installations come with no extensions by default.
|
||||
|
||||
## Choose Your Networking Library
|
||||
|
||||
Promise chains commonly start with a network operation. Thus, we offer
|
||||
extensions for `URLSession`:
|
||||
|
||||
```swift
|
||||
// pod 'PromiseKit/Foundation' # https://github.com/PromiseKit/Foundation
|
||||
|
||||
firstly {
|
||||
URLSession.shared.dataTask(.promise, with: try makeUrlRequest()).validate()
|
||||
// ^^ we provide `.validate()` so that eg. 404s get converted to errors
|
||||
}.map {
|
||||
try JSONDecoder().decode(Foo.self, with: $0.data)
|
||||
}.done { foo in
|
||||
//…
|
||||
}.catch { error in
|
||||
//…
|
||||
}
|
||||
|
||||
func makeUrlRequest() throws -> URLRequest {
|
||||
var rq = URLRequest(url: url)
|
||||
rq.httpMethod = "POST"
|
||||
rq.addValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
rq.addValue("application/json", forHTTPHeaderField: "Accept")
|
||||
rq.httpBody = try JSONEncoder().encode(obj)
|
||||
return rq
|
||||
}
|
||||
```
|
||||
|
||||
And [Alamofire]:
|
||||
|
||||
```swift
|
||||
// pod 'PromiseKit/Alamofire' # https://github.com/PromiseKit/Alamofire-
|
||||
|
||||
firstly {
|
||||
Alamofire
|
||||
.request("http://example.com", method: .post, parameters: params)
|
||||
.responseDecodable(Foo.self)
|
||||
}.done { foo in
|
||||
//…
|
||||
}.catch { error in
|
||||
//…
|
||||
}
|
||||
```
|
||||
|
||||
Nowadays, considering that:
|
||||
|
||||
* We almost always POST JSON
|
||||
* We now have `JSONDecoder`
|
||||
* PromiseKit now has `map` and other functional primitives
|
||||
* PromiseKit (like Alamofire, but not raw-`URLSession`) also defaults to having
|
||||
callbacks go to the main thread
|
||||
|
||||
We recommend vanilla `URLSession`. It uses fewer black boxes and sticks closer to the metal. Alamofire was essential until the three bullet points above
|
||||
became true, but nowadays it isn’t really necessary.
|
||||
|
||||
# Support
|
||||
|
||||
Please check our [Troubleshooting Guide](Documentation/Troubleshooting.md), and
|
||||
if after that you still have a question, ask at our [Gitter chat channel] or on [our bug tracker].
|
||||
|
||||
## Security & Vulnerability Reporting or Disclosure
|
||||
|
||||
https://tidelift.com/security
|
||||
|
||||
|
||||
[badge-pod]: https://img.shields.io/cocoapods/v/PromiseKit.svg?label=version
|
||||
[badge-pms]: https://img.shields.io/badge/supports-CocoaPods%20%7C%20Carthage%20%7C%20Accio%20%7C%20SwiftPM-green.svg
|
||||
[badge-languages]: https://img.shields.io/badge/languages-Swift%20%7C%20ObjC-orange.svg
|
||||
[badge-platforms]: https://img.shields.io/badge/platforms-macOS%20%7C%20iOS%20%7C%20watchOS%20%7C%20tvOS%20%7C%20Linux-lightgrey.svg
|
||||
[badge-mit]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[OMGHTTPURLRQ]: https://github.com/PromiseKit/OMGHTTPURLRQ
|
||||
[Alamofire]: http://github.com/PromiseKit/Alamofire-
|
||||
[PromiseKit organization]: https://github.com/PromiseKit
|
||||
[Gitter chat channel]: https://gitter.im/mxcl/PromiseKit
|
||||
[our bug tracker]: https://github.com/mxcl/PromiseKit/issues/new
|
||||
[Podfile]: https://guides.cocoapods.org/syntax/podfile.html
|
||||
[PMK6]: http://mxcl.dev/PromiseKit/news/2018/02/PromiseKit-6.0-Released/
|
||||
[Installation Guide]: Documentation/Installation.md
|
||||
[badge-travis]: https://travis-ci.org/mxcl/PromiseKit.svg?branch=master
|
||||
[travis]: https://travis-ci.org/mxcl/PromiseKit
|
||||
[cocoapods]: https://cocoapods.org/pods/PromiseKit
|
32
Pods/PromiseKit/Sources/AnyPromise+Private.h
generated
Normal file
32
Pods/PromiseKit/Sources/AnyPromise+Private.h
generated
Normal file
@ -0,0 +1,32 @@
|
||||
@import Foundation.NSError;
|
||||
@import Foundation.NSPointerArray;
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
#define NSPointerArrayMake(N) ({ \
|
||||
NSPointerArray *aa = [NSPointerArray strongObjectsPointerArray]; \
|
||||
aa.count = N; \
|
||||
aa; \
|
||||
})
|
||||
#else
|
||||
static inline NSPointerArray * __nonnull NSPointerArrayMake(NSUInteger count) {
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
NSPointerArray *aa = [[NSPointerArray class] respondsToSelector:@selector(strongObjectsPointerArray)]
|
||||
? [NSPointerArray strongObjectsPointerArray]
|
||||
: [NSPointerArray pointerArrayWithStrongObjects];
|
||||
#pragma clang diagnostic pop
|
||||
aa.count = count;
|
||||
return aa;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define IsError(o) [o isKindOfClass:[NSError class]]
|
||||
#define IsPromise(o) [o isKindOfClass:[AnyPromise class]]
|
||||
|
||||
#import "AnyPromise.h"
|
||||
|
||||
@class PMKArray;
|
||||
|
||||
@interface AnyPromise ()
|
||||
- (void)__pipe:(void(^ __nonnull)(__nullable id))block NS_REFINED_FOR_SWIFT;
|
||||
@end
|
308
Pods/PromiseKit/Sources/AnyPromise.h
generated
Normal file
308
Pods/PromiseKit/Sources/AnyPromise.h
generated
Normal file
@ -0,0 +1,308 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <dispatch/dispatch.h>
|
||||
#import <PromiseKit/fwd.h>
|
||||
|
||||
/// INTERNAL DO NOT USE
|
||||
@class __AnyPromise;
|
||||
|
||||
/// Provided to simplify some usage sites
|
||||
typedef void (^PMKResolver)(id __nullable) NS_REFINED_FOR_SWIFT;
|
||||
|
||||
|
||||
/// An Objective-C implementation of the promise pattern.
|
||||
@interface AnyPromise: NSObject
|
||||
|
||||
/**
|
||||
Create a new promise that resolves with the provided block.
|
||||
|
||||
Use this method when wrapping asynchronous code that does *not* use promises so that this code can be used in promise chains.
|
||||
|
||||
If `resolve` is called with an `NSError` object, the promise is rejected, otherwise the promise is fulfilled.
|
||||
|
||||
Don’t use this method if you already have promises! Instead, just return your promise.
|
||||
|
||||
Should you need to fulfill a promise but have no sensical value to use: your promise is a `void` promise: fulfill with `nil`.
|
||||
|
||||
The block you pass is executed immediately on the calling thread.
|
||||
|
||||
- Parameter block: The provided block is immediately executed, inside the block call `resolve` to resolve this promise and cause any attached handlers to execute. If you are wrapping a delegate-based system, we recommend instead to use: initWithResolver:
|
||||
- Returns: A new promise.
|
||||
- Warning: Resolving a promise with `nil` fulfills it.
|
||||
- SeeAlso: https://github.com/mxcl/PromiseKit/blob/master/Documentation/GettingStarted.md#making-promises
|
||||
- SeeAlso: https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#wrapping-delegate-systems
|
||||
*/
|
||||
+ (instancetype __nonnull)promiseWithResolverBlock:(void (^ __nonnull)(__nonnull PMKResolver))resolveBlock NS_REFINED_FOR_SWIFT;
|
||||
|
||||
|
||||
/// INTERNAL DO NOT USE
|
||||
- (instancetype __nonnull)initWith__D:(__AnyPromise * __nonnull)d;
|
||||
|
||||
/**
|
||||
Creates a resolved promise.
|
||||
|
||||
When developing your own promise systems, it is occasionally useful to be able to return an already resolved promise.
|
||||
|
||||
- Parameter value: The value with which to resolve this promise. Passing an `NSError` will cause the promise to be rejected, passing an AnyPromise will return a new AnyPromise bound to that promise, otherwise the promise will be fulfilled with the value passed.
|
||||
- Returns: A resolved promise.
|
||||
*/
|
||||
+ (instancetype __nonnull)promiseWithValue:(__nullable id)value NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
The value of the asynchronous task this promise represents.
|
||||
|
||||
A promise has `nil` value if the asynchronous task it represents has not finished. If the value is `nil` the promise is still `pending`.
|
||||
|
||||
- Warning: *Note* Our Swift variant’s value property returns nil if the promise is rejected where AnyPromise will return the error object. This fits with the pattern where AnyPromise is not strictly typed and is more dynamic, but you should be aware of the distinction.
|
||||
|
||||
- Note: If the AnyPromise was fulfilled with a `PMKManifold`, returns only the first fulfillment object.
|
||||
|
||||
- Returns: The value with which this promise was resolved or `nil` if this promise is pending.
|
||||
*/
|
||||
@property (nonatomic, readonly) __nullable id value NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/// - Returns: if the promise is pending resolution.
|
||||
@property (nonatomic, readonly) BOOL pending NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/// - Returns: if the promise is resolved and fulfilled.
|
||||
@property (nonatomic, readonly) BOOL fulfilled NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/// - Returns: if the promise is resolved and rejected.
|
||||
@property (nonatomic, readonly) BOOL rejected NS_REFINED_FOR_SWIFT;
|
||||
|
||||
|
||||
/**
|
||||
The provided block is executed when its receiver is fulfilled.
|
||||
|
||||
If you provide a block that takes a parameter, the value of the receiver will be passed as that parameter.
|
||||
|
||||
[NSURLSession GET:url].then(^(NSData *data){
|
||||
// do something with data
|
||||
});
|
||||
|
||||
@return A new promise that is resolved with the value returned from the provided block. For example:
|
||||
|
||||
[NSURLSession GET:url].then(^(NSData *data){
|
||||
return data.length;
|
||||
}).then(^(NSNumber *number){
|
||||
//…
|
||||
});
|
||||
|
||||
@warning *Important* The block passed to `then` may take zero, one, two or three arguments, and return an object or return nothing. This flexibility is why the method signature for then is `id`, which means you will not get completion for the block parameter, and must type it yourself. It is safe to type any block syntax here, so to start with try just: `^{}`.
|
||||
|
||||
@warning *Important* If an `NSError` or `NSString` is thrown inside your block, or you return an `NSError` object the next `Promise` will be rejected. See `catch` for documentation on error handling.
|
||||
|
||||
@warning *Important* `then` is always executed on the main queue.
|
||||
|
||||
@see thenOn
|
||||
@see thenInBackground
|
||||
*/
|
||||
- (AnyPromise * __nonnull (^ __nonnull)(id __nonnull))then NS_REFINED_FOR_SWIFT;
|
||||
|
||||
|
||||
/**
|
||||
The provided block is executed on the default queue when the receiver is fulfilled.
|
||||
|
||||
This method is provided as a convenience for `thenOn`.
|
||||
|
||||
@see then
|
||||
@see thenOn
|
||||
*/
|
||||
- (AnyPromise * __nonnull(^ __nonnull)(id __nonnull))thenInBackground NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
The provided block is executed on the dispatch queue of your choice when the receiver is fulfilled.
|
||||
|
||||
@see then
|
||||
@see thenInBackground
|
||||
*/
|
||||
- (AnyPromise * __nonnull(^ __nonnull)(dispatch_queue_t __nonnull, id __nonnull))thenOn NS_REFINED_FOR_SWIFT;
|
||||
|
||||
#ifndef __cplusplus
|
||||
/**
|
||||
The provided block is executed when the receiver is rejected.
|
||||
|
||||
Provide a block of form `^(NSError *){}` or simply `^{}`. The parameter has type `id` to give you the freedom to choose either.
|
||||
|
||||
The provided block always runs on the main queue.
|
||||
|
||||
@warning *Note* Cancellation errors are not caught.
|
||||
|
||||
@warning *Note* Since catch is a c++ keyword, this method is not available in Objective-C++ files. Instead use catchOn.
|
||||
|
||||
@see catchOn
|
||||
@see catchInBackground
|
||||
*/
|
||||
- (AnyPromise * __nonnull(^ __nonnull)(id __nonnull))catch NS_REFINED_FOR_SWIFT;
|
||||
#endif
|
||||
|
||||
/**
|
||||
The provided block is executed when the receiver is rejected.
|
||||
|
||||
Provide a block of form `^(NSError *){}` or simply `^{}`. The parameter has type `id` to give you the freedom to choose either.
|
||||
|
||||
The provided block always runs on the global background queue.
|
||||
|
||||
@warning *Note* Cancellation errors are not caught.
|
||||
|
||||
@warning *Note* Since catch is a c++ keyword, this method is not available in Objective-C++ files. Instead use catchWithPolicy.
|
||||
|
||||
@see catch
|
||||
@see catchOn
|
||||
*/
|
||||
- (AnyPromise * __nonnull(^ __nonnull)(id __nonnull))catchInBackground NS_REFINED_FOR_SWIFT;
|
||||
|
||||
|
||||
/**
|
||||
The provided block is executed when the receiver is rejected.
|
||||
|
||||
Provide a block of form `^(NSError *){}` or simply `^{}`. The parameter has type `id` to give you the freedom to choose either.
|
||||
|
||||
The provided block always runs on queue provided.
|
||||
|
||||
@warning *Note* Cancellation errors are not caught.
|
||||
|
||||
@see catch
|
||||
@see catchInBackground
|
||||
*/
|
||||
- (AnyPromise * __nonnull(^ __nonnull)(dispatch_queue_t __nonnull, id __nonnull))catchOn NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
The provided block is executed when the receiver is resolved.
|
||||
|
||||
The provided block always runs on the main queue.
|
||||
|
||||
@see ensureOn
|
||||
*/
|
||||
- (AnyPromise * __nonnull(^ __nonnull)(dispatch_block_t __nonnull))ensure NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
The provided block is executed on the dispatch queue of your choice when the receiver is resolved.
|
||||
|
||||
@see ensure
|
||||
*/
|
||||
- (AnyPromise * __nonnull(^ __nonnull)(dispatch_queue_t __nonnull, dispatch_block_t __nonnull))ensureOn NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
Wait until the promise is resolved.
|
||||
|
||||
@return Value if fulfilled or error if rejected.
|
||||
*/
|
||||
- (id __nullable)wait NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
Create a new promise with an associated resolver.
|
||||
|
||||
Use this method when wrapping asynchronous code that does *not* use
|
||||
promises so that this code can be used in promise chains. Generally,
|
||||
prefer `promiseWithResolverBlock:` as the resulting code is more elegant.
|
||||
|
||||
PMKResolver resolve;
|
||||
AnyPromise *promise = [[AnyPromise alloc] initWithResolver:&resolve];
|
||||
|
||||
// later
|
||||
resolve(@"foo");
|
||||
|
||||
@param resolver A reference to a block pointer of PMKResolver type.
|
||||
You can then call your resolver to resolve this promise.
|
||||
|
||||
@return A new promise.
|
||||
|
||||
@warning *Important* The resolver strongly retains the promise.
|
||||
|
||||
@see promiseWithResolverBlock:
|
||||
*/
|
||||
- (instancetype __nonnull)initWithResolver:(PMKResolver __strong __nonnull * __nonnull)resolver NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
Unavailable methods
|
||||
*/
|
||||
|
||||
- (instancetype __nonnull)init __attribute__((unavailable("It is illegal to create an unresolvable promise.")));
|
||||
+ (instancetype __nonnull)new __attribute__((unavailable("It is illegal to create an unresolvable promise.")));
|
||||
- (AnyPromise * __nonnull(^ __nonnull)(dispatch_block_t __nonnull))always __attribute__((unavailable("See -ensure")));
|
||||
- (AnyPromise * __nonnull(^ __nonnull)(dispatch_block_t __nonnull))alwaysOn __attribute__((unavailable("See -ensureOn")));
|
||||
- (AnyPromise * __nonnull(^ __nonnull)(dispatch_block_t __nonnull))finally __attribute__((unavailable("See -ensure")));
|
||||
- (AnyPromise * __nonnull(^ __nonnull)(dispatch_block_t __nonnull, dispatch_block_t __nonnull))finallyOn __attribute__((unavailable("See -ensureOn")));
|
||||
|
||||
@end
|
||||
|
||||
|
||||
typedef void (^PMKAdapter)(id __nullable, NSError * __nullable) NS_REFINED_FOR_SWIFT;
|
||||
typedef void (^PMKIntegerAdapter)(NSInteger, NSError * __nullable) NS_REFINED_FOR_SWIFT;
|
||||
typedef void (^PMKBooleanAdapter)(BOOL, NSError * __nullable) NS_REFINED_FOR_SWIFT;
|
||||
|
||||
|
||||
@interface AnyPromise (Adapters)
|
||||
|
||||
/**
|
||||
Create a new promise by adapting an existing asynchronous system.
|
||||
|
||||
The pattern of a completion block that passes two parameters, the first
|
||||
the result and the second an `NSError` object is so common that we
|
||||
provide this convenience adapter to make wrapping such systems more
|
||||
elegant.
|
||||
|
||||
return [PMKPromise promiseWithAdapterBlock:^(PMKAdapter adapter){
|
||||
PFQuery *query = [PFQuery …];
|
||||
[query findObjectsInBackgroundWithBlock:adapter];
|
||||
}];
|
||||
|
||||
@warning *Important* If both parameters are nil, the promise fulfills,
|
||||
if both are non-nil the promise rejects. This is per the convention.
|
||||
|
||||
@see https://github.com/mxcl/PromiseKit/blob/master/Documentation/GettingStarted.md#making-promises
|
||||
*/
|
||||
+ (instancetype __nonnull)promiseWithAdapterBlock:(void (^ __nonnull)(PMKAdapter __nonnull adapter))block NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
Create a new promise by adapting an existing asynchronous system.
|
||||
|
||||
Adapts asynchronous systems that complete with `^(NSInteger, NSError *)`.
|
||||
NSInteger will cast to enums provided the enum has been wrapped with
|
||||
`NS_ENUM`. All of Apple’s enums are, so if you find one that hasn’t you
|
||||
may need to make a pull-request.
|
||||
|
||||
@see promiseWithAdapter
|
||||
*/
|
||||
+ (instancetype __nonnull)promiseWithIntegerAdapterBlock:(void (^ __nonnull)(PMKIntegerAdapter __nonnull adapter))block NS_REFINED_FOR_SWIFT;
|
||||
|
||||
/**
|
||||
Create a new promise by adapting an existing asynchronous system.
|
||||
|
||||
Adapts asynchronous systems that complete with `^(BOOL, NSError *)`.
|
||||
|
||||
@see promiseWithAdapter
|
||||
*/
|
||||
+ (instancetype __nonnull)promiseWithBooleanAdapterBlock:(void (^ __nonnull)(PMKBooleanAdapter __nonnull adapter))block NS_REFINED_FOR_SWIFT;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
Whenever resolving a promise you may resolve with a tuple, eg.
|
||||
returning from a `then` or `catch` handler or resolving a new promise.
|
||||
|
||||
Consumers of your Promise are not compelled to consume any arguments and
|
||||
in fact will often only consume the first parameter. Thus ensure the
|
||||
order of parameters is: from most-important to least-important.
|
||||
|
||||
Currently PromiseKit limits you to THREE parameters to the manifold.
|
||||
*/
|
||||
#define PMKManifold(...) __PMKManifold(__VA_ARGS__, 3, 2, 1)
|
||||
#define __PMKManifold(_1, _2, _3, N, ...) __PMKArrayWithCount(N, _1, _2, _3)
|
||||
extern id __nonnull __PMKArrayWithCount(NSUInteger, ...);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // Extern C
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
__attribute__((unavailable("See AnyPromise")))
|
||||
@interface PMKPromise
|
||||
@end
|
179
Pods/PromiseKit/Sources/AnyPromise.m
generated
Normal file
179
Pods/PromiseKit/Sources/AnyPromise.m
generated
Normal file
@ -0,0 +1,179 @@
|
||||
#if __has_include("PromiseKit-Swift.h")
|
||||
#import "PromiseKit-Swift.h"
|
||||
#else
|
||||
#import <PromiseKit/PromiseKit-Swift.h>
|
||||
#endif
|
||||
#import "PMKCallVariadicBlock.m"
|
||||
#import "AnyPromise+Private.h"
|
||||
#import "AnyPromise.h"
|
||||
|
||||
NSString *const PMKErrorDomain = @"PMKErrorDomain";
|
||||
|
||||
|
||||
@implementation AnyPromise {
|
||||
__AnyPromise *d;
|
||||
}
|
||||
|
||||
- (instancetype)initWith__D:(__AnyPromise *)dd {
|
||||
self = [super init];
|
||||
if (self) self->d = dd;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initWithResolver:(PMKResolver __strong *)resolver {
|
||||
self = [super init];
|
||||
if (self)
|
||||
d = [[__AnyPromise alloc] initWithResolver:^(void (^resolve)(id)) {
|
||||
*resolver = resolve;
|
||||
}];
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)promiseWithResolverBlock:(void (^)(PMKResolver _Nonnull))resolveBlock {
|
||||
id d = [[__AnyPromise alloc] initWithResolver:resolveBlock];
|
||||
return [[self alloc] initWith__D:d];
|
||||
}
|
||||
|
||||
+ (instancetype)promiseWithValue:(id)value {
|
||||
//TODO provide a more efficient route for sealed promises
|
||||
id d = [[__AnyPromise alloc] initWithResolver:^(void (^resolve)(id)) {
|
||||
resolve(value);
|
||||
}];
|
||||
return [[self alloc] initWith__D:d];
|
||||
}
|
||||
|
||||
//TODO remove if possible, but used by when.m
|
||||
- (void)__pipe:(void (^)(id _Nullable))block {
|
||||
[d __pipe:block];
|
||||
}
|
||||
|
||||
//NOTE used by AnyPromise.swift
|
||||
- (id)__d {
|
||||
return d;
|
||||
}
|
||||
|
||||
- (AnyPromise *(^)(id))then {
|
||||
return ^(id block) {
|
||||
return [self->d __thenOn:dispatch_get_main_queue() execute:^(id obj) {
|
||||
return PMKCallVariadicBlock(block, obj);
|
||||
}];
|
||||
};
|
||||
}
|
||||
|
||||
- (AnyPromise *(^)(dispatch_queue_t, id))thenOn {
|
||||
return ^(dispatch_queue_t queue, id block) {
|
||||
return [self->d __thenOn:queue execute:^(id obj) {
|
||||
return PMKCallVariadicBlock(block, obj);
|
||||
}];
|
||||
};
|
||||
}
|
||||
|
||||
- (AnyPromise *(^)(id))thenInBackground {
|
||||
return ^(id block) {
|
||||
return [self->d __thenOn:dispatch_get_global_queue(0, 0) execute:^(id obj) {
|
||||
return PMKCallVariadicBlock(block, obj);
|
||||
}];
|
||||
};
|
||||
}
|
||||
|
||||
- (AnyPromise *(^)(dispatch_queue_t, id))catchOn {
|
||||
return ^(dispatch_queue_t q, id block) {
|
||||
return [self->d __catchOn:q execute:^(id obj) {
|
||||
return PMKCallVariadicBlock(block, obj);
|
||||
}];
|
||||
};
|
||||
}
|
||||
|
||||
- (AnyPromise *(^)(id))catch {
|
||||
return ^(id block) {
|
||||
return [self->d __catchOn:dispatch_get_main_queue() execute:^(id obj) {
|
||||
return PMKCallVariadicBlock(block, obj);
|
||||
}];
|
||||
};
|
||||
}
|
||||
|
||||
- (AnyPromise *(^)(id))catchInBackground {
|
||||
return ^(id block) {
|
||||
return [self->d __catchOn:dispatch_get_global_queue(0, 0) execute:^(id obj) {
|
||||
return PMKCallVariadicBlock(block, obj);
|
||||
}];
|
||||
};
|
||||
}
|
||||
|
||||
- (AnyPromise *(^)(dispatch_block_t))ensure {
|
||||
return ^(dispatch_block_t block) {
|
||||
return [self->d __ensureOn:dispatch_get_main_queue() execute:block];
|
||||
};
|
||||
}
|
||||
|
||||
- (AnyPromise *(^)(dispatch_queue_t, dispatch_block_t))ensureOn {
|
||||
return ^(dispatch_queue_t queue, dispatch_block_t block) {
|
||||
return [self->d __ensureOn:queue execute:block];
|
||||
};
|
||||
}
|
||||
|
||||
- (id)wait {
|
||||
return [d __wait];
|
||||
}
|
||||
|
||||
- (BOOL)pending {
|
||||
return [[d valueForKey:@"__pending"] boolValue];
|
||||
}
|
||||
|
||||
- (BOOL)rejected {
|
||||
return IsError([d __value]);
|
||||
}
|
||||
|
||||
- (BOOL)fulfilled {
|
||||
return !self.rejected;
|
||||
}
|
||||
|
||||
- (id)value {
|
||||
id obj = [d __value];
|
||||
|
||||
if ([obj isKindOfClass:[PMKArray class]]) {
|
||||
return obj[0];
|
||||
} else {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
||||
@implementation AnyPromise (Adapters)
|
||||
|
||||
+ (instancetype)promiseWithAdapterBlock:(void (^)(PMKAdapter))block {
|
||||
return [self promiseWithResolverBlock:^(PMKResolver resolve) {
|
||||
block(^(id value, id error){
|
||||
resolve(error ?: value);
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
+ (instancetype)promiseWithIntegerAdapterBlock:(void (^)(PMKIntegerAdapter))block {
|
||||
return [self promiseWithResolverBlock:^(PMKResolver resolve) {
|
||||
block(^(NSInteger value, id error){
|
||||
if (error) {
|
||||
resolve(error);
|
||||
} else {
|
||||
resolve(@(value));
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
+ (instancetype)promiseWithBooleanAdapterBlock:(void (^)(PMKBooleanAdapter adapter))block {
|
||||
return [self promiseWithResolverBlock:^(PMKResolver resolve) {
|
||||
block(^(BOOL value, id error){
|
||||
if (error) {
|
||||
resolve(error);
|
||||
} else {
|
||||
resolve(@(value));
|
||||
}
|
||||
});
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
224
Pods/PromiseKit/Sources/AnyPromise.swift
generated
Normal file
224
Pods/PromiseKit/Sources/AnyPromise.swift
generated
Normal file
@ -0,0 +1,224 @@
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
__AnyPromise is an implementation detail.
|
||||
|
||||
Because of how ObjC/Swift compatibility work we have to compose our AnyPromise
|
||||
with this internal object, however this is still part of the public interface.
|
||||
Sadly. Please don’t use it.
|
||||
*/
|
||||
@objc(__AnyPromise) public class __AnyPromise: NSObject {
|
||||
fileprivate let box: Box<Any?>
|
||||
|
||||
@objc public init(resolver body: (@escaping (Any?) -> Void) -> Void) {
|
||||
box = EmptyBox<Any?>()
|
||||
super.init()
|
||||
body {
|
||||
if let p = $0 as? AnyPromise {
|
||||
p.d.__pipe(self.box.seal)
|
||||
} else {
|
||||
self.box.seal($0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc public func __thenOn(_ q: DispatchQueue, execute: @escaping (Any?) -> Any?) -> AnyPromise {
|
||||
return AnyPromise(__D: __AnyPromise(resolver: { resolve in
|
||||
self.__pipe { obj in
|
||||
if !(obj is NSError) {
|
||||
q.async {
|
||||
resolve(execute(obj))
|
||||
}
|
||||
} else {
|
||||
resolve(obj)
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
@objc public func __catchOn(_ q: DispatchQueue, execute: @escaping (Any?) -> Any?) -> AnyPromise {
|
||||
return AnyPromise(__D: __AnyPromise(resolver: { resolve in
|
||||
self.__pipe { obj in
|
||||
if obj is NSError {
|
||||
q.async {
|
||||
resolve(execute(obj))
|
||||
}
|
||||
} else {
|
||||
resolve(obj)
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
@objc public func __ensureOn(_ q: DispatchQueue, execute: @escaping () -> Void) -> AnyPromise {
|
||||
return AnyPromise(__D: __AnyPromise(resolver: { resolve in
|
||||
self.__pipe { obj in
|
||||
q.async {
|
||||
execute()
|
||||
resolve(obj)
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
@objc public func __wait() -> Any? {
|
||||
if Thread.isMainThread {
|
||||
conf.logHandler(.waitOnMainThread)
|
||||
}
|
||||
|
||||
var result = __value
|
||||
|
||||
if result == nil {
|
||||
let group = DispatchGroup()
|
||||
group.enter()
|
||||
self.__pipe { obj in
|
||||
result = obj
|
||||
group.leave()
|
||||
}
|
||||
group.wait()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
/// Internal, do not use! Some behaviors undefined.
|
||||
@objc public func __pipe(_ to: @escaping (Any?) -> Void) {
|
||||
let to = { (obj: Any?) -> Void in
|
||||
if obj is NSError {
|
||||
to(obj) // or we cannot determine if objects are errors in objc land
|
||||
} else {
|
||||
to(obj)
|
||||
}
|
||||
}
|
||||
switch box.inspect() {
|
||||
case .pending:
|
||||
box.inspect {
|
||||
switch $0 {
|
||||
case .pending(let handlers):
|
||||
handlers.append { obj in
|
||||
to(obj)
|
||||
}
|
||||
case .resolved(let obj):
|
||||
to(obj)
|
||||
}
|
||||
}
|
||||
case .resolved(let obj):
|
||||
to(obj)
|
||||
}
|
||||
}
|
||||
|
||||
@objc public var __value: Any? {
|
||||
switch box.inspect() {
|
||||
case .resolved(let obj):
|
||||
return obj
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@objc public var __pending: Bool {
|
||||
switch box.inspect() {
|
||||
case .pending:
|
||||
return true
|
||||
case .resolved:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AnyPromise: Thenable, CatchMixin {
|
||||
|
||||
/// - Returns: A new `AnyPromise` bound to a `Promise<Any>`.
|
||||
public convenience init<U: Thenable>(_ bridge: U) {
|
||||
self.init(__D: __AnyPromise(resolver: { resolve in
|
||||
bridge.pipe {
|
||||
switch $0 {
|
||||
case .rejected(let error):
|
||||
resolve(error as NSError)
|
||||
case .fulfilled(let value):
|
||||
resolve(value)
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
public func pipe(to body: @escaping (Result<Any?>) -> Void) {
|
||||
|
||||
func fulfill() {
|
||||
// calling through to the ObjC `value` property unwraps (any) PMKManifold
|
||||
// and considering this is the Swift pipe; we want that.
|
||||
body(.fulfilled(self.value(forKey: "value")))
|
||||
}
|
||||
|
||||
switch box.inspect() {
|
||||
case .pending:
|
||||
box.inspect {
|
||||
switch $0 {
|
||||
case .pending(let handlers):
|
||||
handlers.append {
|
||||
if let error = $0 as? Error {
|
||||
body(.rejected(error))
|
||||
} else {
|
||||
fulfill()
|
||||
}
|
||||
}
|
||||
case .resolved(let error as Error):
|
||||
body(.rejected(error))
|
||||
case .resolved:
|
||||
fulfill()
|
||||
}
|
||||
}
|
||||
case .resolved(let error as Error):
|
||||
body(.rejected(error))
|
||||
case .resolved:
|
||||
fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate var d: __AnyPromise {
|
||||
return value(forKey: "__d") as! __AnyPromise
|
||||
}
|
||||
|
||||
var box: Box<Any?> {
|
||||
return d.box
|
||||
}
|
||||
|
||||
public var result: Result<Any?>? {
|
||||
guard let value = __value else {
|
||||
return nil
|
||||
}
|
||||
if let error = value as? Error {
|
||||
return .rejected(error)
|
||||
} else {
|
||||
return .fulfilled(value)
|
||||
}
|
||||
}
|
||||
|
||||
public typealias T = Any?
|
||||
}
|
||||
|
||||
|
||||
#if swift(>=3.1)
|
||||
public extension Promise where T == Any? {
|
||||
convenience init(_ anyPromise: AnyPromise) {
|
||||
self.init {
|
||||
anyPromise.pipe(to: $0.resolve)
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
extension AnyPromise {
|
||||
public func asPromise() -> Promise<Any?> {
|
||||
return Promise(.pending, resolver: { resolve in
|
||||
pipe { result in
|
||||
switch result {
|
||||
case .rejected(let error):
|
||||
resolve.reject(error)
|
||||
case .fulfilled(let obj):
|
||||
resolve.fulfill(obj)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
#endif
|
101
Pods/PromiseKit/Sources/Box.swift
generated
Normal file
101
Pods/PromiseKit/Sources/Box.swift
generated
Normal file
@ -0,0 +1,101 @@
|
||||
import Dispatch
|
||||
|
||||
enum Sealant<R> {
|
||||
case pending(Handlers<R>)
|
||||
case resolved(R)
|
||||
}
|
||||
|
||||
final class Handlers<R> {
|
||||
var bodies: [(R) -> Void] = []
|
||||
func append(_ item: @escaping(R) -> Void) { bodies.append(item) }
|
||||
}
|
||||
|
||||
/// - Remark: not protocol ∵ http://www.russbishop.net/swift-associated-types-cont
|
||||
class Box<T> {
|
||||
func inspect() -> Sealant<T> { fatalError() }
|
||||
func inspect(_: (Sealant<T>) -> Void) { fatalError() }
|
||||
func seal(_: T) {}
|
||||
}
|
||||
|
||||
final class SealedBox<T>: Box<T> {
|
||||
let value: T
|
||||
|
||||
init(value: T) {
|
||||
self.value = value
|
||||
}
|
||||
|
||||
override func inspect() -> Sealant<T> {
|
||||
return .resolved(value)
|
||||
}
|
||||
}
|
||||
|
||||
class EmptyBox<T>: Box<T> {
|
||||
private var sealant = Sealant<T>.pending(.init())
|
||||
private let barrier = DispatchQueue(label: "org.promisekit.barrier", attributes: .concurrent)
|
||||
|
||||
override func seal(_ value: T) {
|
||||
var handlers: Handlers<T>!
|
||||
barrier.sync(flags: .barrier) {
|
||||
guard case .pending(let _handlers) = self.sealant else {
|
||||
return // already fulfilled!
|
||||
}
|
||||
handlers = _handlers
|
||||
self.sealant = .resolved(value)
|
||||
}
|
||||
|
||||
//FIXME we are resolved so should `pipe(to:)` be called at this instant, “thens are called in order” would be invalid
|
||||
//NOTE we don’t do this in the above `sync` because that could potentially deadlock
|
||||
//THOUGH since `then` etc. typically invoke after a run-loop cycle, this issue is somewhat less severe
|
||||
|
||||
if let handlers = handlers {
|
||||
handlers.bodies.forEach{ $0(value) }
|
||||
}
|
||||
|
||||
//TODO solution is an unfortunate third state “sealed” where then's get added
|
||||
// to a separate handler pool for that state
|
||||
// any other solution has potential races
|
||||
}
|
||||
|
||||
override func inspect() -> Sealant<T> {
|
||||
var rv: Sealant<T>!
|
||||
barrier.sync {
|
||||
rv = self.sealant
|
||||
}
|
||||
return rv
|
||||
}
|
||||
|
||||
override func inspect(_ body: (Sealant<T>) -> Void) {
|
||||
var sealed = false
|
||||
barrier.sync(flags: .barrier) {
|
||||
switch sealant {
|
||||
case .pending:
|
||||
// body will append to handlers, so we must stay barrier’d
|
||||
body(sealant)
|
||||
case .resolved:
|
||||
sealed = true
|
||||
}
|
||||
}
|
||||
if sealed {
|
||||
// we do this outside the barrier to prevent potential deadlocks
|
||||
// it's safe because we never transition away from this state
|
||||
body(sealant)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
extension Optional where Wrapped: DispatchQueue {
|
||||
@inline(__always)
|
||||
func async(flags: DispatchWorkItemFlags?, _ body: @escaping() -> Void) {
|
||||
switch self {
|
||||
case .none:
|
||||
body()
|
||||
case .some(let q):
|
||||
if let flags = flags {
|
||||
q.async(flags: flags, execute: body)
|
||||
} else {
|
||||
q.async(execute: body)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
256
Pods/PromiseKit/Sources/Catchable.swift
generated
Normal file
256
Pods/PromiseKit/Sources/Catchable.swift
generated
Normal file
@ -0,0 +1,256 @@
|
||||
import Dispatch
|
||||
|
||||
/// Provides `catch` and `recover` to your object that conforms to `Thenable`
|
||||
public protocol CatchMixin: Thenable
|
||||
{}
|
||||
|
||||
public extension CatchMixin {
|
||||
|
||||
/**
|
||||
The provided closure executes when this promise rejects.
|
||||
|
||||
Rejecting a promise cascades: rejecting all subsequent promises (unless
|
||||
recover is invoked) thus you will typically place your catch at the end
|
||||
of a chain. Often utility promises will not have a catch, instead
|
||||
delegating the error handling to the caller.
|
||||
|
||||
- Parameter on: The queue to which the provided closure dispatches.
|
||||
- Parameter policy: The default policy does not execute your handler for cancellation errors.
|
||||
- Parameter execute: The handler to execute if this promise is rejected.
|
||||
- Returns: A promise finalizer.
|
||||
- SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation)
|
||||
*/
|
||||
@discardableResult
|
||||
func `catch`(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) -> Void) -> PMKFinalizer {
|
||||
let finalizer = PMKFinalizer()
|
||||
pipe {
|
||||
switch $0 {
|
||||
case .rejected(let error):
|
||||
guard policy == .allErrors || !error.isCancelled else {
|
||||
fallthrough
|
||||
}
|
||||
on.async(flags: flags) {
|
||||
body(error)
|
||||
finalizer.pending.resolve(())
|
||||
}
|
||||
case .fulfilled:
|
||||
finalizer.pending.resolve(())
|
||||
}
|
||||
}
|
||||
return finalizer
|
||||
}
|
||||
}
|
||||
|
||||
public class PMKFinalizer {
|
||||
let pending = Guarantee<Void>.pending()
|
||||
|
||||
/// `finally` is the same as `ensure`, but it is not chainable
|
||||
public func finally(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> Void) {
|
||||
pending.guarantee.done(on: on, flags: flags) {
|
||||
body()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public extension CatchMixin {
|
||||
|
||||
/**
|
||||
The provided closure executes when this promise rejects.
|
||||
|
||||
Unlike `catch`, `recover` continues the chain.
|
||||
Use `recover` in circumstances where recovering the chain from certain errors is a possibility. For example:
|
||||
|
||||
firstly {
|
||||
CLLocationManager.requestLocation()
|
||||
}.recover { error in
|
||||
guard error == CLError.unknownLocation else { throw error }
|
||||
return .value(CLLocation.chicago)
|
||||
}
|
||||
|
||||
- Parameter on: The queue to which the provided closure dispatches.
|
||||
- Parameter body: The handler to execute if this promise is rejected.
|
||||
- SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation)
|
||||
*/
|
||||
func recover<U: Thenable>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> U) -> Promise<T> where U.T == T {
|
||||
let rp = Promise<U.T>(.pending)
|
||||
pipe {
|
||||
switch $0 {
|
||||
case .fulfilled(let value):
|
||||
rp.box.seal(.fulfilled(value))
|
||||
case .rejected(let error):
|
||||
if policy == .allErrors || !error.isCancelled {
|
||||
on.async(flags: flags) {
|
||||
do {
|
||||
let rv = try body(error)
|
||||
guard rv !== rp else { throw PMKError.returnedSelf }
|
||||
rv.pipe(to: rp.box.seal)
|
||||
} catch {
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
return rp
|
||||
}
|
||||
|
||||
/**
|
||||
The provided closure executes when this promise rejects.
|
||||
This variant of `recover` requires the handler to return a Guarantee, thus it returns a Guarantee itself and your closure cannot `throw`.
|
||||
- Note it is logically impossible for this to take a `catchPolicy`, thus `allErrors` are handled.
|
||||
- Parameter on: The queue to which the provided closure dispatches.
|
||||
- Parameter body: The handler to execute if this promise is rejected.
|
||||
- SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation)
|
||||
*/
|
||||
@discardableResult
|
||||
func recover(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Guarantee<T>) -> Guarantee<T> {
|
||||
let rg = Guarantee<T>(.pending)
|
||||
pipe {
|
||||
switch $0 {
|
||||
case .fulfilled(let value):
|
||||
rg.box.seal(value)
|
||||
case .rejected(let error):
|
||||
on.async(flags: flags) {
|
||||
body(error).pipe(to: rg.box.seal)
|
||||
}
|
||||
}
|
||||
}
|
||||
return rg
|
||||
}
|
||||
|
||||
/**
|
||||
The provided closure executes when this promise resolves, whether it rejects or not.
|
||||
|
||||
firstly {
|
||||
UIApplication.shared.networkActivityIndicatorVisible = true
|
||||
}.done {
|
||||
//…
|
||||
}.ensure {
|
||||
UIApplication.shared.networkActivityIndicatorVisible = false
|
||||
}.catch {
|
||||
//…
|
||||
}
|
||||
|
||||
- Parameter on: The queue to which the provided closure dispatches.
|
||||
- Parameter body: The closure that executes when this promise resolves.
|
||||
- Returns: A new promise, resolved with this promise’s resolution.
|
||||
*/
|
||||
func ensure(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> Void) -> Promise<T> {
|
||||
let rp = Promise<T>(.pending)
|
||||
pipe { result in
|
||||
on.async(flags: flags) {
|
||||
body()
|
||||
rp.box.seal(result)
|
||||
}
|
||||
}
|
||||
return rp
|
||||
}
|
||||
|
||||
/**
|
||||
The provided closure executes when this promise resolves, whether it rejects or not.
|
||||
The chain waits on the returned `Guarantee<Void>`.
|
||||
|
||||
firstly {
|
||||
setup()
|
||||
}.done {
|
||||
//…
|
||||
}.ensureThen {
|
||||
teardown() // -> Guarante<Void>
|
||||
}.catch {
|
||||
//…
|
||||
}
|
||||
|
||||
- Parameter on: The queue to which the provided closure dispatches.
|
||||
- Parameter body: The closure that executes when this promise resolves.
|
||||
- Returns: A new promise, resolved with this promise’s resolution.
|
||||
*/
|
||||
func ensureThen(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping () -> Guarantee<Void>) -> Promise<T> {
|
||||
let rp = Promise<T>(.pending)
|
||||
pipe { result in
|
||||
on.async(flags: flags) {
|
||||
body().done {
|
||||
rp.box.seal(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
return rp
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Consumes the Swift unused-result warning.
|
||||
- Note: You should `catch`, but in situations where you know you don’t need a `catch`, `cauterize` makes your intentions clear.
|
||||
*/
|
||||
@discardableResult
|
||||
func cauterize() -> PMKFinalizer {
|
||||
return self.catch {
|
||||
conf.logHandler(.cauterized($0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public extension CatchMixin where T == Void {
|
||||
|
||||
/**
|
||||
The provided closure executes when this promise rejects.
|
||||
|
||||
This variant of `recover` is specialized for `Void` promises and de-errors your chain returning a `Guarantee`, thus you cannot `throw` and you must handle all errors including cancellation.
|
||||
|
||||
- Parameter on: The queue to which the provided closure dispatches.
|
||||
- Parameter body: The handler to execute if this promise is rejected.
|
||||
- SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation)
|
||||
*/
|
||||
@discardableResult
|
||||
func recover(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Error) -> Void) -> Guarantee<Void> {
|
||||
let rg = Guarantee<Void>(.pending)
|
||||
pipe {
|
||||
switch $0 {
|
||||
case .fulfilled:
|
||||
rg.box.seal(())
|
||||
case .rejected(let error):
|
||||
on.async(flags: flags) {
|
||||
body(error)
|
||||
rg.box.seal(())
|
||||
}
|
||||
}
|
||||
}
|
||||
return rg
|
||||
}
|
||||
|
||||
/**
|
||||
The provided closure executes when this promise rejects.
|
||||
|
||||
This variant of `recover` ensures that no error is thrown from the handler and allows specifying a catch policy.
|
||||
|
||||
- Parameter on: The queue to which the provided closure dispatches.
|
||||
- Parameter body: The handler to execute if this promise is rejected.
|
||||
- SeeAlso: [Cancellation](https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md#cancellation)
|
||||
*/
|
||||
func recover(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, policy: CatchPolicy = conf.catchPolicy, _ body: @escaping(Error) throws -> Void) -> Promise<Void> {
|
||||
let rg = Promise<Void>(.pending)
|
||||
pipe {
|
||||
switch $0 {
|
||||
case .fulfilled:
|
||||
rg.box.seal(.fulfilled(()))
|
||||
case .rejected(let error):
|
||||
if policy == .allErrors || !error.isCancelled {
|
||||
on.async(flags: flags) {
|
||||
do {
|
||||
rg.box.seal(.fulfilled(try body(error)))
|
||||
} catch {
|
||||
rg.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rg.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
return rg
|
||||
}
|
||||
}
|
35
Pods/PromiseKit/Sources/Configuration.swift
generated
Normal file
35
Pods/PromiseKit/Sources/Configuration.swift
generated
Normal file
@ -0,0 +1,35 @@
|
||||
import Dispatch
|
||||
|
||||
/**
|
||||
PromiseKit’s configurable parameters.
|
||||
|
||||
Do not change these after any Promise machinery executes as the configuration object is not thread-safe.
|
||||
|
||||
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.
|
||||
*/
|
||||
public struct PMKConfiguration {
|
||||
/// The default queues that promises handlers dispatch to
|
||||
public var Q: (map: DispatchQueue?, return: DispatchQueue?) = (map: DispatchQueue.main, return: DispatchQueue.main)
|
||||
|
||||
/// The default catch-policy for all `catch` and `resolve`
|
||||
public var catchPolicy = CatchPolicy.allErrorsExceptCancellation
|
||||
|
||||
/// The closure used to log PromiseKit events.
|
||||
/// Not thread safe; change before processing any promises.
|
||||
/// - Note: The default handler calls `print()`
|
||||
public var logHandler: (LogEvent) -> Void = { event in
|
||||
switch event {
|
||||
case .waitOnMainThread:
|
||||
print("PromiseKit: warning: `wait()` called on main thread!")
|
||||
case .pendingPromiseDeallocated:
|
||||
print("PromiseKit: warning: pending promise deallocated")
|
||||
case .pendingGuaranteeDeallocated:
|
||||
print("PromiseKit: warning: pending guarantee deallocated")
|
||||
case .cauterized (let error):
|
||||
print("PromiseKit:cauterized-error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Modify this as soon as possible in your application’s lifetime
|
||||
public var conf = PMKConfiguration()
|
44
Pods/PromiseKit/Sources/CustomStringConvertible.swift
generated
Normal file
44
Pods/PromiseKit/Sources/CustomStringConvertible.swift
generated
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
extension Promise: CustomStringConvertible {
|
||||
/// - Returns: A description of the state of this promise.
|
||||
public var description: String {
|
||||
switch result {
|
||||
case nil:
|
||||
return "Promise(…\(T.self))"
|
||||
case .rejected(let error)?:
|
||||
return "Promise(\(error))"
|
||||
case .fulfilled(let value)?:
|
||||
return "Promise(\(value))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Promise: CustomDebugStringConvertible {
|
||||
/// - Returns: A debug-friendly description of the state of this promise.
|
||||
public var debugDescription: String {
|
||||
switch box.inspect() {
|
||||
case .pending(let handlers):
|
||||
return "Promise<\(T.self)>.pending(handlers: \(handlers.bodies.count))"
|
||||
case .resolved(.rejected(let error)):
|
||||
return "Promise<\(T.self)>.rejected(\(type(of: error)).\(error))"
|
||||
case .resolved(.fulfilled(let value)):
|
||||
return "Promise<\(T.self)>.fulfilled(\(value))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !SWIFT_PACKAGE
|
||||
extension AnyPromise {
|
||||
/// - Returns: A description of the state of this promise.
|
||||
override open var description: String {
|
||||
switch box.inspect() {
|
||||
case .pending:
|
||||
return "AnyPromise(…)"
|
||||
case .resolved(let obj?):
|
||||
return "AnyPromise(\(obj))"
|
||||
case .resolved(nil):
|
||||
return "AnyPromise(nil)"
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
93
Pods/PromiseKit/Sources/Deprecations.swift
generated
Normal file
93
Pods/PromiseKit/Sources/Deprecations.swift
generated
Normal file
@ -0,0 +1,93 @@
|
||||
import Dispatch
|
||||
|
||||
@available(*, deprecated, message: "See `init(resolver:)`")
|
||||
public func wrap<T>(_ body: (@escaping (T?, Error?) -> Void) throws -> Void) -> Promise<T> {
|
||||
return Promise { seal in
|
||||
try body(seal.resolve)
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "See `init(resolver:)`")
|
||||
public func wrap<T>(_ body: (@escaping (T, Error?) -> Void) throws -> Void) -> Promise<T> {
|
||||
return Promise { seal in
|
||||
try body(seal.resolve)
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "See `init(resolver:)`")
|
||||
public func wrap<T>(_ body: (@escaping (Error?, T?) -> Void) throws -> Void) -> Promise<T> {
|
||||
return Promise { seal in
|
||||
try body(seal.resolve)
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "See `init(resolver:)`")
|
||||
public func wrap(_ body: (@escaping (Error?) -> Void) throws -> Void) -> Promise<Void> {
|
||||
return Promise { seal in
|
||||
try body(seal.resolve)
|
||||
}
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "See `init(resolver:)`")
|
||||
public func wrap<T>(_ body: (@escaping (T) -> Void) throws -> Void) -> Promise<T> {
|
||||
return Promise { seal in
|
||||
try body(seal.fulfill)
|
||||
}
|
||||
}
|
||||
|
||||
public extension Promise {
|
||||
@available(*, deprecated, message: "See `ensure`")
|
||||
func always(on q: DispatchQueue = .main, execute body: @escaping () -> Void) -> Promise {
|
||||
return ensure(on: q, body)
|
||||
}
|
||||
}
|
||||
|
||||
public extension Thenable {
|
||||
#if PMKFullDeprecations
|
||||
/// disabled due to ambiguity with the other `.flatMap`
|
||||
@available(*, deprecated, message: "See: `compactMap`")
|
||||
func flatMap<U>(on: DispatchQueue? = conf.Q.map, _ transform: @escaping(T) throws -> U?) -> Promise<U> {
|
||||
return compactMap(on: on, transform)
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public extension Thenable where T: Sequence {
|
||||
#if PMKFullDeprecations
|
||||
/// disabled due to ambiguity with the other `.map`
|
||||
@available(*, deprecated, message: "See: `mapValues`")
|
||||
func map<U>(on: DispatchQueue? = conf.Q.map, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U]> {
|
||||
return mapValues(on: on, transform)
|
||||
}
|
||||
|
||||
/// disabled due to ambiguity with the other `.flatMap`
|
||||
@available(*, deprecated, message: "See: `flatMapValues`")
|
||||
func flatMap<U: Sequence>(on: DispatchQueue? = conf.Q.map, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.Iterator.Element]> {
|
||||
return flatMapValues(on: on, transform)
|
||||
}
|
||||
#endif
|
||||
|
||||
@available(*, deprecated, message: "See: `filterValues`")
|
||||
func filter(on: DispatchQueue? = conf.Q.map, test: @escaping (T.Iterator.Element) -> Bool) -> Promise<[T.Iterator.Element]> {
|
||||
return filterValues(on: on, test)
|
||||
}
|
||||
}
|
||||
|
||||
public extension Thenable where T: Collection {
|
||||
@available(*, deprecated, message: "See: `firstValue`")
|
||||
var first: Promise<T.Iterator.Element> {
|
||||
return firstValue
|
||||
}
|
||||
|
||||
@available(*, deprecated, message: "See: `lastValue`")
|
||||
var last: Promise<T.Iterator.Element> {
|
||||
return lastValue
|
||||
}
|
||||
}
|
||||
|
||||
public extension Thenable where T: Sequence, T.Iterator.Element: Comparable {
|
||||
@available(*, deprecated, message: "See: `sortedValues`")
|
||||
func sorted(on: DispatchQueue? = conf.Q.map) -> Promise<[T.Iterator.Element]> {
|
||||
return sortedValues(on: on)
|
||||
}
|
||||
}
|
111
Pods/PromiseKit/Sources/Error.swift
generated
Normal file
111
Pods/PromiseKit/Sources/Error.swift
generated
Normal file
@ -0,0 +1,111 @@
|
||||
import Foundation
|
||||
|
||||
public enum PMKError: Error {
|
||||
/**
|
||||
The completionHandler with form `(T?, Error?)` was called with `(nil, nil)`.
|
||||
This is invalid as per Cocoa/Apple calling conventions.
|
||||
*/
|
||||
case invalidCallingConvention
|
||||
|
||||
/**
|
||||
A handler returned its own promise. 99% of the time, this is likely a
|
||||
programming error. It is also invalid per Promises/A+.
|
||||
*/
|
||||
case returnedSelf
|
||||
|
||||
/** `when()`, `race()` etc. were called with invalid parameters, eg. an empty array. */
|
||||
case badInput
|
||||
|
||||
/// The operation was cancelled
|
||||
case cancelled
|
||||
|
||||
/// `nil` was returned from `flatMap`
|
||||
@available(*, deprecated, message: "See: `compactMap`")
|
||||
case flatMap(Any, Any.Type)
|
||||
|
||||
/// `nil` was returned from `compactMap`
|
||||
case compactMap(Any, Any.Type)
|
||||
|
||||
/**
|
||||
The lastValue or firstValue of a sequence was requested but the sequence was empty.
|
||||
|
||||
Also used if all values of this collection failed the test passed to `firstValue(where:)`.
|
||||
*/
|
||||
case emptySequence
|
||||
|
||||
/// no winner in `race(fulfilled:)`
|
||||
case noWinner
|
||||
}
|
||||
|
||||
extension PMKError: CustomDebugStringConvertible {
|
||||
public var debugDescription: String {
|
||||
switch self {
|
||||
case .flatMap(let obj, let type):
|
||||
return "Could not `flatMap<\(type)>`: \(obj)"
|
||||
case .compactMap(let obj, let type):
|
||||
return "Could not `compactMap<\(type)>`: \(obj)"
|
||||
case .invalidCallingConvention:
|
||||
return "A closure was called with an invalid calling convention, probably (nil, nil)"
|
||||
case .returnedSelf:
|
||||
return "A promise handler returned itself"
|
||||
case .badInput:
|
||||
return "Bad input was provided to a PromiseKit function"
|
||||
case .cancelled:
|
||||
return "The asynchronous sequence was cancelled"
|
||||
case .emptySequence:
|
||||
return "The first or last element was requested for an empty sequence"
|
||||
case .noWinner:
|
||||
return "All thenables passed to race(fulfilled:) were rejected"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension PMKError: LocalizedError {
|
||||
public var errorDescription: String? {
|
||||
return debugDescription
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////// Cancellation
|
||||
|
||||
/// An error that may represent the cancelled condition
|
||||
public protocol CancellableError: Error {
|
||||
/// returns true if this Error represents a cancelled condition
|
||||
var isCancelled: Bool { get }
|
||||
}
|
||||
|
||||
extension Error {
|
||||
public var isCancelled: Bool {
|
||||
do {
|
||||
throw self
|
||||
} catch PMKError.cancelled {
|
||||
return true
|
||||
} catch let error as CancellableError {
|
||||
return error.isCancelled
|
||||
} catch URLError.cancelled {
|
||||
return true
|
||||
} catch CocoaError.userCancelled {
|
||||
return true
|
||||
} catch let error as NSError {
|
||||
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
|
||||
let domain = error.domain
|
||||
let code = error.code
|
||||
return ("SKErrorDomain", 2) == (domain, code)
|
||||
#else
|
||||
return false
|
||||
#endif
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Used by `catch` and `recover`
|
||||
public enum CatchPolicy {
|
||||
/// Indicates that `catch` or `recover` handle all error types including cancellable-errors.
|
||||
case allErrors
|
||||
|
||||
/// Indicates that `catch` or `recover` handle all error except cancellable-errors.
|
||||
case allErrorsExceptCancellation
|
||||
}
|
390
Pods/PromiseKit/Sources/Guarantee.swift
generated
Normal file
390
Pods/PromiseKit/Sources/Guarantee.swift
generated
Normal file
@ -0,0 +1,390 @@
|
||||
import class Foundation.Thread
|
||||
import Dispatch
|
||||
|
||||
/**
|
||||
A `Guarantee` is a functional abstraction around an asynchronous operation that cannot error.
|
||||
- See: `Thenable`
|
||||
*/
|
||||
public final class Guarantee<T>: Thenable {
|
||||
let box: PromiseKit.Box<T>
|
||||
|
||||
fileprivate init(box: SealedBox<T>) {
|
||||
self.box = box
|
||||
}
|
||||
|
||||
/// Returns a `Guarantee` sealed with the provided value.
|
||||
public static func value(_ value: T) -> Guarantee<T> {
|
||||
return .init(box: SealedBox(value: value))
|
||||
}
|
||||
|
||||
/// Returns a pending `Guarantee` that can be resolved with the provided closure’s parameter.
|
||||
public init(resolver body: (@escaping(T) -> Void) -> Void) {
|
||||
box = Box()
|
||||
body(box.seal)
|
||||
}
|
||||
|
||||
/// - See: `Thenable.pipe`
|
||||
public func pipe(to: @escaping(Result<T>) -> Void) {
|
||||
pipe{ to(.fulfilled($0)) }
|
||||
}
|
||||
|
||||
func pipe(to: @escaping(T) -> Void) {
|
||||
switch box.inspect() {
|
||||
case .pending:
|
||||
box.inspect {
|
||||
switch $0 {
|
||||
case .pending(let handlers):
|
||||
handlers.append(to)
|
||||
case .resolved(let value):
|
||||
to(value)
|
||||
}
|
||||
}
|
||||
case .resolved(let value):
|
||||
to(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// - See: `Thenable.result`
|
||||
public var result: Result<T>? {
|
||||
switch box.inspect() {
|
||||
case .pending:
|
||||
return nil
|
||||
case .resolved(let value):
|
||||
return .fulfilled(value)
|
||||
}
|
||||
}
|
||||
|
||||
final private class Box<T>: EmptyBox<T> {
|
||||
deinit {
|
||||
switch inspect() {
|
||||
case .pending:
|
||||
PromiseKit.conf.logHandler(.pendingGuaranteeDeallocated)
|
||||
case .resolved:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init(_: PMKUnambiguousInitializer) {
|
||||
box = Box()
|
||||
}
|
||||
|
||||
/// Returns a tuple of a pending `Guarantee` and a function that resolves it.
|
||||
public class func pending() -> (guarantee: Guarantee<T>, resolve: (T) -> Void) {
|
||||
return { ($0, $0.box.seal) }(Guarantee<T>(.pending))
|
||||
}
|
||||
}
|
||||
|
||||
public extension Guarantee {
|
||||
@discardableResult
|
||||
func done(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> Void) -> Guarantee<Void> {
|
||||
let rg = Guarantee<Void>(.pending)
|
||||
pipe { (value: T) in
|
||||
on.async(flags: flags) {
|
||||
body(value)
|
||||
rg.box.seal(())
|
||||
}
|
||||
}
|
||||
return rg
|
||||
}
|
||||
|
||||
func get(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (T) -> Void) -> Guarantee<T> {
|
||||
return map(on: on, flags: flags) {
|
||||
body($0)
|
||||
return $0
|
||||
}
|
||||
}
|
||||
|
||||
func map<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> U) -> Guarantee<U> {
|
||||
let rg = Guarantee<U>(.pending)
|
||||
pipe { value in
|
||||
on.async(flags: flags) {
|
||||
rg.box.seal(body(value))
|
||||
}
|
||||
}
|
||||
return rg
|
||||
}
|
||||
|
||||
#if swift(>=4) && !swift(>=5.2)
|
||||
func map<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath<T, U>) -> Guarantee<U> {
|
||||
let rg = Guarantee<U>(.pending)
|
||||
pipe { value in
|
||||
on.async(flags: flags) {
|
||||
rg.box.seal(value[keyPath: keyPath])
|
||||
}
|
||||
}
|
||||
return rg
|
||||
}
|
||||
#endif
|
||||
|
||||
@discardableResult
|
||||
func then<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) -> Guarantee<U>) -> Guarantee<U> {
|
||||
let rg = Guarantee<U>(.pending)
|
||||
pipe { value in
|
||||
on.async(flags: flags) {
|
||||
body(value).pipe(to: rg.box.seal)
|
||||
}
|
||||
}
|
||||
return rg
|
||||
}
|
||||
|
||||
func asVoid() -> Guarantee<Void> {
|
||||
return map(on: nil) { _ in }
|
||||
}
|
||||
|
||||
/**
|
||||
Blocks this thread, so you know, don’t call this on a serial thread that
|
||||
any part of your chain may use. Like the main thread for example.
|
||||
*/
|
||||
func wait() -> T {
|
||||
|
||||
if Thread.isMainThread {
|
||||
conf.logHandler(.waitOnMainThread)
|
||||
}
|
||||
|
||||
var result = value
|
||||
|
||||
if result == nil {
|
||||
let group = DispatchGroup()
|
||||
group.enter()
|
||||
pipe { (foo: T) in result = foo; group.leave() }
|
||||
group.wait()
|
||||
}
|
||||
|
||||
return result!
|
||||
}
|
||||
}
|
||||
|
||||
public extension Guarantee where T: Sequence {
|
||||
/**
|
||||
`Guarantee<[T]>` => `T` -> `U` => `Guarantee<[U]>`
|
||||
|
||||
Guarantee.value([1,2,3])
|
||||
.mapValues { integer in integer * 2 }
|
||||
.done {
|
||||
// $0 => [2,4,6]
|
||||
}
|
||||
*/
|
||||
func mapValues<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) -> U) -> Guarantee<[U]> {
|
||||
return map(on: on, flags: flags) { $0.map(transform) }
|
||||
}
|
||||
|
||||
#if swift(>=4) && !swift(>=5.2)
|
||||
/**
|
||||
`Guarantee<[T]>` => `KeyPath<T, U>` => `Guarantee<[U]>`
|
||||
|
||||
Guarantee.value([Person(name: "Max"), Person(name: "Roman"), Person(name: "John")])
|
||||
.mapValues(\.name)
|
||||
.done {
|
||||
// $0 => ["Max", "Roman", "John"]
|
||||
}
|
||||
*/
|
||||
func mapValues<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath<T.Iterator.Element, U>) -> Guarantee<[U]> {
|
||||
return map(on: on, flags: flags) { $0.map { $0[keyPath: keyPath] } }
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
`Guarantee<[T]>` => `T` -> `[U]` => `Guarantee<[U]>`
|
||||
|
||||
Guarantee.value([1,2,3])
|
||||
.flatMapValues { integer in [integer, integer] }
|
||||
.done {
|
||||
// $0 => [1,1,2,2,3,3]
|
||||
}
|
||||
*/
|
||||
func flatMapValues<U: Sequence>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) -> U) -> Guarantee<[U.Iterator.Element]> {
|
||||
return map(on: on, flags: flags) { (foo: T) in
|
||||
foo.flatMap { transform($0) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
`Guarantee<[T]>` => `T` -> `U?` => `Guarantee<[U]>`
|
||||
|
||||
Guarantee.value(["1","2","a","3"])
|
||||
.compactMapValues { Int($0) }
|
||||
.done {
|
||||
// $0 => [1,2,3]
|
||||
}
|
||||
*/
|
||||
func compactMapValues<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) -> U?) -> Guarantee<[U]> {
|
||||
return map(on: on, flags: flags) { foo -> [U] in
|
||||
#if !swift(>=3.3) || (swift(>=4) && !swift(>=4.1))
|
||||
return foo.flatMap(transform)
|
||||
#else
|
||||
return foo.compactMap(transform)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=4) && !swift(>=5.2)
|
||||
/**
|
||||
`Guarantee<[T]>` => `KeyPath<T, U?>` => `Guarantee<[U]>`
|
||||
|
||||
Guarantee.value([Person(name: "Max"), Person(name: "Roman", age: 26), Person(name: "John", age: 23)])
|
||||
.compactMapValues(\.age)
|
||||
.done {
|
||||
// $0 => [26, 23]
|
||||
}
|
||||
*/
|
||||
func compactMapValues<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath<T.Iterator.Element, U?>) -> Guarantee<[U]> {
|
||||
return map(on: on, flags: flags) { foo -> [U] in
|
||||
#if !swift(>=4.1)
|
||||
return foo.flatMap { $0[keyPath: keyPath] }
|
||||
#else
|
||||
return foo.compactMap { $0[keyPath: keyPath] }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
`Guarantee<[T]>` => `T` -> `Guarantee<U>` => `Guaranetee<[U]>`
|
||||
|
||||
Guarantee.value([1,2,3])
|
||||
.thenMap { .value($0 * 2) }
|
||||
.done {
|
||||
// $0 => [2,4,6]
|
||||
}
|
||||
*/
|
||||
func thenMap<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) -> Guarantee<U>) -> Guarantee<[U]> {
|
||||
return then(on: on, flags: flags) {
|
||||
when(fulfilled: $0.map(transform))
|
||||
}.recover {
|
||||
// if happens then is bug inside PromiseKit
|
||||
fatalError(String(describing: $0))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
`Guarantee<[T]>` => `T` -> `Guarantee<[U]>` => `Guarantee<[U]>`
|
||||
|
||||
Guarantee.value([1,2,3])
|
||||
.thenFlatMap { integer in .value([integer, integer]) }
|
||||
.done {
|
||||
// $0 => [1,1,2,2,3,3]
|
||||
}
|
||||
*/
|
||||
func thenFlatMap<U: Thenable>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) -> U) -> Guarantee<[U.T.Iterator.Element]> where U.T: Sequence {
|
||||
return then(on: on, flags: flags) {
|
||||
when(fulfilled: $0.map(transform))
|
||||
}.map(on: nil) {
|
||||
$0.flatMap { $0 }
|
||||
}.recover {
|
||||
// if happens then is bug inside PromiseKit
|
||||
fatalError(String(describing: $0))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
`Guarantee<[T]>` => `T` -> Bool => `Guarantee<[T]>`
|
||||
|
||||
Guarantee.value([1,2,3])
|
||||
.filterValues { $0 > 1 }
|
||||
.done {
|
||||
// $0 => [2,3]
|
||||
}
|
||||
*/
|
||||
func filterValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ isIncluded: @escaping(T.Iterator.Element) -> Bool) -> Guarantee<[T.Iterator.Element]> {
|
||||
return map(on: on, flags: flags) {
|
||||
$0.filter(isIncluded)
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=4) && !swift(>=5.2)
|
||||
/**
|
||||
`Guarantee<[T]>` => `KeyPath<T, Bool>` => `Guarantee<[T]>`
|
||||
|
||||
Guarantee.value([Person(name: "Max"), Person(name: "Roman", age: 26, isStudent: false), Person(name: "John", age: 23, isStudent: true)])
|
||||
.filterValues(\.isStudent)
|
||||
.done {
|
||||
// $0 => [Person(name: "John", age: 23, isStudent: true)]
|
||||
}
|
||||
*/
|
||||
func filterValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath<T.Iterator.Element, Bool>) -> Guarantee<[T.Iterator.Element]> {
|
||||
return map(on: on, flags: flags) {
|
||||
$0.filter { $0[keyPath: keyPath] }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
`Guarantee<[T]>` => (`T`, `T`) -> Bool => `Guarantee<[T]>`
|
||||
|
||||
Guarantee.value([5,2,3,4,1])
|
||||
.sortedValues { $0 > $1 }
|
||||
.done {
|
||||
// $0 => [5,4,3,2,1]
|
||||
}
|
||||
*/
|
||||
func sortedValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ areInIncreasingOrder: @escaping(T.Iterator.Element, T.Iterator.Element) -> Bool) -> Guarantee<[T.Iterator.Element]> {
|
||||
return map(on: on, flags: flags) {
|
||||
$0.sorted(by: areInIncreasingOrder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Guarantee where T: Sequence, T.Iterator.Element: Comparable {
|
||||
/**
|
||||
`Guarantee<[T]>` => `Guarantee<[T]>`
|
||||
|
||||
Guarantee.value([5,2,3,4,1])
|
||||
.sortedValues()
|
||||
.done {
|
||||
// $0 => [1,2,3,4,5]
|
||||
}
|
||||
*/
|
||||
func sortedValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil) -> Guarantee<[T.Iterator.Element]> {
|
||||
return map(on: on, flags: flags) { $0.sorted() }
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=3.1)
|
||||
public extension Guarantee where T == Void {
|
||||
convenience init() {
|
||||
self.init(box: SealedBox(value: Void()))
|
||||
}
|
||||
|
||||
static var value: Guarantee<Void> {
|
||||
return .value(Void())
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
public extension DispatchQueue {
|
||||
/**
|
||||
Asynchronously executes the provided closure on a dispatch queue.
|
||||
|
||||
DispatchQueue.global().async(.promise) {
|
||||
md5(input)
|
||||
}.done { md5 in
|
||||
//…
|
||||
}
|
||||
|
||||
- Parameter body: The closure that resolves this promise.
|
||||
- Returns: A new `Guarantee` resolved by the result of the provided closure.
|
||||
- Note: There is no Promise/Thenable version of this due to Swift compiler ambiguity issues.
|
||||
*/
|
||||
@available(macOS 10.10, iOS 2.0, tvOS 10.0, watchOS 2.0, *)
|
||||
final func async<T>(_: PMKNamespacer, group: DispatchGroup? = nil, qos: DispatchQoS = .default, flags: DispatchWorkItemFlags = [], execute body: @escaping () -> T) -> Guarantee<T> {
|
||||
let rg = Guarantee<T>(.pending)
|
||||
async(group: group, qos: qos, flags: flags) {
|
||||
rg.box.seal(body())
|
||||
}
|
||||
return rg
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if os(Linux)
|
||||
import func CoreFoundation._CFIsMainThread
|
||||
|
||||
extension Thread {
|
||||
// `isMainThread` is not implemented yet in swift-corelibs-foundation.
|
||||
static var isMainThread: Bool {
|
||||
return _CFIsMainThread()
|
||||
}
|
||||
}
|
||||
#endif
|
30
Pods/PromiseKit/Sources/LogEvent.swift
generated
Normal file
30
Pods/PromiseKit/Sources/LogEvent.swift
generated
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
The PromiseKit events which may be logged.
|
||||
|
||||
````
|
||||
/// A promise or guarantee has blocked the main thread
|
||||
case waitOnMainThread
|
||||
|
||||
/// A promise has been deallocated without being resolved
|
||||
case pendingPromiseDeallocated
|
||||
|
||||
/// An error which occurred while fulfilling a promise was swallowed
|
||||
case cauterized(Error)
|
||||
|
||||
/// Errors which give a string error message
|
||||
case misc (String)
|
||||
````
|
||||
*/
|
||||
public enum LogEvent {
|
||||
/// A promise or guarantee has blocked the main thread
|
||||
case waitOnMainThread
|
||||
|
||||
/// A promise has been deallocated without being resolved
|
||||
case pendingPromiseDeallocated
|
||||
|
||||
/// A guarantee has been deallocated without being resolved
|
||||
case pendingGuaranteeDeallocated
|
||||
|
||||
/// An error which occurred while resolving a promise was swallowed
|
||||
case cauterized(Error)
|
||||
}
|
77
Pods/PromiseKit/Sources/NSMethodSignatureForBlock.m
generated
Normal file
77
Pods/PromiseKit/Sources/NSMethodSignatureForBlock.m
generated
Normal file
@ -0,0 +1,77 @@
|
||||
#import <Foundation/NSMethodSignature.h>
|
||||
|
||||
struct PMKBlockLiteral {
|
||||
void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
|
||||
int flags;
|
||||
int reserved;
|
||||
void (*invoke)(void *, ...);
|
||||
struct block_descriptor {
|
||||
unsigned long int reserved; // NULL
|
||||
unsigned long int size; // sizeof(struct Block_literal_1)
|
||||
// optional helper functions
|
||||
void (*copy_helper)(void *dst, void *src); // IFF (1<<25)
|
||||
void (*dispose_helper)(void *src); // IFF (1<<25)
|
||||
// required ABI.2010.3.16
|
||||
const char *signature; // IFF (1<<30)
|
||||
} *descriptor;
|
||||
// imported variables
|
||||
};
|
||||
|
||||
typedef NS_OPTIONS(NSUInteger, PMKBlockDescriptionFlags) {
|
||||
PMKBlockDescriptionFlagsHasCopyDispose = (1 << 25),
|
||||
PMKBlockDescriptionFlagsHasCtor = (1 << 26), // helpers have C++ code
|
||||
PMKBlockDescriptionFlagsIsGlobal = (1 << 28),
|
||||
PMKBlockDescriptionFlagsHasStret = (1 << 29), // IFF BLOCK_HAS_SIGNATURE
|
||||
PMKBlockDescriptionFlagsHasSignature = (1 << 30)
|
||||
};
|
||||
|
||||
// It appears 10.7 doesn't support quotes in method signatures. Remove them
|
||||
// via @rabovik's method. See https://github.com/OliverLetterer/SLObjectiveCRuntimeAdditions/pull/2
|
||||
#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_8
|
||||
NS_INLINE static const char * pmk_removeQuotesFromMethodSignature(const char *str){
|
||||
char *result = malloc(strlen(str) + 1);
|
||||
BOOL skip = NO;
|
||||
char *to = result;
|
||||
char c;
|
||||
while ((c = *str++)) {
|
||||
if ('"' == c) {
|
||||
skip = !skip;
|
||||
continue;
|
||||
}
|
||||
if (skip) continue;
|
||||
*to++ = c;
|
||||
}
|
||||
*to = '\0';
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
static NSMethodSignature *NSMethodSignatureForBlock(id block) {
|
||||
if (!block)
|
||||
return nil;
|
||||
|
||||
struct PMKBlockLiteral *blockRef = (__bridge struct PMKBlockLiteral *)block;
|
||||
PMKBlockDescriptionFlags flags = (PMKBlockDescriptionFlags)blockRef->flags;
|
||||
|
||||
if (flags & PMKBlockDescriptionFlagsHasSignature) {
|
||||
void *signatureLocation = blockRef->descriptor;
|
||||
signatureLocation += sizeof(unsigned long int);
|
||||
signatureLocation += sizeof(unsigned long int);
|
||||
|
||||
if (flags & PMKBlockDescriptionFlagsHasCopyDispose) {
|
||||
signatureLocation += sizeof(void(*)(void *dst, void *src));
|
||||
signatureLocation += sizeof(void (*)(void *src));
|
||||
}
|
||||
|
||||
const char *signature = (*(const char **)signatureLocation);
|
||||
#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_8
|
||||
signature = pmk_removeQuotesFromMethodSignature(signature);
|
||||
NSMethodSignature *nsSignature = [NSMethodSignature signatureWithObjCTypes:signature];
|
||||
free((void *)signature);
|
||||
|
||||
return nsSignature;
|
||||
#endif
|
||||
return [NSMethodSignature signatureWithObjCTypes:signature];
|
||||
}
|
||||
return 0;
|
||||
}
|
120
Pods/PromiseKit/Sources/PMKCallVariadicBlock.m
generated
Normal file
120
Pods/PromiseKit/Sources/PMKCallVariadicBlock.m
generated
Normal file
@ -0,0 +1,120 @@
|
||||
#import "NSMethodSignatureForBlock.m"
|
||||
#import <Foundation/NSDictionary.h>
|
||||
#import <Foundation/NSException.h>
|
||||
#import "AnyPromise+Private.h"
|
||||
#import <Foundation/NSError.h>
|
||||
#import <dispatch/once.h>
|
||||
#import <string.h>
|
||||
|
||||
#ifndef PMKLog
|
||||
#define PMKLog NSLog
|
||||
#endif
|
||||
|
||||
@interface PMKArray : NSObject {
|
||||
@public
|
||||
id objs[3];
|
||||
NSUInteger count;
|
||||
} @end
|
||||
|
||||
@implementation PMKArray
|
||||
|
||||
- (id)objectAtIndexedSubscript:(NSUInteger)idx {
|
||||
if (count <= idx) {
|
||||
// this check is necessary due to lack of checks in `pmk_safely_call_block`
|
||||
return nil;
|
||||
}
|
||||
return objs[idx];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
id __PMKArrayWithCount(NSUInteger count, ...) {
|
||||
PMKArray *this = [PMKArray new];
|
||||
this->count = count;
|
||||
va_list args;
|
||||
va_start(args, count);
|
||||
for (NSUInteger x = 0; x < count; ++x)
|
||||
this->objs[x] = va_arg(args, id);
|
||||
va_end(args);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
static inline id _PMKCallVariadicBlock(id frock, id result) {
|
||||
NSCAssert(frock, @"");
|
||||
|
||||
NSMethodSignature *sig = NSMethodSignatureForBlock(frock);
|
||||
const NSUInteger nargs = sig.numberOfArguments;
|
||||
const char rtype = sig.methodReturnType[0];
|
||||
|
||||
#define call_block_with_rtype(type) ({^type{ \
|
||||
switch (nargs) { \
|
||||
case 1: \
|
||||
return ((type(^)(void))frock)(); \
|
||||
case 2: { \
|
||||
const id arg = [result class] == [PMKArray class] ? result[0] : result; \
|
||||
return ((type(^)(id))frock)(arg); \
|
||||
} \
|
||||
case 3: { \
|
||||
type (^block)(id, id) = frock; \
|
||||
return [result class] == [PMKArray class] \
|
||||
? block(result[0], result[1]) \
|
||||
: block(result, nil); \
|
||||
} \
|
||||
case 4: { \
|
||||
type (^block)(id, id, id) = frock; \
|
||||
return [result class] == [PMKArray class] \
|
||||
? block(result[0], result[1], result[2]) \
|
||||
: block(result, nil, nil); \
|
||||
} \
|
||||
default: \
|
||||
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"PromiseKit: The provided block’s argument count is unsupported." userInfo:nil]; \
|
||||
}}();})
|
||||
|
||||
switch (rtype) {
|
||||
case 'v':
|
||||
call_block_with_rtype(void);
|
||||
return nil;
|
||||
case '@':
|
||||
return call_block_with_rtype(id) ?: nil;
|
||||
case '*': {
|
||||
char *str = call_block_with_rtype(char *);
|
||||
return str ? @(str) : nil;
|
||||
}
|
||||
case 'c': return @(call_block_with_rtype(char));
|
||||
case 'i': return @(call_block_with_rtype(int));
|
||||
case 's': return @(call_block_with_rtype(short));
|
||||
case 'l': return @(call_block_with_rtype(long));
|
||||
case 'q': return @(call_block_with_rtype(long long));
|
||||
case 'C': return @(call_block_with_rtype(unsigned char));
|
||||
case 'I': return @(call_block_with_rtype(unsigned int));
|
||||
case 'S': return @(call_block_with_rtype(unsigned short));
|
||||
case 'L': return @(call_block_with_rtype(unsigned long));
|
||||
case 'Q': return @(call_block_with_rtype(unsigned long long));
|
||||
case 'f': return @(call_block_with_rtype(float));
|
||||
case 'd': return @(call_block_with_rtype(double));
|
||||
case 'B': return @(call_block_with_rtype(_Bool));
|
||||
case '^':
|
||||
if (strcmp(sig.methodReturnType, "^v") == 0) {
|
||||
call_block_with_rtype(void);
|
||||
return nil;
|
||||
}
|
||||
// else fall through!
|
||||
default:
|
||||
@throw [NSException exceptionWithName:@"PromiseKit" reason:@"PromiseKit: Unsupported method signature." userInfo:nil];
|
||||
}
|
||||
}
|
||||
|
||||
static id PMKCallVariadicBlock(id frock, id result) {
|
||||
@try {
|
||||
return _PMKCallVariadicBlock(frock, result);
|
||||
} @catch (id thrown) {
|
||||
if ([thrown isKindOfClass:[NSString class]])
|
||||
return thrown;
|
||||
if ([thrown isKindOfClass:[NSError class]])
|
||||
return thrown;
|
||||
|
||||
// we don’t catch objc exceptions: they are meant to crash your app
|
||||
@throw thrown;
|
||||
}
|
||||
}
|
184
Pods/PromiseKit/Sources/Promise.swift
generated
Normal file
184
Pods/PromiseKit/Sources/Promise.swift
generated
Normal file
@ -0,0 +1,184 @@
|
||||
import class Foundation.Thread
|
||||
import Dispatch
|
||||
|
||||
/**
|
||||
A `Promise` is a functional abstraction around a failable asynchronous operation.
|
||||
- See: `Thenable`
|
||||
*/
|
||||
public final class Promise<T>: Thenable, CatchMixin {
|
||||
let box: Box<Result<T>>
|
||||
|
||||
fileprivate init(box: SealedBox<Result<T>>) {
|
||||
self.box = box
|
||||
}
|
||||
|
||||
/**
|
||||
Initialize a new fulfilled promise.
|
||||
|
||||
We do not provide `init(value:)` because Swift is “greedy”
|
||||
and would pick that initializer in cases where it should pick
|
||||
one of the other more specific options leading to Promises with
|
||||
`T` that is eg: `Error` or worse `(T->Void,Error->Void)` for
|
||||
uses of our PMK < 4 pending initializer due to Swift trailing
|
||||
closure syntax (nothing good comes without pain!).
|
||||
|
||||
Though often easy to detect, sometimes these issues would be
|
||||
hidden by other type inference leading to some nasty bugs in
|
||||
production.
|
||||
|
||||
In PMK5 we tried to work around this by making the pending
|
||||
initializer take the form `Promise(.pending)` but this led to
|
||||
bad migration errors for PMK4 users. Hence instead we quickly
|
||||
released PMK6 and now only provide this initializer for making
|
||||
sealed & fulfilled promises.
|
||||
|
||||
Usage is still (usually) good:
|
||||
|
||||
guard foo else {
|
||||
return .value(bar)
|
||||
}
|
||||
*/
|
||||
public static func value(_ value: T) -> Promise<T> {
|
||||
return Promise(box: SealedBox(value: .fulfilled(value)))
|
||||
}
|
||||
|
||||
/// Initialize a new rejected promise.
|
||||
public init(error: Error) {
|
||||
box = SealedBox(value: .rejected(error))
|
||||
}
|
||||
|
||||
/// Initialize a new promise bound to the provided `Thenable`.
|
||||
public init<U: Thenable>(_ bridge: U) where U.T == T {
|
||||
box = EmptyBox()
|
||||
bridge.pipe(to: box.seal)
|
||||
}
|
||||
|
||||
/// Initialize a new promise that can be resolved with the provided `Resolver`.
|
||||
public init(resolver body: (Resolver<T>) throws -> Void) {
|
||||
box = EmptyBox()
|
||||
let resolver = Resolver(box)
|
||||
do {
|
||||
try body(resolver)
|
||||
} catch {
|
||||
resolver.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
/// - Returns: a tuple of a new pending promise and its `Resolver`.
|
||||
public class func pending() -> (promise: Promise<T>, resolver: Resolver<T>) {
|
||||
return { ($0, Resolver($0.box)) }(Promise<T>(.pending))
|
||||
}
|
||||
|
||||
/// - See: `Thenable.pipe`
|
||||
public func pipe(to: @escaping(Result<T>) -> Void) {
|
||||
switch box.inspect() {
|
||||
case .pending:
|
||||
box.inspect {
|
||||
switch $0 {
|
||||
case .pending(let handlers):
|
||||
handlers.append(to)
|
||||
case .resolved(let value):
|
||||
to(value)
|
||||
}
|
||||
}
|
||||
case .resolved(let value):
|
||||
to(value)
|
||||
}
|
||||
}
|
||||
|
||||
/// - See: `Thenable.result`
|
||||
public var result: Result<T>? {
|
||||
switch box.inspect() {
|
||||
case .pending:
|
||||
return nil
|
||||
case .resolved(let result):
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
init(_: PMKUnambiguousInitializer) {
|
||||
box = EmptyBox()
|
||||
}
|
||||
}
|
||||
|
||||
public extension Promise {
|
||||
/**
|
||||
Blocks this thread, so—you know—don’t call this on a serial thread that
|
||||
any part of your chain may use. Like the main thread for example.
|
||||
*/
|
||||
func wait() throws -> T {
|
||||
|
||||
if Thread.isMainThread {
|
||||
conf.logHandler(LogEvent.waitOnMainThread)
|
||||
}
|
||||
|
||||
var result = self.result
|
||||
|
||||
if result == nil {
|
||||
let group = DispatchGroup()
|
||||
group.enter()
|
||||
pipe { result = $0; group.leave() }
|
||||
group.wait()
|
||||
}
|
||||
|
||||
switch result! {
|
||||
case .rejected(let error):
|
||||
throw error
|
||||
case .fulfilled(let value):
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=3.1)
|
||||
extension Promise where T == Void {
|
||||
/// Initializes a new promise fulfilled with `Void`
|
||||
public convenience init() {
|
||||
self.init(box: SealedBox(value: .fulfilled(Void())))
|
||||
}
|
||||
|
||||
/// Returns a new promise fulfilled with `Void`
|
||||
public static var value: Promise<Void> {
|
||||
return .value(Void())
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
public extension DispatchQueue {
|
||||
/**
|
||||
Asynchronously executes the provided closure on a dispatch queue.
|
||||
|
||||
DispatchQueue.global().async(.promise) {
|
||||
try md5(input)
|
||||
}.done { md5 in
|
||||
//…
|
||||
}
|
||||
|
||||
- Parameter body: The closure that resolves this promise.
|
||||
- Returns: A new `Promise` resolved by the result of the provided closure.
|
||||
- Note: There is no Promise/Thenable version of this due to Swift compiler ambiguity issues.
|
||||
*/
|
||||
@available(macOS 10.10, iOS 8.0, tvOS 9.0, watchOS 2.0, *)
|
||||
final func async<T>(_: PMKNamespacer, group: DispatchGroup? = nil, qos: DispatchQoS = .default, flags: DispatchWorkItemFlags = [], execute body: @escaping () throws -> T) -> Promise<T> {
|
||||
let promise = Promise<T>(.pending)
|
||||
async(group: group, qos: qos, flags: flags) {
|
||||
do {
|
||||
promise.box.seal(.fulfilled(try body()))
|
||||
} catch {
|
||||
promise.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
return promise
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// used by our extensions to provide unambiguous functions with the same name as the original function
|
||||
public enum PMKNamespacer {
|
||||
case promise
|
||||
}
|
||||
|
||||
enum PMKUnambiguousInitializer {
|
||||
case pending
|
||||
}
|
7
Pods/PromiseKit/Sources/PromiseKit.h
generated
Normal file
7
Pods/PromiseKit/Sources/PromiseKit.h
generated
Normal file
@ -0,0 +1,7 @@
|
||||
#import <PromiseKit/fwd.h>
|
||||
#import <PromiseKit/AnyPromise.h>
|
||||
|
||||
#import <Foundation/NSObjCRuntime.h> // `FOUNDATION_EXPORT`
|
||||
|
||||
FOUNDATION_EXPORT double PromiseKitVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char PromiseKitVersionString[];
|
111
Pods/PromiseKit/Sources/Resolver.swift
generated
Normal file
111
Pods/PromiseKit/Sources/Resolver.swift
generated
Normal file
@ -0,0 +1,111 @@
|
||||
/// An object for resolving promises
|
||||
public final class Resolver<T> {
|
||||
let box: Box<Result<T>>
|
||||
|
||||
init(_ box: Box<Result<T>>) {
|
||||
self.box = box
|
||||
}
|
||||
|
||||
deinit {
|
||||
if case .pending = box.inspect() {
|
||||
conf.logHandler(.pendingPromiseDeallocated)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Resolver {
|
||||
/// Fulfills the promise with the provided value
|
||||
func fulfill(_ value: T) {
|
||||
box.seal(.fulfilled(value))
|
||||
}
|
||||
|
||||
/// Rejects the promise with the provided error
|
||||
func reject(_ error: Error) {
|
||||
box.seal(.rejected(error))
|
||||
}
|
||||
|
||||
/// Resolves the promise with the provided result
|
||||
func resolve(_ result: Result<T>) {
|
||||
box.seal(result)
|
||||
}
|
||||
|
||||
/// Resolves the promise with the provided value or error
|
||||
func resolve(_ obj: T?, _ error: Error?) {
|
||||
if let error = error {
|
||||
reject(error)
|
||||
} else if let obj = obj {
|
||||
fulfill(obj)
|
||||
} else {
|
||||
reject(PMKError.invalidCallingConvention)
|
||||
}
|
||||
}
|
||||
|
||||
/// Fulfills the promise with the provided value unless the provided error is non-nil
|
||||
func resolve(_ obj: T, _ error: Error?) {
|
||||
if let error = error {
|
||||
reject(error)
|
||||
} else {
|
||||
fulfill(obj)
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolves the promise, provided for non-conventional value-error ordered completion handlers.
|
||||
func resolve(_ error: Error?, _ obj: T?) {
|
||||
resolve(obj, error)
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=3.1)
|
||||
extension Resolver where T == Void {
|
||||
/// Fulfills the promise unless error is non-nil
|
||||
public func resolve(_ error: Error?) {
|
||||
if let error = error {
|
||||
reject(error)
|
||||
} else {
|
||||
fulfill(())
|
||||
}
|
||||
}
|
||||
#if false
|
||||
// disabled ∵ https://github.com/mxcl/PromiseKit/issues/990
|
||||
|
||||
/// Fulfills the promise
|
||||
public func fulfill() {
|
||||
self.fulfill(())
|
||||
}
|
||||
#else
|
||||
/// Fulfills the promise
|
||||
/// - Note: underscore is present due to: https://github.com/mxcl/PromiseKit/issues/990
|
||||
public func fulfill_() {
|
||||
self.fulfill(())
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if swift(>=5.0)
|
||||
extension Resolver {
|
||||
/// Resolves the promise with the provided result
|
||||
public func resolve<E: Error>(_ result: Swift.Result<T, E>) {
|
||||
switch result {
|
||||
case .failure(let error): self.reject(error)
|
||||
case .success(let value): self.fulfill(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public enum Result<T> {
|
||||
case fulfilled(T)
|
||||
case rejected(Error)
|
||||
}
|
||||
|
||||
public extension PromiseKit.Result {
|
||||
var isFulfilled: Bool {
|
||||
switch self {
|
||||
case .fulfilled:
|
||||
return true
|
||||
case .rejected:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
533
Pods/PromiseKit/Sources/Thenable.swift
generated
Normal file
533
Pods/PromiseKit/Sources/Thenable.swift
generated
Normal file
@ -0,0 +1,533 @@
|
||||
import Dispatch
|
||||
|
||||
/// Thenable represents an asynchronous operation that can be chained.
|
||||
public protocol Thenable: AnyObject {
|
||||
/// The type of the wrapped value
|
||||
associatedtype T
|
||||
|
||||
/// `pipe` is immediately executed when this `Thenable` is resolved
|
||||
func pipe(to: @escaping(Result<T>) -> Void)
|
||||
|
||||
/// The resolved result or nil if pending.
|
||||
var result: Result<T>? { get }
|
||||
}
|
||||
|
||||
public extension Thenable {
|
||||
/**
|
||||
The provided closure executes when this promise is fulfilled.
|
||||
|
||||
This allows chaining promises. The promise returned by the provided closure is resolved before the promise returned by this closure resolves.
|
||||
|
||||
- Parameter on: The queue to which the provided closure dispatches.
|
||||
- Parameter body: The closure that executes when this promise is fulfilled. It must return a promise.
|
||||
- Returns: A new promise that resolves when the promise returned from the provided closure resolves. For example:
|
||||
|
||||
firstly {
|
||||
URLSession.shared.dataTask(.promise, with: url1)
|
||||
}.then { response in
|
||||
transform(data: response.data)
|
||||
}.done { transformation in
|
||||
//…
|
||||
}
|
||||
*/
|
||||
func then<U: Thenable>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> U) -> Promise<U.T> {
|
||||
let rp = Promise<U.T>(.pending)
|
||||
pipe {
|
||||
switch $0 {
|
||||
case .fulfilled(let value):
|
||||
on.async(flags: flags) {
|
||||
do {
|
||||
let rv = try body(value)
|
||||
guard rv !== rp else { throw PMKError.returnedSelf }
|
||||
rv.pipe(to: rp.box.seal)
|
||||
} catch {
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
case .rejected(let error):
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
return rp
|
||||
}
|
||||
|
||||
/**
|
||||
The provided closure is executed when this promise is fulfilled.
|
||||
|
||||
This is like `then` but it requires the closure to return a non-promise.
|
||||
|
||||
- Parameter on: The queue to which the provided closure dispatches.
|
||||
- Parameter transform: The closure that is executed when this Promise is fulfilled. It must return a non-promise.
|
||||
- Returns: A new promise that is fulfilled with the value returned from the provided closure or rejected if the provided closure throws. For example:
|
||||
|
||||
firstly {
|
||||
URLSession.shared.dataTask(.promise, with: url1)
|
||||
}.map { response in
|
||||
response.data.length
|
||||
}.done { length in
|
||||
//…
|
||||
}
|
||||
*/
|
||||
func map<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U) -> Promise<U> {
|
||||
let rp = Promise<U>(.pending)
|
||||
pipe {
|
||||
switch $0 {
|
||||
case .fulfilled(let value):
|
||||
on.async(flags: flags) {
|
||||
do {
|
||||
rp.box.seal(.fulfilled(try transform(value)))
|
||||
} catch {
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
case .rejected(let error):
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
return rp
|
||||
}
|
||||
|
||||
#if swift(>=4) && !swift(>=5.2)
|
||||
/**
|
||||
Similar to func `map<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U) -> Promise<U>`, but accepts a key path instead of a closure.
|
||||
|
||||
- Parameter on: The queue to which the provided key path for value dispatches.
|
||||
- Parameter keyPath: The key path to the value that is using when this Promise is fulfilled.
|
||||
- Returns: A new promise that is fulfilled with the value for the provided key path.
|
||||
*/
|
||||
func map<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath<T, U>) -> Promise<U> {
|
||||
let rp = Promise<U>(.pending)
|
||||
pipe {
|
||||
switch $0 {
|
||||
case .fulfilled(let value):
|
||||
on.async(flags: flags) {
|
||||
rp.box.seal(.fulfilled(value[keyPath: keyPath]))
|
||||
}
|
||||
case .rejected(let error):
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
return rp
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
The provided closure is executed when this promise is fulfilled.
|
||||
|
||||
In your closure return an `Optional`, if you return `nil` the resulting promise is rejected with `PMKError.compactMap`, otherwise the promise is fulfilled with the unwrapped value.
|
||||
|
||||
firstly {
|
||||
URLSession.shared.dataTask(.promise, with: url)
|
||||
}.compactMap {
|
||||
try JSONSerialization.jsonObject(with: $0.data) as? [String: String]
|
||||
}.done { dictionary in
|
||||
//…
|
||||
}.catch {
|
||||
// either `PMKError.compactMap` or a `JSONError`
|
||||
}
|
||||
*/
|
||||
func compactMap<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U?) -> Promise<U> {
|
||||
let rp = Promise<U>(.pending)
|
||||
pipe {
|
||||
switch $0 {
|
||||
case .fulfilled(let value):
|
||||
on.async(flags: flags) {
|
||||
do {
|
||||
if let rv = try transform(value) {
|
||||
rp.box.seal(.fulfilled(rv))
|
||||
} else {
|
||||
throw PMKError.compactMap(value, U.self)
|
||||
}
|
||||
} catch {
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
case .rejected(let error):
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
return rp
|
||||
}
|
||||
|
||||
#if swift(>=4) && !swift(>=5.2)
|
||||
/**
|
||||
Similar to func `compactMap<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T) throws -> U?) -> Promise<U>`, but accepts a key path instead of a closure.
|
||||
|
||||
- Parameter on: The queue to which the provided key path for value dispatches.
|
||||
- Parameter keyPath: The key path to the value that is using when this Promise is fulfilled. If the value for `keyPath` is `nil` the resulting promise is rejected with `PMKError.compactMap`.
|
||||
- Returns: A new promise that is fulfilled with the value for the provided key path.
|
||||
*/
|
||||
func compactMap<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath<T, U?>) -> Promise<U> {
|
||||
let rp = Promise<U>(.pending)
|
||||
pipe {
|
||||
switch $0 {
|
||||
case .fulfilled(let value):
|
||||
on.async(flags: flags) {
|
||||
do {
|
||||
if let rv = value[keyPath: keyPath] {
|
||||
rp.box.seal(.fulfilled(rv))
|
||||
} else {
|
||||
throw PMKError.compactMap(value, U.self)
|
||||
}
|
||||
} catch {
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
case .rejected(let error):
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
return rp
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
The provided closure is executed when this promise is fulfilled.
|
||||
|
||||
Equivalent to `map { x -> Void in`, but since we force the `Void` return Swift
|
||||
is happier and gives you less hassle about your closure’s qualification.
|
||||
|
||||
- Parameter on: The queue to which the provided closure dispatches.
|
||||
- Parameter body: The closure that is executed when this Promise is fulfilled.
|
||||
- Returns: A new promise fulfilled as `Void` or rejected if the provided closure throws.
|
||||
|
||||
firstly {
|
||||
URLSession.shared.dataTask(.promise, with: url)
|
||||
}.done { response in
|
||||
print(response.data)
|
||||
}
|
||||
*/
|
||||
func done(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(T) throws -> Void) -> Promise<Void> {
|
||||
let rp = Promise<Void>(.pending)
|
||||
pipe {
|
||||
switch $0 {
|
||||
case .fulfilled(let value):
|
||||
on.async(flags: flags) {
|
||||
do {
|
||||
try body(value)
|
||||
rp.box.seal(.fulfilled(()))
|
||||
} catch {
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
case .rejected(let error):
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
}
|
||||
return rp
|
||||
}
|
||||
|
||||
/**
|
||||
The provided closure is executed when this promise is fulfilled.
|
||||
|
||||
This is like `done` but it returns the same value that the handler is fed.
|
||||
`get` immutably accesses the fulfilled value; the returned Promise maintains that value.
|
||||
|
||||
- Parameter on: The queue to which the provided closure dispatches.
|
||||
- Parameter body: The closure that is executed when this Promise is fulfilled.
|
||||
- Returns: A new promise that is fulfilled with the value that the handler is fed or rejected if the provided closure throws. For example:
|
||||
|
||||
firstly {
|
||||
.value(1)
|
||||
}.get { foo in
|
||||
print(foo, " is 1")
|
||||
}.done { foo in
|
||||
print(foo, " is 1")
|
||||
}.done { foo in
|
||||
print(foo, " is Void")
|
||||
}
|
||||
*/
|
||||
func get(on: DispatchQueue? = conf.Q.return, flags: DispatchWorkItemFlags? = nil, _ body: @escaping (T) throws -> Void) -> Promise<T> {
|
||||
return map(on: on, flags: flags) {
|
||||
try body($0)
|
||||
return $0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
The provided closure is executed with promise result.
|
||||
|
||||
This is like `get` but provides the Result<T> of the Promise so you can inspect the value of the chain at this point without causing any side effects.
|
||||
|
||||
- Parameter on: The queue to which the provided closure dispatches.
|
||||
- Parameter body: The closure that is executed with Result of Promise.
|
||||
- Returns: A new promise that is resolved with the result that the handler is fed. For example:
|
||||
|
||||
promise.tap{ print($0) }.then{ /*…*/ }
|
||||
*/
|
||||
func tap(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ body: @escaping(Result<T>) -> Void) -> Promise<T> {
|
||||
return Promise { seal in
|
||||
pipe { result in
|
||||
on.async(flags: flags) {
|
||||
body(result)
|
||||
seal.resolve(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// - Returns: a new promise chained off this promise but with its value discarded.
|
||||
func asVoid() -> Promise<Void> {
|
||||
return map(on: nil) { _ in }
|
||||
}
|
||||
}
|
||||
|
||||
public extension Thenable {
|
||||
/**
|
||||
- Returns: The error with which this promise was rejected; `nil` if this promise is not rejected.
|
||||
*/
|
||||
var error: Error? {
|
||||
switch result {
|
||||
case .none:
|
||||
return nil
|
||||
case .some(.fulfilled):
|
||||
return nil
|
||||
case .some(.rejected(let error)):
|
||||
return error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
- Returns: `true` if the promise has not yet resolved.
|
||||
*/
|
||||
var isPending: Bool {
|
||||
return result == nil
|
||||
}
|
||||
|
||||
/**
|
||||
- Returns: `true` if the promise has resolved.
|
||||
*/
|
||||
var isResolved: Bool {
|
||||
return !isPending
|
||||
}
|
||||
|
||||
/**
|
||||
- Returns: `true` if the promise was fulfilled.
|
||||
*/
|
||||
var isFulfilled: Bool {
|
||||
return value != nil
|
||||
}
|
||||
|
||||
/**
|
||||
- Returns: `true` if the promise was rejected.
|
||||
*/
|
||||
var isRejected: Bool {
|
||||
return error != nil
|
||||
}
|
||||
|
||||
/**
|
||||
- Returns: The value with which this promise was fulfilled or `nil` if this promise is pending or rejected.
|
||||
*/
|
||||
var value: T? {
|
||||
switch result {
|
||||
case .none:
|
||||
return nil
|
||||
case .some(.fulfilled(let value)):
|
||||
return value
|
||||
case .some(.rejected):
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Thenable where T: Sequence {
|
||||
/**
|
||||
`Promise<[T]>` => `T` -> `U` => `Promise<[U]>`
|
||||
|
||||
firstly {
|
||||
.value([1,2,3])
|
||||
}.mapValues { integer in
|
||||
integer * 2
|
||||
}.done {
|
||||
// $0 => [2,4,6]
|
||||
}
|
||||
*/
|
||||
func mapValues<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U]> {
|
||||
return map(on: on, flags: flags){ try $0.map(transform) }
|
||||
}
|
||||
|
||||
#if swift(>=4) && !swift(>=5.2)
|
||||
/**
|
||||
`Promise<[T]>` => `KeyPath<T, U>` => `Promise<[U]>`
|
||||
|
||||
firstly {
|
||||
.value([Person(name: "Max"), Person(name: "Roman"), Person(name: "John")])
|
||||
}.mapValues(\.name).done {
|
||||
// $0 => ["Max", "Roman", "John"]
|
||||
}
|
||||
*/
|
||||
func mapValues<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath<T.Iterator.Element, U>) -> Promise<[U]> {
|
||||
return map(on: on, flags: flags){ $0.map { $0[keyPath: keyPath] } }
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
`Promise<[T]>` => `T` -> `[U]` => `Promise<[U]>`
|
||||
|
||||
firstly {
|
||||
.value([1,2,3])
|
||||
}.flatMapValues { integer in
|
||||
[integer, integer]
|
||||
}.done {
|
||||
// $0 => [1,1,2,2,3,3]
|
||||
}
|
||||
*/
|
||||
func flatMapValues<U: Sequence>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.Iterator.Element]> {
|
||||
return map(on: on, flags: flags){ (foo: T) in
|
||||
try foo.flatMap{ try transform($0) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
`Promise<[T]>` => `T` -> `U?` => `Promise<[U]>`
|
||||
|
||||
firstly {
|
||||
.value(["1","2","a","3"])
|
||||
}.compactMapValues {
|
||||
Int($0)
|
||||
}.done {
|
||||
// $0 => [1,2,3]
|
||||
}
|
||||
*/
|
||||
func compactMapValues<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U?) -> Promise<[U]> {
|
||||
return map(on: on, flags: flags) { foo -> [U] in
|
||||
#if !swift(>=3.3) || (swift(>=4) && !swift(>=4.1))
|
||||
return try foo.flatMap(transform)
|
||||
#else
|
||||
return try foo.compactMap(transform)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=4) && !swift(>=5.2)
|
||||
/**
|
||||
`Promise<[T]>` => `KeyPath<T, U?>` => `Promise<[U]>`
|
||||
|
||||
firstly {
|
||||
.value([Person(name: "Max"), Person(name: "Roman", age: 26), Person(name: "John", age: 23)])
|
||||
}.compactMapValues(\.age).done {
|
||||
// $0 => [26, 23]
|
||||
}
|
||||
*/
|
||||
func compactMapValues<U>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath<T.Iterator.Element, U?>) -> Promise<[U]> {
|
||||
return map(on: on, flags: flags) { foo -> [U] in
|
||||
#if !swift(>=4.1)
|
||||
return foo.flatMap { $0[keyPath: keyPath] }
|
||||
#else
|
||||
return foo.compactMap { $0[keyPath: keyPath] }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
`Promise<[T]>` => `T` -> `Promise<U>` => `Promise<[U]>`
|
||||
|
||||
firstly {
|
||||
.value([1,2,3])
|
||||
}.thenMap { integer in
|
||||
.value(integer * 2)
|
||||
}.done {
|
||||
// $0 => [2,4,6]
|
||||
}
|
||||
*/
|
||||
func thenMap<U: Thenable>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.T]> {
|
||||
return then(on: on, flags: flags) {
|
||||
when(fulfilled: try $0.map(transform))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
`Promise<[T]>` => `T` -> `Promise<[U]>` => `Promise<[U]>`
|
||||
|
||||
firstly {
|
||||
.value([1,2,3])
|
||||
}.thenFlatMap { integer in
|
||||
.value([integer, integer])
|
||||
}.done {
|
||||
// $0 => [1,1,2,2,3,3]
|
||||
}
|
||||
*/
|
||||
func thenFlatMap<U: Thenable>(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ transform: @escaping(T.Iterator.Element) throws -> U) -> Promise<[U.T.Iterator.Element]> where U.T: Sequence {
|
||||
return then(on: on, flags: flags) {
|
||||
when(fulfilled: try $0.map(transform))
|
||||
}.map(on: nil) {
|
||||
$0.flatMap{ $0 }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
`Promise<[T]>` => `T` -> Bool => `Promise<[T]>`
|
||||
|
||||
firstly {
|
||||
.value([1,2,3])
|
||||
}.filterValues {
|
||||
$0 > 1
|
||||
}.done {
|
||||
// $0 => [2,3]
|
||||
}
|
||||
*/
|
||||
func filterValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ isIncluded: @escaping (T.Iterator.Element) -> Bool) -> Promise<[T.Iterator.Element]> {
|
||||
return map(on: on, flags: flags) {
|
||||
$0.filter(isIncluded)
|
||||
}
|
||||
}
|
||||
|
||||
#if swift(>=4) && !swift(>=5.2)
|
||||
/**
|
||||
`Promise<[T]>` => `KeyPath<T, Bool>` => `Promise<[T]>`
|
||||
|
||||
firstly {
|
||||
.value([Person(name: "Max"), Person(name: "Roman", age: 26, isStudent: false), Person(name: "John", age: 23, isStudent: true)])
|
||||
}.filterValues(\.isStudent).done {
|
||||
// $0 => [Person(name: "John", age: 23, isStudent: true)]
|
||||
}
|
||||
*/
|
||||
func filterValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, _ keyPath: KeyPath<T.Iterator.Element, Bool>) -> Promise<[T.Iterator.Element]> {
|
||||
return map(on: on, flags: flags) {
|
||||
$0.filter { $0[keyPath: keyPath] }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public extension Thenable where T: Collection {
|
||||
/// - Returns: a promise fulfilled with the first value of this `Collection` or, if empty, a promise rejected with PMKError.emptySequence.
|
||||
var firstValue: Promise<T.Iterator.Element> {
|
||||
return map(on: nil) { aa in
|
||||
if let a1 = aa.first {
|
||||
return a1
|
||||
} else {
|
||||
throw PMKError.emptySequence
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func firstValue(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil, where test: @escaping (T.Iterator.Element) -> Bool) -> Promise<T.Iterator.Element> {
|
||||
return map(on: on, flags: flags) {
|
||||
for x in $0 where test(x) {
|
||||
return x
|
||||
}
|
||||
throw PMKError.emptySequence
|
||||
}
|
||||
}
|
||||
|
||||
/// - Returns: a promise fulfilled with the last value of this `Collection` or, if empty, a promise rejected with PMKError.emptySequence.
|
||||
var lastValue: Promise<T.Iterator.Element> {
|
||||
return map(on: nil) { aa in
|
||||
if aa.isEmpty {
|
||||
throw PMKError.emptySequence
|
||||
} else {
|
||||
let i = aa.index(aa.endIndex, offsetBy: -1)
|
||||
return aa[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Thenable where T: Sequence, T.Iterator.Element: Comparable {
|
||||
/// - Returns: a promise fulfilled with the sorted values of this `Sequence`.
|
||||
func sortedValues(on: DispatchQueue? = conf.Q.map, flags: DispatchWorkItemFlags? = nil) -> Promise<[T.Iterator.Element]> {
|
||||
return map(on: on, flags: flags){ $0.sorted() }
|
||||
}
|
||||
}
|
14
Pods/PromiseKit/Sources/after.m
generated
Normal file
14
Pods/PromiseKit/Sources/after.m
generated
Normal file
@ -0,0 +1,14 @@
|
||||
#import "AnyPromise.h"
|
||||
@import Dispatch;
|
||||
@import Foundation.NSDate;
|
||||
@import Foundation.NSValue;
|
||||
|
||||
/// @return A promise that fulfills after the specified duration.
|
||||
AnyPromise *PMKAfter(NSTimeInterval duration) {
|
||||
return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
|
||||
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(duration * NSEC_PER_SEC));
|
||||
dispatch_after(time, dispatch_get_global_queue(0, 0), ^{
|
||||
resolve(@(duration));
|
||||
});
|
||||
}];
|
||||
}
|
46
Pods/PromiseKit/Sources/after.swift
generated
Normal file
46
Pods/PromiseKit/Sources/after.swift
generated
Normal file
@ -0,0 +1,46 @@
|
||||
import struct Foundation.TimeInterval
|
||||
import Dispatch
|
||||
|
||||
/**
|
||||
after(seconds: 1.5).then {
|
||||
//…
|
||||
}
|
||||
|
||||
- Returns: A guarantee that resolves after the specified duration.
|
||||
*/
|
||||
public func after(seconds: TimeInterval) -> Guarantee<Void> {
|
||||
let (rg, seal) = Guarantee<Void>.pending()
|
||||
let when = DispatchTime.now() + seconds
|
||||
#if swift(>=4.0)
|
||||
q.asyncAfter(deadline: when) { seal(()) }
|
||||
#else
|
||||
q.asyncAfter(deadline: when, execute: seal)
|
||||
#endif
|
||||
return rg
|
||||
}
|
||||
|
||||
/**
|
||||
after(.seconds(2)).then {
|
||||
//…
|
||||
}
|
||||
|
||||
- Returns: A guarantee that resolves after the specified duration.
|
||||
*/
|
||||
public func after(_ interval: DispatchTimeInterval) -> Guarantee<Void> {
|
||||
let (rg, seal) = Guarantee<Void>.pending()
|
||||
let when = DispatchTime.now() + interval
|
||||
#if swift(>=4.0)
|
||||
q.asyncAfter(deadline: when) { seal(()) }
|
||||
#else
|
||||
q.asyncAfter(deadline: when, execute: seal)
|
||||
#endif
|
||||
return rg
|
||||
}
|
||||
|
||||
private var q: DispatchQueue {
|
||||
if #available(macOS 10.10, iOS 8.0, tvOS 9.0, watchOS 2.0, *) {
|
||||
return DispatchQueue.global(qos: .default)
|
||||
} else {
|
||||
return DispatchQueue.global(priority: .default)
|
||||
}
|
||||
}
|
10
Pods/PromiseKit/Sources/dispatch_promise.m
generated
Normal file
10
Pods/PromiseKit/Sources/dispatch_promise.m
generated
Normal file
@ -0,0 +1,10 @@
|
||||
#import "AnyPromise.h"
|
||||
@import Dispatch;
|
||||
|
||||
AnyPromise *dispatch_promise_on(dispatch_queue_t queue, id block) {
|
||||
return [AnyPromise promiseWithValue:nil].thenOn(queue, block);
|
||||
}
|
||||
|
||||
AnyPromise *dispatch_promise(id block) {
|
||||
return dispatch_promise_on(dispatch_get_global_queue(0, 0), block);
|
||||
}
|
39
Pods/PromiseKit/Sources/firstly.swift
generated
Normal file
39
Pods/PromiseKit/Sources/firstly.swift
generated
Normal file
@ -0,0 +1,39 @@
|
||||
import Dispatch
|
||||
|
||||
/**
|
||||
Judicious use of `firstly` *may* make chains more readable.
|
||||
|
||||
Compare:
|
||||
|
||||
URLSession.shared.dataTask(url: url1).then {
|
||||
URLSession.shared.dataTask(url: url2)
|
||||
}.then {
|
||||
URLSession.shared.dataTask(url: url3)
|
||||
}
|
||||
|
||||
With:
|
||||
|
||||
firstly {
|
||||
URLSession.shared.dataTask(url: url1)
|
||||
}.then {
|
||||
URLSession.shared.dataTask(url: url2)
|
||||
}.then {
|
||||
URLSession.shared.dataTask(url: url3)
|
||||
}
|
||||
|
||||
- Note: the block you pass executes immediately on the current thread/queue.
|
||||
*/
|
||||
public func firstly<U: Thenable>(execute body: () throws -> U) -> Promise<U.T> {
|
||||
do {
|
||||
let rp = Promise<U.T>(.pending)
|
||||
try body().pipe(to: rp.box.seal)
|
||||
return rp
|
||||
} catch {
|
||||
return Promise(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
/// - See: firstly()
|
||||
public func firstly<T>(execute body: () -> Guarantee<T>) -> Guarantee<T> {
|
||||
return body()
|
||||
}
|
165
Pods/PromiseKit/Sources/fwd.h
generated
Normal file
165
Pods/PromiseKit/Sources/fwd.h
generated
Normal file
@ -0,0 +1,165 @@
|
||||
#import <Foundation/NSDate.h>
|
||||
#import <dispatch/dispatch.h>
|
||||
|
||||
@class AnyPromise;
|
||||
extern NSString * __nonnull const PMKErrorDomain;
|
||||
|
||||
#define PMKFailingPromiseIndexKey @"PMKFailingPromiseIndexKey"
|
||||
#define PMKJoinPromisesKey @"PMKJoinPromisesKey"
|
||||
|
||||
#define PMKUnexpectedError 1l
|
||||
#define PMKInvalidUsageError 3l
|
||||
#define PMKAccessDeniedError 4l
|
||||
#define PMKOperationFailed 8l
|
||||
#define PMKTaskError 9l
|
||||
#define PMKJoinError 10l
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
@return A new promise that resolves after the specified duration.
|
||||
|
||||
@parameter duration The duration in seconds to wait before this promise is resolve.
|
||||
|
||||
For example:
|
||||
|
||||
PMKAfter(1).then(^{
|
||||
//…
|
||||
});
|
||||
*/
|
||||
extern AnyPromise * __nonnull PMKAfter(NSTimeInterval duration) NS_REFINED_FOR_SWIFT;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
`when` is a mechanism for waiting more than one asynchronous task and responding when they are all complete.
|
||||
|
||||
`PMKWhen` accepts varied input. If an array is passed then when those promises fulfill, when’s promise fulfills with an array of fulfillment values. If a dictionary is passed then the same occurs, but when’s promise fulfills with a dictionary of fulfillments keyed as per the input.
|
||||
|
||||
Interestingly, if a single promise is passed then when waits on that single promise, and if a single non-promise object is passed then when fulfills immediately with that object. If the array or dictionary that is passed contains objects that are not promises, then these objects are considered fulfilled promises. The reason we do this is to allow a pattern know as "abstracting away asynchronicity".
|
||||
|
||||
If *any* of the provided promises reject, the returned promise is immediately rejected with that promise’s rejection. The error’s `userInfo` object is supplemented with `PMKFailingPromiseIndexKey`.
|
||||
|
||||
For example:
|
||||
|
||||
PMKWhen(@[promise1, promise2]).then(^(NSArray *results){
|
||||
//…
|
||||
});
|
||||
|
||||
@warning *Important* 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 `PMKJoin`.
|
||||
|
||||
@param input The input upon which to wait before resolving this promise.
|
||||
|
||||
@return A promise that is resolved with either:
|
||||
|
||||
1. An array of values from the provided array of promises.
|
||||
2. The value from the provided promise.
|
||||
3. The provided non-promise object.
|
||||
|
||||
@see PMKJoin
|
||||
|
||||
*/
|
||||
extern AnyPromise * __nonnull PMKWhen(id __nonnull input) NS_REFINED_FOR_SWIFT;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Creates a new promise that resolves only when all provided promises have resolved.
|
||||
|
||||
Typically, you should use `PMKWhen`.
|
||||
|
||||
For example:
|
||||
|
||||
PMKJoin(@[promise1, promise2]).then(^(NSArray *resultingValues){
|
||||
//…
|
||||
}).catch(^(NSError *error){
|
||||
assert(error.domain == PMKErrorDomain);
|
||||
assert(error.code == PMKJoinError);
|
||||
|
||||
NSArray *promises = error.userInfo[PMKJoinPromisesKey];
|
||||
for (AnyPromise *promise in promises) {
|
||||
if (promise.rejected) {
|
||||
//…
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@param promises An array of promises.
|
||||
|
||||
@return A promise that thens three parameters:
|
||||
|
||||
1) An array of mixed values and errors from the resolved input.
|
||||
2) An array of values from the promises that fulfilled.
|
||||
3) An array of errors from the promises that rejected or nil if all promises fulfilled.
|
||||
|
||||
@see when
|
||||
*/
|
||||
AnyPromise *__nonnull PMKJoin(NSArray * __nonnull promises) NS_REFINED_FOR_SWIFT;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Literally hangs this thread until the promise has resolved.
|
||||
|
||||
Do not use hang… unless you are testing, playing or debugging.
|
||||
|
||||
If you use it in production code I will literally and honestly cry like a child.
|
||||
|
||||
@return The resolved value of the promise.
|
||||
|
||||
@warning T SAFE. IT IS NOT SAFE. IT IS NOT SAFE. IT IS NOT SAFE. IT IS NO
|
||||
*/
|
||||
extern id __nullable PMKHang(AnyPromise * __nonnull promise);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Executes the provided block on a background queue.
|
||||
|
||||
dispatch_promise is a convenient way to start a promise chain where the
|
||||
first step needs to run synchronously on a background queue.
|
||||
|
||||
dispatch_promise(^{
|
||||
return md5(input);
|
||||
}).then(^(NSString *md5){
|
||||
NSLog(@"md5: %@", md5);
|
||||
});
|
||||
|
||||
@param block The block to be executed in the background. Returning an `NSError` will reject the promise, everything else (including void) fulfills the promise.
|
||||
|
||||
@return A promise resolved with the return value of the provided block.
|
||||
|
||||
@see dispatch_async
|
||||
*/
|
||||
extern AnyPromise * __nonnull dispatch_promise(id __nonnull block) NS_SWIFT_UNAVAILABLE("Use our `DispatchQueue.async` override instead");
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Executes the provided block on the specified background queue.
|
||||
|
||||
dispatch_promise_on(myDispatchQueue, ^{
|
||||
return md5(input);
|
||||
}).then(^(NSString *md5){
|
||||
NSLog(@"md5: %@", md5);
|
||||
});
|
||||
|
||||
@param block The block to be executed in the background. Returning an `NSError` will reject the promise, everything else (including void) fulfills the promise.
|
||||
|
||||
@return A promise resolved with the return value of the provided block.
|
||||
|
||||
@see dispatch_promise
|
||||
*/
|
||||
extern AnyPromise * __nonnull dispatch_promise_on(dispatch_queue_t __nonnull queue, id __nonnull block) NS_SWIFT_UNAVAILABLE("Use our `DispatchQueue.async` override instead");
|
||||
|
||||
/**
|
||||
Returns a new promise that resolves when the value of the first resolved promise in the provided array of promises.
|
||||
*/
|
||||
extern AnyPromise * __nonnull PMKRace(NSArray * __nonnull promises) NS_REFINED_FOR_SWIFT;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // Extern C
|
||||
#endif
|
29
Pods/PromiseKit/Sources/hang.m
generated
Normal file
29
Pods/PromiseKit/Sources/hang.m
generated
Normal file
@ -0,0 +1,29 @@
|
||||
#import "AnyPromise.h"
|
||||
#import "AnyPromise+Private.h"
|
||||
@import CoreFoundation.CFRunLoop;
|
||||
|
||||
/**
|
||||
Suspends the active thread waiting on the provided promise.
|
||||
|
||||
@return The value of the provided promise once resolved.
|
||||
*/
|
||||
id PMKHang(AnyPromise *promise) {
|
||||
if (promise.pending) {
|
||||
static CFRunLoopSourceContext context;
|
||||
|
||||
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
|
||||
CFRunLoopSourceRef runLoopSource = CFRunLoopSourceCreate(NULL, 0, &context);
|
||||
CFRunLoopAddSource(runLoop, runLoopSource, kCFRunLoopDefaultMode);
|
||||
|
||||
promise.ensure(^{
|
||||
CFRunLoopStop(runLoop);
|
||||
});
|
||||
while (promise.pending) {
|
||||
CFRunLoopRun();
|
||||
}
|
||||
CFRunLoopRemoveSource(runLoop, runLoopSource, kCFRunLoopDefaultMode);
|
||||
CFRelease(runLoopSource);
|
||||
}
|
||||
|
||||
return promise.value;
|
||||
}
|
55
Pods/PromiseKit/Sources/hang.swift
generated
Normal file
55
Pods/PromiseKit/Sources/hang.swift
generated
Normal file
@ -0,0 +1,55 @@
|
||||
import Foundation
|
||||
import CoreFoundation
|
||||
|
||||
/**
|
||||
Runs the active run-loop until the provided promise resolves.
|
||||
|
||||
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.
|
||||
|
||||
Still if you like, study how it works (by reading the sources!) and use at your own risk.
|
||||
|
||||
- Returns: The value of the resolved promise
|
||||
- Throws: An error, should the promise be rejected
|
||||
- See: `wait()`
|
||||
*/
|
||||
public func hang<T>(_ promise: Promise<T>) throws -> T {
|
||||
#if os(Linux) || os(Android)
|
||||
#if swift(>=4)
|
||||
let runLoopMode: CFRunLoopMode = kCFRunLoopDefaultMode
|
||||
#else
|
||||
// isMainThread is not yet implemented on Linux.
|
||||
let runLoopModeRaw = RunLoopMode.defaultRunLoopMode.rawValue._bridgeToObjectiveC()
|
||||
let runLoopMode: CFString = unsafeBitCast(runLoopModeRaw, to: CFString.self)
|
||||
#endif
|
||||
#else
|
||||
guard Thread.isMainThread else {
|
||||
// hang doesn't make sense on threads that aren't the main thread.
|
||||
// use `.wait()` on those threads.
|
||||
fatalError("Only call hang() on the main thread.")
|
||||
}
|
||||
let runLoopMode: CFRunLoopMode = CFRunLoopMode.defaultMode
|
||||
#endif
|
||||
|
||||
if promise.isPending {
|
||||
var context = CFRunLoopSourceContext()
|
||||
let runLoop = CFRunLoopGetCurrent()
|
||||
let runLoopSource = CFRunLoopSourceCreate(nil, 0, &context)
|
||||
CFRunLoopAddSource(runLoop, runLoopSource, runLoopMode)
|
||||
|
||||
_ = promise.ensure {
|
||||
CFRunLoopStop(runLoop)
|
||||
}
|
||||
|
||||
while promise.isPending {
|
||||
CFRunLoopRun()
|
||||
}
|
||||
CFRunLoopRemoveSource(runLoop, runLoopSource, runLoopMode)
|
||||
}
|
||||
|
||||
switch promise.result! {
|
||||
case .rejected(let error):
|
||||
throw error
|
||||
case .fulfilled(let value):
|
||||
return value
|
||||
}
|
||||
}
|
54
Pods/PromiseKit/Sources/join.m
generated
Normal file
54
Pods/PromiseKit/Sources/join.m
generated
Normal file
@ -0,0 +1,54 @@
|
||||
@import Foundation.NSDictionary;
|
||||
#import "AnyPromise+Private.h"
|
||||
#import <libkern/OSAtomic.h>
|
||||
@import Foundation.NSError;
|
||||
@import Foundation.NSNull;
|
||||
#import "PromiseKit.h"
|
||||
#import <stdatomic.h>
|
||||
|
||||
/**
|
||||
Waits on all provided promises.
|
||||
|
||||
`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.
|
||||
|
||||
- Returns: A new promise that resolves once all the provided promises resolve.
|
||||
*/
|
||||
AnyPromise *PMKJoin(NSArray *promises) {
|
||||
if (promises == nil)
|
||||
return [AnyPromise promiseWithValue:[NSError errorWithDomain:PMKErrorDomain code:PMKInvalidUsageError userInfo:@{NSLocalizedDescriptionKey: @"PMKJoin(nil)"}]];
|
||||
|
||||
if (promises.count == 0)
|
||||
return [AnyPromise promiseWithValue:promises];
|
||||
|
||||
return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
|
||||
NSPointerArray *results = NSPointerArrayMake(promises.count);
|
||||
__block atomic_int countdown = promises.count;
|
||||
__block BOOL rejected = NO;
|
||||
|
||||
[promises enumerateObjectsUsingBlock:^(AnyPromise *promise, NSUInteger ii, BOOL *stop) {
|
||||
[promise __pipe:^(id value) {
|
||||
|
||||
if (IsError(value)) {
|
||||
rejected = YES;
|
||||
}
|
||||
|
||||
//FIXME surely this isn't thread safe on multiple cores?
|
||||
[results replacePointerAtIndex:ii withPointer:(__bridge void *)(value ?: [NSNull null])];
|
||||
|
||||
atomic_fetch_sub_explicit(&countdown, 1, memory_order_relaxed);
|
||||
|
||||
if (countdown == 0) {
|
||||
if (!rejected) {
|
||||
resolve(results.allObjects);
|
||||
} else {
|
||||
id userInfo = @{PMKJoinPromisesKey: promises};
|
||||
id err = [NSError errorWithDomain:PMKErrorDomain code:PMKJoinError userInfo:userInfo];
|
||||
resolve(err);
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
(void) stop;
|
||||
}];
|
||||
}];
|
||||
}
|
9
Pods/PromiseKit/Sources/race.m
generated
Normal file
9
Pods/PromiseKit/Sources/race.m
generated
Normal file
@ -0,0 +1,9 @@
|
||||
#import "AnyPromise+Private.h"
|
||||
|
||||
AnyPromise *PMKRace(NSArray *promises) {
|
||||
return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
|
||||
for (AnyPromise *promise in promises) {
|
||||
[promise __pipe:resolve];
|
||||
}
|
||||
}];
|
||||
}
|
102
Pods/PromiseKit/Sources/race.swift
generated
Normal file
102
Pods/PromiseKit/Sources/race.swift
generated
Normal file
@ -0,0 +1,102 @@
|
||||
import Dispatch
|
||||
|
||||
@inline(__always)
|
||||
private func _race<U: Thenable>(_ thenables: [U]) -> Promise<U.T> {
|
||||
let rp = Promise<U.T>(.pending)
|
||||
for thenable in thenables {
|
||||
thenable.pipe(to: rp.box.seal)
|
||||
}
|
||||
return rp
|
||||
}
|
||||
|
||||
/**
|
||||
Waits for one promise to resolve
|
||||
|
||||
race(promise1, promise2, promise3).then { winner in
|
||||
//…
|
||||
}
|
||||
|
||||
- Returns: The promise that resolves first
|
||||
- Warning: If the first resolution is a rejection, the returned promise is rejected
|
||||
*/
|
||||
public func race<U: Thenable>(_ thenables: U...) -> Promise<U.T> {
|
||||
return _race(thenables)
|
||||
}
|
||||
|
||||
/**
|
||||
Waits for one promise to resolve
|
||||
|
||||
race(promise1, promise2, promise3).then { winner in
|
||||
//…
|
||||
}
|
||||
|
||||
- Returns: The promise that resolves first
|
||||
- Warning: If the first resolution is a rejection, the returned promise is rejected
|
||||
- Remark: If the provided array is empty the returned promise is rejected with PMKError.badInput
|
||||
*/
|
||||
public func race<U: Thenable>(_ thenables: [U]) -> Promise<U.T> {
|
||||
guard !thenables.isEmpty else {
|
||||
return Promise(error: PMKError.badInput)
|
||||
}
|
||||
return _race(thenables)
|
||||
}
|
||||
|
||||
/**
|
||||
Waits for one guarantee to resolve
|
||||
|
||||
race(promise1, promise2, promise3).then { winner in
|
||||
//…
|
||||
}
|
||||
|
||||
- Returns: The guarantee that resolves first
|
||||
*/
|
||||
public func race<T>(_ guarantees: Guarantee<T>...) -> Guarantee<T> {
|
||||
let rg = Guarantee<T>(.pending)
|
||||
for guarantee in guarantees {
|
||||
guarantee.pipe(to: rg.box.seal)
|
||||
}
|
||||
return rg
|
||||
}
|
||||
|
||||
/**
|
||||
Waits for one promise to fulfill
|
||||
|
||||
race(fulfilled: [promise1, promise2, promise3]).then { winner in
|
||||
//…
|
||||
}
|
||||
|
||||
- Returns: The promise that was fulfilled first.
|
||||
- Warning: Skips all rejected promises.
|
||||
- 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`.
|
||||
*/
|
||||
public func race<U: Thenable>(fulfilled thenables: [U]) -> Promise<U.T> {
|
||||
var countdown = thenables.count
|
||||
guard countdown > 0 else {
|
||||
return Promise(error: PMKError.badInput)
|
||||
}
|
||||
|
||||
let rp = Promise<U.T>(.pending)
|
||||
|
||||
let barrier = DispatchQueue(label: "org.promisekit.barrier.race", attributes: .concurrent)
|
||||
|
||||
for promise in thenables {
|
||||
promise.pipe { result in
|
||||
barrier.sync(flags: .barrier) {
|
||||
switch result {
|
||||
case .rejected:
|
||||
guard rp.isPending else { return }
|
||||
countdown -= 1
|
||||
if countdown == 0 {
|
||||
rp.box.seal(.rejected(PMKError.noWinner))
|
||||
}
|
||||
case .fulfilled(let value):
|
||||
guard rp.isPending else { return }
|
||||
countdown = 0
|
||||
rp.box.seal(.fulfilled(value))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rp
|
||||
}
|
107
Pods/PromiseKit/Sources/when.m
generated
Normal file
107
Pods/PromiseKit/Sources/when.m
generated
Normal file
@ -0,0 +1,107 @@
|
||||
@import Foundation.NSDictionary;
|
||||
#import "AnyPromise+Private.h"
|
||||
@import Foundation.NSProgress;
|
||||
#import <libkern/OSAtomic.h>
|
||||
@import Foundation.NSError;
|
||||
@import Foundation.NSNull;
|
||||
#import "PromiseKit.h"
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
// ^^ OSAtomicDecrement32 is deprecated on watchOS
|
||||
|
||||
|
||||
// NSProgress resources:
|
||||
// * https://robots.thoughtbot.com/asynchronous-nsprogress
|
||||
// * http://oleb.net/blog/2014/03/nsprogress/
|
||||
// NSProgress! Beware!
|
||||
// * https://github.com/AFNetworking/AFNetworking/issues/2261
|
||||
|
||||
/**
|
||||
Wait for all promises in a set to resolve.
|
||||
|
||||
@note If *any* of the provided promises reject, the returned promise is immediately rejected with that error.
|
||||
@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:)`.
|
||||
@param promises The promises upon which to wait before the returned promise resolves.
|
||||
@note PMKWhen provides NSProgress.
|
||||
@return A new promise that resolves when all the provided promises fulfill or one of the provided promises rejects.
|
||||
*/
|
||||
AnyPromise *PMKWhen(id promises) {
|
||||
if (promises == nil)
|
||||
return [AnyPromise promiseWithValue:[NSError errorWithDomain:PMKErrorDomain code:PMKInvalidUsageError userInfo:@{NSLocalizedDescriptionKey: @"PMKWhen(nil)"}]];
|
||||
|
||||
if ([promises isKindOfClass:[NSArray class]] || [promises isKindOfClass:[NSDictionary class]]) {
|
||||
if ([promises count] == 0)
|
||||
return [AnyPromise promiseWithValue:promises];
|
||||
} else if ([promises isKindOfClass:[AnyPromise class]]) {
|
||||
promises = @[promises];
|
||||
} else {
|
||||
return [AnyPromise promiseWithValue:promises];
|
||||
}
|
||||
|
||||
#ifndef PMKDisableProgress
|
||||
NSProgress *progress = [NSProgress progressWithTotalUnitCount:(int64_t)[promises count]];
|
||||
progress.pausable = NO;
|
||||
progress.cancellable = NO;
|
||||
#else
|
||||
struct PMKProgress {
|
||||
int completedUnitCount;
|
||||
int totalUnitCount;
|
||||
double fractionCompleted;
|
||||
};
|
||||
__block struct PMKProgress progress;
|
||||
#endif
|
||||
|
||||
__block int32_t countdown = (int32_t)[promises count];
|
||||
BOOL const isdict = [promises isKindOfClass:[NSDictionary class]];
|
||||
|
||||
return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve) {
|
||||
NSInteger index = 0;
|
||||
|
||||
for (__strong id key in promises) {
|
||||
AnyPromise *promise = isdict ? promises[key] : key;
|
||||
if (!isdict) key = @(index);
|
||||
|
||||
if (![promise isKindOfClass:[AnyPromise class]])
|
||||
promise = [AnyPromise promiseWithValue:promise];
|
||||
|
||||
[promise __pipe:^(id value){
|
||||
if (progress.fractionCompleted >= 1)
|
||||
return;
|
||||
|
||||
if (IsError(value)) {
|
||||
progress.completedUnitCount = progress.totalUnitCount;
|
||||
|
||||
NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:[(NSError *)value userInfo] ?: @{}];
|
||||
userInfo[PMKFailingPromiseIndexKey] = key;
|
||||
[userInfo setObject:value forKey:NSUnderlyingErrorKey];
|
||||
id err = [[NSError alloc] initWithDomain:[value domain] code:[value code] userInfo:userInfo];
|
||||
resolve(err);
|
||||
}
|
||||
else if (OSAtomicDecrement32(&countdown) == 0) {
|
||||
progress.completedUnitCount = progress.totalUnitCount;
|
||||
|
||||
id results;
|
||||
if (isdict) {
|
||||
results = [NSMutableDictionary new];
|
||||
for (id key in promises) {
|
||||
id promise = promises[key];
|
||||
results[key] = IsPromise(promise) ? ((AnyPromise *)promise).value : promise;
|
||||
}
|
||||
} else {
|
||||
results = [NSMutableArray new];
|
||||
for (AnyPromise *promise in promises) {
|
||||
id value = IsPromise(promise) ? (promise.value ?: [NSNull null]) : promise;
|
||||
[results addObject:value];
|
||||
}
|
||||
}
|
||||
resolve(results);
|
||||
} else {
|
||||
progress.completedUnitCount++;
|
||||
}
|
||||
}];
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic pop
|
363
Pods/PromiseKit/Sources/when.swift
generated
Normal file
363
Pods/PromiseKit/Sources/when.swift
generated
Normal file
@ -0,0 +1,363 @@
|
||||
import Foundation
|
||||
import Dispatch
|
||||
|
||||
private func _when<U: Thenable>(_ thenables: [U]) -> Promise<Void> {
|
||||
var countdown = thenables.count
|
||||
guard countdown > 0 else {
|
||||
return .value(Void())
|
||||
}
|
||||
|
||||
let rp = Promise<Void>(.pending)
|
||||
|
||||
#if PMKDisableProgress || os(Linux)
|
||||
var progress: (completedUnitCount: Int, totalUnitCount: Int) = (0, 0)
|
||||
#else
|
||||
let progress = Progress(totalUnitCount: Int64(thenables.count))
|
||||
progress.isCancellable = false
|
||||
progress.isPausable = false
|
||||
#endif
|
||||
|
||||
let barrier = DispatchQueue(label: "org.promisekit.barrier.when", attributes: .concurrent)
|
||||
|
||||
for promise in thenables {
|
||||
promise.pipe { result in
|
||||
barrier.sync(flags: .barrier) {
|
||||
switch result {
|
||||
case .rejected(let error):
|
||||
if rp.isPending {
|
||||
progress.completedUnitCount = progress.totalUnitCount
|
||||
rp.box.seal(.rejected(error))
|
||||
}
|
||||
case .fulfilled:
|
||||
guard rp.isPending else { return }
|
||||
progress.completedUnitCount += 1
|
||||
countdown -= 1
|
||||
if countdown == 0 {
|
||||
rp.box.seal(.fulfilled(()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rp
|
||||
}
|
||||
|
||||
/**
|
||||
Wait for all promises in a set to fulfill.
|
||||
|
||||
For example:
|
||||
|
||||
when(fulfilled: promise1, promise2).then { results in
|
||||
//…
|
||||
}.catch { error in
|
||||
switch error {
|
||||
case URLError.notConnectedToInternet:
|
||||
//…
|
||||
case CLError.denied:
|
||||
//…
|
||||
}
|
||||
}
|
||||
|
||||
- Note: If *any* of the provided promises reject, the returned promise is immediately rejected with that error.
|
||||
- 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:)`.
|
||||
- Parameter promises: The promises upon which to wait before the returned promise resolves.
|
||||
- Returns: A new promise that resolves when all the provided promises fulfill or one of the provided promises rejects.
|
||||
- Note: `when` provides `NSProgress`.
|
||||
- SeeAlso: `when(resolved:)`
|
||||
*/
|
||||
public func when<U: Thenable>(fulfilled thenables: [U]) -> Promise<[U.T]> {
|
||||
return _when(thenables).map(on: nil) { thenables.map{ $0.value! } }
|
||||
}
|
||||
|
||||
/// Wait for all promises in a set to fulfill.
|
||||
public func when<U: Thenable>(fulfilled promises: U...) -> Promise<Void> where U.T == Void {
|
||||
return _when(promises)
|
||||
}
|
||||
|
||||
/// Wait for all promises in a set to fulfill.
|
||||
public func when<U: Thenable>(fulfilled promises: [U]) -> Promise<Void> where U.T == Void {
|
||||
return _when(promises)
|
||||
}
|
||||
|
||||
/// Wait for all promises in a set to fulfill.
|
||||
public func when<U: Thenable, V: Thenable>(fulfilled pu: U, _ pv: V) -> Promise<(U.T, V.T)> {
|
||||
return _when([pu.asVoid(), pv.asVoid()]).map(on: nil) { (pu.value!, pv.value!) }
|
||||
}
|
||||
|
||||
/// Wait for all promises in a set to fulfill.
|
||||
public func when<U: Thenable, V: Thenable, W: Thenable>(fulfilled pu: U, _ pv: V, _ pw: W) -> Promise<(U.T, V.T, W.T)> {
|
||||
return _when([pu.asVoid(), pv.asVoid(), pw.asVoid()]).map(on: nil) { (pu.value!, pv.value!, pw.value!) }
|
||||
}
|
||||
|
||||
/// Wait for all promises in a set to fulfill.
|
||||
public func when<U: Thenable, V: Thenable, W: Thenable, X: Thenable>(fulfilled pu: U, _ pv: V, _ pw: W, _ px: X) -> Promise<(U.T, V.T, W.T, X.T)> {
|
||||
return _when([pu.asVoid(), pv.asVoid(), pw.asVoid(), px.asVoid()]).map(on: nil) { (pu.value!, pv.value!, pw.value!, px.value!) }
|
||||
}
|
||||
|
||||
/// Wait for all promises in a set to fulfill.
|
||||
public func when<U: Thenable, V: Thenable, W: Thenable, X: Thenable, Y: Thenable>(fulfilled pu: U, _ pv: V, _ pw: W, _ px: X, _ py: Y) -> Promise<(U.T, V.T, W.T, X.T, Y.T)> {
|
||||
return _when([pu.asVoid(), pv.asVoid(), pw.asVoid(), px.asVoid(), py.asVoid()]).map(on: nil) { (pu.value!, pv.value!, pw.value!, px.value!, py.value!) }
|
||||
}
|
||||
|
||||
/**
|
||||
Generate promises at a limited rate and wait for all to fulfill.
|
||||
|
||||
For example:
|
||||
|
||||
func downloadFile(url: URL) -> Promise<Data> {
|
||||
// ...
|
||||
}
|
||||
|
||||
let urls: [URL] = /*…*/
|
||||
let urlGenerator = urls.makeIterator()
|
||||
|
||||
let generator = AnyIterator<Promise<Data>> {
|
||||
guard url = urlGenerator.next() else {
|
||||
return nil
|
||||
}
|
||||
return downloadFile(url)
|
||||
}
|
||||
|
||||
when(generator, concurrently: 3).done { datas in
|
||||
// ...
|
||||
}
|
||||
|
||||
No more than three downloads will occur simultaneously.
|
||||
|
||||
- Note: The generator is called *serially* on a *background* queue.
|
||||
- Warning: Refer to the warnings on `when(fulfilled:)`
|
||||
- Parameter promiseGenerator: Generator of promises.
|
||||
- Returns: A new promise that resolves when all the provided promises fulfill or one of the provided promises rejects.
|
||||
- SeeAlso: `when(resolved:)`
|
||||
*/
|
||||
|
||||
public func when<It: IteratorProtocol>(fulfilled promiseIterator: It, concurrently: Int) -> Promise<[It.Element.T]> where It.Element: Thenable {
|
||||
|
||||
guard concurrently > 0 else {
|
||||
return Promise(error: PMKError.badInput)
|
||||
}
|
||||
|
||||
var generator = promiseIterator
|
||||
let root = Promise<[It.Element.T]>.pending()
|
||||
var pendingPromises = 0
|
||||
var promises: [It.Element] = []
|
||||
|
||||
let barrier = DispatchQueue(label: "org.promisekit.barrier.when", attributes: [.concurrent])
|
||||
|
||||
func dequeue() {
|
||||
guard root.promise.isPending else { return } // don’t continue dequeueing if root has been rejected
|
||||
|
||||
var shouldDequeue = false
|
||||
barrier.sync {
|
||||
shouldDequeue = pendingPromises < concurrently
|
||||
}
|
||||
guard shouldDequeue else { return }
|
||||
|
||||
var promise: It.Element!
|
||||
|
||||
barrier.sync(flags: .barrier) {
|
||||
guard let next = generator.next() else { return }
|
||||
promise = next
|
||||
pendingPromises += 1
|
||||
promises.append(next)
|
||||
}
|
||||
|
||||
func testDone() {
|
||||
barrier.sync {
|
||||
if pendingPromises == 0 {
|
||||
#if !swift(>=3.3) || (swift(>=4) && !swift(>=4.1))
|
||||
root.resolver.fulfill(promises.flatMap{ $0.value })
|
||||
#else
|
||||
root.resolver.fulfill(promises.compactMap{ $0.value })
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
guard promise != nil else {
|
||||
return testDone()
|
||||
}
|
||||
|
||||
promise.pipe { resolution in
|
||||
barrier.sync(flags: .barrier) {
|
||||
pendingPromises -= 1
|
||||
}
|
||||
|
||||
switch resolution {
|
||||
case .fulfilled:
|
||||
dequeue()
|
||||
testDone()
|
||||
case .rejected(let error):
|
||||
root.resolver.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
dequeue()
|
||||
}
|
||||
|
||||
dequeue()
|
||||
|
||||
return root.promise
|
||||
}
|
||||
|
||||
/**
|
||||
Waits on all provided promises.
|
||||
|
||||
`when(fulfilled:)` rejects as soon as one of the provided promises rejects. `when(resolved:)` waits on all provided promises whatever their result, and then provides an array of `Result<T>` so you can individually inspect the results. As a consequence this function returns a `Guarantee`, ie. errors are lifted from the individual promises into the results array of the returned `Guarantee`.
|
||||
|
||||
when(resolved: promise1, promise2, promise3).then { results in
|
||||
for result in results where case .fulfilled(let value) {
|
||||
//…
|
||||
}
|
||||
}.catch { error in
|
||||
// invalid! Never rejects
|
||||
}
|
||||
|
||||
- Returns: A new promise that resolves once all the provided promises resolve. The array is ordered the same as the input, ie. the result order is *not* resolution order.
|
||||
- Note: we do not provide tuple variants for `when(resolved:)` but will accept a pull-request
|
||||
- Remark: Doesn't take Thenable due to protocol `associatedtype` paradox
|
||||
*/
|
||||
public func when<T>(resolved promises: Promise<T>...) -> Guarantee<[Result<T>]> {
|
||||
return when(resolved: promises)
|
||||
}
|
||||
|
||||
/// - See: `when(resolved: Promise<T>...)`
|
||||
public func when<T>(resolved promises: [Promise<T>]) -> Guarantee<[Result<T>]> {
|
||||
guard !promises.isEmpty else {
|
||||
return .value([])
|
||||
}
|
||||
|
||||
var countdown = promises.count
|
||||
let barrier = DispatchQueue(label: "org.promisekit.barrier.join", attributes: .concurrent)
|
||||
|
||||
let rg = Guarantee<[Result<T>]>(.pending)
|
||||
for promise in promises {
|
||||
promise.pipe { result in
|
||||
barrier.sync(flags: .barrier) {
|
||||
countdown -= 1
|
||||
}
|
||||
barrier.sync {
|
||||
if countdown == 0 {
|
||||
rg.box.seal(promises.map{ $0.result! })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rg
|
||||
}
|
||||
|
||||
/**
|
||||
Generate promises at a limited rate and wait for all to resolve.
|
||||
|
||||
For example:
|
||||
|
||||
func downloadFile(url: URL) -> Promise<Data> {
|
||||
// ...
|
||||
}
|
||||
|
||||
let urls: [URL] = /*…*/
|
||||
let urlGenerator = urls.makeIterator()
|
||||
|
||||
let generator = AnyIterator<Promise<Data>> {
|
||||
guard url = urlGenerator.next() else {
|
||||
return nil
|
||||
}
|
||||
return downloadFile(url)
|
||||
}
|
||||
|
||||
when(resolved: generator, concurrently: 3).done { results in
|
||||
// ...
|
||||
}
|
||||
|
||||
No more than three downloads will occur simultaneously. Downloads will continue if one of them fails
|
||||
|
||||
- Note: The generator is called *serially* on a *background* queue.
|
||||
- Warning: Refer to the warnings on `when(resolved:)`
|
||||
- Parameter promiseGenerator: Generator of promises.
|
||||
- Returns: A new promise that resolves once all the provided promises resolve. The array is ordered the same as the input, ie. the result order is *not* resolution order.
|
||||
- SeeAlso: `when(resolved:)`
|
||||
*/
|
||||
#if swift(>=5.3)
|
||||
public func when<It: IteratorProtocol>(resolved promiseIterator: It, concurrently: Int)
|
||||
-> Guarantee<[Result<It.Element.T>]> where It.Element: Thenable {
|
||||
guard concurrently > 0 else {
|
||||
return Guarantee.value([Result.rejected(PMKError.badInput)])
|
||||
}
|
||||
|
||||
var generator = promiseIterator
|
||||
let root = Guarantee<[Result<It.Element.T>]>.pending()
|
||||
var pendingPromises = 0
|
||||
var promises: [It.Element] = []
|
||||
|
||||
let barrier = DispatchQueue(label: "org.promisekit.barrier.when", attributes: [.concurrent])
|
||||
|
||||
func dequeue() {
|
||||
guard root.guarantee.isPending else {
|
||||
return
|
||||
} // don’t continue dequeueing if root has been rejected
|
||||
|
||||
var shouldDequeue = false
|
||||
barrier.sync {
|
||||
shouldDequeue = pendingPromises < concurrently
|
||||
}
|
||||
guard shouldDequeue else {
|
||||
return
|
||||
}
|
||||
|
||||
var promise: It.Element!
|
||||
|
||||
barrier.sync(flags: .barrier) {
|
||||
guard let next = generator.next() else {
|
||||
return
|
||||
}
|
||||
|
||||
promise = next
|
||||
|
||||
pendingPromises += 1
|
||||
promises.append(next)
|
||||
}
|
||||
|
||||
func testDone() {
|
||||
barrier.sync {
|
||||
if pendingPromises == 0 {
|
||||
#if !swift(>=3.3) || (swift(>=4) && !swift(>=4.1))
|
||||
root.resolve(promises.flatMap { $0.result })
|
||||
#else
|
||||
root.resolve(promises.compactMap { $0.result })
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
guard promise != nil else {
|
||||
return testDone()
|
||||
}
|
||||
|
||||
promise.pipe { _ in
|
||||
barrier.sync(flags: .barrier) {
|
||||
pendingPromises -= 1
|
||||
}
|
||||
|
||||
dequeue()
|
||||
testDone()
|
||||
}
|
||||
|
||||
dequeue()
|
||||
}
|
||||
|
||||
dequeue()
|
||||
|
||||
return root.guarantee
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Waits on all provided Guarantees.
|
||||
public func when(_ guarantees: Guarantee<Void>...) -> Guarantee<Void> {
|
||||
return when(guarantees: guarantees)
|
||||
}
|
||||
|
||||
// Waits on all provided Guarantees.
|
||||
public func when(guarantees: [Guarantee<Void>]) -> Guarantee<Void> {
|
||||
return when(fulfilled: guarantees).recover{ _ in }.asVoid()
|
||||
}
|
176
Pods/Starscream/LICENSE
generated
Normal file
176
Pods/Starscream/LICENSE
generated
Normal file
@ -0,0 +1,176 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
Copyright (c) 2014-2016 Dalton Cherry.
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
308
Pods/Starscream/README.md
generated
Normal file
308
Pods/Starscream/README.md
generated
Normal file
@ -0,0 +1,308 @@
|
||||
![starscream](https://raw.githubusercontent.com/daltoniam/starscream/assets/starscream.jpg)
|
||||
|
||||
Starscream is a conforming WebSocket ([RFC 6455](http://tools.ietf.org/html/rfc6455)) library in Swift.
|
||||
|
||||
## Features
|
||||
|
||||
- Conforms to all of the base [Autobahn test suite](https://crossbar.io/autobahn/).
|
||||
- Nonblocking. Everything happens in the background, thanks to GCD.
|
||||
- TLS/WSS support.
|
||||
- Compression Extensions support ([RFC 7692](https://tools.ietf.org/html/rfc7692))
|
||||
|
||||
### Import the framework
|
||||
|
||||
First thing is to import the framework. See the Installation instructions on how to add the framework to your project.
|
||||
|
||||
```swift
|
||||
import Starscream
|
||||
```
|
||||
|
||||
### Connect to the WebSocket Server
|
||||
|
||||
Once imported, you can open a connection to your WebSocket server. Note that `socket` is probably best as a property, so it doesn't get deallocated right after being setup.
|
||||
|
||||
```swift
|
||||
var request = URLRequest(url: URL(string: "http://localhost:8080")!)
|
||||
request.timeoutInterval = 5
|
||||
socket = WebSocket(request: request)
|
||||
socket.delegate = self
|
||||
socket.connect()
|
||||
```
|
||||
|
||||
After you are connected, there is either a delegate or closure you can use for process WebSocket events.
|
||||
|
||||
### Receiving data from a WebSocket
|
||||
|
||||
`didReceive` receives all the WebSocket events in a single easy to handle enum.
|
||||
|
||||
```swift
|
||||
func didReceive(event: WebSocketEvent, client: WebSocket) {
|
||||
switch event {
|
||||
case .connected(let headers):
|
||||
isConnected = true
|
||||
print("websocket is connected: \(headers)")
|
||||
case .disconnected(let reason, let code):
|
||||
isConnected = false
|
||||
print("websocket is disconnected: \(reason) with code: \(code)")
|
||||
case .text(let string):
|
||||
print("Received text: \(string)")
|
||||
case .binary(let data):
|
||||
print("Received data: \(data.count)")
|
||||
case .ping(_):
|
||||
break
|
||||
case .pong(_):
|
||||
break
|
||||
case .viabilityChanged(_):
|
||||
break
|
||||
case .reconnectSuggested(_):
|
||||
break
|
||||
case .cancelled:
|
||||
isConnected = false
|
||||
case .error(let error):
|
||||
isConnected = false
|
||||
handleError(error)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The closure of this would be:
|
||||
|
||||
```swift
|
||||
socket.onEvent = { event in
|
||||
switch event {
|
||||
// handle events just like above...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Writing to a WebSocket
|
||||
|
||||
### write a binary frame
|
||||
|
||||
The writeData method gives you a simple way to send `Data` (binary) data to the server.
|
||||
|
||||
```swift
|
||||
socket.write(data: data) //write some Data over the socket!
|
||||
```
|
||||
|
||||
### write a string frame
|
||||
|
||||
The writeString method is the same as writeData, but sends text/string.
|
||||
|
||||
```swift
|
||||
socket.write(string: "Hi Server!") //example on how to write text over the socket!
|
||||
```
|
||||
|
||||
### write a ping frame
|
||||
|
||||
The writePing method is the same as write, but sends a ping control frame.
|
||||
|
||||
```swift
|
||||
socket.write(ping: Data()) //example on how to write a ping control frame over the socket!
|
||||
```
|
||||
|
||||
### write a pong frame
|
||||
|
||||
|
||||
the writePong method is the same as writePing, but sends a pong control frame.
|
||||
|
||||
```swift
|
||||
socket.write(pong: Data()) //example on how to write a pong control frame over the socket!
|
||||
```
|
||||
|
||||
Starscream will automatically respond to incoming `ping` control frames so you do not need to manually send `pong`s.
|
||||
|
||||
However if for some reason you need to control this process you can turn off the automatic `ping` response by disabling `respondToPingWithPong`.
|
||||
|
||||
```swift
|
||||
socket.respondToPingWithPong = false //Do not automaticaly respond to incoming pings with pongs.
|
||||
```
|
||||
|
||||
In most cases you will not need to do this.
|
||||
|
||||
### disconnect
|
||||
|
||||
The disconnect method does what you would expect and closes the socket.
|
||||
|
||||
```swift
|
||||
socket.disconnect()
|
||||
```
|
||||
|
||||
The disconnect method can also send a custom close code if desired.
|
||||
|
||||
```swift
|
||||
socket.disconnect(closeCode: CloseCode.normal.rawValue)
|
||||
```
|
||||
|
||||
### Custom Headers, Protocols and Timeout
|
||||
|
||||
You can override the default websocket headers, add your own custom ones and set a timeout:
|
||||
|
||||
```swift
|
||||
var request = URLRequest(url: URL(string: "ws://localhost:8080/")!)
|
||||
request.timeoutInterval = 5 // Sets the timeout for the connection
|
||||
request.setValue("someother protocols", forHTTPHeaderField: "Sec-WebSocket-Protocol")
|
||||
request.setValue("14", forHTTPHeaderField: "Sec-WebSocket-Version")
|
||||
request.setValue("chat,superchat", forHTTPHeaderField: "Sec-WebSocket-Protocol")
|
||||
request.setValue("Everything is Awesome!", forHTTPHeaderField: "My-Awesome-Header")
|
||||
let socket = WebSocket(request: request)
|
||||
```
|
||||
|
||||
### SSL Pinning
|
||||
|
||||
SSL Pinning is also supported in Starscream.
|
||||
|
||||
|
||||
Allow Self-signed certificates:
|
||||
|
||||
```swift
|
||||
var request = URLRequest(url: URL(string: "ws://localhost:8080/")!)
|
||||
let pinner = FoundationSecurity(allowSelfSigned: true) // don't validate SSL certificates
|
||||
let socket = WebSocket(request: request, certPinner: pinner)
|
||||
```
|
||||
|
||||
TODO: Update docs on how to load certificates and public keys into an app bundle, use the builtin pinner and TrustKit.
|
||||
|
||||
### Compression Extensions
|
||||
|
||||
Compression Extensions ([RFC 7692](https://tools.ietf.org/html/rfc7692)) is supported in Starscream. Compression is enabled by default, however compression will only be used if it is supported by the server as well. You may enable or disable compression via the `.enableCompression` property:
|
||||
|
||||
```swift
|
||||
var request = URLRequest(url: URL(string: "ws://localhost:8080/")!)
|
||||
let compression = WSCompression()
|
||||
let socket = WebSocket(request: request, compressionHandler: compression)
|
||||
```
|
||||
|
||||
Compression should be disabled if your application is transmitting already-compressed, random, or other uncompressable data.
|
||||
|
||||
### Custom Queue
|
||||
|
||||
A custom queue can be specified when delegate methods are called. By default `DispatchQueue.main` is used, thus making all delegate methods calls run on the main thread. It is important to note that all WebSocket processing is done on a background thread, only the delegate method calls are changed when modifying the queue. The actual processing is always on a background thread and will not pause your app.
|
||||
|
||||
```swift
|
||||
socket = WebSocket(url: URL(string: "ws://localhost:8080/")!, protocols: ["chat","superchat"])
|
||||
//create a custom queue
|
||||
socket.callbackQueue = DispatchQueue(label: "com.vluxe.starscream.myapp")
|
||||
```
|
||||
|
||||
## Example Project
|
||||
|
||||
Check out the SimpleTest project in the examples directory to see how to setup a simple connection to a WebSocket server.
|
||||
|
||||
## Requirements
|
||||
|
||||
Starscream works with iOS 8/10.10 or above for CocoaPods/framework support. To use Starscream with a project targeting iOS 7, you must include all Swift files directly in your project.
|
||||
|
||||
## Installation
|
||||
|
||||
### CocoaPods
|
||||
|
||||
Check out [Get Started](http://cocoapods.org/) tab on [cocoapods.org](http://cocoapods.org/).
|
||||
|
||||
To use Starscream in your project add the following 'Podfile' to your project
|
||||
|
||||
source 'https://github.com/CocoaPods/Specs.git'
|
||||
platform :ios, '9.0'
|
||||
use_frameworks!
|
||||
|
||||
pod 'Starscream', '~> 4.0.0'
|
||||
|
||||
Then run:
|
||||
|
||||
pod install
|
||||
|
||||
### Carthage
|
||||
|
||||
Check out the [Carthage](https://github.com/Carthage/Carthage) docs on how to add a install. The `Starscream` framework is already setup with shared schemes.
|
||||
|
||||
[Carthage Install](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application)
|
||||
|
||||
You can install Carthage with [Homebrew](http://brew.sh/) using the following command:
|
||||
|
||||
```bash
|
||||
$ brew update
|
||||
$ brew install carthage
|
||||
```
|
||||
|
||||
To integrate Starscream into your Xcode project using Carthage, specify it in your `Cartfile`:
|
||||
|
||||
```
|
||||
github "daltoniam/Starscream" >= 4.0.0
|
||||
```
|
||||
|
||||
### Accio
|
||||
|
||||
Check out the [Accio](https://github.com/JamitLabs/Accio) docs on how to add a install.
|
||||
|
||||
Add the following to your Package.swift:
|
||||
|
||||
```swift
|
||||
.package(url: "https://github.com/daltoniam/Starscream.git", .upToNextMajor(from: "4.0.0")),
|
||||
```
|
||||
|
||||
Next, add `Starscream` to your App targets dependencies like so:
|
||||
|
||||
```swift
|
||||
.target(
|
||||
name: "App",
|
||||
dependencies: [
|
||||
"Starscream",
|
||||
]
|
||||
),
|
||||
```
|
||||
|
||||
Then run `accio update`.
|
||||
|
||||
### Rogue
|
||||
|
||||
First see the [installation docs](https://github.com/acmacalister/Rogue) for how to install Rogue.
|
||||
|
||||
To install Starscream run the command below in the directory you created the rogue file.
|
||||
|
||||
```
|
||||
rogue add https://github.com/daltoniam/Starscream
|
||||
```
|
||||
|
||||
Next open the `libs` folder and add the `Starscream.xcodeproj` to your Xcode project. Once that is complete, in your "Build Phases" add the `Starscream.framework` to your "Link Binary with Libraries" phase. Make sure to add the `libs` folder to your `.gitignore` file.
|
||||
|
||||
### Swift Package Manager
|
||||
|
||||
The [Swift Package Manager](https://swift.org/package-manager/) is a tool for automating the distribution of Swift code and is integrated into the `swift` compiler.
|
||||
|
||||
Once you have your Swift package set up, adding Starscream as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`.
|
||||
|
||||
```swift
|
||||
dependencies: [
|
||||
.Package(url: "https://github.com/daltoniam/Starscream.git", majorVersion: 4)
|
||||
]
|
||||
```
|
||||
|
||||
### Other
|
||||
|
||||
Simply grab the framework (either via git submodule or another package manager).
|
||||
|
||||
Add the `Starscream.xcodeproj` to your Xcode project. Once that is complete, in your "Build Phases" add the `Starscream.framework` to your "Link Binary with Libraries" phase.
|
||||
|
||||
### Add Copy Frameworks Phase
|
||||
|
||||
If you are running this in an OSX app or on a physical iOS device you will need to make sure you add the `Starscream.framework` to be included in your app bundle. To do this, in Xcode, navigate to the target configuration window by clicking on the blue project icon, and selecting the application target under the "Targets" heading in the sidebar. In the tab bar at the top of that window, open the "Build Phases" panel. Expand the "Link Binary with Libraries" group, and add `Starscream.framework`. Click on the + button at the top left of the panel and select "New Copy Files Phase". Rename this new phase to "Copy Frameworks", set the "Destination" to "Frameworks", and add `Starscream.framework` respectively.
|
||||
|
||||
## TODOs
|
||||
|
||||
- [ ] Proxy support
|
||||
|
||||
## License
|
||||
|
||||
Starscream is licensed under the Apache v2 License.
|
||||
|
||||
## Contact
|
||||
|
||||
### Dalton Cherry
|
||||
* https://github.com/daltoniam
|
||||
* http://twitter.com/daltoniam
|
||||
* http://daltoniam.com
|
||||
|
||||
### Austin Cherry ###
|
||||
* https://github.com/acmacalister
|
||||
* http://twitter.com/acmacalister
|
||||
* http://austincherry.me
|
29
Pods/Starscream/Sources/Compression/Compression.swift
generated
Normal file
29
Pods/Starscream/Sources/Compression/Compression.swift
generated
Normal file
@ -0,0 +1,29 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Compression.swift
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 2/4/19.
|
||||
// Copyright © 2019 Vluxe. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol CompressionHandler {
|
||||
func load(headers: [String: String])
|
||||
func decompress(data: Data, isFinal: Bool) -> Data?
|
||||
func compress(data: Data) -> Data?
|
||||
}
|
247
Pods/Starscream/Sources/Compression/WSCompression.swift
generated
Normal file
247
Pods/Starscream/Sources/Compression/WSCompression.swift
generated
Normal file
@ -0,0 +1,247 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// WSCompression.swift
|
||||
//
|
||||
// Created by Joseph Ross on 7/16/14.
|
||||
// Copyright © 2017 Joseph Ross & Vluxe. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Compression implementation is implemented in conformance with RFC 7692 Compression Extensions
|
||||
// for WebSocket: https://tools.ietf.org/html/rfc7692
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import Foundation
|
||||
import zlib
|
||||
|
||||
public class WSCompression: CompressionHandler {
|
||||
let headerWSExtensionName = "Sec-WebSocket-Extensions"
|
||||
var decompressor: Decompressor?
|
||||
var compressor: Compressor?
|
||||
var decompressorTakeOver = false
|
||||
var compressorTakeOver = false
|
||||
|
||||
public init() {
|
||||
|
||||
}
|
||||
|
||||
public func load(headers: [String: String]) {
|
||||
guard let extensionHeader = headers[headerWSExtensionName] else { return }
|
||||
decompressorTakeOver = false
|
||||
compressorTakeOver = false
|
||||
|
||||
let parts = extensionHeader.components(separatedBy: ";")
|
||||
for p in parts {
|
||||
let part = p.trimmingCharacters(in: .whitespaces)
|
||||
if part.hasPrefix("server_max_window_bits=") {
|
||||
let valString = part.components(separatedBy: "=")[1]
|
||||
if let val = Int(valString.trimmingCharacters(in: .whitespaces)) {
|
||||
decompressor = Decompressor(windowBits: val)
|
||||
}
|
||||
} else if part.hasPrefix("client_max_window_bits=") {
|
||||
let valString = part.components(separatedBy: "=")[1]
|
||||
if let val = Int(valString.trimmingCharacters(in: .whitespaces)) {
|
||||
compressor = Compressor(windowBits: val)
|
||||
}
|
||||
} else if part == "client_no_context_takeover" {
|
||||
compressorTakeOver = true
|
||||
} else if part == "server_no_context_takeover" {
|
||||
decompressorTakeOver = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func decompress(data: Data, isFinal: Bool) -> Data? {
|
||||
guard let decompressor = decompressor else { return nil }
|
||||
do {
|
||||
let decompressedData = try decompressor.decompress(data, finish: isFinal)
|
||||
if decompressorTakeOver {
|
||||
try decompressor.reset()
|
||||
}
|
||||
return decompressedData
|
||||
} catch {
|
||||
//do nothing with the error for now
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public func compress(data: Data) -> Data? {
|
||||
guard let compressor = compressor else { return nil }
|
||||
do {
|
||||
let compressedData = try compressor.compress(data)
|
||||
if compressorTakeOver {
|
||||
try compressor.reset()
|
||||
}
|
||||
return compressedData
|
||||
} catch {
|
||||
//do nothing with the error for now
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
class Decompressor {
|
||||
private var strm = z_stream()
|
||||
private var buffer = [UInt8](repeating: 0, count: 0x2000)
|
||||
private var inflateInitialized = false
|
||||
private let windowBits: Int
|
||||
|
||||
init?(windowBits: Int) {
|
||||
self.windowBits = windowBits
|
||||
guard initInflate() else { return nil }
|
||||
}
|
||||
|
||||
private func initInflate() -> Bool {
|
||||
if Z_OK == inflateInit2_(&strm, -CInt(windowBits),
|
||||
ZLIB_VERSION, CInt(MemoryLayout<z_stream>.size))
|
||||
{
|
||||
inflateInitialized = true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func reset() throws {
|
||||
teardownInflate()
|
||||
guard initInflate() else { throw WSError(type: .compressionError, message: "Error for decompressor on reset", code: 0) }
|
||||
}
|
||||
|
||||
func decompress(_ data: Data, finish: Bool) throws -> Data {
|
||||
return try data.withUnsafeBytes { (bytes: UnsafePointer<UInt8>) -> Data in
|
||||
return try decompress(bytes: bytes, count: data.count, finish: finish)
|
||||
}
|
||||
}
|
||||
|
||||
func decompress(bytes: UnsafePointer<UInt8>, count: Int, finish: Bool) throws -> Data {
|
||||
var decompressed = Data()
|
||||
try decompress(bytes: bytes, count: count, out: &decompressed)
|
||||
|
||||
if finish {
|
||||
let tail:[UInt8] = [0x00, 0x00, 0xFF, 0xFF]
|
||||
try decompress(bytes: tail, count: tail.count, out: &decompressed)
|
||||
}
|
||||
|
||||
return decompressed
|
||||
}
|
||||
|
||||
private func decompress(bytes: UnsafePointer<UInt8>, count: Int, out: inout Data) throws {
|
||||
var res: CInt = 0
|
||||
strm.next_in = UnsafeMutablePointer<UInt8>(mutating: bytes)
|
||||
strm.avail_in = CUnsignedInt(count)
|
||||
|
||||
repeat {
|
||||
buffer.withUnsafeMutableBytes { (bufferPtr) in
|
||||
strm.next_out = bufferPtr.bindMemory(to: UInt8.self).baseAddress
|
||||
strm.avail_out = CUnsignedInt(bufferPtr.count)
|
||||
|
||||
res = inflate(&strm, 0)
|
||||
}
|
||||
|
||||
let byteCount = buffer.count - Int(strm.avail_out)
|
||||
out.append(buffer, count: byteCount)
|
||||
} while res == Z_OK && strm.avail_out == 0
|
||||
|
||||
guard (res == Z_OK && strm.avail_out > 0)
|
||||
|| (res == Z_BUF_ERROR && Int(strm.avail_out) == buffer.count)
|
||||
else {
|
||||
throw WSError(type: .compressionError, message: "Error on decompressing", code: 0)
|
||||
}
|
||||
}
|
||||
|
||||
private func teardownInflate() {
|
||||
if inflateInitialized, Z_OK == inflateEnd(&strm) {
|
||||
inflateInitialized = false
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
teardownInflate()
|
||||
}
|
||||
}
|
||||
|
||||
class Compressor {
|
||||
private var strm = z_stream()
|
||||
private var buffer = [UInt8](repeating: 0, count: 0x2000)
|
||||
private var deflateInitialized = false
|
||||
private let windowBits: Int
|
||||
|
||||
init?(windowBits: Int) {
|
||||
self.windowBits = windowBits
|
||||
guard initDeflate() else { return nil }
|
||||
}
|
||||
|
||||
private func initDeflate() -> Bool {
|
||||
if Z_OK == deflateInit2_(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
|
||||
-CInt(windowBits), 8, Z_DEFAULT_STRATEGY,
|
||||
ZLIB_VERSION, CInt(MemoryLayout<z_stream>.size))
|
||||
{
|
||||
deflateInitialized = true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func reset() throws {
|
||||
teardownDeflate()
|
||||
guard initDeflate() else { throw WSError(type: .compressionError, message: "Error for compressor on reset", code: 0) }
|
||||
}
|
||||
|
||||
func compress(_ data: Data) throws -> Data {
|
||||
var compressed = Data()
|
||||
var res: CInt = 0
|
||||
data.withUnsafeBytes { (ptr:UnsafePointer<UInt8>) -> Void in
|
||||
strm.next_in = UnsafeMutablePointer<UInt8>(mutating: ptr)
|
||||
strm.avail_in = CUnsignedInt(data.count)
|
||||
|
||||
repeat {
|
||||
buffer.withUnsafeMutableBytes { (bufferPtr) in
|
||||
strm.next_out = bufferPtr.bindMemory(to: UInt8.self).baseAddress
|
||||
strm.avail_out = CUnsignedInt(bufferPtr.count)
|
||||
|
||||
res = deflate(&strm, Z_SYNC_FLUSH)
|
||||
}
|
||||
|
||||
let byteCount = buffer.count - Int(strm.avail_out)
|
||||
compressed.append(buffer, count: byteCount)
|
||||
}
|
||||
while res == Z_OK && strm.avail_out == 0
|
||||
|
||||
}
|
||||
|
||||
guard res == Z_OK && strm.avail_out > 0
|
||||
|| (res == Z_BUF_ERROR && Int(strm.avail_out) == buffer.count)
|
||||
else {
|
||||
throw WSError(type: .compressionError, message: "Error on compressing", code: 0)
|
||||
}
|
||||
|
||||
compressed.removeLast(4)
|
||||
return compressed
|
||||
}
|
||||
|
||||
private func teardownDeflate() {
|
||||
if deflateInitialized, Z_OK == deflateEnd(&strm) {
|
||||
deflateInitialized = false
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
teardownDeflate()
|
||||
}
|
||||
}
|
53
Pods/Starscream/Sources/DataBytes/Data+Extensions.swift
generated
Normal file
53
Pods/Starscream/Sources/DataBytes/Data+Extensions.swift
generated
Normal file
@ -0,0 +1,53 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Data+Extensions.swift
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 3/27/19.
|
||||
// Copyright © 2019 Vluxe. All rights reserved.
|
||||
//
|
||||
// Fix for deprecation warnings
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import Foundation
|
||||
|
||||
internal extension Data {
|
||||
struct ByteError: Swift.Error {}
|
||||
|
||||
#if swift(>=5.0)
|
||||
func withUnsafeBytes<ResultType, ContentType>(_ completion: (UnsafePointer<ContentType>) throws -> ResultType) rethrows -> ResultType {
|
||||
return try withUnsafeBytes {
|
||||
if let baseAddress = $0.baseAddress, $0.count > 0 {
|
||||
return try completion(baseAddress.assumingMemoryBound(to: ContentType.self))
|
||||
} else {
|
||||
throw ByteError()
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if swift(>=5.0)
|
||||
mutating func withUnsafeMutableBytes<ResultType, ContentType>(_ completion: (UnsafeMutablePointer<ContentType>) throws -> ResultType) rethrows -> ResultType {
|
||||
return try withUnsafeMutableBytes {
|
||||
if let baseAddress = $0.baseAddress, $0.count > 0 {
|
||||
return try completion(baseAddress.assumingMemoryBound(to: ContentType.self))
|
||||
} else {
|
||||
throw ByteError()
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
22
Pods/Starscream/Sources/Engine/Engine.swift
generated
Normal file
22
Pods/Starscream/Sources/Engine/Engine.swift
generated
Normal file
@ -0,0 +1,22 @@
|
||||
//
|
||||
// Engine.swift
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 6/15/19.
|
||||
// Copyright © 2019 Vluxe. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol EngineDelegate: class {
|
||||
func didReceive(event: WebSocketEvent)
|
||||
}
|
||||
|
||||
public protocol Engine {
|
||||
func register(delegate: EngineDelegate)
|
||||
func start(request: URLRequest)
|
||||
func stop(closeCode: UInt16)
|
||||
func forceStop()
|
||||
func write(data: Data, opcode: FrameOpCode, completion: (() -> ())?)
|
||||
func write(string: String, completion: (() -> ())?)
|
||||
}
|
96
Pods/Starscream/Sources/Engine/NativeEngine.swift
generated
Normal file
96
Pods/Starscream/Sources/Engine/NativeEngine.swift
generated
Normal file
@ -0,0 +1,96 @@
|
||||
//
|
||||
// NativeEngine.swift
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 6/15/19.
|
||||
// Copyright © 2019 Vluxe. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
|
||||
public class NativeEngine: NSObject, Engine, URLSessionDataDelegate, URLSessionWebSocketDelegate {
|
||||
private var task: URLSessionWebSocketTask?
|
||||
weak var delegate: EngineDelegate?
|
||||
|
||||
public func register(delegate: EngineDelegate) {
|
||||
self.delegate = delegate
|
||||
}
|
||||
|
||||
public func start(request: URLRequest) {
|
||||
let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil)
|
||||
task = session.webSocketTask(with: request)
|
||||
doRead()
|
||||
task?.resume()
|
||||
}
|
||||
|
||||
public func stop(closeCode: UInt16) {
|
||||
let closeCode = URLSessionWebSocketTask.CloseCode(rawValue: Int(closeCode)) ?? .normalClosure
|
||||
task?.cancel(with: closeCode, reason: nil)
|
||||
}
|
||||
|
||||
public func forceStop() {
|
||||
stop(closeCode: UInt16(URLSessionWebSocketTask.CloseCode.abnormalClosure.rawValue))
|
||||
}
|
||||
|
||||
public func write(string: String, completion: (() -> ())?) {
|
||||
task?.send(.string(string), completionHandler: { (error) in
|
||||
completion?()
|
||||
})
|
||||
}
|
||||
|
||||
public func write(data: Data, opcode: FrameOpCode, completion: (() -> ())?) {
|
||||
switch opcode {
|
||||
case .binaryFrame:
|
||||
task?.send(.data(data), completionHandler: { (error) in
|
||||
completion?()
|
||||
})
|
||||
case .textFrame:
|
||||
let text = String(data: data, encoding: .utf8)!
|
||||
write(string: text, completion: completion)
|
||||
case .ping:
|
||||
task?.sendPing(pongReceiveHandler: { (error) in
|
||||
completion?()
|
||||
})
|
||||
default:
|
||||
break //unsupported
|
||||
}
|
||||
}
|
||||
|
||||
private func doRead() {
|
||||
task?.receive { [weak self] (result) in
|
||||
switch result {
|
||||
case .success(let message):
|
||||
switch message {
|
||||
case .string(let string):
|
||||
self?.broadcast(event: .text(string))
|
||||
case .data(let data):
|
||||
self?.broadcast(event: .binary(data))
|
||||
@unknown default:
|
||||
break
|
||||
}
|
||||
break
|
||||
case .failure(let error):
|
||||
self?.broadcast(event: .error(error))
|
||||
}
|
||||
self?.doRead()
|
||||
}
|
||||
}
|
||||
|
||||
private func broadcast(event: WebSocketEvent) {
|
||||
delegate?.didReceive(event: event)
|
||||
}
|
||||
|
||||
public func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol protocol: String?) {
|
||||
let p = `protocol` ?? ""
|
||||
broadcast(event: .connected([HTTPWSHeader.protocolName: p]))
|
||||
}
|
||||
|
||||
public func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) {
|
||||
var r = ""
|
||||
if let d = reason {
|
||||
r = String(data: d, encoding: .utf8) ?? ""
|
||||
}
|
||||
broadcast(event: .disconnected(r, UInt16(closeCode.rawValue)))
|
||||
}
|
||||
}
|
234
Pods/Starscream/Sources/Engine/WSEngine.swift
generated
Normal file
234
Pods/Starscream/Sources/Engine/WSEngine.swift
generated
Normal file
@ -0,0 +1,234 @@
|
||||
//
|
||||
// WSEngine.swift
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 6/15/19.
|
||||
// Copyright © 2019 Vluxe. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class WSEngine: Engine, TransportEventClient, FramerEventClient,
|
||||
FrameCollectorDelegate, HTTPHandlerDelegate {
|
||||
private let transport: Transport
|
||||
private let framer: Framer
|
||||
private let httpHandler: HTTPHandler
|
||||
private let compressionHandler: CompressionHandler?
|
||||
private let certPinner: CertificatePinning?
|
||||
private let headerChecker: HeaderValidator
|
||||
private var request: URLRequest!
|
||||
|
||||
private let frameHandler = FrameCollector()
|
||||
private var didUpgrade = false
|
||||
private var secKeyValue = ""
|
||||
private let writeQueue = DispatchQueue(label: "com.vluxe.starscream.writequeue")
|
||||
private let mutex = DispatchSemaphore(value: 1)
|
||||
private var canSend = false
|
||||
|
||||
weak var delegate: EngineDelegate?
|
||||
public var respondToPingWithPong: Bool = true
|
||||
|
||||
public init(transport: Transport,
|
||||
certPinner: CertificatePinning? = nil,
|
||||
headerValidator: HeaderValidator = FoundationSecurity(),
|
||||
httpHandler: HTTPHandler = FoundationHTTPHandler(),
|
||||
framer: Framer = WSFramer(),
|
||||
compressionHandler: CompressionHandler? = nil) {
|
||||
self.transport = transport
|
||||
self.framer = framer
|
||||
self.httpHandler = httpHandler
|
||||
self.certPinner = certPinner
|
||||
self.headerChecker = headerValidator
|
||||
self.compressionHandler = compressionHandler
|
||||
framer.updateCompression(supports: compressionHandler != nil)
|
||||
frameHandler.delegate = self
|
||||
}
|
||||
|
||||
public func register(delegate: EngineDelegate) {
|
||||
self.delegate = delegate
|
||||
}
|
||||
|
||||
public func start(request: URLRequest) {
|
||||
mutex.wait()
|
||||
let isConnected = canSend
|
||||
mutex.signal()
|
||||
if isConnected {
|
||||
return
|
||||
}
|
||||
|
||||
self.request = request
|
||||
transport.register(delegate: self)
|
||||
framer.register(delegate: self)
|
||||
httpHandler.register(delegate: self)
|
||||
frameHandler.delegate = self
|
||||
guard let url = request.url else {
|
||||
return
|
||||
}
|
||||
transport.connect(url: url, timeout: request.timeoutInterval, certificatePinning: certPinner)
|
||||
}
|
||||
|
||||
public func stop(closeCode: UInt16 = CloseCode.normal.rawValue) {
|
||||
let capacity = MemoryLayout<UInt16>.size
|
||||
var pointer = [UInt8](repeating: 0, count: capacity)
|
||||
writeUint16(&pointer, offset: 0, value: closeCode)
|
||||
let payload = Data(bytes: pointer, count: MemoryLayout<UInt16>.size)
|
||||
write(data: payload, opcode: .connectionClose, completion: { [weak self] in
|
||||
self?.reset()
|
||||
self?.forceStop()
|
||||
})
|
||||
}
|
||||
|
||||
public func forceStop() {
|
||||
transport.disconnect()
|
||||
}
|
||||
|
||||
public func write(string: String, completion: (() -> ())?) {
|
||||
let data = string.data(using: .utf8)!
|
||||
write(data: data, opcode: .textFrame, completion: completion)
|
||||
}
|
||||
|
||||
public func write(data: Data, opcode: FrameOpCode, completion: (() -> ())?) {
|
||||
writeQueue.async { [weak self] in
|
||||
guard let s = self else { return }
|
||||
s.mutex.wait()
|
||||
let canWrite = s.canSend
|
||||
s.mutex.signal()
|
||||
if !canWrite {
|
||||
return
|
||||
}
|
||||
|
||||
var isCompressed = false
|
||||
var sendData = data
|
||||
if let compressedData = s.compressionHandler?.compress(data: data) {
|
||||
sendData = compressedData
|
||||
isCompressed = true
|
||||
}
|
||||
|
||||
let frameData = s.framer.createWriteFrame(opcode: opcode, payload: sendData, isCompressed: isCompressed)
|
||||
s.transport.write(data: frameData, completion: {_ in
|
||||
completion?()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - TransportEventClient
|
||||
|
||||
public func connectionChanged(state: ConnectionState) {
|
||||
switch state {
|
||||
case .connected:
|
||||
secKeyValue = HTTPWSHeader.generateWebSocketKey()
|
||||
let wsReq = HTTPWSHeader.createUpgrade(request: request, supportsCompression: framer.supportsCompression(), secKeyValue: secKeyValue)
|
||||
let data = httpHandler.convert(request: wsReq)
|
||||
transport.write(data: data, completion: {_ in })
|
||||
case .waiting:
|
||||
break
|
||||
case .failed(let error):
|
||||
handleError(error)
|
||||
case .viability(let isViable):
|
||||
broadcast(event: .viabilityChanged(isViable))
|
||||
case .shouldReconnect(let status):
|
||||
broadcast(event: .reconnectSuggested(status))
|
||||
case .receive(let data):
|
||||
if didUpgrade {
|
||||
framer.add(data: data)
|
||||
} else {
|
||||
let offset = httpHandler.parse(data: data)
|
||||
if offset > 0 {
|
||||
let extraData = data.subdata(in: offset..<data.endIndex)
|
||||
framer.add(data: extraData)
|
||||
}
|
||||
}
|
||||
case .cancelled:
|
||||
broadcast(event: .cancelled)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - HTTPHandlerDelegate
|
||||
|
||||
public func didReceiveHTTP(event: HTTPEvent) {
|
||||
switch event {
|
||||
case .success(let headers):
|
||||
if let error = headerChecker.validate(headers: headers, key: secKeyValue) {
|
||||
handleError(error)
|
||||
return
|
||||
}
|
||||
mutex.wait()
|
||||
didUpgrade = true
|
||||
canSend = true
|
||||
mutex.signal()
|
||||
compressionHandler?.load(headers: headers)
|
||||
if let url = request.url {
|
||||
HTTPCookie.cookies(withResponseHeaderFields: headers, for: url).forEach {
|
||||
HTTPCookieStorage.shared.setCookie($0)
|
||||
}
|
||||
}
|
||||
|
||||
broadcast(event: .connected(headers))
|
||||
case .failure(let error):
|
||||
handleError(error)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - FramerEventClient
|
||||
|
||||
public func frameProcessed(event: FrameEvent) {
|
||||
switch event {
|
||||
case .frame(let frame):
|
||||
frameHandler.add(frame: frame)
|
||||
case .error(let error):
|
||||
handleError(error)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - FrameCollectorDelegate
|
||||
|
||||
public func decompress(data: Data, isFinal: Bool) -> Data? {
|
||||
return compressionHandler?.decompress(data: data, isFinal: isFinal)
|
||||
}
|
||||
|
||||
public func didForm(event: FrameCollector.Event) {
|
||||
switch event {
|
||||
case .text(let string):
|
||||
broadcast(event: .text(string))
|
||||
case .binary(let data):
|
||||
broadcast(event: .binary(data))
|
||||
case .pong(let data):
|
||||
broadcast(event: .pong(data))
|
||||
case .ping(let data):
|
||||
broadcast(event: .ping(data))
|
||||
if respondToPingWithPong {
|
||||
write(data: data ?? Data(), opcode: .pong, completion: nil)
|
||||
}
|
||||
case .closed(let reason, let code):
|
||||
broadcast(event: .disconnected(reason, code))
|
||||
stop(closeCode: code)
|
||||
case .error(let error):
|
||||
handleError(error)
|
||||
}
|
||||
}
|
||||
|
||||
private func broadcast(event: WebSocketEvent) {
|
||||
delegate?.didReceive(event: event)
|
||||
}
|
||||
|
||||
//This call can be coming from a lot of different queues/threads.
|
||||
//be aware of that when modifying shared variables
|
||||
private func handleError(_ error: Error?) {
|
||||
if let wsError = error as? WSError {
|
||||
stop(closeCode: wsError.code)
|
||||
} else {
|
||||
stop()
|
||||
}
|
||||
|
||||
delegate?.didReceive(event: .error(error))
|
||||
}
|
||||
|
||||
private func reset() {
|
||||
mutex.wait()
|
||||
canSend = false
|
||||
didUpgrade = false
|
||||
mutex.signal()
|
||||
}
|
||||
|
||||
|
||||
}
|
123
Pods/Starscream/Sources/Framer/FoundationHTTPHandler.swift
generated
Normal file
123
Pods/Starscream/Sources/Framer/FoundationHTTPHandler.swift
generated
Normal file
@ -0,0 +1,123 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FoundationHTTPHandler.swift
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 1/25/19.
|
||||
// Copyright © 2019 Vluxe. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import Foundation
|
||||
#if os(watchOS)
|
||||
public typealias FoundationHTTPHandler = StringHTTPHandler
|
||||
#else
|
||||
public class FoundationHTTPHandler: HTTPHandler {
|
||||
|
||||
var buffer = Data()
|
||||
weak var delegate: HTTPHandlerDelegate?
|
||||
|
||||
public init() {
|
||||
|
||||
}
|
||||
|
||||
public func convert(request: URLRequest) -> Data {
|
||||
let msg = CFHTTPMessageCreateRequest(kCFAllocatorDefault, request.httpMethod! as CFString,
|
||||
request.url! as CFURL, kCFHTTPVersion1_1).takeRetainedValue()
|
||||
if let headers = request.allHTTPHeaderFields {
|
||||
for (aKey, aValue) in headers {
|
||||
CFHTTPMessageSetHeaderFieldValue(msg, aKey as CFString, aValue as CFString)
|
||||
}
|
||||
}
|
||||
if let body = request.httpBody {
|
||||
CFHTTPMessageSetBody(msg, body as CFData)
|
||||
}
|
||||
guard let data = CFHTTPMessageCopySerializedMessage(msg) else {
|
||||
return Data()
|
||||
}
|
||||
return data.takeRetainedValue() as Data
|
||||
}
|
||||
|
||||
public func parse(data: Data) -> Int {
|
||||
let offset = findEndOfHTTP(data: data)
|
||||
if offset > 0 {
|
||||
buffer.append(data.subdata(in: 0..<offset))
|
||||
} else {
|
||||
buffer.append(data)
|
||||
}
|
||||
if parseContent(data: buffer) {
|
||||
buffer = Data()
|
||||
}
|
||||
return offset
|
||||
}
|
||||
|
||||
//returns true when the buffer should be cleared
|
||||
func parseContent(data: Data) -> Bool {
|
||||
var pointer = [UInt8]()
|
||||
data.withUnsafeBytes { pointer.append(contentsOf: $0) }
|
||||
|
||||
let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, false).takeRetainedValue()
|
||||
if !CFHTTPMessageAppendBytes(response, pointer, data.count) {
|
||||
return false //not enough data, wait for more
|
||||
}
|
||||
if !CFHTTPMessageIsHeaderComplete(response) {
|
||||
return false //not enough data, wait for more
|
||||
}
|
||||
|
||||
let code = CFHTTPMessageGetResponseStatusCode(response)
|
||||
if code != HTTPWSHeader.switchProtocolCode {
|
||||
delegate?.didReceiveHTTP(event: .failure(HTTPUpgradeError.notAnUpgrade(code)))
|
||||
return true
|
||||
}
|
||||
|
||||
if let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) {
|
||||
let nsHeaders = cfHeaders.takeRetainedValue() as NSDictionary
|
||||
var headers = [String: String]()
|
||||
for (key, value) in nsHeaders {
|
||||
if let key = key as? String, let value = value as? String {
|
||||
headers[key] = value
|
||||
}
|
||||
}
|
||||
delegate?.didReceiveHTTP(event: .success(headers))
|
||||
return true
|
||||
}
|
||||
|
||||
delegate?.didReceiveHTTP(event: .failure(HTTPUpgradeError.invalidData))
|
||||
return true
|
||||
}
|
||||
|
||||
public func register(delegate: HTTPHandlerDelegate) {
|
||||
self.delegate = delegate
|
||||
}
|
||||
|
||||
private func findEndOfHTTP(data: Data) -> Int {
|
||||
let endBytes = [UInt8(ascii: "\r"), UInt8(ascii: "\n"), UInt8(ascii: "\r"), UInt8(ascii: "\n")]
|
||||
var pointer = [UInt8]()
|
||||
data.withUnsafeBytes { pointer.append(contentsOf: $0) }
|
||||
var k = 0
|
||||
for i in 0..<data.count {
|
||||
if pointer[i] == endBytes[k] {
|
||||
k += 1
|
||||
if k == 4 {
|
||||
return i + 1
|
||||
}
|
||||
} else {
|
||||
k = 0
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
}
|
||||
#endif
|
99
Pods/Starscream/Sources/Framer/FoundationHTTPServerHandler.swift
generated
Normal file
99
Pods/Starscream/Sources/Framer/FoundationHTTPServerHandler.swift
generated
Normal file
@ -0,0 +1,99 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FoundationHTTPHandler.swift
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 4/2/19.
|
||||
// Copyright © 2019 Vluxe. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import Foundation
|
||||
|
||||
public class FoundationHTTPServerHandler: HTTPServerHandler {
|
||||
var buffer = Data()
|
||||
weak var delegate: HTTPServerDelegate?
|
||||
let getVerb: NSString = "GET"
|
||||
|
||||
public func register(delegate: HTTPServerDelegate) {
|
||||
self.delegate = delegate
|
||||
}
|
||||
|
||||
public func createResponse(headers: [String: String]) -> Data {
|
||||
#if os(watchOS)
|
||||
//TODO: build response header
|
||||
return Data()
|
||||
#else
|
||||
let response = CFHTTPMessageCreateResponse(kCFAllocatorDefault, HTTPWSHeader.switchProtocolCode,
|
||||
nil, kCFHTTPVersion1_1).takeRetainedValue()
|
||||
|
||||
//TODO: add other values to make a proper response here...
|
||||
//TODO: also sec key thing (Sec-WebSocket-Key)
|
||||
for (key, value) in headers {
|
||||
CFHTTPMessageSetHeaderFieldValue(response, key as CFString, value as CFString)
|
||||
}
|
||||
guard let cfData = CFHTTPMessageCopySerializedMessage(response)?.takeRetainedValue() else {
|
||||
return Data()
|
||||
}
|
||||
return cfData as Data
|
||||
#endif
|
||||
}
|
||||
|
||||
public func parse(data: Data) {
|
||||
buffer.append(data)
|
||||
if parseContent(data: buffer) {
|
||||
buffer = Data()
|
||||
}
|
||||
}
|
||||
|
||||
//returns true when the buffer should be cleared
|
||||
func parseContent(data: Data) -> Bool {
|
||||
var pointer = [UInt8]()
|
||||
data.withUnsafeBytes { pointer.append(contentsOf: $0) }
|
||||
#if os(watchOS)
|
||||
//TODO: parse data
|
||||
return false
|
||||
#else
|
||||
let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true).takeRetainedValue()
|
||||
if !CFHTTPMessageAppendBytes(response, pointer, data.count) {
|
||||
return false //not enough data, wait for more
|
||||
}
|
||||
if !CFHTTPMessageIsHeaderComplete(response) {
|
||||
return false //not enough data, wait for more
|
||||
}
|
||||
if let method = CFHTTPMessageCopyRequestMethod(response)?.takeRetainedValue() {
|
||||
if (method as NSString) != getVerb {
|
||||
delegate?.didReceive(event: .failure(HTTPUpgradeError.invalidData))
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) {
|
||||
let nsHeaders = cfHeaders.takeRetainedValue() as NSDictionary
|
||||
var headers = [String: String]()
|
||||
for (key, value) in nsHeaders {
|
||||
if let key = key as? String, let value = value as? String {
|
||||
headers[key] = value
|
||||
}
|
||||
}
|
||||
delegate?.didReceive(event: .success(headers))
|
||||
return true
|
||||
}
|
||||
|
||||
delegate?.didReceive(event: .failure(HTTPUpgradeError.invalidData))
|
||||
return true
|
||||
#endif
|
||||
}
|
||||
}
|
107
Pods/Starscream/Sources/Framer/FrameCollector.swift
generated
Normal file
107
Pods/Starscream/Sources/Framer/FrameCollector.swift
generated
Normal file
@ -0,0 +1,107 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FrameCollector.swift
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 1/24/19.
|
||||
// Copyright © 2019 Vluxe. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol FrameCollectorDelegate: class {
|
||||
func didForm(event: FrameCollector.Event)
|
||||
func decompress(data: Data, isFinal: Bool) -> Data?
|
||||
}
|
||||
|
||||
public class FrameCollector {
|
||||
public enum Event {
|
||||
case text(String)
|
||||
case binary(Data)
|
||||
case pong(Data?)
|
||||
case ping(Data?)
|
||||
case error(Error)
|
||||
case closed(String, UInt16)
|
||||
}
|
||||
weak var delegate: FrameCollectorDelegate?
|
||||
var buffer = Data()
|
||||
var frameCount = 0
|
||||
var isText = false //was the first frame a text frame or a binary frame?
|
||||
var needsDecompression = false
|
||||
|
||||
public func add(frame: Frame) {
|
||||
//check single frame action and out of order frames
|
||||
if frame.opcode == .connectionClose {
|
||||
var code = frame.closeCode
|
||||
var reason = "connection closed by server"
|
||||
if let customCloseReason = String(data: frame.payload, encoding: .utf8) {
|
||||
reason = customCloseReason
|
||||
} else {
|
||||
code = CloseCode.protocolError.rawValue
|
||||
}
|
||||
delegate?.didForm(event: .closed(reason, code))
|
||||
return
|
||||
} else if frame.opcode == .pong {
|
||||
delegate?.didForm(event: .pong(frame.payload))
|
||||
return
|
||||
} else if frame.opcode == .ping {
|
||||
delegate?.didForm(event: .ping(frame.payload))
|
||||
return
|
||||
} else if frame.opcode == .continueFrame && frameCount == 0 {
|
||||
let errCode = CloseCode.protocolError.rawValue
|
||||
delegate?.didForm(event: .error(WSError(type: .protocolError, message: "first frame can't be a continue frame", code: errCode)))
|
||||
reset()
|
||||
return
|
||||
} else if frameCount > 0 && frame.opcode != .continueFrame {
|
||||
let errCode = CloseCode.protocolError.rawValue
|
||||
delegate?.didForm(event: .error(WSError(type: .protocolError, message: "second and beyond of fragment message must be a continue frame", code: errCode)))
|
||||
reset()
|
||||
return
|
||||
}
|
||||
if frameCount == 0 {
|
||||
isText = frame.opcode == .textFrame
|
||||
needsDecompression = frame.needsDecompression
|
||||
}
|
||||
|
||||
let payload: Data
|
||||
if needsDecompression {
|
||||
payload = delegate?.decompress(data: frame.payload, isFinal: frame.isFin) ?? frame.payload
|
||||
} else {
|
||||
payload = frame.payload
|
||||
}
|
||||
buffer.append(payload)
|
||||
frameCount += 1
|
||||
|
||||
if frame.isFin {
|
||||
if isText {
|
||||
if let string = String(data: buffer, encoding: .utf8) {
|
||||
delegate?.didForm(event: .text(string))
|
||||
} else {
|
||||
let errCode = CloseCode.protocolError.rawValue
|
||||
delegate?.didForm(event: .error(WSError(type: .protocolError, message: "not valid UTF-8 data", code: errCode)))
|
||||
}
|
||||
} else {
|
||||
delegate?.didForm(event: .binary(buffer))
|
||||
}
|
||||
reset()
|
||||
}
|
||||
}
|
||||
|
||||
func reset() {
|
||||
buffer = Data()
|
||||
frameCount = 0
|
||||
}
|
||||
}
|
365
Pods/Starscream/Sources/Framer/Framer.swift
generated
Normal file
365
Pods/Starscream/Sources/Framer/Framer.swift
generated
Normal file
@ -0,0 +1,365 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Framer.swift
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 1/23/19.
|
||||
// Copyright © 2019 Vluxe. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import Foundation
|
||||
|
||||
let FinMask: UInt8 = 0x80
|
||||
let OpCodeMask: UInt8 = 0x0F
|
||||
let RSVMask: UInt8 = 0x70
|
||||
let RSV1Mask: UInt8 = 0x40
|
||||
let MaskMask: UInt8 = 0x80
|
||||
let PayloadLenMask: UInt8 = 0x7F
|
||||
let MaxFrameSize: Int = 32
|
||||
|
||||
// Standard WebSocket close codes
|
||||
public enum CloseCode: UInt16 {
|
||||
case normal = 1000
|
||||
case goingAway = 1001
|
||||
case protocolError = 1002
|
||||
case protocolUnhandledType = 1003
|
||||
// 1004 reserved.
|
||||
case noStatusReceived = 1005
|
||||
//1006 reserved.
|
||||
case encoding = 1007
|
||||
case policyViolated = 1008
|
||||
case messageTooBig = 1009
|
||||
}
|
||||
|
||||
public enum FrameOpCode: UInt8 {
|
||||
case continueFrame = 0x0
|
||||
case textFrame = 0x1
|
||||
case binaryFrame = 0x2
|
||||
// 3-7 are reserved.
|
||||
case connectionClose = 0x8
|
||||
case ping = 0x9
|
||||
case pong = 0xA
|
||||
// B-F reserved.
|
||||
case unknown = 100
|
||||
}
|
||||
|
||||
public struct Frame {
|
||||
let isFin: Bool
|
||||
let needsDecompression: Bool
|
||||
let isMasked: Bool
|
||||
let opcode: FrameOpCode
|
||||
let payloadLength: UInt64
|
||||
let payload: Data
|
||||
let closeCode: UInt16 //only used by connectionClose opcode
|
||||
}
|
||||
|
||||
public enum FrameEvent {
|
||||
case frame(Frame)
|
||||
case error(Error)
|
||||
}
|
||||
|
||||
public protocol FramerEventClient: class {
|
||||
func frameProcessed(event: FrameEvent)
|
||||
}
|
||||
|
||||
public protocol Framer {
|
||||
func add(data: Data)
|
||||
func register(delegate: FramerEventClient)
|
||||
func createWriteFrame(opcode: FrameOpCode, payload: Data, isCompressed: Bool) -> Data
|
||||
func updateCompression(supports: Bool)
|
||||
func supportsCompression() -> Bool
|
||||
}
|
||||
|
||||
public class WSFramer: Framer {
|
||||
private let queue = DispatchQueue(label: "com.vluxe.starscream.wsframer", attributes: [])
|
||||
private weak var delegate: FramerEventClient?
|
||||
private var buffer = Data()
|
||||
public var compressionEnabled = false
|
||||
private let isServer: Bool
|
||||
|
||||
public init(isServer: Bool = false) {
|
||||
self.isServer = isServer
|
||||
}
|
||||
|
||||
public func updateCompression(supports: Bool) {
|
||||
compressionEnabled = supports
|
||||
}
|
||||
|
||||
public func supportsCompression() -> Bool {
|
||||
return compressionEnabled
|
||||
}
|
||||
|
||||
enum ProcessEvent {
|
||||
case needsMoreData
|
||||
case processedFrame(Frame, Int)
|
||||
case failed(Error)
|
||||
}
|
||||
|
||||
public func add(data: Data) {
|
||||
queue.async { [weak self] in
|
||||
self?.buffer.append(data)
|
||||
while(true) {
|
||||
let event = self?.process() ?? .needsMoreData
|
||||
switch event {
|
||||
case .needsMoreData:
|
||||
return
|
||||
case .processedFrame(let frame, let split):
|
||||
guard let s = self else { return }
|
||||
s.delegate?.frameProcessed(event: .frame(frame))
|
||||
if split >= s.buffer.count {
|
||||
s.buffer = Data()
|
||||
return
|
||||
}
|
||||
s.buffer = s.buffer.advanced(by: split)
|
||||
case .failed(let error):
|
||||
self?.delegate?.frameProcessed(event: .error(error))
|
||||
self?.buffer = Data()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func register(delegate: FramerEventClient) {
|
||||
self.delegate = delegate
|
||||
}
|
||||
|
||||
private func process() -> ProcessEvent {
|
||||
if buffer.count < 2 {
|
||||
return .needsMoreData
|
||||
}
|
||||
var pointer = [UInt8]()
|
||||
buffer.withUnsafeBytes { pointer.append(contentsOf: $0) }
|
||||
|
||||
let isFin = (FinMask & pointer[0])
|
||||
let opcodeRawValue = (OpCodeMask & pointer[0])
|
||||
let opcode = FrameOpCode(rawValue: opcodeRawValue) ?? .unknown
|
||||
let isMasked = (MaskMask & pointer[1])
|
||||
let payloadLen = (PayloadLenMask & pointer[1])
|
||||
let RSV1 = (RSVMask & pointer[0])
|
||||
var needsDecompression = false
|
||||
|
||||
if compressionEnabled && opcode != .continueFrame {
|
||||
needsDecompression = (RSV1Mask & pointer[0]) > 0
|
||||
}
|
||||
if !isServer && (isMasked > 0 || RSV1 > 0) && opcode != .pong && !needsDecompression {
|
||||
let errCode = CloseCode.protocolError.rawValue
|
||||
return .failed(WSError(type: .protocolError, message: "masked and rsv data is not currently supported", code: errCode))
|
||||
}
|
||||
let isControlFrame = (opcode == .connectionClose || opcode == .ping)
|
||||
if !isControlFrame && (opcode != .binaryFrame && opcode != .continueFrame &&
|
||||
opcode != .textFrame && opcode != .pong) {
|
||||
let errCode = CloseCode.protocolError.rawValue
|
||||
return .failed(WSError(type: .protocolError, message: "unknown opcode: \(opcodeRawValue)", code: errCode))
|
||||
}
|
||||
if isControlFrame && isFin == 0 {
|
||||
let errCode = CloseCode.protocolError.rawValue
|
||||
return .failed(WSError(type: .protocolError, message: "control frames can't be fragmented", code: errCode))
|
||||
}
|
||||
|
||||
var offset = 2
|
||||
|
||||
if isControlFrame && payloadLen > 125 {
|
||||
return .failed(WSError(type: .protocolError, message: "payload length is longer than allowed for a control frame", code: CloseCode.protocolError.rawValue))
|
||||
}
|
||||
|
||||
var dataLength = UInt64(payloadLen)
|
||||
var closeCode = CloseCode.normal.rawValue
|
||||
if opcode == .connectionClose {
|
||||
if payloadLen == 1 {
|
||||
closeCode = CloseCode.protocolError.rawValue
|
||||
dataLength = 0
|
||||
} else if payloadLen > 1 {
|
||||
if pointer.count < 4 {
|
||||
return .needsMoreData
|
||||
}
|
||||
let size = MemoryLayout<UInt16>.size
|
||||
closeCode = pointer.readUint16(offset: offset)
|
||||
offset += size
|
||||
dataLength -= UInt64(size)
|
||||
if closeCode < 1000 || (closeCode > 1003 && closeCode < 1007) || (closeCode > 1013 && closeCode < 3000) {
|
||||
closeCode = CloseCode.protocolError.rawValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if payloadLen == 127 {
|
||||
let size = MemoryLayout<UInt64>.size
|
||||
if size + offset > pointer.count {
|
||||
return .needsMoreData
|
||||
}
|
||||
dataLength = pointer.readUint64(offset: offset)
|
||||
offset += size
|
||||
} else if payloadLen == 126 {
|
||||
let size = MemoryLayout<UInt16>.size
|
||||
if size + offset > pointer.count {
|
||||
return .needsMoreData
|
||||
}
|
||||
dataLength = UInt64(pointer.readUint16(offset: offset))
|
||||
offset += size
|
||||
}
|
||||
|
||||
let maskStart = offset
|
||||
if isServer {
|
||||
offset += MemoryLayout<UInt32>.size
|
||||
}
|
||||
|
||||
if dataLength > (pointer.count - offset) {
|
||||
return .needsMoreData
|
||||
}
|
||||
|
||||
//I don't like this cast, but Data's count returns an Int.
|
||||
//Might be a problem with huge payloads. Need to revisit.
|
||||
let readDataLength = Int(dataLength)
|
||||
|
||||
let payload: Data
|
||||
if readDataLength == 0 {
|
||||
payload = Data()
|
||||
} else {
|
||||
if isServer {
|
||||
payload = pointer.unmaskData(maskStart: maskStart, offset: offset, length: readDataLength)
|
||||
} else {
|
||||
let end = offset + readDataLength
|
||||
payload = Data(pointer[offset..<end])
|
||||
}
|
||||
}
|
||||
offset += readDataLength
|
||||
|
||||
let frame = Frame(isFin: isFin > 0, needsDecompression: needsDecompression, isMasked: isMasked > 0, opcode: opcode, payloadLength: dataLength, payload: payload, closeCode: closeCode)
|
||||
return .processedFrame(frame, offset)
|
||||
}
|
||||
|
||||
public func createWriteFrame(opcode: FrameOpCode, payload: Data, isCompressed: Bool) -> Data {
|
||||
let payloadLength = payload.count
|
||||
|
||||
let capacity = payloadLength + MaxFrameSize
|
||||
var pointer = [UInt8](repeating: 0, count: capacity)
|
||||
|
||||
//set the framing info
|
||||
pointer[0] = FinMask | opcode.rawValue
|
||||
if isCompressed {
|
||||
pointer[0] |= RSV1Mask
|
||||
}
|
||||
|
||||
var offset = 2 //skip pass the framing info
|
||||
if payloadLength < 126 {
|
||||
pointer[1] = UInt8(payloadLength)
|
||||
} else if payloadLength <= Int(UInt16.max) {
|
||||
pointer[1] = 126
|
||||
writeUint16(&pointer, offset: offset, value: UInt16(payloadLength))
|
||||
offset += MemoryLayout<UInt16>.size
|
||||
} else {
|
||||
pointer[1] = 127
|
||||
writeUint64(&pointer, offset: offset, value: UInt64(payloadLength))
|
||||
offset += MemoryLayout<UInt64>.size
|
||||
}
|
||||
|
||||
//clients are required to mask the payload data, but server don't according to the RFC
|
||||
if !isServer {
|
||||
pointer[1] |= MaskMask
|
||||
|
||||
//write the random mask key in
|
||||
let maskKey: UInt32 = UInt32.random(in: 0...UInt32.max)
|
||||
|
||||
writeUint32(&pointer, offset: offset, value: maskKey)
|
||||
let maskStart = offset
|
||||
offset += MemoryLayout<UInt32>.size
|
||||
|
||||
//now write the payload data in
|
||||
for i in 0..<payloadLength {
|
||||
pointer[offset] = payload[i] ^ pointer[maskStart + (i % MemoryLayout<UInt32>.size)]
|
||||
offset += 1
|
||||
}
|
||||
} else {
|
||||
for i in 0..<payloadLength {
|
||||
pointer[offset] = payload[i]
|
||||
offset += 1
|
||||
}
|
||||
}
|
||||
return Data(pointer[0..<offset])
|
||||
}
|
||||
}
|
||||
|
||||
/// MARK: - functions for simpler array buffer reading and writing
|
||||
|
||||
public protocol MyWSArrayType {}
|
||||
extension UInt8: MyWSArrayType {}
|
||||
|
||||
public extension Array where Element: MyWSArrayType & UnsignedInteger {
|
||||
|
||||
/**
|
||||
Read a UInt16 from a buffer.
|
||||
- parameter offset: is the offset index to start the read from (e.g. buffer[0], buffer[1], etc).
|
||||
- returns: a UInt16 of the value from the buffer
|
||||
*/
|
||||
func readUint16(offset: Int) -> UInt16 {
|
||||
return (UInt16(self[offset + 0]) << 8) | UInt16(self[offset + 1])
|
||||
}
|
||||
|
||||
/**
|
||||
Read a UInt64 from a buffer.
|
||||
- parameter offset: is the offset index to start the read from (e.g. buffer[0], buffer[1], etc).
|
||||
- returns: a UInt64 of the value from the buffer
|
||||
*/
|
||||
func readUint64(offset: Int) -> UInt64 {
|
||||
var value = UInt64(0)
|
||||
for i in 0...7 {
|
||||
value = (value << 8) | UInt64(self[offset + i])
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func unmaskData(maskStart: Int, offset: Int, length: Int) -> Data {
|
||||
var unmaskedBytes = [UInt8](repeating: 0, count: length)
|
||||
let maskSize = MemoryLayout<UInt32>.size
|
||||
for i in 0..<length {
|
||||
unmaskedBytes[i] = UInt8(self[offset + i] ^ self[maskStart + (i % maskSize)])
|
||||
}
|
||||
return Data(unmaskedBytes)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Write a UInt16 to the buffer. It fills the 2 array "slots" of the UInt8 array.
|
||||
- parameter buffer: is the UInt8 array (pointer) to write the value too.
|
||||
- parameter offset: is the offset index to start the write from (e.g. buffer[0], buffer[1], etc).
|
||||
*/
|
||||
public func writeUint16( _ buffer: inout [UInt8], offset: Int, value: UInt16) {
|
||||
buffer[offset + 0] = UInt8(value >> 8)
|
||||
buffer[offset + 1] = UInt8(value & 0xff)
|
||||
}
|
||||
|
||||
/**
|
||||
Write a UInt32 to the buffer. It fills the 4 array "slots" of the UInt8 array.
|
||||
- parameter buffer: is the UInt8 array (pointer) to write the value too.
|
||||
- parameter offset: is the offset index to start the write from (e.g. buffer[0], buffer[1], etc).
|
||||
*/
|
||||
public func writeUint32( _ buffer: inout [UInt8], offset: Int, value: UInt32) {
|
||||
for i in 0...3 {
|
||||
buffer[offset + i] = UInt8((value >> (8*UInt32(3 - i))) & 0xff)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Write a UInt64 to the buffer. It fills the 8 array "slots" of the UInt8 array.
|
||||
- parameter buffer: is the UInt8 array (pointer) to write the value too.
|
||||
- parameter offset: is the offset index to start the write from (e.g. buffer[0], buffer[1], etc).
|
||||
*/
|
||||
public func writeUint64( _ buffer: inout [UInt8], offset: Int, value: UInt64) {
|
||||
for i in 0...7 {
|
||||
buffer[offset + i] = UInt8((value >> (8*UInt64(7 - i))) & 0xff)
|
||||
}
|
||||
}
|
148
Pods/Starscream/Sources/Framer/HTTPHandler.swift
generated
Normal file
148
Pods/Starscream/Sources/Framer/HTTPHandler.swift
generated
Normal file
@ -0,0 +1,148 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// HTTPHandler.swift
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 1/24/19.
|
||||
// Copyright © 2019 Vluxe. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum HTTPUpgradeError: Error {
|
||||
case notAnUpgrade(Int)
|
||||
case invalidData
|
||||
}
|
||||
|
||||
public struct HTTPWSHeader {
|
||||
static let upgradeName = "Upgrade"
|
||||
static let upgradeValue = "websocket"
|
||||
static let hostName = "Host"
|
||||
static let connectionName = "Connection"
|
||||
static let connectionValue = "Upgrade"
|
||||
static let protocolName = "Sec-WebSocket-Protocol"
|
||||
static let versionName = "Sec-WebSocket-Version"
|
||||
static let versionValue = "13"
|
||||
static let extensionName = "Sec-WebSocket-Extensions"
|
||||
static let keyName = "Sec-WebSocket-Key"
|
||||
static let originName = "Origin"
|
||||
static let acceptName = "Sec-WebSocket-Accept"
|
||||
static let switchProtocolCode = 101
|
||||
static let defaultSSLSchemes = ["wss", "https"]
|
||||
|
||||
/// Creates a new URLRequest based off the source URLRequest.
|
||||
/// - Parameter request: the request to "upgrade" the WebSocket request by adding headers.
|
||||
/// - Parameter supportsCompression: set if the client support text compression.
|
||||
/// - Parameter secKeyName: the security key to use in the WebSocket request. https://tools.ietf.org/html/rfc6455#section-1.3
|
||||
/// - returns: A URLRequest request to be converted to data and sent to the server.
|
||||
public static func createUpgrade(request: URLRequest, supportsCompression: Bool, secKeyValue: String) -> URLRequest {
|
||||
guard let url = request.url, let parts = url.getParts() else {
|
||||
return request
|
||||
}
|
||||
|
||||
var req = request
|
||||
if request.value(forHTTPHeaderField: HTTPWSHeader.originName) == nil {
|
||||
var origin = url.absoluteString
|
||||
if let hostUrl = URL (string: "/", relativeTo: url) {
|
||||
origin = hostUrl.absoluteString
|
||||
origin.remove(at: origin.index(before: origin.endIndex))
|
||||
}
|
||||
req.setValue(origin, forHTTPHeaderField: HTTPWSHeader.originName)
|
||||
}
|
||||
req.setValue(HTTPWSHeader.upgradeValue, forHTTPHeaderField: HTTPWSHeader.upgradeName)
|
||||
req.setValue(HTTPWSHeader.connectionValue, forHTTPHeaderField: HTTPWSHeader.connectionName)
|
||||
req.setValue(HTTPWSHeader.versionValue, forHTTPHeaderField: HTTPWSHeader.versionName)
|
||||
req.setValue(secKeyValue, forHTTPHeaderField: HTTPWSHeader.keyName)
|
||||
|
||||
if let cookies = HTTPCookieStorage.shared.cookies(for: url), !cookies.isEmpty {
|
||||
let headers = HTTPCookie.requestHeaderFields(with: cookies)
|
||||
for (key, val) in headers {
|
||||
req.setValue(val, forHTTPHeaderField: key)
|
||||
}
|
||||
}
|
||||
|
||||
if supportsCompression {
|
||||
let val = "permessage-deflate; client_max_window_bits; server_max_window_bits=15"
|
||||
req.setValue(val, forHTTPHeaderField: HTTPWSHeader.extensionName)
|
||||
}
|
||||
let hostValue = req.allHTTPHeaderFields?[HTTPWSHeader.hostName] ?? "\(parts.host):\(parts.port)"
|
||||
req.setValue(hostValue, forHTTPHeaderField: HTTPWSHeader.hostName)
|
||||
return req
|
||||
}
|
||||
|
||||
// generateWebSocketKey 16 random characters between a-z and return them as a base64 string
|
||||
public static func generateWebSocketKey() -> String {
|
||||
return Data((0..<16).map{ _ in UInt8.random(in: 97...122) }).base64EncodedString()
|
||||
}
|
||||
}
|
||||
|
||||
public enum HTTPEvent {
|
||||
case success([String: String])
|
||||
case failure(Error)
|
||||
}
|
||||
|
||||
public protocol HTTPHandlerDelegate: class {
|
||||
func didReceiveHTTP(event: HTTPEvent)
|
||||
}
|
||||
|
||||
public protocol HTTPHandler {
|
||||
func register(delegate: HTTPHandlerDelegate)
|
||||
func convert(request: URLRequest) -> Data
|
||||
func parse(data: Data) -> Int
|
||||
}
|
||||
|
||||
public protocol HTTPServerDelegate: class {
|
||||
func didReceive(event: HTTPEvent)
|
||||
}
|
||||
|
||||
public protocol HTTPServerHandler {
|
||||
func register(delegate: HTTPServerDelegate)
|
||||
func parse(data: Data)
|
||||
func createResponse(headers: [String: String]) -> Data
|
||||
}
|
||||
|
||||
public struct URLParts {
|
||||
let port: Int
|
||||
let host: String
|
||||
let isTLS: Bool
|
||||
}
|
||||
|
||||
public extension URL {
|
||||
/// isTLSScheme returns true if the scheme is https or wss
|
||||
var isTLSScheme: Bool {
|
||||
guard let scheme = self.scheme else {
|
||||
return false
|
||||
}
|
||||
return HTTPWSHeader.defaultSSLSchemes.contains(scheme)
|
||||
}
|
||||
|
||||
/// getParts pulls host and port from the url.
|
||||
func getParts() -> URLParts? {
|
||||
guard let host = self.host else {
|
||||
return nil // no host, this isn't a valid url
|
||||
}
|
||||
let isTLS = isTLSScheme
|
||||
var port = self.port ?? 0
|
||||
if self.port == nil {
|
||||
if isTLS {
|
||||
port = 443
|
||||
} else {
|
||||
port = 80
|
||||
}
|
||||
}
|
||||
return URLParts(port: port, host: host, isTLS: isTLS)
|
||||
}
|
||||
}
|
143
Pods/Starscream/Sources/Framer/StringHTTPHandler.swift
generated
Normal file
143
Pods/Starscream/Sources/Framer/StringHTTPHandler.swift
generated
Normal file
@ -0,0 +1,143 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// StringHTTPHandler.swift
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 8/25/19.
|
||||
// Copyright © 2019 Vluxe. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import Foundation
|
||||
|
||||
public class StringHTTPHandler: HTTPHandler {
|
||||
|
||||
var buffer = Data()
|
||||
weak var delegate: HTTPHandlerDelegate?
|
||||
|
||||
public init() {
|
||||
|
||||
}
|
||||
|
||||
public func convert(request: URLRequest) -> Data {
|
||||
guard let url = request.url else {
|
||||
return Data()
|
||||
}
|
||||
|
||||
var path = url.absoluteString
|
||||
let offset = (url.scheme?.count ?? 2) + 3
|
||||
path = String(path[path.index(path.startIndex, offsetBy: offset)..<path.endIndex])
|
||||
if let range = path.range(of: "/") {
|
||||
path = String(path[range.lowerBound..<path.endIndex])
|
||||
} else {
|
||||
path = "/"
|
||||
if let query = url.query {
|
||||
path += "?" + query
|
||||
}
|
||||
}
|
||||
|
||||
var httpBody = "\(request.httpMethod ?? "GET") \(path) HTTP/1.1\r\n"
|
||||
if let headers = request.allHTTPHeaderFields {
|
||||
for (key, val) in headers {
|
||||
httpBody += "\(key): \(val)\r\n"
|
||||
}
|
||||
}
|
||||
httpBody += "\r\n"
|
||||
|
||||
guard var data = httpBody.data(using: .utf8) else {
|
||||
return Data()
|
||||
}
|
||||
|
||||
if let body = request.httpBody {
|
||||
data.append(body)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
public func parse(data: Data) -> Int {
|
||||
let offset = findEndOfHTTP(data: data)
|
||||
if offset > 0 {
|
||||
buffer.append(data.subdata(in: 0..<offset))
|
||||
if parseContent(data: buffer) {
|
||||
buffer = Data()
|
||||
}
|
||||
} else {
|
||||
buffer.append(data)
|
||||
}
|
||||
return offset
|
||||
}
|
||||
|
||||
//returns true when the buffer should be cleared
|
||||
func parseContent(data: Data) -> Bool {
|
||||
guard let str = String(data: data, encoding: .utf8) else {
|
||||
delegate?.didReceiveHTTP(event: .failure(HTTPUpgradeError.invalidData))
|
||||
return true
|
||||
}
|
||||
let splitArr = str.components(separatedBy: "\r\n")
|
||||
var code = -1
|
||||
var i = 0
|
||||
var headers = [String: String]()
|
||||
for str in splitArr {
|
||||
if i == 0 {
|
||||
let responseSplit = str.components(separatedBy: .whitespaces)
|
||||
guard responseSplit.count > 1 else {
|
||||
delegate?.didReceiveHTTP(event: .failure(HTTPUpgradeError.invalidData))
|
||||
return true
|
||||
}
|
||||
if let c = Int(responseSplit[1]) {
|
||||
code = c
|
||||
}
|
||||
} else {
|
||||
guard let separatorIndex = str.firstIndex(of: ":") else { break }
|
||||
let key = str.prefix(upTo: separatorIndex).trimmingCharacters(in: .whitespaces)
|
||||
let val = str.suffix(from: str.index(after: separatorIndex)).trimmingCharacters(in: .whitespaces)
|
||||
headers[key.lowercased()] = val
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
|
||||
if code != HTTPWSHeader.switchProtocolCode {
|
||||
delegate?.didReceiveHTTP(event: .failure(HTTPUpgradeError.notAnUpgrade(code)))
|
||||
return true
|
||||
}
|
||||
|
||||
delegate?.didReceiveHTTP(event: .success(headers))
|
||||
return true
|
||||
}
|
||||
|
||||
public func register(delegate: HTTPHandlerDelegate) {
|
||||
self.delegate = delegate
|
||||
}
|
||||
|
||||
private func findEndOfHTTP(data: Data) -> Int {
|
||||
let endBytes = [UInt8(ascii: "\r"), UInt8(ascii: "\n"), UInt8(ascii: "\r"), UInt8(ascii: "\n")]
|
||||
var pointer = [UInt8]()
|
||||
data.withUnsafeBytes { pointer.append(contentsOf: $0) }
|
||||
var k = 0
|
||||
for i in 0..<data.count {
|
||||
if pointer[i] == endBytes[k] {
|
||||
k += 1
|
||||
if k == 4 {
|
||||
return i + 1
|
||||
}
|
||||
} else {
|
||||
k = 0
|
||||
}
|
||||
}
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
101
Pods/Starscream/Sources/Security/FoundationSecurity.swift
generated
Normal file
101
Pods/Starscream/Sources/Security/FoundationSecurity.swift
generated
Normal file
@ -0,0 +1,101 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FoundationSecurity.swift
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 3/16/19.
|
||||
// Copyright © 2019 Vluxe. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import Foundation
|
||||
import CommonCrypto
|
||||
|
||||
public enum FoundationSecurityError: Error {
|
||||
case invalidRequest
|
||||
}
|
||||
|
||||
public class FoundationSecurity {
|
||||
var allowSelfSigned = false
|
||||
|
||||
public init(allowSelfSigned: Bool = false) {
|
||||
self.allowSelfSigned = allowSelfSigned
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
extension FoundationSecurity: CertificatePinning {
|
||||
public func evaluateTrust(trust: SecTrust, domain: String?, completion: ((PinningState) -> ())) {
|
||||
if allowSelfSigned {
|
||||
completion(.success)
|
||||
return
|
||||
}
|
||||
|
||||
if let validateDomain = domain {
|
||||
SecTrustSetPolicies(trust, SecPolicyCreateSSL(true, validateDomain as NSString?))
|
||||
}
|
||||
|
||||
handleSecurityTrust(trust: trust, completion: completion)
|
||||
}
|
||||
|
||||
private func handleSecurityTrust(trust: SecTrust, completion: ((PinningState) -> ())) {
|
||||
if #available(iOS 12.0, OSX 10.14, watchOS 5.0, tvOS 12.0, *) {
|
||||
var error: CFError?
|
||||
if SecTrustEvaluateWithError(trust, &error) {
|
||||
completion(.success)
|
||||
} else {
|
||||
completion(.failed(error))
|
||||
}
|
||||
} else {
|
||||
handleOldSecurityTrust(trust: trust, completion: completion)
|
||||
}
|
||||
}
|
||||
|
||||
private func handleOldSecurityTrust(trust: SecTrust, completion: ((PinningState) -> ())) {
|
||||
var result: SecTrustResultType = .unspecified
|
||||
SecTrustEvaluate(trust, &result)
|
||||
if result == .unspecified || result == .proceed {
|
||||
completion(.success)
|
||||
} else {
|
||||
let e = CFErrorCreate(kCFAllocatorDefault, "FoundationSecurityError" as NSString?, Int(result.rawValue), nil)
|
||||
completion(.failed(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension FoundationSecurity: HeaderValidator {
|
||||
public func validate(headers: [String: String], key: String) -> Error? {
|
||||
if let acceptKey = headers[HTTPWSHeader.acceptName] {
|
||||
let sha = "\(key)258EAFA5-E914-47DA-95CA-C5AB0DC85B11".sha1Base64()
|
||||
if sha != acceptKey {
|
||||
return WSError(type: .securityError, message: "accept header doesn't match", code: SecurityErrorCode.acceptFailed.rawValue)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private extension String {
|
||||
func sha1Base64() -> String {
|
||||
let data = self.data(using: .utf8)!
|
||||
let pointer = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) -> [UInt8] in
|
||||
var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
|
||||
CC_SHA1(bytes.baseAddress, CC_LONG(data.count), &digest)
|
||||
return digest
|
||||
}
|
||||
return Data(pointer).base64EncodedString()
|
||||
}
|
||||
}
|
45
Pods/Starscream/Sources/Security/Security.swift
generated
Normal file
45
Pods/Starscream/Sources/Security/Security.swift
generated
Normal file
@ -0,0 +1,45 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Security.swift
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 3/16/19.
|
||||
// Copyright © 2019 Vluxe. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum SecurityErrorCode: UInt16 {
|
||||
case acceptFailed = 1
|
||||
case pinningFailed = 2
|
||||
}
|
||||
|
||||
public enum PinningState {
|
||||
case success
|
||||
case failed(CFError?)
|
||||
}
|
||||
|
||||
// CertificatePinning protocol provides an interface for Transports to handle Certificate
|
||||
// or Public Key Pinning.
|
||||
public protocol CertificatePinning: class {
|
||||
func evaluateTrust(trust: SecTrust, domain: String?, completion: ((PinningState) -> ()))
|
||||
}
|
||||
|
||||
// validates the "Sec-WebSocket-Accept" header as defined 1.3 of the RFC 6455
|
||||
// https://tools.ietf.org/html/rfc6455#section-1.3
|
||||
public protocol HeaderValidator: class {
|
||||
func validate(headers: [String: String], key: String) -> Error?
|
||||
}
|
56
Pods/Starscream/Sources/Server/Server.swift
generated
Normal file
56
Pods/Starscream/Sources/Server/Server.swift
generated
Normal file
@ -0,0 +1,56 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Server.swift
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 4/2/19.
|
||||
// Copyright © 2019 Vluxe. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum ConnectionEvent {
|
||||
case connected([String: String])
|
||||
case disconnected(String, UInt16)
|
||||
case text(String)
|
||||
case binary(Data)
|
||||
case pong(Data?)
|
||||
case ping(Data?)
|
||||
case error(Error)
|
||||
}
|
||||
|
||||
public protocol Connection {
|
||||
func write(data: Data, opcode: FrameOpCode)
|
||||
}
|
||||
|
||||
public protocol ConnectionDelegate: class {
|
||||
func didReceive(event: ServerEvent)
|
||||
}
|
||||
|
||||
public enum ServerEvent {
|
||||
case connected(Connection, [String: String])
|
||||
case disconnected(Connection, String, UInt16)
|
||||
case text(Connection, String)
|
||||
case binary(Connection, Data)
|
||||
case pong(Connection, Data?)
|
||||
case ping(Connection, Data?)
|
||||
}
|
||||
|
||||
public protocol Server {
|
||||
func start(address: String, port: UInt16) -> Error?
|
||||
}
|
||||
|
||||
|
196
Pods/Starscream/Sources/Server/WebSocketServer.swift
generated
Normal file
196
Pods/Starscream/Sources/Server/WebSocketServer.swift
generated
Normal file
@ -0,0 +1,196 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// WebSocketServer.swift
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 4/5/19.
|
||||
// Copyright © 2019 Vluxe. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if canImport(Network)
|
||||
import Foundation
|
||||
import Network
|
||||
|
||||
/// WebSocketServer is a Network.framework implementation of a WebSocket server
|
||||
@available(watchOS, unavailable)
|
||||
@available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *)
|
||||
public class WebSocketServer: Server, ConnectionDelegate {
|
||||
public var onEvent: ((ServerEvent) -> Void)?
|
||||
private var connections = [String: ServerConnection]()
|
||||
private var listener: NWListener?
|
||||
private let queue = DispatchQueue(label: "com.vluxe.starscream.server.networkstream", attributes: [])
|
||||
|
||||
public init() {
|
||||
|
||||
}
|
||||
|
||||
public func start(address: String, port: UInt16) -> Error? {
|
||||
//TODO: support TLS cert adding/binding
|
||||
let parameters = NWParameters(tls: nil, tcp: NWProtocolTCP.Options())
|
||||
let p = NWEndpoint.Port(rawValue: port)!
|
||||
parameters.requiredLocalEndpoint = NWEndpoint.hostPort(host: NWEndpoint.Host.name(address, nil), port: p)
|
||||
|
||||
guard let listener = try? NWListener(using: parameters, on: p) else {
|
||||
return WSError(type: .serverError, message: "unable to start the listener at: \(address):\(port)", code: 0)
|
||||
}
|
||||
listener.newConnectionHandler = {[weak self] conn in
|
||||
let transport = TCPTransport(connection: conn)
|
||||
let c = ServerConnection(transport: transport)
|
||||
c.delegate = self
|
||||
self?.connections[c.uuid] = c
|
||||
}
|
||||
// listener.stateUpdateHandler = { state in
|
||||
// switch state {
|
||||
// case .ready:
|
||||
// print("ready to get sockets!")
|
||||
// case .setup:
|
||||
// print("setup to get sockets!")
|
||||
// case .cancelled:
|
||||
// print("server cancelled!")
|
||||
// case .waiting(let error):
|
||||
// print("waiting error: \(error)")
|
||||
// case .failed(let error):
|
||||
// print("server failed: \(error)")
|
||||
// @unknown default:
|
||||
// print("wat?")
|
||||
// }
|
||||
// }
|
||||
self.listener = listener
|
||||
listener.start(queue: queue)
|
||||
return nil
|
||||
}
|
||||
|
||||
public func didReceive(event: ServerEvent) {
|
||||
onEvent?(event)
|
||||
switch event {
|
||||
case .disconnected(let conn, _, _):
|
||||
guard let conn = conn as? ServerConnection else {
|
||||
return
|
||||
}
|
||||
connections.removeValue(forKey: conn.uuid)
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *)
|
||||
public class ServerConnection: Connection, HTTPServerDelegate, FramerEventClient, FrameCollectorDelegate, TransportEventClient {
|
||||
let transport: TCPTransport
|
||||
private let httpHandler = FoundationHTTPServerHandler()
|
||||
private let framer = WSFramer(isServer: true)
|
||||
private let frameHandler = FrameCollector()
|
||||
private var didUpgrade = false
|
||||
public var onEvent: ((ConnectionEvent) -> Void)?
|
||||
public weak var delegate: ConnectionDelegate?
|
||||
private let id: String
|
||||
var uuid: String {
|
||||
return id
|
||||
}
|
||||
|
||||
init(transport: TCPTransport) {
|
||||
self.id = UUID().uuidString
|
||||
self.transport = transport
|
||||
transport.register(delegate: self)
|
||||
httpHandler.register(delegate: self)
|
||||
framer.register(delegate: self)
|
||||
frameHandler.delegate = self
|
||||
}
|
||||
|
||||
public func write(data: Data, opcode: FrameOpCode) {
|
||||
let wsData = framer.createWriteFrame(opcode: opcode, payload: data, isCompressed: false)
|
||||
transport.write(data: wsData, completion: {_ in })
|
||||
}
|
||||
|
||||
// MARK: - TransportEventClient
|
||||
|
||||
public func connectionChanged(state: ConnectionState) {
|
||||
switch state {
|
||||
case .connected:
|
||||
break
|
||||
case .waiting:
|
||||
break
|
||||
case .failed(let error):
|
||||
print("server connection error: \(error ?? WSError(type: .protocolError, message: "default error, no extra data", code: 0))") //handleError(error)
|
||||
case .viability(_):
|
||||
break
|
||||
case .shouldReconnect(_):
|
||||
break
|
||||
case .receive(let data):
|
||||
if didUpgrade {
|
||||
framer.add(data: data)
|
||||
} else {
|
||||
httpHandler.parse(data: data)
|
||||
}
|
||||
case .cancelled:
|
||||
print("server connection cancelled!")
|
||||
//broadcast(event: .cancelled)
|
||||
}
|
||||
}
|
||||
|
||||
/// MARK: - HTTPServerDelegate
|
||||
|
||||
public func didReceive(event: HTTPEvent) {
|
||||
switch event {
|
||||
case .success(let headers):
|
||||
didUpgrade = true
|
||||
let response = httpHandler.createResponse(headers: [:])
|
||||
transport.write(data: response, completion: {_ in })
|
||||
delegate?.didReceive(event: .connected(self, headers))
|
||||
onEvent?(.connected(headers))
|
||||
case .failure(let error):
|
||||
onEvent?(.error(error))
|
||||
}
|
||||
}
|
||||
|
||||
/// MARK: - FrameCollectorDelegate
|
||||
|
||||
public func frameProcessed(event: FrameEvent) {
|
||||
switch event {
|
||||
case .frame(let frame):
|
||||
frameHandler.add(frame: frame)
|
||||
case .error(let error):
|
||||
onEvent?(.error(error))
|
||||
}
|
||||
}
|
||||
|
||||
public func didForm(event: FrameCollector.Event) {
|
||||
switch event {
|
||||
case .text(let string):
|
||||
delegate?.didReceive(event: .text(self, string))
|
||||
onEvent?(.text(string))
|
||||
case .binary(let data):
|
||||
delegate?.didReceive(event: .binary(self, data))
|
||||
onEvent?(.binary(data))
|
||||
case .pong(let data):
|
||||
delegate?.didReceive(event: .pong(self, data))
|
||||
onEvent?(.pong(data))
|
||||
case .ping(let data):
|
||||
delegate?.didReceive(event: .ping(self, data))
|
||||
onEvent?(.ping(data))
|
||||
case .closed(let reason, let code):
|
||||
delegate?.didReceive(event: .disconnected(self, reason, code))
|
||||
onEvent?(.disconnected(reason, code))
|
||||
case .error(let error):
|
||||
onEvent?(.error(error))
|
||||
}
|
||||
}
|
||||
|
||||
public func decompress(data: Data, isFinal: Bool) -> Data? {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
#endif
|
178
Pods/Starscream/Sources/Starscream/WebSocket.swift
generated
Normal file
178
Pods/Starscream/Sources/Starscream/WebSocket.swift
generated
Normal file
@ -0,0 +1,178 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Websocket.swift
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 7/16/14.
|
||||
// Copyright (c) 2014-2019 Dalton Cherry.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum ErrorType: Error {
|
||||
case compressionError
|
||||
case securityError
|
||||
case protocolError //There was an error parsing the WebSocket frames
|
||||
case serverError
|
||||
}
|
||||
|
||||
public struct WSError: Error {
|
||||
public let type: ErrorType
|
||||
public let message: String
|
||||
public let code: UInt16
|
||||
|
||||
public init(type: ErrorType, message: String, code: UInt16) {
|
||||
self.type = type
|
||||
self.message = message
|
||||
self.code = code
|
||||
}
|
||||
}
|
||||
|
||||
public protocol WebSocketClient: class {
|
||||
func connect()
|
||||
func disconnect(closeCode: UInt16)
|
||||
func write(string: String, completion: (() -> ())?)
|
||||
func write(stringData: Data, completion: (() -> ())?)
|
||||
func write(data: Data, completion: (() -> ())?)
|
||||
func write(ping: Data, completion: (() -> ())?)
|
||||
func write(pong: Data, completion: (() -> ())?)
|
||||
}
|
||||
|
||||
//implements some of the base behaviors
|
||||
extension WebSocketClient {
|
||||
public func write(string: String) {
|
||||
write(string: string, completion: nil)
|
||||
}
|
||||
|
||||
public func write(data: Data) {
|
||||
write(data: data, completion: nil)
|
||||
}
|
||||
|
||||
public func write(ping: Data) {
|
||||
write(ping: ping, completion: nil)
|
||||
}
|
||||
|
||||
public func write(pong: Data) {
|
||||
write(pong: pong, completion: nil)
|
||||
}
|
||||
|
||||
public func disconnect() {
|
||||
disconnect(closeCode: CloseCode.normal.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
public enum WebSocketEvent {
|
||||
case connected([String: String])
|
||||
case disconnected(String, UInt16)
|
||||
case text(String)
|
||||
case binary(Data)
|
||||
case pong(Data?)
|
||||
case ping(Data?)
|
||||
case error(Error?)
|
||||
case viabilityChanged(Bool)
|
||||
case reconnectSuggested(Bool)
|
||||
case cancelled
|
||||
}
|
||||
|
||||
public protocol WebSocketDelegate: class {
|
||||
func didReceive(event: WebSocketEvent, client: WebSocket)
|
||||
}
|
||||
|
||||
open class WebSocket: WebSocketClient, EngineDelegate {
|
||||
private let engine: Engine
|
||||
public weak var delegate: WebSocketDelegate?
|
||||
public var onEvent: ((WebSocketEvent) -> Void)?
|
||||
|
||||
public var request: URLRequest
|
||||
// Where the callback is executed. It defaults to the main UI thread queue.
|
||||
public var callbackQueue = DispatchQueue.main
|
||||
public var respondToPingWithPong: Bool {
|
||||
set {
|
||||
guard let e = engine as? WSEngine else { return }
|
||||
e.respondToPingWithPong = newValue
|
||||
}
|
||||
get {
|
||||
guard let e = engine as? WSEngine else { return true }
|
||||
return e.respondToPingWithPong
|
||||
}
|
||||
}
|
||||
|
||||
// serial write queue to ensure writes happen in order
|
||||
private let writeQueue = DispatchQueue(label: "com.vluxe.starscream.writequeue")
|
||||
private var canSend = false
|
||||
private let mutex = DispatchSemaphore(value: 1)
|
||||
|
||||
public init(request: URLRequest, engine: Engine) {
|
||||
self.request = request
|
||||
self.engine = engine
|
||||
}
|
||||
|
||||
public convenience init(request: URLRequest, certPinner: CertificatePinning? = FoundationSecurity(), compressionHandler: CompressionHandler? = nil, useCustomEngine: Bool = true) {
|
||||
if #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *), !useCustomEngine {
|
||||
self.init(request: request, engine: NativeEngine())
|
||||
} else if #available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) {
|
||||
self.init(request: request, engine: WSEngine(transport: TCPTransport(), certPinner: certPinner, compressionHandler: compressionHandler))
|
||||
} else {
|
||||
self.init(request: request, engine: WSEngine(transport: FoundationTransport(), certPinner: certPinner, compressionHandler: compressionHandler))
|
||||
}
|
||||
}
|
||||
|
||||
public func connect() {
|
||||
engine.register(delegate: self)
|
||||
engine.start(request: request)
|
||||
}
|
||||
|
||||
public func disconnect(closeCode: UInt16 = CloseCode.normal.rawValue) {
|
||||
engine.stop(closeCode: closeCode)
|
||||
}
|
||||
|
||||
public func forceDisconnect() {
|
||||
engine.forceStop()
|
||||
}
|
||||
|
||||
public func write(data: Data, completion: (() -> ())?) {
|
||||
write(data: data, opcode: .binaryFrame, completion: completion)
|
||||
}
|
||||
|
||||
public func write(string: String, completion: (() -> ())?) {
|
||||
engine.write(string: string, completion: completion)
|
||||
}
|
||||
|
||||
public func write(stringData: Data, completion: (() -> ())?) {
|
||||
write(data: stringData, opcode: .textFrame, completion: completion)
|
||||
}
|
||||
|
||||
public func write(ping: Data, completion: (() -> ())?) {
|
||||
write(data: ping, opcode: .ping, completion: completion)
|
||||
}
|
||||
|
||||
public func write(pong: Data, completion: (() -> ())?) {
|
||||
write(data: pong, opcode: .pong, completion: completion)
|
||||
}
|
||||
|
||||
private func write(data: Data, opcode: FrameOpCode, completion: (() -> ())?) {
|
||||
engine.write(data: data, opcode: opcode, completion: completion)
|
||||
}
|
||||
|
||||
// MARK: - EngineDelegate
|
||||
public func didReceive(event: WebSocketEvent) {
|
||||
callbackQueue.async { [weak self] in
|
||||
guard let s = self else { return }
|
||||
s.delegate?.didReceive(event: event, client: s)
|
||||
s.onEvent?(event)
|
||||
}
|
||||
}
|
||||
}
|
218
Pods/Starscream/Sources/Transport/FoundationTransport.swift
generated
Normal file
218
Pods/Starscream/Sources/Transport/FoundationTransport.swift
generated
Normal file
@ -0,0 +1,218 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// FoundationTransport.swift
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 1/23/19.
|
||||
// Copyright © 2019 Vluxe. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum FoundationTransportError: Error {
|
||||
case invalidRequest
|
||||
case invalidOutputStream
|
||||
case timeout
|
||||
}
|
||||
|
||||
public class FoundationTransport: NSObject, Transport, StreamDelegate {
|
||||
private weak var delegate: TransportEventClient?
|
||||
private let workQueue = DispatchQueue(label: "com.vluxe.starscream.websocket", attributes: [])
|
||||
private var inputStream: InputStream?
|
||||
private var outputStream: OutputStream?
|
||||
private var isOpen = false
|
||||
private var onConnect: ((InputStream, OutputStream) -> Void)?
|
||||
private var isTLS = false
|
||||
private var certPinner: CertificatePinning?
|
||||
|
||||
public var usingTLS: Bool {
|
||||
return self.isTLS
|
||||
}
|
||||
|
||||
public init(streamConfiguration: ((InputStream, OutputStream) -> Void)? = nil) {
|
||||
super.init()
|
||||
onConnect = streamConfiguration
|
||||
}
|
||||
|
||||
deinit {
|
||||
inputStream?.delegate = nil
|
||||
outputStream?.delegate = nil
|
||||
}
|
||||
|
||||
public func connect(url: URL, timeout: Double = 10, certificatePinning: CertificatePinning? = nil) {
|
||||
guard let parts = url.getParts() else {
|
||||
delegate?.connectionChanged(state: .failed(FoundationTransportError.invalidRequest))
|
||||
return
|
||||
}
|
||||
self.certPinner = certificatePinning
|
||||
self.isTLS = parts.isTLS
|
||||
var readStream: Unmanaged<CFReadStream>?
|
||||
var writeStream: Unmanaged<CFWriteStream>?
|
||||
let h = parts.host as NSString
|
||||
CFStreamCreatePairWithSocketToHost(nil, h, UInt32(parts.port), &readStream, &writeStream)
|
||||
inputStream = readStream!.takeRetainedValue()
|
||||
outputStream = writeStream!.takeRetainedValue()
|
||||
guard let inStream = inputStream, let outStream = outputStream else {
|
||||
return
|
||||
}
|
||||
inStream.delegate = self
|
||||
outStream.delegate = self
|
||||
|
||||
if isTLS {
|
||||
let key = CFStreamPropertyKey(rawValue: kCFStreamPropertySocketSecurityLevel)
|
||||
CFReadStreamSetProperty(inStream, key, kCFStreamSocketSecurityLevelNegotiatedSSL)
|
||||
CFWriteStreamSetProperty(outStream, key, kCFStreamSocketSecurityLevelNegotiatedSSL)
|
||||
}
|
||||
|
||||
onConnect?(inStream, outStream)
|
||||
|
||||
isOpen = false
|
||||
CFReadStreamSetDispatchQueue(inStream, workQueue)
|
||||
CFWriteStreamSetDispatchQueue(outStream, workQueue)
|
||||
inStream.open()
|
||||
outStream.open()
|
||||
|
||||
|
||||
workQueue.asyncAfter(deadline: .now() + timeout, execute: { [weak self] in
|
||||
guard let s = self else { return }
|
||||
if !s.isOpen {
|
||||
s.delegate?.connectionChanged(state: .failed(FoundationTransportError.timeout))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public func disconnect() {
|
||||
if let stream = inputStream {
|
||||
stream.delegate = nil
|
||||
CFReadStreamSetDispatchQueue(stream, nil)
|
||||
stream.close()
|
||||
}
|
||||
if let stream = outputStream {
|
||||
stream.delegate = nil
|
||||
CFWriteStreamSetDispatchQueue(stream, nil)
|
||||
stream.close()
|
||||
}
|
||||
isOpen = false
|
||||
outputStream = nil
|
||||
inputStream = nil
|
||||
}
|
||||
|
||||
public func register(delegate: TransportEventClient) {
|
||||
self.delegate = delegate
|
||||
}
|
||||
|
||||
public func write(data: Data, completion: @escaping ((Error?) -> ())) {
|
||||
guard let outStream = outputStream else {
|
||||
completion(FoundationTransportError.invalidOutputStream)
|
||||
return
|
||||
}
|
||||
var total = 0
|
||||
let buffer = UnsafeRawPointer((data as NSData).bytes).assumingMemoryBound(to: UInt8.self)
|
||||
//NOTE: this might need to be dispatched to the work queue instead of being written inline. TBD.
|
||||
while total < data.count {
|
||||
let written = outStream.write(buffer, maxLength: data.count)
|
||||
if written < 0 {
|
||||
completion(FoundationTransportError.invalidOutputStream)
|
||||
return
|
||||
}
|
||||
total += written
|
||||
}
|
||||
completion(nil)
|
||||
}
|
||||
|
||||
private func getSecurityData() -> (SecTrust?, String?) {
|
||||
#if os(watchOS)
|
||||
return (nil, nil)
|
||||
#else
|
||||
guard let outputStream = outputStream else {
|
||||
return (nil, nil)
|
||||
}
|
||||
let trust = outputStream.property(forKey: kCFStreamPropertySSLPeerTrust as Stream.PropertyKey) as! SecTrust?
|
||||
var domain = outputStream.property(forKey: kCFStreamSSLPeerName as Stream.PropertyKey) as! String?
|
||||
|
||||
if domain == nil,
|
||||
let sslContextOut = CFWriteStreamCopyProperty(outputStream, CFStreamPropertyKey(rawValue: kCFStreamPropertySSLContext)) as! SSLContext? {
|
||||
var peerNameLen: Int = 0
|
||||
SSLGetPeerDomainNameLength(sslContextOut, &peerNameLen)
|
||||
var peerName = Data(count: peerNameLen)
|
||||
let _ = peerName.withUnsafeMutableBytes { (peerNamePtr: UnsafeMutablePointer<Int8>) in
|
||||
SSLGetPeerDomainName(sslContextOut, peerNamePtr, &peerNameLen)
|
||||
}
|
||||
if let peerDomain = String(bytes: peerName, encoding: .utf8), peerDomain.count > 0 {
|
||||
domain = peerDomain
|
||||
}
|
||||
}
|
||||
return (trust, domain)
|
||||
#endif
|
||||
}
|
||||
|
||||
private func read() {
|
||||
guard let stream = inputStream else {
|
||||
return
|
||||
}
|
||||
let maxBuffer = 4096
|
||||
let buf = NSMutableData(capacity: maxBuffer)
|
||||
let buffer = UnsafeMutableRawPointer(mutating: buf!.bytes).assumingMemoryBound(to: UInt8.self)
|
||||
let length = stream.read(buffer, maxLength: maxBuffer)
|
||||
if length < 1 {
|
||||
return
|
||||
}
|
||||
let data = Data(bytes: buffer, count: length)
|
||||
delegate?.connectionChanged(state: .receive(data))
|
||||
}
|
||||
|
||||
// MARK: - StreamDelegate
|
||||
|
||||
open func stream(_ aStream: Stream, handle eventCode: Stream.Event) {
|
||||
switch eventCode {
|
||||
case .hasBytesAvailable:
|
||||
if aStream == inputStream {
|
||||
read()
|
||||
}
|
||||
case .errorOccurred:
|
||||
delegate?.connectionChanged(state: .failed(aStream.streamError))
|
||||
case .endEncountered:
|
||||
if aStream == inputStream {
|
||||
delegate?.connectionChanged(state: .cancelled)
|
||||
}
|
||||
case .openCompleted:
|
||||
if aStream == inputStream {
|
||||
let (trust, domain) = getSecurityData()
|
||||
if let pinner = certPinner, let trust = trust {
|
||||
pinner.evaluateTrust(trust: trust, domain: domain, completion: { [weak self] (state) in
|
||||
switch state {
|
||||
case .success:
|
||||
self?.isOpen = true
|
||||
self?.delegate?.connectionChanged(state: .connected)
|
||||
case .failed(let error):
|
||||
self?.delegate?.connectionChanged(state: .failed(error))
|
||||
}
|
||||
|
||||
})
|
||||
} else {
|
||||
isOpen = true
|
||||
delegate?.connectionChanged(state: .connected)
|
||||
}
|
||||
}
|
||||
case .endEncountered:
|
||||
if aStream == inputStream {
|
||||
delegate?.connectionChanged(state: .cancelled)
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
159
Pods/Starscream/Sources/Transport/TCPTransport.swift
generated
Normal file
159
Pods/Starscream/Sources/Transport/TCPTransport.swift
generated
Normal file
@ -0,0 +1,159 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// HTTPTransport.swift
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 1/23/19.
|
||||
// Copyright © 2019 Vluxe. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if canImport(Network)
|
||||
import Foundation
|
||||
import Network
|
||||
|
||||
public enum TCPTransportError: Error {
|
||||
case invalidRequest
|
||||
}
|
||||
|
||||
@available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *)
|
||||
public class TCPTransport: Transport {
|
||||
private var connection: NWConnection?
|
||||
private let queue = DispatchQueue(label: "com.vluxe.starscream.networkstream", attributes: [])
|
||||
private weak var delegate: TransportEventClient?
|
||||
private var isRunning = false
|
||||
private var isTLS = false
|
||||
|
||||
public var usingTLS: Bool {
|
||||
return self.isTLS
|
||||
}
|
||||
|
||||
public init(connection: NWConnection) {
|
||||
self.connection = connection
|
||||
start()
|
||||
}
|
||||
|
||||
public init() {
|
||||
//normal connection, will use the "connect" method below
|
||||
}
|
||||
|
||||
public func connect(url: URL, timeout: Double = 10, certificatePinning: CertificatePinning? = nil) {
|
||||
guard let parts = url.getParts() else {
|
||||
delegate?.connectionChanged(state: .failed(TCPTransportError.invalidRequest))
|
||||
return
|
||||
}
|
||||
self.isTLS = parts.isTLS
|
||||
let options = NWProtocolTCP.Options()
|
||||
options.connectionTimeout = Int(timeout.rounded(.up))
|
||||
|
||||
let tlsOptions = isTLS ? NWProtocolTLS.Options() : nil
|
||||
if let tlsOpts = tlsOptions {
|
||||
sec_protocol_options_set_verify_block(tlsOpts.securityProtocolOptions, { (sec_protocol_metadata, sec_trust, sec_protocol_verify_complete) in
|
||||
let trust = sec_trust_copy_ref(sec_trust).takeRetainedValue()
|
||||
guard let pinner = certificatePinning else {
|
||||
sec_protocol_verify_complete(true)
|
||||
return
|
||||
}
|
||||
pinner.evaluateTrust(trust: trust, domain: parts.host, completion: { (state) in
|
||||
switch state {
|
||||
case .success:
|
||||
sec_protocol_verify_complete(true)
|
||||
case .failed(_):
|
||||
sec_protocol_verify_complete(false)
|
||||
}
|
||||
})
|
||||
}, queue)
|
||||
}
|
||||
let parameters = NWParameters(tls: tlsOptions, tcp: options)
|
||||
let conn = NWConnection(host: NWEndpoint.Host.name(parts.host, nil), port: NWEndpoint.Port(rawValue: UInt16(parts.port))!, using: parameters)
|
||||
connection = conn
|
||||
start()
|
||||
}
|
||||
|
||||
public func disconnect() {
|
||||
isRunning = false
|
||||
connection?.cancel()
|
||||
}
|
||||
|
||||
public func register(delegate: TransportEventClient) {
|
||||
self.delegate = delegate
|
||||
}
|
||||
|
||||
public func write(data: Data, completion: @escaping ((Error?) -> ())) {
|
||||
connection?.send(content: data, completion: .contentProcessed { (error) in
|
||||
completion(error)
|
||||
})
|
||||
}
|
||||
|
||||
private func start() {
|
||||
guard let conn = connection else {
|
||||
return
|
||||
}
|
||||
conn.stateUpdateHandler = { [weak self] (newState) in
|
||||
switch newState {
|
||||
case .ready:
|
||||
self?.delegate?.connectionChanged(state: .connected)
|
||||
case .waiting:
|
||||
self?.delegate?.connectionChanged(state: .waiting)
|
||||
case .cancelled:
|
||||
self?.delegate?.connectionChanged(state: .cancelled)
|
||||
case .failed(let error):
|
||||
self?.delegate?.connectionChanged(state: .failed(error))
|
||||
case .setup, .preparing:
|
||||
break
|
||||
@unknown default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
conn.viabilityUpdateHandler = { [weak self] (isViable) in
|
||||
self?.delegate?.connectionChanged(state: .viability(isViable))
|
||||
}
|
||||
|
||||
conn.betterPathUpdateHandler = { [weak self] (isBetter) in
|
||||
self?.delegate?.connectionChanged(state: .shouldReconnect(isBetter))
|
||||
}
|
||||
|
||||
conn.start(queue: queue)
|
||||
isRunning = true
|
||||
readLoop()
|
||||
}
|
||||
|
||||
//readLoop keeps reading from the connection to get the latest content
|
||||
private func readLoop() {
|
||||
if !isRunning {
|
||||
return
|
||||
}
|
||||
connection?.receive(minimumIncompleteLength: 2, maximumLength: 4096, completion: {[weak self] (data, context, isComplete, error) in
|
||||
guard let s = self else {return}
|
||||
if let data = data {
|
||||
s.delegate?.connectionChanged(state: .receive(data))
|
||||
}
|
||||
|
||||
// Refer to https://developer.apple.com/documentation/network/implementing_netcat_with_network_framework
|
||||
if let context = context, context.isFinal, isComplete {
|
||||
return
|
||||
}
|
||||
|
||||
if error == nil {
|
||||
s.readLoop()
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
#else
|
||||
typealias TCPTransport = FoundationTransport
|
||||
#endif
|
55
Pods/Starscream/Sources/Transport/Transport.swift
generated
Normal file
55
Pods/Starscream/Sources/Transport/Transport.swift
generated
Normal file
@ -0,0 +1,55 @@
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Transport.swift
|
||||
// Starscream
|
||||
//
|
||||
// Created by Dalton Cherry on 1/23/19.
|
||||
// Copyright © 2019 Vluxe. All rights reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum ConnectionState {
|
||||
case connected
|
||||
case waiting
|
||||
case cancelled
|
||||
case failed(Error?)
|
||||
|
||||
//the viability (connection status) of the connection has updated
|
||||
//e.g. connection is down, connection came back up, etc
|
||||
case viability(Bool)
|
||||
|
||||
//the connection has upgrade to wifi from cellular.
|
||||
//you should consider reconnecting to take advantage of this
|
||||
case shouldReconnect(Bool)
|
||||
|
||||
//the connection receive data
|
||||
case receive(Data)
|
||||
}
|
||||
|
||||
public protocol TransportEventClient: class {
|
||||
func connectionChanged(state: ConnectionState)
|
||||
}
|
||||
|
||||
public protocol Transport: class {
|
||||
func register(delegate: TransportEventClient)
|
||||
func connect(url: URL, timeout: Double, certificatePinning: CertificatePinning?)
|
||||
func disconnect()
|
||||
func write(data: Data, completion: @escaping ((Error?) -> ()))
|
||||
var usingTLS: Bool { get }
|
||||
}
|
@ -65,6 +65,209 @@ Permission is granted to anyone to use this software for any purpose,including c
|
||||
- Redistributions of any form whatsoever must retain the following acknowledgment: 'This product includes software developed by the "Marcin Krzyzanowski" (http://krzyzanowskim.com/).'
|
||||
|
||||
|
||||
## PromiseKit
|
||||
|
||||
Copyright 2016-present, Max Howell; mxcl@me.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
|
||||
## Starscream
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
Copyright (c) 2014-2016 Dalton Cherry.
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
## SwiftyJSON
|
||||
|
||||
The MIT License (MIT)
|
||||
@ -90,6 +293,29 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
## WalletConnect
|
||||
|
||||
Copyright (c) 2019 hewigovens <360470+hewigovens@users.noreply.github.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
|
||||
## Web3Swift.io
|
||||
|
||||
Apache License
|
||||
|
@ -94,6 +94,221 @@ Permission is granted to anyone to use this software for any purpose,including c
|
||||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>Copyright 2016-present, Max Howell; mxcl@me.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included
|
||||
in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
</string>
|
||||
<key>License</key>
|
||||
<string>MIT</string>
|
||||
<key>Title</key>
|
||||
<string>PromiseKit</string>
|
||||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string> Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
Copyright (c) 2014-2016 Dalton Cherry.
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.</string>
|
||||
<key>License</key>
|
||||
<string>Apache License, Version 2.0</string>
|
||||
<key>Title</key>
|
||||
<string>Starscream</string>
|
||||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>The MIT License (MIT)
|
||||
@ -125,6 +340,35 @@ THE SOFTWARE.
|
||||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>Copyright (c) 2019 hewigovens <360470+hewigovens@users.noreply.github.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
</string>
|
||||
<key>License</key>
|
||||
<string>MIT</string>
|
||||
<key>Title</key>
|
||||
<string>WalletConnect</string>
|
||||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>Apache License
|
||||
|
@ -2,6 +2,9 @@ ${PODS_ROOT}/Target Support Files/Pods-Encrypted Ink/Pods-Encrypted Ink-framewor
|
||||
${BUILT_PRODUCTS_DIR}/BigInt/BigInt.framework
|
||||
${BUILT_PRODUCTS_DIR}/BlockiesSwift/BlockiesSwift.framework
|
||||
${BUILT_PRODUCTS_DIR}/CryptoSwift/CryptoSwift.framework
|
||||
${BUILT_PRODUCTS_DIR}/PromiseKit/PromiseKit.framework
|
||||
${BUILT_PRODUCTS_DIR}/Starscream/Starscream.framework
|
||||
${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework
|
||||
${BUILT_PRODUCTS_DIR}/WalletConnect/WalletConnect.framework
|
||||
${BUILT_PRODUCTS_DIR}/Web3Swift.io/Web3Swift.framework
|
||||
${BUILT_PRODUCTS_DIR}/secp256k1.swift/secp256k1.framework
|
@ -1,6 +1,9 @@
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BigInt.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BlockiesSwift.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CryptoSwift.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PromiseKit.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Starscream.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WalletConnect.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Web3Swift.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/secp256k1.framework
|
@ -2,6 +2,9 @@ ${PODS_ROOT}/Target Support Files/Pods-Encrypted Ink/Pods-Encrypted Ink-framewor
|
||||
${BUILT_PRODUCTS_DIR}/BigInt/BigInt.framework
|
||||
${BUILT_PRODUCTS_DIR}/BlockiesSwift/BlockiesSwift.framework
|
||||
${BUILT_PRODUCTS_DIR}/CryptoSwift/CryptoSwift.framework
|
||||
${BUILT_PRODUCTS_DIR}/PromiseKit/PromiseKit.framework
|
||||
${BUILT_PRODUCTS_DIR}/Starscream/Starscream.framework
|
||||
${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework
|
||||
${BUILT_PRODUCTS_DIR}/WalletConnect/WalletConnect.framework
|
||||
${BUILT_PRODUCTS_DIR}/Web3Swift.io/Web3Swift.framework
|
||||
${BUILT_PRODUCTS_DIR}/secp256k1.swift/secp256k1.framework
|
@ -1,6 +1,9 @@
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BigInt.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/BlockiesSwift.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CryptoSwift.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PromiseKit.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Starscream.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WalletConnect.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Web3Swift.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/secp256k1.framework
|
@ -178,7 +178,10 @@ if [[ "$CONFIGURATION" == "Debug" ]]; then
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/BigInt/BigInt.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/BlockiesSwift/BlockiesSwift.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/CryptoSwift/CryptoSwift.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/PromiseKit/PromiseKit.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/Starscream/Starscream.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/WalletConnect/WalletConnect.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/Web3Swift.io/Web3Swift.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/secp256k1.swift/secp256k1.framework"
|
||||
fi
|
||||
@ -186,7 +189,10 @@ if [[ "$CONFIGURATION" == "Release" ]]; then
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/BigInt/BigInt.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/BlockiesSwift/BlockiesSwift.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/CryptoSwift/CryptoSwift.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/PromiseKit/PromiseKit.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/Starscream/Starscream.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/WalletConnect/WalletConnect.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/Web3Swift.io/Web3Swift.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/secp256k1.swift/secp256k1.framework"
|
||||
fi
|
||||
|
@ -1,10 +1,10 @@
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BigInt" "${PODS_CONFIGURATION_BUILD_DIR}/BlockiesSwift" "${PODS_CONFIGURATION_BUILD_DIR}/CryptoSwift" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/Web3Swift.io" "${PODS_CONFIGURATION_BUILD_DIR}/secp256k1.swift"
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BigInt" "${PODS_CONFIGURATION_BUILD_DIR}/BlockiesSwift" "${PODS_CONFIGURATION_BUILD_DIR}/CryptoSwift" "${PODS_CONFIGURATION_BUILD_DIR}/PromiseKit" "${PODS_CONFIGURATION_BUILD_DIR}/Starscream" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/WalletConnect" "${PODS_CONFIGURATION_BUILD_DIR}/Web3Swift.io" "${PODS_CONFIGURATION_BUILD_DIR}/secp256k1.swift"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BigInt/BigInt.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlockiesSwift/BlockiesSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CryptoSwift/CryptoSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON/SwiftyJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Web3Swift.io/Web3Swift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/secp256k1.swift/secp256k1.framework/Headers"
|
||||
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BigInt/BigInt.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlockiesSwift/BlockiesSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CryptoSwift/CryptoSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PromiseKit/PromiseKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Starscream/Starscream.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON/SwiftyJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/WalletConnect/WalletConnect.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Web3Swift.io/Web3Swift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/secp256k1.swift/secp256k1.framework/Headers"
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/../Frameworks' '@loader_path/Frameworks'
|
||||
OTHER_LDFLAGS = $(inherited) -framework "BigInt" -framework "BlockiesSwift" -framework "CryptoSwift" -framework "SwiftyJSON" -framework "Web3Swift" -framework "secp256k1"
|
||||
OTHER_LDFLAGS = $(inherited) -framework "BigInt" -framework "BlockiesSwift" -framework "CryptoSwift" -framework "Foundation" -framework "PromiseKit" -framework "Starscream" -framework "SwiftyJSON" -framework "WalletConnect" -framework "Web3Swift" -framework "secp256k1"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
|
@ -1,10 +1,10 @@
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BigInt" "${PODS_CONFIGURATION_BUILD_DIR}/BlockiesSwift" "${PODS_CONFIGURATION_BUILD_DIR}/CryptoSwift" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/Web3Swift.io" "${PODS_CONFIGURATION_BUILD_DIR}/secp256k1.swift"
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BigInt" "${PODS_CONFIGURATION_BUILD_DIR}/BlockiesSwift" "${PODS_CONFIGURATION_BUILD_DIR}/CryptoSwift" "${PODS_CONFIGURATION_BUILD_DIR}/PromiseKit" "${PODS_CONFIGURATION_BUILD_DIR}/Starscream" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/WalletConnect" "${PODS_CONFIGURATION_BUILD_DIR}/Web3Swift.io" "${PODS_CONFIGURATION_BUILD_DIR}/secp256k1.swift"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BigInt/BigInt.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlockiesSwift/BlockiesSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CryptoSwift/CryptoSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON/SwiftyJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Web3Swift.io/Web3Swift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/secp256k1.swift/secp256k1.framework/Headers"
|
||||
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/BigInt/BigInt.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/BlockiesSwift/BlockiesSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CryptoSwift/CryptoSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/PromiseKit/PromiseKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Starscream/Starscream.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON/SwiftyJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/WalletConnect/WalletConnect.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Web3Swift.io/Web3Swift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/secp256k1.swift/secp256k1.framework/Headers"
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/../Frameworks' '@loader_path/Frameworks'
|
||||
OTHER_LDFLAGS = $(inherited) -framework "BigInt" -framework "BlockiesSwift" -framework "CryptoSwift" -framework "SwiftyJSON" -framework "Web3Swift" -framework "secp256k1"
|
||||
OTHER_LDFLAGS = $(inherited) -framework "BigInt" -framework "BlockiesSwift" -framework "CryptoSwift" -framework "Foundation" -framework "PromiseKit" -framework "Starscream" -framework "SwiftyJSON" -framework "WalletConnect" -framework "Web3Swift" -framework "secp256k1"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
|
26
Pods/Target Support Files/PromiseKit/PromiseKit-Info.plist
generated
Normal file
26
Pods/Target Support Files/PromiseKit/PromiseKit-Info.plist
generated
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>6.15.3</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${CURRENT_PROJECT_VERSION}</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
5
Pods/Target Support Files/PromiseKit/PromiseKit-dummy.m
generated
Normal file
5
Pods/Target Support Files/PromiseKit/PromiseKit-dummy.m
generated
Normal file
@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
@interface PodsDummy_PromiseKit : NSObject
|
||||
@end
|
||||
@implementation PodsDummy_PromiseKit
|
||||
@end
|
12
Pods/Target Support Files/PromiseKit/PromiseKit-prefix.pch
generated
Normal file
12
Pods/Target Support Files/PromiseKit/PromiseKit-prefix.pch
generated
Normal file
@ -0,0 +1,12 @@
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#else
|
||||
#ifndef FOUNDATION_EXPORT
|
||||
#if defined(__cplusplus)
|
||||
#define FOUNDATION_EXPORT extern "C"
|
||||
#else
|
||||
#define FOUNDATION_EXPORT extern
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
23
Pods/Target Support Files/PromiseKit/PromiseKit-umbrella.h
generated
Normal file
23
Pods/Target Support Files/PromiseKit/PromiseKit-umbrella.h
generated
Normal file
@ -0,0 +1,23 @@
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#else
|
||||
#ifndef FOUNDATION_EXPORT
|
||||
#if defined(__cplusplus)
|
||||
#define FOUNDATION_EXPORT extern "C"
|
||||
#else
|
||||
#define FOUNDATION_EXPORT extern
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#import "fwd.h"
|
||||
#import "AnyPromise.h"
|
||||
#import "PromiseKit.h"
|
||||
#import "NSURLSession+AnyPromise.h"
|
||||
#import "NSTask+AnyPromise.h"
|
||||
#import "NSNotificationCenter+AnyPromise.h"
|
||||
#import "PMKFoundation.h"
|
||||
|
||||
FOUNDATION_EXPORT double PromiseKitVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char PromiseKitVersionString[];
|
||||
|
14
Pods/Target Support Files/PromiseKit/PromiseKit.debug.xcconfig
generated
Normal file
14
Pods/Target Support Files/PromiseKit/PromiseKit.debug.xcconfig
generated
Normal file
@ -0,0 +1,14 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
CODE_SIGN_IDENTITY =
|
||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/PromiseKit
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
OTHER_LDFLAGS = $(inherited) -framework "Foundation"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -DPMKCocoaPods
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/PromiseKit
|
||||
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||
SKIP_INSTALL = YES
|
||||
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
|
6
Pods/Target Support Files/PromiseKit/PromiseKit.modulemap
generated
Normal file
6
Pods/Target Support Files/PromiseKit/PromiseKit.modulemap
generated
Normal file
@ -0,0 +1,6 @@
|
||||
framework module PromiseKit {
|
||||
umbrella header "PromiseKit-umbrella.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
14
Pods/Target Support Files/PromiseKit/PromiseKit.release.xcconfig
generated
Normal file
14
Pods/Target Support Files/PromiseKit/PromiseKit.release.xcconfig
generated
Normal file
@ -0,0 +1,14 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
CODE_SIGN_IDENTITY =
|
||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/PromiseKit
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
OTHER_LDFLAGS = $(inherited) -framework "Foundation"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS -DPMKCocoaPods
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/PromiseKit
|
||||
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||
SKIP_INSTALL = YES
|
||||
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
|
26
Pods/Target Support Files/Starscream/Starscream-Info.plist
generated
Normal file
26
Pods/Target Support Files/Starscream/Starscream-Info.plist
generated
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>4.0.4</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${CURRENT_PROJECT_VERSION}</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
5
Pods/Target Support Files/Starscream/Starscream-dummy.m
generated
Normal file
5
Pods/Target Support Files/Starscream/Starscream-dummy.m
generated
Normal file
@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
@interface PodsDummy_Starscream : NSObject
|
||||
@end
|
||||
@implementation PodsDummy_Starscream
|
||||
@end
|
12
Pods/Target Support Files/Starscream/Starscream-prefix.pch
generated
Normal file
12
Pods/Target Support Files/Starscream/Starscream-prefix.pch
generated
Normal file
@ -0,0 +1,12 @@
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#else
|
||||
#ifndef FOUNDATION_EXPORT
|
||||
#if defined(__cplusplus)
|
||||
#define FOUNDATION_EXPORT extern "C"
|
||||
#else
|
||||
#define FOUNDATION_EXPORT extern
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
16
Pods/Target Support Files/Starscream/Starscream-umbrella.h
generated
Normal file
16
Pods/Target Support Files/Starscream/Starscream-umbrella.h
generated
Normal file
@ -0,0 +1,16 @@
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#else
|
||||
#ifndef FOUNDATION_EXPORT
|
||||
#if defined(__cplusplus)
|
||||
#define FOUNDATION_EXPORT extern "C"
|
||||
#else
|
||||
#define FOUNDATION_EXPORT extern
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
FOUNDATION_EXPORT double StarscreamVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char StarscreamVersionString[];
|
||||
|
13
Pods/Target Support Files/Starscream/Starscream.debug.xcconfig
generated
Normal file
13
Pods/Target Support Files/Starscream/Starscream.debug.xcconfig
generated
Normal file
@ -0,0 +1,13 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
CODE_SIGN_IDENTITY =
|
||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Starscream
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/Starscream
|
||||
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||
SKIP_INSTALL = YES
|
||||
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
|
6
Pods/Target Support Files/Starscream/Starscream.modulemap
generated
Normal file
6
Pods/Target Support Files/Starscream/Starscream.modulemap
generated
Normal file
@ -0,0 +1,6 @@
|
||||
framework module Starscream {
|
||||
umbrella header "Starscream-umbrella.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
13
Pods/Target Support Files/Starscream/Starscream.release.xcconfig
generated
Normal file
13
Pods/Target Support Files/Starscream/Starscream.release.xcconfig
generated
Normal file
@ -0,0 +1,13 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
CODE_SIGN_IDENTITY =
|
||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/Starscream
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/Starscream
|
||||
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||
SKIP_INSTALL = YES
|
||||
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
|
26
Pods/Target Support Files/WalletConnect/WalletConnect-Info.plist
generated
Normal file
26
Pods/Target Support Files/WalletConnect/WalletConnect-Info.plist
generated
Normal file
@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${CURRENT_PROJECT_VERSION}</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
5
Pods/Target Support Files/WalletConnect/WalletConnect-dummy.m
generated
Normal file
5
Pods/Target Support Files/WalletConnect/WalletConnect-dummy.m
generated
Normal file
@ -0,0 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
@interface PodsDummy_WalletConnect : NSObject
|
||||
@end
|
||||
@implementation PodsDummy_WalletConnect
|
||||
@end
|
12
Pods/Target Support Files/WalletConnect/WalletConnect-prefix.pch
generated
Normal file
12
Pods/Target Support Files/WalletConnect/WalletConnect-prefix.pch
generated
Normal file
@ -0,0 +1,12 @@
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#else
|
||||
#ifndef FOUNDATION_EXPORT
|
||||
#if defined(__cplusplus)
|
||||
#define FOUNDATION_EXPORT extern "C"
|
||||
#else
|
||||
#define FOUNDATION_EXPORT extern
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
17
Pods/Target Support Files/WalletConnect/WalletConnect-umbrella.h
generated
Normal file
17
Pods/Target Support Files/WalletConnect/WalletConnect-umbrella.h
generated
Normal file
@ -0,0 +1,17 @@
|
||||
#ifdef __OBJC__
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#else
|
||||
#ifndef FOUNDATION_EXPORT
|
||||
#if defined(__cplusplus)
|
||||
#define FOUNDATION_EXPORT extern "C"
|
||||
#else
|
||||
#define FOUNDATION_EXPORT extern
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#import "WalletConnect.h"
|
||||
|
||||
FOUNDATION_EXPORT double WalletConnectVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char WalletConnectVersionString[];
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user