Stop using Swift PM, update Pods

This commit is contained in:
Ivan Grachyov 2021-06-17 11:22:31 +03:00
parent 4cbed12d92
commit d97e87042c
134 changed files with 13052 additions and 1697 deletions

View File

@ -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 */;
}

View File

@ -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
}

View File

@ -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

View File

@ -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

View 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
View File

@ -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

File diff suppressed because it is too large Load Diff

View 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 NSNotifications userInfo property.
*/
+ (AnyPromise *)once:(NSString *)notificationName NS_REFINED_FOR_SWIFT;
@end

View 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

View 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
}
}

View 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: Apples 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()
}()
}

View 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

View 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

View 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 shouldnt 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

View 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

View 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 dont 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

View File

@ -0,0 +1,3 @@
#import "NSNotificationCenter+AnyPromise.h"
#import "NSURLSession+AnyPromise.h"
#import "NSTask+AnyPromise.h"

View 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 PromiseKits `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 Foundations 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

View 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
View 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
View 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, Im Max Howell. Im 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 its 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 Apples 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 dont 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 isnt 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

View 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
View 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.
Dont 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 variants 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 Apples enums are, so if you find one that hasnt 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
View 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
View 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 dont 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
View 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 dont 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 barrierd
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
View 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 promises 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 promises 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 dont 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
}
}

View File

@ -0,0 +1,35 @@
import Dispatch
/**
PromiseKits 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 applications lifetime
public var conf = PMKConfiguration()

View 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

View 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
View 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
View 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 closures 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, dont 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
View 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)
}

View 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;
}

View 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 blocks 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 dont catch objc exceptions: they are meant to crash your app
@throw thrown;
}
}

184
Pods/PromiseKit/Sources/Promise.swift generated Normal file
View 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, soyou knowdont 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
View 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
View 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
View 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 closures qualification.
- Parameter on: The queue to which the provided closure dispatches.
- Parameter body: The closure that is executed when this Promise is fulfilled.
- Returns: A new promise fulfilled as `Void` 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
View 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
View 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)
}
}

View 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
View 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
View 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, whens promise fulfills with an array of fulfillment values. If a dictionary is passed then the same occurs, but whens 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 promises rejection. The errors `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
View 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
View 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
View 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
View 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
View 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
View 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
View 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 } // dont 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
} // dont 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
View 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
View 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

View 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?
}

View 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()
}
}

View 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
}

View 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: (() -> ())?)
}

View 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)))
}
}

View 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()
}
}

View 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

View 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
}
}

View 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
}
}

View 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)
}
}

View 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)
}
}

View 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
}
}

View 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()
}
}

View 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?
}

View 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?
}

View 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

View 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)
}
}
}

View 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
}
}
}

View 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

View 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 }
}

View File

@ -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

View File

@ -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 &lt;360470+hewigovens@users.noreply.github.com&gt;
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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View 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>

View File

@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_PromiseKit : NSObject
@end
@implementation PodsDummy_PromiseKit
@end

View 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

View 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[];

View 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

View File

@ -0,0 +1,6 @@
framework module PromiseKit {
umbrella header "PromiseKit-umbrella.h"
export *
module * { export * }
}

View 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

View 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>

View File

@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_Starscream : NSObject
@end
@implementation PodsDummy_Starscream
@end

View 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

View 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[];

View 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

View File

@ -0,0 +1,6 @@
framework module Starscream {
umbrella header "Starscream-umbrella.h"
export *
module * { export * }
}

View 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

View 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>

View File

@ -0,0 +1,5 @@
#import <Foundation/Foundation.h>
@interface PodsDummy_WalletConnect : NSObject
@end
@implementation PodsDummy_WalletConnect
@end

View 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

View 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