{"id":30113774,"url":"https://github.com/tinglesoftware/swift-apiclients","last_synced_at":"2025-08-10T07:29:46.464Z","repository":{"id":47277429,"uuid":"286923154","full_name":"tinglesoftware/swift-apiclients","owner":"tinglesoftware","description":null,"archived":false,"fork":false,"pushed_at":"2024-12-02T03:01:26.000Z","size":177,"stargazers_count":0,"open_issues_count":1,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-12-13T12:40:28.762Z","etag":null,"topics":[],"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/tinglesoftware.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,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2020-08-12T05:17:24.000Z","updated_at":"2024-09-16T05:28:01.000Z","dependencies_parsed_at":"2024-01-19T08:06:02.215Z","dependency_job_id":"d7dd2714-6e1a-4954-b887-f5f367c9c585","html_url":"https://github.com/tinglesoftware/swift-apiclients","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/tinglesoftware/swift-apiclients","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tinglesoftware%2Fswift-apiclients","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tinglesoftware%2Fswift-apiclients/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tinglesoftware%2Fswift-apiclients/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tinglesoftware%2Fswift-apiclients/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tinglesoftware","download_url":"https://codeload.github.com/tinglesoftware/swift-apiclients/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tinglesoftware%2Fswift-apiclients/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269693144,"owners_count":24460222,"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","status":"online","status_checked_at":"2025-08-10T02:00:08.965Z","response_time":71,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":"2025-08-10T07:29:41.970Z","updated_at":"2025-08-10T07:29:46.450Z","avatar_url":"https://github.com/tinglesoftware.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `TingleApiClient`\n\n![Swift](https://github.com/tinglesoftware/swift-apiclients/workflows/Swift/badge.svg)\n![Language](https://img.shields.io/badge/language-Swift%205.0-orange.svg)\n\n`TingleApiClient` is a simple class for making API calls in iOS, macOS and tvOS apps. It has specific support for parsing errors and content.\nThis library eases working with HTTP APIs built by Tingle Software but can also work with other APIs.\n\n## Usage (Simple Client)\n\n```swift\n// Initialize\nlet apiClient = TingleApiClient()\n\n// prepare request\nlet url = URL(string: \"https://api.example.com/v2/profiles\")!\nvar request = URLRequest(url: url)\nrequest.httpMethod = \"GET\"\n\n// make request\napiClient.send(\u0026request) { (response: ResourceResponse\u003c[Profile]\u003e?, error: Error?) in\n\n    // first check for a network error (such as no internet)\n    if (error != nil) {\n        // handle error here\n        return\n    }\n\n    // if the error is nil the response should not be nil\n    // but lets just check for the sake of checking\n    if (response == nil) {\n        // should not happen\n        return\n    }\n\n    // check if the response was successful\n    if (response!.successful \u0026\u0026 response!.resource != nil) {\n        let profiles = response!.resource!\n        if (!profiles.isEmpty) {\n            // save the entries in the database or elsewhere\n            // this is called in the background you should not access the UI thread directly without a dispatcher\n        }\n    }\n}\n```\n\n## Usage (Derived Client)\n\n```swift\nimport Foundation\nimport TingleApiClient\n\npublic class ProfilesApiClient: TingleApiClient {\n    private let baseUrl = \"https://api.example.com\"\n\n    override public func setupJsonSerialization(encoder: JSONEncoder, decoder: JSONDecoder) {\n        encoder.dateEncodingStrategy = .iso8601\n        decoder.dateDecodingStrategy = .iso8601\n    }\n\n    override public func buildMiddleware() -\u003e [TingleApiClientMiddleware] {\n        [\n            AppDetailsMiddleware(Bundle.main.bundleIdentifier ?? \"\", Bundle.main.shortBundleVersion, Bundle.main.shortBundleVersion),\n            LoggingMiddleware(.BODY, .info)\n        ]\n    }\n\n    @discardableResult\n    public func getProfiles(_ completionHandler: @escaping (ResourceResponse\u003c[Profile]\u003e?, error: Error?) -\u003e Void) -\u003e URLSessionTask {\n        let url = URL(string: \"\\(baseUrl)/v2/profiles\")!\n        var request = URLRequest(url: url)\n        request.httpMethod = \"GET\"\n        return send(\u0026request, completionHandler)\n    }\n}\n```\n\n```swift\nimport Foundation\nimport RealmSwift\nimport TingleApiClient\n\npublic class DownloadManager {\n    private static let client = ProfilesApiClient()\n\n    @discardableResult\n    public static func downloadProfiles() -\u003e URLSessionTask {\n        return client.getProfiles { (response: ResourceResponse\u003c[Profile]\u003e) in\n            if (response.successful \u0026\u0026 response.resource != nil) {\n                let profiles = response.resource!\n                if (!profiles.isEmpty) {\n                    if let realm = try? Realm() {\n                        try? realm.write {\n                            realm.add(profiles, update: .all)\n                        }\n                    }\n                }\n            }\n        }\n    }\n}\n```\n\nSee the samples project to see advanced usage\n\n## Installation\n\n### Swift Package Manager\n\n`TingleApiClient` is available on SPM. Just add the following to your Package file:\n\n```swift\nimport PackageDescription\n\nlet package = Package(\n    dependencies: [\n        .package(url: \"https://github.com/tinglesoftware/swift-apiclients.git\", from: 0.2.0)\n    ]\n)\n```\n\n### Manual Installation\n\nJust drag the `Sources/*.swift` files into your project.\n\n## `TingleApiClient` properties\n\n```swift\nencoder\n```\n\nThe instance of `JsonEncoder` to use in creating JSON payloads from objects\n\n```swift\ndecoder\n```\n\nThe instance of `JSONDecoder` to use in creating objects from JSON payloads\n\n## `TingleApiClient` methods\n\n```swift\ninit(session: URLSession? = nil, authenticationProvider: IAuthenticationProvider? = nil)\ninit(_ authenticationProvider: IAuthenticationProvider)\n```\n\nThese methods create a new `TingleApiClient` instance that uses the session and authentication provider passed.\n\n```swift\nfunc buildMiddleware() -\u003e [TingleApiClientMiddleware]\n```\n\nBuilds the middleware that is used to process requests and responses. There are different reasons why you might want to use middleware, such as logging, setting extra headers etc.\nThe `IAuthenticationProvider` is itself middleware dedicated towards authenticating the request before going out.\n\n```swift\nfunc func setupJsonSerialization(encoder: JSONEncoder, decoder: JSONDecoder)\n```\n\nSetups the instances of `JSONEncoder` and `JSONDecoder` already created. These instances are used to encode/decode requests/responses respectively in the `send` functions\n\n```swift\nfunc sendRequest\u003cTResource, TResourceResponse\u003e(_ request: inout URLRequest,\n                                               _ resultBuilder: @escaping (Int, Any, TResource?, HttpApiResponseProblem?) -\u003e TResourceResponse,\n                                               _ completionHandler: @escaping (TResourceResponse?, Error?) -\u003e Void) -\u003e URLSessionTask\n```\n\nThis method sends a HTTP request as per the details in the `request` parameter. The response is parsed to produce a `TResource` and `HttpApiResponseProblem`.\nThese two are supplied to the `resultBuilder` closure to produce a `TResourceResponse`.\nWhen the network call fails such as there being no internet access or being unable to reach the server, the `resultBuilder` closure is not called. Instead,\nthe `completionHandler` closure is called with the `TResourceResponse?` argument set to `nil` and the `Error?` argument not `nil`.\nWhen the network call succeeds, the `resultBuilder` closure is called to produce an instance of `TResourceResponse` and the result is passed to the\n`completionHandler` closure but the `Error?` parameter is set to `nil`.\n\n```swift\nfunc sendRequest\u003cTResource\u003e(_ request: inout URLRequest,\n                            _ completionHandler: @escaping (AnyResourceResponse\u003cTResource\u003e?, Error?) -\u003e Void) -\u003e URLSessionTask\n```\n\nThis is similar to calling the `sendRequest` method above but instead produces a `AnyResourceResponse\u003cTResource, TProblem\u003e` for the `TResourceResponse`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftinglesoftware%2Fswift-apiclients","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftinglesoftware%2Fswift-apiclients","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftinglesoftware%2Fswift-apiclients/lists"}