{"id":1831,"url":"https://github.com/jaychang0917/SimpleApiClient-ios","last_synced_at":"2025-08-02T05:32:54.703Z","repository":{"id":62455423,"uuid":"107765307","full_name":"jaychang0917/SimpleApiClient-ios","owner":"jaychang0917","description":"A configurable api client based on Alamofire4 and RxSwift4 for iOS","archived":true,"fork":false,"pushed_at":"2017-10-21T11:42:23.000Z","size":2247,"stargazers_count":67,"open_issues_count":0,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-12-01T23:22:38.917Z","etag":null,"topics":["alamofire","api-client","rxswift","swift4"],"latest_commit_sha":null,"homepage":null,"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/jaychang0917.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":"2017-10-21T08:48:48.000Z","updated_at":"2023-01-28T17:38:10.000Z","dependencies_parsed_at":"2022-11-02T00:01:11.902Z","dependency_job_id":null,"html_url":"https://github.com/jaychang0917/SimpleApiClient-ios","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaychang0917%2FSimpleApiClient-ios","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaychang0917%2FSimpleApiClient-ios/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaychang0917%2FSimpleApiClient-ios/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaychang0917%2FSimpleApiClient-ios/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jaychang0917","download_url":"https://codeload.github.com/jaychang0917/SimpleApiClient-ios/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228443621,"owners_count":17920760,"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":["alamofire","api-client","rxswift","swift4"],"created_at":"2024-01-05T20:15:56.843Z","updated_at":"2024-12-06T09:30:26.450Z","avatar_url":"https://github.com/jaychang0917.png","language":"Swift","readme":"# SimpleApiClient\n\n[![Swift](https://img.shields.io/badge/Swift-4.0-orange.svg?style=flat)](https://developer.apple.com/swift/)\n[![CI Status](http://img.shields.io/travis/jaychang0917/SimpleApiClient.svg?style=flat)](https://travis-ci.org/jaychang0917/SimpleApiClient)\n[![Version](https://img.shields.io/cocoapods/v/SimpleApiClient.svg?style=flat)](http://cocoapods.org/pods/SimpleApiClient)\n[![License](https://img.shields.io/cocoapods/l/SimpleApiClient.svg?style=flat)](http://cocoapods.org/pods/SimpleApiClient)\n[![Platform](https://img.shields.io/cocoapods/p/SimpleApiClient.svg?style=flat)](http://cocoapods.org/pods/SimpleApiClient)\n\nA configurable api client based on Alamofire4 and RxSwift4 for iOS\n\n## Requirements\n* iOS 8.0+\n* Swift 4\n\n## Table of Contents\n* [Basic Usage](#basic_usage)\n* [Unwrap Response by KeyPath](#unwrap_keypath)\n* [Upload File(s)](#upload)\n* [Serial / Parallel Calls](#serial_parallel_calls)\n* [Retry Interval / Exponential backoff](#retry)\n* [Call Cancellation](#call_cancel)\n* [Mock Response](#mock_response)\n\n## Installation\n\nSimpleApiClient is available through [CocoaPods](http://cocoapods.org). To install\nit, simply add the following line to your Podfile:\n\n```ruby\npod 'SimpleApiClient'\n```\n\n## \u003ca name=basic_usage\u003eBasic Usage\u003c/a\u003e\n### Step 1\nConfigurate the api client\n```swift\nlet config = SimpleApiClient.Config(\n  baseUrl: \"https://api.github.com\",\n  defaultParameters: [\"foo\": \"bar\"],\n  defaultHeaders: [\"foo\": \"bar\"],\n  timeout: 120, // default is 60s\n  certificatePins: [\n    CertificatePin(hostname: \"https://api.github.com\", certificateUrl: Bundle.main.url(forResource: \"serverCert\", withExtension: \"cer\")!)\n  ],\n  errorMessageKeyPath: \"message\",\n  jsonDecoder: JSONDecoder(),  // default is JSONDecoder()\n  isMockResponseEnabled: true, // default is false\n  logHandler: { request, response in\n    ...\n  },\n  errorHandler: { error in\n    // you can centralize the handling of general error here\n    switch error {\n    case .authenticationError(let code, let message):\n      ...\n    case .clientError(let code, let message):\n      ...\n    case .serverError(let code, let message):\n      ...\n    case .networkError(let source):\n      ...\n    case .sslError(let source):\n      ...\n    case .uncategorizedError(let source):\n      ...\n    }\n  }\n)\n\nlet githubClient = SimpleApiClient(config: config)\n```\n### Step 2\nCreate the API\n```swift\nimport SimpleApiClient\n\nstruct GetRepoApi: SimpleApi {\n  let user: String\n  let repo: String\n  \n  var path: String {\n    return \"/repos/\\(user)/\\(repo)\"\n  }\n  \n  var method: HTTPMethod {\n    return .get\n  }\n  \n  // optional\n  var parameters: Parameters {\n    return [:]\n  }\n  \n  // optional\n  var headers: HTTPHeaders {\n    return [:]\n  }\n}\n\nextension SimpleApiClient {\n  func getRepo(user: String, repo: String) -\u003e Observable\u003cRepo\u003e {\n    return request(api: GetRepoApi(user: user, repo: repo))\n  }\n}\n```\n\n### Step 3\nUse `observe()` to enqueue the call, do your stuff in corresponding parameter block. All blocks run on main thread by default and are optional.\n```swift\ngithubClient.getRepo(user: \"foo\", repo: \"bar\")\n  .observe(\n    onStart: { print(\"show loading\") },\n    onEnd: { print(\"hide loading\") },\n    onSuccess: { print(\"sucess: \\($0)\") },\n    onError: { print(\"error: \\($0)\" }\n  )\n```\n\n### Model\nThe library uses [JSONDecoder](https://developer.apple.com/documentation/foundation/jsondecoder) to deserialize JSON to model object, so the model should conform to [Decodable](https://developer.apple.com/documentation/swift/decodable) or [Codable](https://developer.apple.com/documentation/swift/codable)\n```swift\nstruct User: Decodable {\n  let name: String\n  let profileUrl: URL\n  // if the serialized name is different from the property name\n  private enum CodingKeys: String, CodingKey {\n    case name = \"login\"\n    case profileUrl = \"avatar_url\"\n  }\n}\n```\n\n## \u003ca name=unwrap_keypath\u003eUnwrap Response by KeyPath\u003c/a\u003e\nSometimes the api response includes metadata that we don't need, but in order to map the response we create a wrapper class and make the function return that wrapper class.\nThis approach leaks the implementation of service to calling code.\n\nAssuming the response json looks like the following:\n```xml\n{\n  total_count: 33909,\n  incomplete_results: false,\n  foo: {\n    bar: {\n      items: [\n        {\n          login: \"foo\",\n          ...\n        }\n        ...\n      ]\n    }\n  }\n}\n```\nAnd you only need the `items` part, implement `Unwrappable` to indicate which part of response you want. \n```swift\nstruct GetUsersApi: SimpleApi, Unwrappable {\n  ... \n  \n  var responseKeyPath: String {\n    return \"foo.bar.items\"\n  }\n}\n\n// then your response will be a list of User\nextension SimpleApiClient {\n  func getUsers(query: String) -\u003e Observable\u003c[User]\u003e {\n    return request(api: GetUsersApi(query: query))\n  }\n}\n```\n\n## \u003ca name=upload\u003eUpload File(s)\u003c/a\u003e\nTo upload file(s), make the API implements `Uploadable` to provide `Multipart`s\n```swift\nstruct UploadImageApi: SimpleApi, Uploadable {\n  ...\n  \n  var multiParts: [MultiPart] {\n    let multiPart = MultiPart(data: UIImageJPEGRepresentation(image, 1)!, name: \"imagefile\", filename: \"image.jpg\", mimeType: \"image/jpeg\")\n    return [multiPart]\n  }\n}\n\nextension SimpleApiClient {\n  func uploadImage(image: UIImage) -\u003e Observable\u003cImage\u003e {\n    return request(api: UploadImageApi(image))\n  }\n}\n```\n\n## \u003ca name=serial_parallel_calls\u003eSerial / Parallel Calls\u003c/a\u003e\n### Serial\n```swift\ngithubClient.foo()\n  .then { foo in githubClient.bar(foo.name) }\n  .observe(...)\n```\n\n### Serial then Parallel\n```swift\ngithubClient.foo()\n  .then { foo in githubClient.bar(foo.name) }\n  .thenAll { bar in \n    (githubClient.baz(bar.name), githubClient.qux(bar.name)) // return a tuple\n  }\n  .observe(...)\n```\n\n### Parallel\n```swift\nSimpleApiClient.all(\n  githubApi.foo(),\n  githubApi.bar()\n)\n.observe(...)\n```\n\n### Parallel then Serial\n```swift\nSimpleApiClient.all(\n  githubApi.foo(),\n  githubApi.bar()\n).then { array -\u003e // the return type is Array\u003cAny\u003e, you should cast them, e.g. let foo = array[0] as! Foo\n  githubApi.baz()\n}.observe(...)\n```\n\n## \u003ca name=retry\u003eRetry Interval / Exponential backoff\u003c/a\u003e\n```kotlin\ngithubClient.getUsers(\"foo\")\n  .retry(delay: 5, maxRetryCount: 3) // retry up to 3 times, each time delays 5 seconds\n  .retry(exponentialDelay: 5, maxRetryCount: 3) // retry up to 3 times, each time delays 5^n seconds, where n = {1,2,3}\n  .observe(...)\n```\n\n## \u003ca name=call_cancel\u003eCall Cancellation\u003c/a\u003e\n### Auto Call Cancellation\nThe call will be cancelled when the object is deallocated.\n```swift\ngithubClient.getUsers(\"foo\")\n  .cancel(when: self.rx.deallocated)\n  .observe(...)\n```\n\n### Cancel call manually\n```swift\nlet call = githubClient.getUsers(\"foo\").observe(...)\n\ncall.cancel()\n```\n\n## \u003ca name=mock_response\u003eMock Response\u003c/a\u003e\nTo enable response mocking, set `SimpleApiClient.Config.isMockResponseEnabled` to `true` and make the API implements `Mockable` to provide `MockResponse`.\n \n### Mock sample json data\nTo make the api return a successful response with provided json\n\n```swift\nstruct GetUsersApi: SimpleApi, Mockable {\n  ...\n  \n  var mockResponse: MockResponse {\n    let file = Bundle.main.url(forResource: \"get_users\", withExtension: \"json\")!\n    return MockResponse(jsonFile: file)\n  }\n}\n```\n\n### Mock status\nTo make the api return a client side error with provided json\n```swift\nstruct GetUsersApi: SimpleApi, Mockable {\n  ...\n  \n  var mockResponse: MockResponse {\n    let file = Bundle.main.url(forResource: \"get_users_error\", withExtension: \"json\")!\n    return MockResponse(jsonFile: file, status: .clientError)\n  }\n}\n```\n\nthe parameter `jsonFile` of `MockResponse` is optional, you can set the status only, then you receive empty string.\n\nPossible `Status` values:\n```swift\npublic enum Status {\n  case success\n  case authenticationError\n  case clientError\n  case serverError\n  case networkError\n  case sslError\n}\n```\n\nTo mock a response with success status only, you should return `Observable\u003cNothing\u003e`.\n```swift\nstruct DeleteRepoApi: SimpleApi, Mockable {\n  ...\n  \n  var mockResponse: MockResponse {\n    return MockResponse(status: .success)\n  }\n}\n\nextension SimpleApiClient {\n  func deleteRepo(id: String) -\u003e Observable\u003cNothing\u003e {\n    return request(api: DeleteRepoApi(id: id))\n  }\n}\n```\n\n\n## Author\n\njaychang0917, jaychang0917@gmail.com\n\n## License\n\nSimpleApiClient is available under the MIT license. See the LICENSE file for more info.\n","funding_links":[],"categories":["Reactive Programming"],"sub_categories":["Other free courses","Prototyping","Other Parsing"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaychang0917%2FSimpleApiClient-ios","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjaychang0917%2FSimpleApiClient-ios","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaychang0917%2FSimpleApiClient-ios/lists"}