{"id":13465914,"url":"https://github.com/CodyFlame/CodyFire","last_synced_at":"2025-03-25T21:30:41.783Z","repository":{"id":52869767,"uuid":"144174214","full_name":"CodyFlame/CodyFire","owner":"CodyFlame","description":"🌸 Powerful Codable API requests builder and manager for iOS.","archived":false,"fork":false,"pushed_at":"2022-05-23T11:00:20.000Z","size":486,"stargazers_count":255,"open_issues_count":11,"forks_count":18,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-11T20:51:19.735Z","etag":null,"topics":[],"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/CodyFlame.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":"2018-08-09T15:52:04.000Z","updated_at":"2025-02-05T14:16:50.000Z","dependencies_parsed_at":"2022-08-23T11:20:48.876Z","dependency_job_id":null,"html_url":"https://github.com/CodyFlame/CodyFire","commit_stats":null,"previous_names":["mihaelisaev/codyfire"],"tags_count":41,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodyFlame%2FCodyFire","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodyFlame%2FCodyFire/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodyFlame%2FCodyFire/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CodyFlame%2FCodyFire/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CodyFlame","download_url":"https://codeload.github.com/CodyFlame/CodyFire/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245546830,"owners_count":20633240,"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":[],"created_at":"2024-07-31T15:00:36.892Z","updated_at":"2025-03-25T21:30:41.452Z","avatar_url":"https://github.com/CodyFlame.png","language":"Swift","funding_links":[],"categories":["Libs","HarmonyOS","Network [🔝](#readme)"],"sub_categories":["Network","Windows Manager"],"readme":"[![Mihael Isaev](https://user-images.githubusercontent.com/1272610/47399637-7aa53800-d74a-11e8-9faa-4babb8c33236.png)](http://mihaelisaev.com)\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"LICENSE\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/license-MIT-brightgreen.svg\" alt=\"MIT License\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://swift.org\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/swift-4.2-brightgreen.svg\" alt=\"Swift 4.2\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://discord.gg/q5wCPYv\"\u003e\n        \u003cimg src=\"https://img.shields.io/discord/612561840765141005\" alt=\"Swift.Stream\"\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cbr\u003e\n\nThis lib is about network requests with blackjack, roulette and craps!\n\nUsing it you will be able to convert your massive API layer code into an awesome convenient controllers with easy and maintainable network requests!\n\nUse Codable models for everything related to API requests:\n- json payload\n- url-encoded payload\n- multipart payload\n- plain and json response\n- url query\n- headers\n\nWondered? That's only little part of what you will get from this lib! 🍻\n\n## Quick examples\n\n### How to send GET request\n\n```swift\nAPIRequest\u003cResponseModel\u003e(\"endpoint\").onSuccess { model in\n    //here's your decoded model!\n    //no need to check http.statusCode, I already did it for you! By default it's 200 OK\n    //of course you can choose which statusCode is equal to success (look at the `POST` and `DELETE` examples below)\n}\n```\n\n### How to send POST request\n\n```swift\nAPIRequest\u003cResponseModel\u003e(\"endpoint\", payload: payloadModel)\n    .method(.post)\n    .desiredStatusCode(.created) //201 CREATED\n    .onSuccess { model in\n    //here's your decoded model!\n    //success was determined by comparing desiredStatusCode with http.statusCode\n}\n```\n\n### How to send DELETE request\n\n```swift\nAPIRequest\u003cNothing\u003e(\"endpoint\")\n    .method(.delete)\n    .desiredStatusCode(.noContent) //204 NO CONTENT\n    .onSuccess { _ in\n    //here's empty successful response!\n    //success was determined by comparing desiredStatusCode with http.statusCode\n}\n```\n\n### How to send several requests?\nYou're able to run up to 10 requests one-by-one!\n```swift\nAPI.employee.all()\n    .and(API.office.all())\n    .and(API.car.all())\n    .and(API.event.all())\n    .and(API.post.all())\n    .onError { error in\n        print(error.description)\n    }.onSuccess { employees, offices, cars, events, posts in\n   // do what you want with received results!!! 🍻\n}\n```\nOr you can run unlimited amount of requests one-by-one or at the same time, if you need just a completion handler.\n```swift\n[API.employee.all(), API.office.all(), API.car.all()].flatten().onError {\n    print(error.description)\n}.onSuccess {\n    print(\"flatten finished!\")\n}\n```\nto run them concurrently just add `.concurrent(by: 3)` to run by 3 at the same time\n\nOf course you'll be able to send PUT and PATCH requests, send multipart codable structs with upload progress callback, catch errors, even redefine error descriptions for every endpoint. Wondered? 😃 Let's read the whole readme below! 🍻\n\n## How to install\n\nCodyFire is available through [CocoaPods](https://cocoapods.org) and SPM.\n\nTo install it, simply add the following line in your Podfile:\n```ruby\npod 'CodyFire', '~\u003e 1.15.4'\n```\n\n**Or you're looking for reactive code support? I have it! 🍺**\n- for RxSwift https://github.com/MihaelIsaev/RxCodyFire\n```ruby\npod 'RxCodyFire', '~\u003e 1.1.0'\n# no need to install `CodyFire` cause it's in dependencies\n```\nusing this pod you should always import just `RxCodyFire` and every APIRequest will have `.observable`\n\n- for ReactiveCocoa https://github.com/MihaelIsaev/ReactiveCodyFire\n```ruby\npod 'ReactiveCodyFire', '~\u003e 1.1.0'\n# no need to install `CodyFire` cause it's in dependencies\n```\nusing this pod you should always import just `ReactiveCodyFire` and every APIRequest will have `.signalProducer`\n\n## How to setup\n\nCodyFire automatically detects which environment you're on, so I suggest you to definitely use this awesome feature 👏\n\n```swift\nimport CodyFire\n\n@UIApplicationMain\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -\u003e Bool {\n        let dev = CodyFireEnvironment(baseURL: \"http://localhost:8080\")\n        let testFlight = CodyFireEnvironment(baseURL: \"https://stage.myapi.com\")\n        let appStore = CodyFireEnvironment(baseURL: \"https://api.myapi.com\")\n        CodyFire.shared.configureEnvironments(dev: dev, testFlight: testFlight, appStore: appStore)\n        //Also if you want to be able to switch environments manually just uncomment the line below (read more about that)\n        //CodyFire.shared.setupEnvByProjectScheme()\n        return true\n    }\n}\n```\n\nIsn't it cool? 😏\n\n**Or you can set different Server URL for every APIRequest**\n```swift\nlet server1 = ServerURL(base: \"https://server1.com\", path: \"v1\")\nlet server2 = ServerURL(base: \"https://server2.com\", path: \"v1\")\nlet server3 = ServerURL(base: \"https://server3.com\")\n```\nAnd then initialize your `APIRequest`s like this 🔥\n```swift\nAPIRequest(server1, \"endpoint\", payload: payloadObject)\nAPIRequest(server2, \"endpoint\", payload: payloadObject)\nAPIRequest(server3, \"endpoint\", payload: payloadObject)\n```\nOr in some cases you even can do it like this 😏\n```swift\nAPIRequest(\"endpoint\", payload: payloadObject).serverURL(server1)\n```\n\n### Create your API controllers\n\nI promise that this is API code architecture from your dreams which are come true!\n\n#### Create an `API` folder and `API.swift` file inside it\n\n```swift\nclass API {\n    typealias auth = AuthController\n    typealias task = TaskController\n}\n```\n\n#### Create a folder named `Controllers` inside `API` folder, and create a folder for each controller\n\n`API/Controllers/Auth/Auth.swift`\n```swift\nclass AuthController {}\n```\n`API/Controllers/Task/Task.swift`\n```swift\nclass TaskController {}\n```\n\n#### Create an extension file for each controller's endpoint\n\n##### Auth login as simple POST request\n\n`API/Controllers/Auth/Auth+Login.swift`\n```swift\nimport CodyFire\nextension AuthController {\n  struct LoginRequest: JSONPayload {\n        let email, password: String\n        init (email: String, password: String) {\n            self.email = email\n            self.password = password\n        }\n    }\n\n    struct LoginResponse: Codable {\n        var token: String\n    }\n\n    static func login(_ request: LoginRequest) -\u003e APIRequest\u003cLoginResponse\u003e {\n        return APIRequest(\"login\", payload: request).method(.post).addCustomError(.notFound, \"User not found\")\n    }\n}\n```\n\n##### Auth login for Basic auth\n\n`API/Controllers/Auth/Auth+Login.swift`\n```swift\nimport CodyFire\nextension AuthController {\n    struct LoginResponse: Codable {\n        var token: String\n    }\n\n    static func login(email: String, password: String) -\u003e APIRequest\u003cLoginResponse\u003e {\n        return APIRequest(\"login\").method(.post).basicAuth(email: email, password: password)\n            .addCustomError(.notFound, \"User not found\")\n    }\n}\n```\n\n##### Task REST endpoints\n\n###### Get task by id or a list of tasks by offset and limit\n\n`API/Controllers/Task/Task+Get.swift`\n```swift\nimport CodyFire\nextension TaskController {\n    struct Task: Codable {\n        var id: UUID\n        var name: String\n    }\n\n    struct ListQuery: Codable {\n        var offset, limit: Int\n        init (offset: Int, limit: Int) {\n            self.offset = offset\n            self.limit = limit\n        }\n    }\n\n    static func get(_ query: ListQuery? = nil) -\u003e APIRequest\u003c[Task]\u003e {\n        return APIRequest(\"task\").query(query)\n    }\n\n    static func get(id: UUID) -\u003e APIRequest\u003cTask\u003e {\n        return APIRequest(\"task/\" + id.uuidString)\n    }\n}\n```\n\n###### Create a task\n\n`API/Controllers/Task/Task+Create.swift`\n```swift\nimport CodyFire\nextension TaskController {\n    struct CreateRequest: JSONPayload {\n        var name: String\n        init (name: String) {\n            self.name = name\n        }\n    }\n\n    static func create(_ request: CreateRequest) -\u003e APIRequest\u003cTask\u003e {\n        return APIRequest(\"post\", payload: request).method(.post).desiredStatusCode(.created)\n    }\n}\n```\n\n###### Edit a task\n\n`API/Controllers/Task/Task+Edit.swift`\n```swift\nimport CodyFire\nextension TaskController {\n    struct EditRequest: JSONPayload {\n        var name: String\n        init (name: String) {\n            self.name = name\n        }\n    }\n\n    static func create(id: UUID, request: EditRequest) -\u003e APIRequest\u003cTask\u003e {\n        return APIRequest(\"post/\" + id.uuidString, payload: request).method(.patch)\n    }\n}\n```\n\n###### Delete a task\n\n`API/Controllers/Task/Task+Delete.swift`\n```swift\nimport CodyFire\nextension TaskController {\n    static func delete(id: UUID) -\u003e APIRequest\u003cNothing\u003e {\n        return APIRequest(\"post/\" + id.uuidString).method(.delete).desiredStatusCode(.noContent)\n    }\n}\n```\n\n### Easily use your API endpoints!\n\n###### Send login request\n\n```swift\nAPI.auth.login(email: \"test@mail.com\", password: \"qwerty\").onError { error in\n    switch error.code {\n    case .notFound: print(\"User not found\")\n    default: print(error.description)\n    }\n}.onSuccess { token in\n    print(\"Received auth token: \"+ token)\n}\n```\n\n###### Get a list of tasks\n\n```swift\nAPI.task.get().onError { error in\n    print(error.description)\n}.onSuccess { tasks in\n    print(\"received \\(tasks.count) tasks\")\n}\n```\n\n###### Create a task\n\n```swift\nAPI.task.create(TaskController.CreateRequest(name: \"Install CodyFire\")).onError { error in\n    print(error.description)\n}.onSuccess { task in\n    print(\"just created new task: \\(task)\")\n}\n```\n\n###### Delete a task\n\n```swift\nlet taskId = UUID()\nAPI.task.delete(id: taskId).onError { error in\n    print(error.description)\n}.onSuccess { _ in\n    print(\"just removed task with id: \\(taskId)\")\n}\n```\n\n### Multipart example\n\n```swift\n//declare a PostController\nclass PostController()\nextension PostController {\n    struct CreateRequest: MultipartPayload {\n        var text: String\n        var tags: [String]\n        var images: [Attachment]\n        var video: Data\n        init (text: String, tags: [String], images: [Attachment], video: Data) {\n            self.text = text\n            self.tags = tags\n            self.images = images\n            self.video = video\n        }\n    }\n\n    struct PostResponse: Codable {\n        let id: UUID\n        let text: String\n        let tags: [String]\n        let linksToImages: [String]\n        let linkToVideo: String\n    }\n\n    static func create(_ request: CreateRequest) -\u003e APIRequest\u003cPostResponse\u003e {\n        return APIRequest(\"post\", payload: request).method(.post)\n    }\n}\n\n//then somewhere send creation request!\n\nlet videoData = FileManager.default.contents(atPath: \"/path/to/video.mp4\")!\nlet imageAttachment = Attachment(data: UIImage(named: \"cat\")!.jpeg(.high)!,\n                                 fileName: \"cat.jpg\",\n                                 mimeType: .jpg)\nlet payload = PostController.CreateRequest(text: \"CodyFire is awesome\",\n                                           tags: [\"codyfire\", \"awesome\"],\n                                           images: [imageAttachment],\n                                           video: videoData)\nAPI.post.create(payload).onProgress { progress in\n    print(\"tracking post uploading progress: \\(progress)\")\n}.onError { error in\n    print(error.description)\n}.onSuccess { createdPost in\n    print(\"just created post: \\(createdPost)\")\n}\n```\n\nEasy right? 🎉\n\n## Details\n\n#### How to put `Authorization Bearer` token into every request?\nFor that we have a global headers wrapper, which is called for every request.\n\nYou need to declare it e.g. somewhere in AppDelegate.\n\nThere are two options\n1. Use Codable model for headers (recommended)\n```swift\nCodyFire.shared.fillCodableHeaders = {\n    struct Headers: Codable {\n        var Authorization: String? //NOTE: nil values will be excluded\n        var anythingElse: String\n    }\n    return Headers(Authorization: nil, anythingElse: \"hello\")\n}\n```\n2. Use [String: String] dictionary\n```swift\nCodyFire.shared.fillHeaders = {\n    guard let apiToken = LocalAuthStorage.savedToken else { return [:] }\n    return [\"Authorization\": \"Bearer \\(apiToken)\"]\n}\n```\n\n#### How to set a global `unauthorized` handler?\nAgain, somewhere in AppDelegate declare it like this\nCodyFire.shared.unauthorizedHandler = {\n    //kick out user\n}\n\n#### Handle if network isn't available (e.g. wifi/lte turned off)\n```swift\n.onNetworkUnavailable {\n    print(\"unfortunately there're no internet connection!\")\n}\n```\n\n#### Run something right before request started (works only if network is available)\n```swift\n.onRequestStarted {\n    print(\"request started normally\")\n}\n```\n\n#### How to avoid log error for request\n```swift\n.avoidLogError()\n```\n\n#### How to set desired status code and what's that means?\nUsually servers response with `200 OK` and CodyFire expect to receive `200 OK` to call `onSuccess` handler by default.\n\nYou may need to specify another code, e.g. `201 CREATED` for some POST requests.\n```swift\n.desiredStatusCode(.created)\n```\nor you even can set your custom code\n```swift\n.desiredStatusCode(.custom(777))\n```\n\n#### How to set some headers for a request?\n\n```swift\n.headers([\"myHeader\":\"myValue\"])\n//or for basic auth\n.basicAuth(email: \"\", password: \"\")\n```\n\n#### Supported HTTP methods\n\nYou may use: GET, POST, PUT, PATCH, DELETE, HEAD, TRACE, CONNECT, OPTIONS\n\n#### How to switch environments through Xcode's run schemes?\nIt's really useful feature and I suggest to use it in every iOS project!\n\nCreate three schemes named: Development, TestFlight, AppStore like on the screenshot below\n\u003cimg width=\"365\" alt=\"2018-10-24 5 30 30\" src=\"https://user-images.githubusercontent.com/1272610/47400378-f359c380-d74d-11e8-8d00-a325d06ed7bb.png\"\u003e\n\nTIP: Make sure that they're marked as `Shared` to have them in `git`\n\nThen in every scheme in `Arguments` tab add `Environment variable` named `env` with one of those values: dev, testFlight, appStore.\n\nTake a look at example below\n\u003cimg width=\"895\" alt=\"2018-10-24 5 34 43\" src=\"https://user-images.githubusercontent.com/1272610/47400503-85fa6280-d74e-11e8-8846-3216b241dd6e.png\"\u003e\n\nThen somewhere in AppDelegate.didFinishLaunchingWithOptions add\n```swift\nCodyFire.shared.setupEnvByProjectScheme()\n```\nAll done, now you're able to easily switch environments!\n\n#### How to execute request without onSuccess clojure?\nSometimes useful for DELETE or PATCH requests\n```swift\nAPIRequest\u003cNothing\u003e(\"endpoint\").method(.delete).execute()\n```\n\n#### How to cancel request?\n```swift\nlet request = APIRequest(\"\").execute()\nrequest.cancel()\n```\nand you're able to handle cancellation\n```swift\n.onCancellation {\n    print(\"request was cancelled :(\")\n}\n```\n\n#### What does custom error means?\n\nYou may define your own custom errors, globally or for each request.\n`onError` block contains `NetworkError` object with `StatusCode` enum, an error description, and a raw response `Data`. Error description you could change to whatever you want for any error code.\nBy default there are already defined some good descriptions for common errors.\n\nLet's take a look how we can use powerful `onError` block\n```swift\n.onError { error in\n    switch error.code {\n    case .notFound: print(\"It's not found :(\")\n    case .internalServerError: print(\"Oooops... Something really went wrong...\")\n    case .custom(let code): print(\"My non-standard-custom error happened: \" + error.description)\n    case .unknown(let code): print(\"Totally unknown error happened: \" + error.description)\n    default:\n        print(\"Another error happened: \" + error.description)\n        if let raw = error.raw, let rawResponse = String(data: raw, encoding: .utf8) {\n            print(\"Raw response: \" + rawResponse)\n        }\n    }\n}\n```\n\nMore than that!!! In your controller while declaring APIRequest you're able to add your own custom errors!!! 🙀\n\n```swift\nAPIRequest(\"login\")\n    .method(.post)\n    .basicAuth(email: \"sam@mail.com\", password: \"qwerty\")\n    .addError(.notFound, \"User not found\")\n```\nI believe that's really awesome and useful! Finally a lot of things may be declared in one place! 🎉\n\n#### How to set response timeout?\n```swift\n.responseTimeout(30) //request timeout set for 30 seconds\n```\nand of course you're able to catch timeout\n```swift\n.onTimeout {\n    //timeout happened :(\n}\n```\n\n#### How to add interactive additional timeout? (my favourite one 😄)\nIf you want to make sure that your request will take 2 or more seconds (to not be too fast 😅) you can do that\n```swift\n.additionalTimeout(2)\n```\ne.g. in case if your request will be executed in 0.5 seconds, `onSuccess` handler will be fired only in 1.5s after that\nbut in case if your request will take more than 2s then `onSuccess` handler will be fired immediatelly\n\n#### How to declare payload model for `multipart` request\nYour struct/class just should conform to `MultipartPayload` protocol\n```swift\nstruct SomePayload: MultipartPayload {\n    let name: String\n    let names: [String]\n    let date: Date\n    let dates: [Dates]\n    let number: Double\n    let numbers: [Int]\n    let attachment: Data\n    let attachments: [Data]\n    let fileAttachment: Attachment\n    let fileAttachments: [Attachment]\n}\n```\n\n#### Supported payload types\n\nYou may conform your struct/class to: `FormURLEncodedPayload`, `MultipartPayload`, and `JSONPayload`\n\n#### How to declare payload model for `json` request\nYour struct/class just should conform to `JSONPayload` protocol\n```swift\nstruct SomePayload: JSONPayload {\n    let name: String\n    let names: [String]\n    let date: Date\n    let dates: [Dates]\n    let number: Double\n    let numbers: [Int]\n}\n```\n\n#### How to declare payload model for `x-www-form-urlencoded` request\nYour struct/class just should conform to `FormURLEncodedPayload` protocol\n```swift\nstruct SomePayload: FormURLEncodedPayload {\n    let name: String\n    let names: [String]\n    let date: Date\n    let dates: [Dates]\n    let number: Double\n    let numbers: [Int]\n}\n```\n\n#### How to declare url query params model\nYour struct/class just should conform to `Codable` protocol\n```swift\nstruct SomePayload: Codable {\n    let name: String\n    let names: [String]\n    let date: Date\n    let dates: [Dates]\n    let number: Double\n    let numbers: [Int]\n}\n```\n\n#### How to set date decoding/encoding strategy\nOur `DateCodingStrategy` support\n - secondsSince1970\n - millisecondsSince1970\n - formatted(customDateFormatter: DateFormatter)\nBy default all the dates are in `yyyy-MM-dd'T'HH:mm:ss'Z'` format\n\nYou have interesting options here:\n - you can set global date decoder/encoder\n ```swift\n CodyFire.shared.dateEncodingStrategy = .secondsSince1970\n let customDateFormatter = DateFormatter()\n CodyFire.shared.dateDecodingStrategy = .formatted(customDateFormatter)\n ```\n - you can set date decoder/encoder for request in your controller\n ```swift\n APIRequest().dateDecodingStrategy(.millisecondsSince1970).dateEncodingStrategy(.secondsSince1970)\n ```\n - or you even can use different date encoder/decoder for each payload type (highest priority)\n ```swift\n struct SomePayload: JSONPayload, CustomDateEncodingStrategy, CustomDateDecodingStrategy {\n    var dateEncodingStrategy: DateCodingStrategy\n    var dateDecodingStrategy: DateCodingStrategy\n }\n ```\n\n#### How to enable/disable logging\ne.g. in AppDelegate you may set logging mode\n```swift\nCodyFire.shared.logLevel = .debug\nCodyFire.shared.logLevel = .error\nCodyFire.shared.logLevel = .info\nCodyFire.shared.logLevel = .off\n```\nand also you can set log handler\n```swift\nCodyFire.shared.logHandler = { level, text in\n    print(\"manually printing codyfire error: \" + text)\n}\n```\nby default for the AppStore the log level if `.off`\n\n#### How you're detecting current environment?\nIt's easy\n\n```swift\n#if DEBUG\n    //DEV environment\n#else\n    if Bundle.main.appStoreReceiptURL?.lastPathComponent == \"sandboxReceipt\" {\n        //TESTFLIGHT environment\n    } else {\n        //APPSTORE environment\n    }\n#endif\n```\n\n### Chained requests\nRun up to 10 requests one-by-one!\n```swift\nAPI.employee.all()\n    .and(API.office.all())\n    .and(API.car.all())\n    .and(API.event.all())\n    .and(API.post.all())\n    .onError { error in\n        print(error.description)\n    }.onSuccess { employees, offices, cars, events, posts in\n   // do what you want with received results!!! 🍻\n}\n```\n`onRequestStarted, onNetworkUnavailable, onCancellation, onNotAuthorized, onTimeout also available!`\n`//TBD: onProgress`\n\nI believe it is awesome! Especially for whom who not familiar or don't like reactive programming 🙂\n\n### Flatten\n\nIf you want to run several requests one-by-one or at the same time but with just completion handler you can do that with `.flatten()`\n```swift\n[API.employee.all(), API.office.all(), API.car.all()].flatten().onError {\n    print(error.description)\n}.onSuccess {\n    print(\"flatten finished!\")\n}\n```\nto run them concurrently just add `.concurrent(by: 3)` to run by 3 at the same time\nto skip errors also add `.avoidCancelOnError()`\nto get progress add `.onProgress`\n\n## Contribution\nPlease feel free to send pull requests and ask your questions in issues\n\nHope this lib will be really useful in your projects! Tell you friends about it! Please press STAR ⭐️ button!!!\n\n## Author\n\nMike Isaev, isaev.mihael@gmail.com\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCodyFlame%2FCodyFire","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FCodyFlame%2FCodyFire","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FCodyFlame%2FCodyFire/lists"}