{"id":18096095,"url":"https://github.com/jkolb/franticapparatus","last_synced_at":"2025-04-13T10:04:35.085Z","repository":{"id":6993617,"uuid":"8257423","full_name":"jkolb/FranticApparatus","owner":"jkolb","description":"Promises framework for Swift 5","archived":false,"fork":false,"pushed_at":"2019-06-02T19:31:16.000Z","size":442,"stargazers_count":25,"open_issues_count":0,"forks_count":0,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-10T17:36:00.779Z","etag":null,"topics":["asynchronous","linux","multiple-promises","parallel-promises","promise","promise-api","promise-chain","promises","promises-aplus","swift","swift-framework","swift-package-manager","swift5"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jkolb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-02-17T22:03:55.000Z","updated_at":"2020-11-17T21:39:45.000Z","dependencies_parsed_at":"2022-11-28T10:34:06.250Z","dependency_job_id":null,"html_url":"https://github.com/jkolb/FranticApparatus","commit_stats":null,"previous_names":[],"tags_count":17,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkolb%2FFranticApparatus","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkolb%2FFranticApparatus/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkolb%2FFranticApparatus/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jkolb%2FFranticApparatus/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jkolb","download_url":"https://codeload.github.com/jkolb/FranticApparatus/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248695342,"owners_count":21146954,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asynchronous","linux","multiple-promises","parallel-promises","promise","promise-api","promise-chain","promises","promises-aplus","swift","swift-framework","swift-package-manager","swift5"],"created_at":"2024-10-31T19:12:38.655Z","updated_at":"2025-04-13T10:04:35.056Z","avatar_url":"https://github.com/jkolb.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FranticApparatus \n[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n\n#### A thread safe, type safe, and memory safe [Promises/A+](https://promisesaplus.com) implementation for Swift 5\n\nHere are some examples pulled directly from the included Demo code.\n\nBuilding a promise that returns the result of a network request:\n```swift\nimport Foundation\nimport FranticApparatus\n\npublic struct NetworkResult {\n    public let response: URLResponse\n    public let data: Data\n}\n\npublic enum NetworkError : Error {\n    case unexpectedData(Data)\n    case unexpectedResponse(URLResponse)\n    case unexpectedStatusCode(Int)\n    case unexpectedContentType(String)\n}\n\npublic protocol NetworkLayer : class {\n    func requestData(_ request: URLRequest) -\u003e Promise\u003cNetworkResult\u003e\n}\n\npublic final class SimpleURLSessionNetworkLayer : NetworkLayer {\n    private let session: URLSession\n\n    public init() {\n        let sessionConfiguration = URLSessionConfiguration.default\n        self.session = URLSession(configuration: sessionConfiguration)\n    }\n\n    deinit {\n        session.invalidateAndCancel()\n    }\n\n    public func requestData(_ request: URLRequest) -\u003e Promise\u003cNetworkResult\u003e {\n        return Promise\u003cNetworkResult\u003e { (fulfill, reject) in\n            session.dataTask(with: request, completionHandler: { (data, response, error) in\n                if let error = error {\n                    reject(error)\n                }\n                else if let data = data, let response = response {\n                    fulfill(NetworkResult(response: response, data: data))\n                }\n                else {\n                    fatalError(\"Unexpected\")\n                }\n            }).resume()\n        }\n    }\n}\n```\n\nChaining off of a network request promise to display thumbnails in a collection view:\n```swift\nfunc loadImage(at indexPath: IndexPath) {\n    let model = models[indexPath.item]\n\n    ApplicationNetworkActvityIndicator.shared.show()\n    promises[indexPath] = fetchImage(url: model.url).then(on: DispatchQueue.main, { (image) in\n        self.images[indexPath] = image\n        self.showImage(at: indexPath)\n    }).catch(on: DispatchQueue.main, { (error) in\n        self.errors[indexPath] = error\n        self.showError(at: indexPath)\n    }).finally(on: DispatchQueue.main, {\n        ApplicationNetworkActvityIndicator.shared.hide()\n        self.promises[indexPath] = nil\n    })\n}\n\nfunc fetchImage(url: URL) -\u003e Promise\u003cUIImage\u003e {\n    return networkLayer.requestData(URLRequest(url: url)).then(on: processQueue, map: { (result) in\n        guard let httpResponse = result.response as? HTTPURLResponse else {\n            throw NetworkError.unexpectedResponse(result.response)\n        }\n\n        guard Set\u003cInt\u003e([200]).contains(httpResponse.statusCode) else {\n            throw NetworkError.unexpectedStatusCode(httpResponse.statusCode)\n        }\n\n        let contentType = httpResponse.mimeType ?? \"\"\n\n        guard Set\u003cString\u003e([\"image/jpeg\", \"image/png\"]).contains(contentType) else {\n            throw NetworkError.unexpectedContentType(contentType)\n        }\n\n        guard let image = UIImage(data: result.data) else {\n            throw NetworkError.unexpectedData(result.data)\n        }\n\n        return image\n    })\n}\n\nfunc showImage(at indexPath: IndexPath) {\n    if let cell = collectionView.cellForItem(at: indexPath) as? ImageCell {\n        cell.hideActivity()\n        cell.image = images[indexPath]\n    }\n}\n\nfunc showError(at indexPath: IndexPath) {\n    if let cell = collectionView.cellForItem(at: indexPath) as? ImageCell {\n        cell.hideActivity()\n\n        if let error = errors[indexPath] {\n            cell.error = messageFor(error: error)\n        }\n        else {\n            cell.error = nil\n        }\n    }\n}\n```\n\nLoading JSON, parsing it, and then waiting for all thumbnails to load before displaying them:\n```swift\noverride func viewDidAppear(_ animated: Bool) {\n    super.viewDidAppear(animated)\n\n    if thumbnailsPromise == nil {\n        ApplicationNetworkActvityIndicator.shared.show()\n        thumbnailsPromise = fetchThumbnails().then(on: DispatchQueue.main, { (thumbnails) in\n            self.thumbnails = thumbnails\n            self.collectionView.reloadData()\n        }).catch(on: DispatchQueue.main, { (error) in\n            NSLog(\"\\(error)\")\n        }).finally(on: DispatchQueue.main, {\n            ApplicationNetworkActvityIndicator.shared.hide()\n            self.thumbnailsPromise = nil\n        })\n    }\n}\n\nfunc fetchThumbnails() -\u003e Promise\u003c[UIImage]\u003e {\n    return networkLayer.requestData(URLRequest(url: URL(string: \"https://reddit.com/.json\")!)).then(on: processQueue, promise: { (result) in\n        guard let httpResponse = result.response as? HTTPURLResponse else {\n            throw NetworkError.unexpectedResponse(result.response)\n        }\n\n        guard Set\u003cInt\u003e([200]).contains(httpResponse.statusCode) else {\n            throw NetworkError.unexpectedStatusCode(httpResponse.statusCode)\n        }\n\n        let contentType = httpResponse.mimeType ?? \"\"\n\n        guard Set\u003cString\u003e([\"application/json\"]).contains(contentType) else {\n            throw NetworkError.unexpectedContentType(contentType)\n        }\n\n        let object = try JSONSerialization.jsonObject(with: result.data, options: [])\n\n        guard let dictionary = object as? NSDictionary else {\n            throw NetworkError.unexpectedData(result.data)\n        }\n\n        let thumbnailURLs = try self.thumbnailsFromJSON(object: dictionary)\n        let thumbnailPromises = thumbnailURLs.map({ self.fetchImage(url: $0) })\n        \n        return all(context: self.processQueue, promises: thumbnailPromises)\n    })\n}\n\nfunc thumbnailsFromJSON(object: NSDictionary) throws -\u003e [URL] {\n    guard let data = object[\"data\"] as? NSDictionary else { throw JSONError.unexpectedJSON }\n    guard let children = data[\"children\"] as? NSArray else { throw JSONError.unexpectedJSON }\n    var thumbnailURLs = [URL]()\n    thumbnailURLs.reserveCapacity(children.count)\n\n    for child in children {\n        if let childObject = child as? NSDictionary {\n            if let childData = childObject[\"data\"] as? NSDictionary {\n                if let thumbnail = childData[\"thumbnail\"] as? NSString {\n                    if thumbnail.hasPrefix(\"http\") {\n                        if let thumbnailURL = URL(string: thumbnail as String) {\n                            thumbnailURLs.append(thumbnailURL)\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n    return thumbnailURLs\n}\n```\n\n## Contact\n\n[Justin Kolb](mailto:franticapparatus@gmail.com)  \n[@nabobnick](https://twitter.com/nabobnick)\n\n## License\n\nFranticApparatus is available under the MIT license. See the LICENSE file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjkolb%2Ffranticapparatus","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjkolb%2Ffranticapparatus","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjkolb%2Ffranticapparatus/lists"}