### Install Dependencies Source: https://github.com/mxcl/promisekit/blob/master/Tests/JS-A+/README.md Install the necessary npm packages for the test suite. ```bash npm install ``` -------------------------------- ### Install Core PromiseKit with CocoaPods Source: https://github.com/mxcl/promisekit/blob/master/README.md Use this Podfile entry if you only want the core PromiseKit library without any extensions. ```ruby pod "PromiseKit/CorePromise", "~> 8" ``` -------------------------------- ### Manual Integration of PromiseKit Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Installation.md For manual installation, drag the PromiseKit.xcodeproj into your project and add PromiseKit.framework to your app's embedded frameworks. ```text You can just drop `PromiseKit.xcodeproj` into your project and then add `PromiseKit.framework` to your app’s embedded frameworks. ``` -------------------------------- ### Install PromiseKit 3.5 with Carthage for Xcode 8 / Swift 2.3 Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Installation.md Use this line in your Cartfile to install PromiseKit version 3.5 when using Carthage for Xcode 8 and Swift 2.3. ```shell github "mxcl/PromiseKit" ~> 3.5 ``` -------------------------------- ### Install PromiseKit Extensions with CocoaPods Source: https://github.com/mxcl/promisekit/blob/master/README.md Specify subspecs in your Podfile to include extensions for specific Apple frameworks like MapKit or CoreLocation. ```ruby pod "PromiseKit/MapKit" # MKDirections().calculate().then { /*…*/ } pod "PromiseKit/CoreLocation" # CLLocationManager.requestLocation().then { /*…*/ } ``` -------------------------------- ### Install PromiseKit 3.5 with CocoaPods for Xcode 8 / Swift 2.3 Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Installation.md Use this configuration in your Podfile to install PromiseKit version 3.5 when targeting Xcode 8 and Swift 2.3. Ensure your swift_version is set correctly. ```ruby swift_version = "2.3" pod "PromiseKit", "~> 3.5" ``` -------------------------------- ### Starting Promise Chain on Background Queue Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Appendix.md To initiate a promise chain on a background queue, use `DispatchQueue.global().async(.promise)`. If the asynchronous operation needs to return a value, wrap it in a `Promise` initializer that accepts a `Seal`. ```swift DispatchQueue.global().async(.promise) { return value }.done { value in //… } ``` ```swift Promise { seal in DispatchQueue.global().async { seal(value) } }.done { value in //… } ``` ```swift DispatchQueue.global().async(.promise) { return try fetch().wait() }.done { value in //… } ``` -------------------------------- ### Explicit Swift closure syntax for promise chains Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md This example shows the explicit syntax for Swift closures within promise chains, including return types and statements, which is equivalent to inferred closures. ```swift foo.then { baz -> Promise in return bar(baz) } ``` -------------------------------- ### Using `get` to access resolved promise values Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md Use `get` to access the resolved value of a promise in a chain. The value passed to `get` is the same value passed to the subsequent `done`. ```swift firstly { foo() }.get { foo in //… }.done { foo in // same foo! } ``` -------------------------------- ### Get Cache Destination URL Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Examples/ImageCache.md Determines the local file system path where an image from a remote URL should be cached. It ensures the cache directory exists and constructs a unique filename based on the URL's hash value, preserving the original file extension. ```swift public func cache(destination remoteUrl: URL) throws -> URL { return try remoteUrl.cacheDestination() } ``` ```swift private func cache() throws -> URL { guard let dst = FileManager.default.docs? .appendingPathComponent("Library") .appendingPathComponent("Caches") .appendingPathComponent("cache.img") else { throw E.unexpectedError } try FileManager.default.createDirectory(at: dst, withIntermediateDirectories: true, attributes: [:]) return dst } ``` ```swift private extension URL { func cacheDestination() throws -> URL { var fn = String(hashValue) let ext = pathExtension // many of Apple's functions don’t recognize file type // unless we preserve the file extension if !ext.isEmpty { fn += ".\(ext)" } return try cache().appendingPathComponent(fn) } } ``` -------------------------------- ### Handling errors in branched promise chains Source: https://github.com/mxcl/promisekit/blob/master/Documentation/FAQ.md When using branched promise chains, ensure that each branch is appropriately handled for errors. This example shows one branch with a catch handler and another that ignores errors after a preceding catch. ```swift promise.then { // branch A }.catch { error in //… } _ = promise.then { print("foo") // ignoring errors here as print cannot error and we handle errors above } ``` -------------------------------- ### Build Test Suite Source: https://github.com/mxcl/promisekit/blob/master/Tests/JS-A+/README.md Build the JavaScript test suite for use with PromiseKit. ```bash npm run build ``` -------------------------------- ### Creating a custom Guarantee Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md Shows how to create a custom `Guarantee` that resolves with a value. This is simpler than creating a `Promise`. ```swift func fetch() -> Promise { return Guarantee { seal in fetch { result in seal(result) } } } ``` -------------------------------- ### Enable Live Rebuilds Source: https://github.com/mxcl/promisekit/blob/master/Tests/JS-A+/README.md Enable live rebuilding of JavaScript files during development. ```bash npm run watch ``` -------------------------------- ### Build with XCFrameworks using Carthage Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Installation.md For Xcode 12 and later, you may need to use the --use-xcframeworks flag when building with Carthage. ```bash carthage build --use-xcframeworks ``` -------------------------------- ### Disable Dispatch for Debugging Source: https://github.com/mxcl/promisekit/blob/master/Documentation/FAQ.md Temporarily disable dispatching for promises during debugging to get more informative backtraces. Remember to re-enable dispatching for normal use. ```swift // Swift DispatchQueue.default = zalgo ``` ```objc //ObjC PMKSetDefaultDispatchQueue(zalgo) ``` -------------------------------- ### Creating a New Promise with AnyPromise Source: https://github.com/mxcl/promisekit/blob/master/Documentation/ObjectiveC.md Shows how to create a new promise using the promiseWithResolverBlock constructor, resolving or rejecting based on the provided value. ```objc - (AnyPromise *)myPromise { return [AnyPromise promiseWithResolverBlock:^(PMKResolver resolve){ resolve(foo); // if foo is an NSError, rejects, else, resolves }]; } ``` -------------------------------- ### Race Promises with Timeout Source: https://github.com/mxcl/promisekit/blob/master/Documentation/CommonPatterns.md Use `race` to get the result of the first promise to settle, often combined with a timeout promise. Ensure all promises in `race` are of the same type, e.g., by using `asVoid()`. ```swift let fetches: [Promise] = makeFetches() let timeout = after(seconds: 4) race(when(fulfilled: fetches).asVoid(), timeout).then { //… } ``` -------------------------------- ### Using Guarantee with `after` Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md Demonstrates a `Guarantee` chain using `after`. Guarantees cannot be caught as they never fail. ```swift firstly { after(seconds: 0.1) }.done { // there is no way to add a `catch` because after cannot fail. } ``` -------------------------------- ### Fetch Image and Location with PromiseKit Source: https://github.com/mxcl/promisekit/blob/master/README.md Demonstrates fetching an image and location concurrently using PromiseKit's `firstly` and `when` functions. Ensures network activity indicator is managed and handles errors. ```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) } ``` -------------------------------- ### Get Cached Image Data Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Examples/ImageCache.md Retrieves image data directly from the local cache if available. This function performs a synchronous check and returns nil if the image is not found or an error occurs during path resolution. ```swift public func cached(image url: URL) -> Data? { guard let dst = try? url.cacheDestination() else { return nil } return try? Data(contentsOf: dst) } ``` -------------------------------- ### Configure Carthage for PromiseKit Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Installation.md When using Carthage, specify only the main PromiseKit repository. Extensions should be managed separately via Git submodules if needed. ```bash github "mxcl/PromiseKit" # ^^ for Carthage *only* have this ``` -------------------------------- ### Chaining Promises in Objective-C Source: https://github.com/mxcl/promisekit/blob/master/Documentation/ObjectiveC.md Demonstrates chaining multiple then and catch blocks for asynchronous operations using AnyPromise. ```objc myPromise.then(^(NSString *bar){ return anotherPromise; }).then(^{ //… }).catch(^(NSError *error){ //… }); ``` -------------------------------- ### Configure PromiseKit for Server-Side Use (Kitura) Source: https://github.com/mxcl/promisekit/blob/master/Documentation/FAQ.md Configure PromiseKit to avoid dispatching to the main queue, which is necessary for server frameworks like Kitura that require the main queue to remain unused. It is recommended to use a custom queue for better performance. ```swift PromiseKit.conf.Q = (map: DispatchQueue.global(), return: DispatchQueue.global()) ``` ```swift import Foundation import HeliumLogger import Kitura import LoggerAPI import PromiseKit HeliumLogger.use(.info) let pmkQ = DispatchQueue(label: "pmkQ", qos: .default, attributes: .concurrent, autoreleaseFrequency: .workItem) PromiseKit.conf.Q = (map: pmkQ, return: pmkQ) let router = Router() router.get("/") { _, response, next in Log.info("Request received") after(seconds: 1.0).done { Log.info("Sending response") response.send("OK") next() } } Log.info("Starting server") Kitura.addHTTPServer(onPort: 8888, with: router) Kitura.run() ``` -------------------------------- ### PromiseKit Extensions for CoreLocation and CoreServices Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md Demonstrates chaining asynchronous operations using PromiseKit extensions for `CLLocationManager` and `CLGeocoder`. Ensure the necessary subspecs are included in your Podfile. ```swift firstly { CLLocationManager.promise() }.then { location in CLGeocoder.reverseGeocode(location) }.done { placemarks in self.placemark.text = "\(placemarks.first)" } ``` -------------------------------- ### Concurrent Operations with `when` Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md Shows how to use `when` to concurrently execute multiple promises and handle their results. If any promise fails, the chain proceeds to the `catch` block. ```swift firstly { when(fulfilled: operation1(), operation2()) }.done { result1, result2 in //… } ``` -------------------------------- ### Add PromiseKit Extensions via Git Submodules Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Installation.md This advanced method involves adding specific PromiseKit extensions as Git submodules. Initialize and add the submodule for UIKit extensions. ```bash git submodule init git submodule add https://github.com/PromiseKit/UIKit Submodules/PMKUIKit ``` -------------------------------- ### Add PromiseKit via Accio Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Installation.md Include this package definition in your Package.swift file for Accio integration. Ensure you also add PromiseKit to your app target's dependencies. ```swift .package(url: "https://github.com/mxcl/PromiseKit.git", .upToNextMajor(from: "6.8.4")), ``` ```swift .target( name: "App", dependencies: [ "PromiseKit", ] ), ``` -------------------------------- ### Add PromiseKit to Podfile Source: https://github.com/mxcl/promisekit/blob/master/README.md Specifies how to add PromiseKit version 8 to your project using CocoaPods. Ensure `use_frameworks!` is included for Swift projects. ```ruby use_frameworks! target "Change Me!" do pod "PromiseKit", "~> 8" end ``` -------------------------------- ### Flexible Promise Handler Syntax in Objective-C Source: https://github.com/mxcl/promisekit/blob/master/Documentation/ObjectiveC.md Highlights the syntactic flexibility of AnyPromise handlers, allowing for zero, one, or up to three parameters, and returning any value or nothing. ```objc myPromise.then(^{ // no parameters is fine }); ``` ```objc myPromise.then(^(id foo){ // one parameter is fine }); ``` ```objc myPromise.then(^(id a, id b, id c){ // up to three parameter is fine, no crash! }); ``` ```objc myPromise.then(^{ return @1; // return anything or nothing, it's fine, no crash }); ``` -------------------------------- ### Traditional Completion Handler Chain Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md Shows the equivalent asynchronous operation using traditional completion handlers, demonstrating the increased complexity and nesting. ```swift login { creds, error in if let creds = creds { fetch(avatar: creds.user) { image, error in if let image = image { self.imageView = image } } } } ``` -------------------------------- ### Upgrade Test Suite Source: https://github.com/mxcl/promisekit/blob/master/Tests/JS-A+/README.md Upgrade the Promises/A+ test suite to the latest version. ```bash npm install --save promises-aplus-tests@latest ``` -------------------------------- ### Completion Handler Equivalent for Cleanup Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md This shows the completion handler pattern for cleanup, illustrating the potential for errors if cleanup logic is forgotten in different code paths. ```swift UIApplication.shared.isNetworkActivityIndicatorVisible = true func handle(error: Error) { UIApplication.shared.isNetworkActivityIndicatorVisible = false //… } login { creds, error in guard let creds = creds else { return handle(error: error!) } fetch(avatar: creds.user) { image, error in guard let image = image else { return handle(error: error!) } self.imageView.image = image UIApplication.shared.isNetworkActivityIndicatorVisible = false } } ``` -------------------------------- ### Promise Execution Timing in Swift Source: https://github.com/mxcl/promisekit/blob/master/Documentation/FAQ.md Demonstrates that the promise body executes immediately upon initialization, not when `then` is called. Use this to understand when synchronous work within a promise begins. ```swift let testPromise = Promise { print("Executing the promise body.") return $0.fulfill(true) } ``` -------------------------------- ### Add PromiseKit via CocoaPods Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Installation.md Use this in your Podfile to include PromiseKit in your project. Ensure you are using Swift 3 or 4. ```ruby use_frameworks! target "Change Me!" do pod "PromiseKit", "~> 6.8" end ``` -------------------------------- ### Convert Completion Handler to Promise Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md This snippet shows how to convert a method with a completion handler to a Promise-returning method. It uses the `Promise` initializer with a closure that takes a `Resolver` object. ```swift func fetch(completion: (String?, Error?) -> Void) ``` ```swift func fetch() -> Promise { return Promise { fetch(completion: $0.resolve) } } ``` ```swift func fetch() -> Promise { return Promise { seal in fetch { result, error in seal.resolve(result, error) } } } ``` -------------------------------- ### Parallel Asynchronous Operations with DispatchGroup Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md Illustrates a faster, parallel approach using `DispatchGroup` for managing multiple asynchronous operations. Requires manual management of group entry and exit. ```swift var result1: …! var result2: …! let group = DispatchGroup() group.enter() group.enter() operation1 { result1 = $0 group.leave() } operation2 { result2 = $0 group.leave() } group.notify(queue: .main) { finish(result1, result2) } ``` -------------------------------- ### Using `firstly` for readable promise chains Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md The `firstly` function is syntactic sugar that enhances the readability of promise chains. It returns a `Promise`. ```swift firstly { login() }.then { creds in //… } ``` -------------------------------- ### Promise Chain with `then` and `done` Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md Illustrates a typical promise chain for sequential asynchronous operations, improving readability over nested completion handlers. ```swift firstly { login() }.then { fetch(avatar: $0.user) }.done { self.imageView = $0 } ``` -------------------------------- ### Abstracting Asynchronicity with Promises Source: https://github.com/mxcl/promisekit/blob/master/Documentation/CommonPatterns.md This snippet demonstrates how to abstract asynchronous operations using PromiseKit. It shows how to handle the resolution of a promise with `then` and how to manage a single ongoing fetch operation. ```swift var fetch = API.fetch() override func viewDidAppear() { fetch.then { items in //… } } func buttonPressed() { fetch.then { items in //… } } func refresh() -> Promise { // ensure only one fetch operation happens at a time if fetch.isResolved { startSpinner() fetch = API.fetch().ensure { stopSpinner() } } return fetch } ``` -------------------------------- ### Fetch Image with PromiseKit Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Examples/ImageCache.md Fetches an image from a URL, caching it locally. It consolidates multiple requests for the same URL and handles downloading and saving the image. If the image is already cached and readable, it returns the cached version. ```swift import Foundation import PromiseKit /** * Small (10 images) * Thread-safe * Consolidates multiple requests to the same URLs * Removes stale entries (FIXME well, strictly we may delete while fetching from cache, but this is unlikely and non-fatal) * Completely _ignores_ server caching headers! */ private let q = DispatchQueue(label: "org.promisekit.cache.image", attributes: .concurrent) private var active: [URL: Promise] = [:] private var cleanup = Promise() public func fetch(image url: URL) -> Promise { var promise: Promise? q.sync { promise = active[url] } if let promise = promise { return promise } q.sync { promise = Promise(.start) { let dst = try url.cacheDestination() guard !FileManager.default.isReadableFile(atPath: dst.path) else { return Promise(dst) } return Promise { seal in URLSession.shared.downloadTask(with: url) { tmpurl, _, error in do { guard let tmpurl = tmpurl else { throw error ?? E.unexpectedError } try FileManager.default.moveItem(at: tmpurl, to: dst) seal.fulfill(dst) } catch { seal.reject(error) } }.resume() } }.then(on: .global(QoS: .userInitiated)) { try Data(contentsOf: $0) } active[url] = promise if cleanup.isFulfilled { cleanup = promise!.asVoid().then(on: .global(QoS: .utility), execute: docleanup) } } return promise! } ``` -------------------------------- ### Rejecting Promises with Errors in Objective-C Source: https://github.com/mxcl/promisekit/blob/master/Documentation/ObjectiveC.md Illustrates how to reject a promise by throwing an NSError within a then block. ```objc myPromise.then(^{ @throw [NSError errorWithDomain:domain code:code userInfo:nil]; }).catch(^(NSError *error){ //… }); ``` -------------------------------- ### Using `compactMap` for JSON parsing Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md Illustrates chaining with `compactMap` to parse JSON data. It handles potential `JSONSerialization` errors or type mismatches. ```swift firstly { URLSession.shared.dataTask(.promise, with: rq) }.compactMap { try JSONSerialization.jsonObject($0.data) as? [String] }.done { // arrayOfStrings }.catch { // Foundation.JSONError if JSON was badly formed // PMKError.compactMap if JSON was of different type } ``` -------------------------------- ### Using Objective-C Promises in Swift Source: https://github.com/mxcl/promisekit/blob/master/Documentation/ObjectiveC.md Demonstrates how to call an Objective-C promise-returning method from Swift and handle the resolved value. The type of the resolved object does not need to be explicitly specified. ```swift let foo = Foo() foo.myPromise.then { (obj: AnyObject?) -> Int in // it is not necessary to specify the type of `obj` // we just do that for demonstrative purposes } ``` -------------------------------- ### Catching and Resolving in Objective-C Source: https://github.com/mxcl/promisekit/blob/master/Documentation/ObjectiveC.md Demonstrates that returning nothing from a catch block resolves the returned promise, ensuring subsequent then blocks always execute. ```objc myPromise.catch(^{ [UIAlertView …]; }).then(^{ // always executes! }); ``` -------------------------------- ### Serial Asynchronous Operations Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md Demonstrates a serial approach to handling multiple asynchronous operations, which can be slow. ```swift operation1 { result1 in operation2 { result2 in finish(result1, result2) } } ``` -------------------------------- ### Background-Loaded Member Variables with PromiseKit Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Appendix.md Use `DispatchQueue.global().async(.promise)` to initialize member variables asynchronously on a background thread. This is useful for tasks that should not block the main thread during initialization. ```swift class MyViewController: UIViewController { private let ambience: Promise = DispatchQueue.global().async(.promise) { guard let asset = NSDataAsset(name: "CreepyPad") else { throw PMKError.badInput } let player = try AVAudioPlayer(data: asset.data) player.prepareToPlay() return player } } ``` -------------------------------- ### Reducing custom Guarantee creation Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md A more concise way to create a `Guarantee` when the resolver function matches the expected signature. ```swift func fetch() -> Promise { return Guarantee(resolver: fetch) } ``` -------------------------------- ### Configure Swift Version for CocoaPods Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Installation.md If your Xcode project warns about PromiseKit needing an upgrade to Swift 4.0 or 4.2, add this post-install hook to your Podfile. ```ruby post_install do |installer| installer.pods_project.targets.each do |target| if target.name == 'PromiseKit' target.build_configurations.each do |config| config.build_settings['SWIFT_VERSION'] = '4.2' end end end end ``` -------------------------------- ### Traditional `login` Function Signature with Completion Handler Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md Defines the signature of a `login` function that accepts a completion handler, highlighting the use of optional parameters for results and errors. ```swift func login(completion: (Creds?, Error?) -> Void) ``` -------------------------------- ### Completion Handler Equivalent for Error Handling Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md This demonstrates the traditional completion handler pattern for asynchronous operations, highlighting its verbosity compared to promise chains. ```swift func handle(error: Error) { //… } login { creds, error in guard let creds = creds else { return handle(error: error!) } fetch(avatar: creds.user) { image, error in guard let image = image else { return handle(error: error!) } self.imageView.image = image } } ``` -------------------------------- ### Add PromiseKit via Carthage Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Installation.md Specify this line in your Cartfile to manage PromiseKit dependencies with Carthage. Note that versions 6.8.1 and later require Swift 4+. ```ruby github "mxcl/PromiseKit" ~> 6.8 ``` -------------------------------- ### Handle Optionals in Promises Correctly Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Appendix.md Avoid returning `Promise` which forces consumers to handle nil. Instead, use the error path for exceptional conditions like empty collections. ```swift return firstly { getItems() }.then { items -> Promise<[Item]?> in guard !items.isEmpty else { return .value(nil) } return Promise(value: items) } ``` ```swift return firstly { getItems() }.map { items -> [Item] in guard !items.isEmpty else { throw MyError.emptyItems } return items } ``` -------------------------------- ### Configure Default Queues for PromiseKit Handlers Source: https://github.com/mxcl/promisekit/blob/master/Documentation/FAQ.md Change the default queues for `then`-type handlers and finalizers. Setting queues to `nil` causes immediate execution, useful for testing. ```swift PromiseKit.conf.Q.map = .global() PromiseKit.conf.Q.return = .main //NOTE this is the default ``` ```swift // in your test suite setup code PromiseKit.conf.Q.map = nil PromiseKit.conf.Q.return = nil ``` -------------------------------- ### Chaining Promises from an Array of Closures Source: https://github.com/mxcl/promisekit/blob/master/Documentation/CommonPatterns.md This snippet shows how to sequentially execute promises generated by an array of closures. It's important to note that `when()` is generally preferred for parallel execution unless sequential execution is strictly required. ```swift var foo = Promise() for nextPromise in arrayOfClosuresThatReturnPromises { foo = foo.then(nextPromise) // ^^ you rarely would want an array of promises instead, since then // they have all already started, you may as well use `when()` } foo.done { // finish } ``` -------------------------------- ### Cache Cleanup Logic Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Examples/ImageCache.md Implements a cleanup routine for the image cache. It sorts cached files by creation date and removes the oldest ones until only a maximum of 10 images remain. This function requires the cache directory to be accessible and files to have creation dates. ```swift private func docleanup() throws { var contents = try FileManager.default .contentsOfDirectory(at: try cache(), includingPropertiesForKeys: [.creationDateKey]) .map { url -> (Date, URL) in guard let date = try url.resourceValues(forKeys: [.creationDateKey]).creationDate else { throw E.noCreationTime } return (date, url) }.sorted(by: { $0.0 > $1.0 }) while contents.count > 10 { let rm = contents.popLast()!.1 try FileManager.default.removeItem(at: rm) } } ``` -------------------------------- ### Handle All Paths in Promise Initializers Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Troubleshooting.md To prevent 'Pending Promise Deallocated!' warnings, ensure all possible resolution paths (fulfill, reject, or other conditions) are handled within the Promise initializer. ```swift Promise { task { if let value = value as? String { seal.fulfill(value) } else if let error = error { seal.reject(error) } } } ``` ```swift Promise { seal in task { value, error in if let value = value as? String { fulfill(value) } else if let error = error { reject(error) } else if value != nil { reject(MyError.valueNotString) } else { reject(PMKError.invalidCallingConvention) } } } ``` -------------------------------- ### Perform Background Work with PromiseKit Source: https://github.com/mxcl/promisekit/blob/master/Documentation/CommonPatterns.md Use the `on` parameter in PromiseKit handlers to specify a background queue for operations. This ensures that computationally intensive tasks do not block the main thread. ```swift class MyRestAPI { func avatar() -> Promise { let bgq = DispatchQueue.global(qos: .userInitiated) return firstly { user() }.then(on: bgq) { URLSession.shared.dataTask(.promise, with: user.imageUrl) }.compactMap(on: bgq) { UIImage(data: $0) } } } ``` -------------------------------- ### Create Fulfilled Void Promise - Swift Source: https://github.com/mxcl/promisekit/blob/master/Documentation/FAQ.md Use `Promise()` for a pending void promise or `Promise.value(())` for an already fulfilled void promise. ```swift let foo = Promise() // or: let bar = Promise.value(()) ``` -------------------------------- ### Chaining Animations with PromiseKit Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Appendix.md Chain multiple UIView animations sequentially using PromiseKit's `.then` block. This ensures animations complete in the desired order before the next one begins. ```swift firstly { UIView.animate(.promise, duration: 0.3) { self.button1.alpha = 0 } }.then { UIView.animate(.promise, duration: 0.3) { self.button2.alpha = 1 } }.then { UIView.animate(.promise, duration: 0.3) { adjustConstraints() self.view.layoutIfNeeded() } } ``` -------------------------------- ### Chaining Sequences of Promises for Sequential Execution Source: https://github.com/mxcl/promisekit/blob/master/Documentation/CommonPatterns.md This pattern is useful for executing a series of asynchronous tasks sequentially, such as animating UI elements. It chains promises using `then` to ensure each task completes before the next begins. ```swift // fade all visible table cells one by one in a “cascading” effect var fade = Guarantee() for cell in tableView.visibleCells { fade = fade.then { UIView.animate(.promise, duration: 0.1) { cell.alpha = 0 } } } fade.done { // finish } ``` -------------------------------- ### Asynchronous Task Execution in Promise Body Source: https://github.com/mxcl/promisekit/blob/master/Documentation/FAQ.md Illustrates how asynchronous tasks initiated within a promise body are managed by their respective dispatch queues, not PromiseKit itself. The promise body executes immediately, while the async task runs later. ```swift let testPromise = Promise { print("Executing the promise body.") DispatchQueue.main.asyncAfter(deadline: .now() + 3.0) { print("Executing asyncAfter.") return seal.fulfill(true) } } ``` -------------------------------- ### Swift Class with Promise-Returning Methods Source: https://github.com/mxcl/promisekit/blob/master/Documentation/ObjectiveC.md A Swift class with methods that return generic Promises. Ensure your project generates a Swift header for Objective-C visibility. ```swift @objc class Foo: NSObject { func stringPromise() -> Promise func barPromise() -> Promise } @objc class Bar: NSObject { /*…*/ } ``` -------------------------------- ### Designing APIs to return Promises in Swift Source: https://github.com/mxcl/promisekit/blob/master/Documentation/CommonPatterns.md Design asynchronous APIs to return `Promise` objects instead of completion blocks. This allows for seamless integration into PromiseKit's composable chains. ```swift class MyRestAPI { func user() -> Promise { return firstly { URLSession.shared.dataTask(.promise, with: url) }.compactMap { try JSONSerialization.jsonObject(with: $0.data) as? [String: Any] }.map { User(dict: $0) } } func avatar() -> Promise { return user().then { URLSession.shared.dataTask(.promise, with: $0.imageUrl) }.compactMap { UIImage(data: $0.data) } } } ``` -------------------------------- ### PromiseKit `login` Function Signature Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md Defines the signature of a `login` function that returns a `Promise`, contrasting with the completion handler version. ```swift func login() -> Promise ``` -------------------------------- ### Cast AnyPromise value in Swift Source: https://github.com/mxcl/promisekit/blob/master/Documentation/ObjectiveC.md Swift code demonstrating how to cast the 'Any' type received from an Objective-C AnyPromise to a specific Swift type. ```swift Foo.fetchThings().done { any in let bar = any as! [String] } ``` -------------------------------- ### Ensure Minimum Task Duration Source: https://github.com/mxcl/promisekit/blob/master/Documentation/CommonPatterns.md Use `after` to enforce a minimum duration for a task, ensuring UI elements like spinners are visible for a sufficient time. This pattern delays the continuation of the promise chain. ```swift let waitAtLeast = after(seconds: 0.3) firstly { foo() }.then { waitAtLeast }.done { //… } ``` -------------------------------- ### Add PromiseKit via Swift Package Manager Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Installation.md Append this dependency to your package's dependencies array in Package.swift for Swift Package Manager integration. ```swift package.dependencies.append( .package(url: "https://github.com/mxcl/PromiseKit", from: "6.8.0") ) ``` -------------------------------- ### Implement Cancellable Promises Source: https://github.com/mxcl/promisekit/blob/master/Documentation/CommonPatterns.md Create a tuple returning a Promise and a cancellation function. Cancellation is handled via a special error type conforming to `CancellableError` and does not trigger `.catch` by default. ```swift func foo() -> (Promise, cancel: () -> Void) { let task = Task(…) var cancelme = false let promise = Promise { seal in task.completion = { value in guard !cancelme else { return reject(PMKError.cancelled) } seal.fulfill(value) } task.start() } let cancel = { cancelme = true task.cancel() } return (promise, cancel) } ``` -------------------------------- ### Swift Stubs for Objective-C Compatibility Source: https://github.com/mxcl/promisekit/blob/master/Documentation/ObjectiveC.md Swift stubs created to make generic Swift promises visible to Objective-C. These stubs wrap generic Promises in AnyPromise. ```swift @objc class Foo: NSObject { @objc func stringPromise() -> AnyPromise { return AnyPromise(stringPromise()) } @objc func barPromise() -> AnyPromise { return AnyPromise(barPromise()) } } ``` -------------------------------- ### Return AnyPromise from Objective-C Source: https://github.com/mxcl/promisekit/blob/master/Documentation/ObjectiveC.md Objective-C method returning an AnyPromise with a value. This is used when integrating with Swift. ```objective-c - (AnyPromise *)fetchThings { return [AnyPromise promiseWithValue:@[@"a", @"b", @"c"]]; } ``` -------------------------------- ### Generated Objective-C Header for Swift Class (After Stubs) Source: https://github.com/mxcl/promisekit/blob/master/Documentation/ObjectiveC.md The Objective-C header generated after adding stubs for generic Swift promises. This allows Objective-C to see and call these promise-returning methods. ```objc @interface Foo - (AnyPromise *)stringPromise; - (AnyPromise *)barPromise; @end @interface Bar @end ``` -------------------------------- ### Wrap CLLocationManager Delegation with Promises Source: https://github.com/mxcl/promisekit/blob/master/Documentation/CommonPatterns.md Create a Promise-based interface for CLLocationManager to handle single location lookups. This pattern is useful for bridging delegate-based APIs to a Promise-based flow. ```swift extension CLLocationManager { static func promise() -> Promise { return PMKCLLocationManagerProxy().promise } } class PMKCLLocationManagerProxy: NSObject, CLLocationManagerDelegate { private let (promise, seal) = Promise<[CLLocation]>.pending() private var retainCycle: PMKCLLocationManagerProxy? private let manager = CLLocationManager() init() { super.init() retainCycle = self manager.delegate = self // does not retain hence the `retainCycle` property promise.ensure { // ensure we break the retain cycle self.retainCycle = nil } } @objc fileprivate func locationManager(_: CLLocationManager, didUpdateLocations locations: [CLLocation]) { seal.fulfill(locations) } @objc func locationManager(_: CLLocationManager, didFailWithError error: Error) { seal.reject(error) } } // use: CLLocationManager.promise().then { //… }.catch { //… } ``` -------------------------------- ### Promises for Modal View Controllers Source: https://github.com/mxcl/promisekit/blob/master/Documentation/CommonPatterns.md Wrap modal view controller presentation and dismissal in a `Promise` or `Guarantee` to manage asynchronous flows. The `seal` is used to fulfill the promise when the modal is dismissed. ```swift class ViewController: UIViewController { private let (promise, seal) = Guarantee<…>.pending() // use Promise if your flow can fail func show(in: UIViewController) -> Promise<…> { in.show(self, sender: in) return promise } func done() { dismiss(animated: true) seal.fulfill(…) } } // use: ViewController().show(in: self).done { //… }.catch { //… } ``` -------------------------------- ### Blocking Main Thread with wait() Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Appendix.md Use the `wait()` method to block the current thread until a promise resolves. Caution: This should not be used on the main thread if the promise's callback might invoke the main thread, as it can lead to deadlocks. ```swift public extension UNUserNotificationCenter { var wasPushRequested: Bool { let settings = Guarantee(resolver: getNotificationSettings).wait() return settings != .notDetermined } } ``` -------------------------------- ### Generated Objective-C Header for Swift Class (Before Stubs) Source: https://github.com/mxcl/promisekit/blob/master/Documentation/ObjectiveC.md The Objective-C header generated for a Swift class before adding stubs for generic types. Objective-C cannot directly import Swift generic objects. ```objc @interface Foo @end @interface Bar @end ``` -------------------------------- ### Using `tap` for debugging promise chains Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md Use `tap` to inspect the `Result` of a promise at a specific point in the chain without side effects. It's useful for debugging. ```swift firstly { foo() }.tap { print($0) }.done { //… }.catch { //… } ``` -------------------------------- ### Objective-C Interface with a Promise Source: https://github.com/mxcl/promisekit/blob/master/Documentation/ObjectiveC.md An Objective-C interface defining a method that returns a promise. Ensure this interface is included in your bridging header for Swift access. ```objc @interface Foo - (AnyPromise *)myPromise; @end ``` -------------------------------- ### Avoid Doubling Promises Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Appendix.md Do not wrap an existing promise in a new Promise constructor. Instead, chain operations directly off the original promise. ```swift func toggleNetworkSpinnerWithPromise(funcToCall: () -> Promise) -> Promise { return Promise { seal in firstly { setNetworkActivityIndicatorVisible(true) return funcToCall() }.then { seal.fulfill($0) }.always { setNetworkActivityIndicatorVisible(false) }.catch { seal.reject($0) } } } ``` ```swift func toggleNetworkSpinnerWithPromise(funcToCall: () -> Promise) -> Promise { return firstly { setNetworkActivityIndicatorVisible(true) return funcToCall() }.always { setNetworkActivityIndicatorVisible(false) } } ``` -------------------------------- ### Saving Previous Results with Nesting Source: https://github.com/mxcl/promisekit/blob/master/Documentation/CommonPatterns.md A straightforward method to access previous promise results is by nesting `then` blocks. This approach can reduce chain clarity for complex flows. ```swift login().then { username in fetch(avatar: username).done { image in // we have access to both `image` and `username` } }.done { // the chain still continues as you'd expect } ``` -------------------------------- ### Add Return Types to Closures for Slow Compilation Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Troubleshooting.md If experiencing slow compilation times, explicitly add return types to your closures to help the compiler resolve them more efficiently. -------------------------------- ### Intercept Cancellation Errors Source: https://github.com/mxcl/promisekit/blob/master/Documentation/CommonPatterns.md Configure the `.catch` block with `.allErrors` policy to handle cancellation errors alongside other errors. Cancellation is not a success or failure by default. ```swift foo.then { //… }.catch(policy: .allErrors) { // cancelled errors are handled *as well* } ``` -------------------------------- ### Chaining asynchronous operations with PromiseKit Source: https://github.com/mxcl/promisekit/blob/master/Documentation/CommonPatterns.md Use chaining to sequence asynchronous tasks. The `then` block waits for the returned promise to resolve before proceeding. `ensure` executes regardless of the outcome, and `catch` handles errors. ```swift firstly { fetch() }.then { map($0) }.then { set($0) return animate() }.ensure { // something that should happen whatever the outcome }.catch { handle(error: $0) } ``` -------------------------------- ### Networking with URLSession and PromiseKit Source: https://github.com/mxcl/promisekit/blob/master/README.md Perform network operations using URLSession and chain them with PromiseKit for asynchronous handling. The `.validate()` method converts HTTP errors into PromiseKit errors. ```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 { //… }.catch { //… } ``` ```swift 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 } ``` -------------------------------- ### Directly chaining promises without `firstly` Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md Promises can be chained directly using methods like `then` without the need for `firstly`, as `login()` itself returns a `Promise`. ```swift login().then { creds in //… } ``` -------------------------------- ### Recombining branched promise chains Source: https://github.com/mxcl/promisekit/blob/master/Documentation/FAQ.md Safely recombine branched promise chains into a single chain using `when(fulfilled:)` to manage errors collectively. ```swift let p1 = promise.then { // branch A } let p2 = promise.then { // branch B } when(fulfilled: p1, p2).catch { error in //… } ``` -------------------------------- ### Retry Asynchronous Operations with PromiseKit Source: https://github.com/mxcl/promisekit/blob/master/Documentation/CommonPatterns.md Implement a function to retry a Promise-returning operation a specified number of times with a delay. Useful for handling transient network errors or flaky tasks. ```swift func attempt(maximumRetryCount: Int = 3, delayBeforeRetry: DispatchTimeInterval = .seconds(2), _ body: @escaping () -> Promise) -> Promise { var attempts = 0 func attempt() -> Promise { attempts += 1 return body().recover { error -> Promise in guard attempts < maximumRetryCount else { throw error } return after(delayBeforeRetry).then(attempt) } } return attempt() } attempt(maximumRetryCount: 3) { flakeyTask(parameters: foo) }.then { //… }.catch { // we attempted three times but still failed } ``` -------------------------------- ### Embedding PromiseKit Chains in Firebase Handlers Source: https://github.com/mxcl/promisekit/blob/master/Documentation/FAQ.md Shows how to integrate PromiseKit chains within Firebase observation handlers. This pattern is recommended for managing asynchronous operations triggered by Firebase events. ```swift foo.observe(.value) { snapshot in firstly { bar(with: snapshot) }.then { baz() }.then { baffle() }.catch { //… } } ``` -------------------------------- ### Specify Void Parameter in Swift 4 Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Troubleshooting.md When encountering the "Missing argument for parameter #1 in call" error, explicitly specify the Void parameter for seals. ```swift seal.fulfill(()) ``` -------------------------------- ### Ensure Cleanup with `ensure` in Promise Chain Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md The `ensure` handler is always called, regardless of whether the promise chain succeeds or fails, making it ideal for cleanup tasks like hiding network activity indicators. ```swift firstly { UIApplication.shared.isNetworkActivityIndicatorVisible = true return login() }.then { fetch(avatar: $0.user) }.done { self.imageView = $0 }.ensure { UIApplication.shared.isNetworkActivityIndicatorVisible = false }.catch { //… } ``` -------------------------------- ### Handle Errors in Promise Chain with `catch` Source: https://github.com/mxcl/promisekit/blob/master/Documentation/GettingStarted.md Use `catch` to handle any errors that occur anywhere in the promise chain. Swift emits a warning if a chain is not `catch`ed. ```swift firstly { login() }.then { fetch(avatar: $0.user) }.done { self.imageView = $0 }.catch { // any errors in the whole chain land here } ``` -------------------------------- ### Update CocoaPods and Git Submodules Source: https://github.com/mxcl/promisekit/blob/master/Documentation/Installation.md When updating dependencies with CocoaPods, ensure your Git submodules are also updated recursively to maintain consistency. ```bash pod update && git submodule update --recursive --remote ``` -------------------------------- ### Early Return from Promise Function - Swift Source: https://github.com/mxcl/promisekit/blob/master/Documentation/FAQ.md Use a `guard` statement to return a pending `Promise()` or a fulfilled `.value()` early from a function returning a `Promise`. ```swift func foo() -> Promise { guard thingy else { return Promise() } //… } func bar() -> Promise { guard thingy else { return .value(instanceOfSomethingNotVoid) } //… } ```