{"id":13340071,"url":"https://github.com/artemisia-absynthium/arachne","last_synced_at":"2025-10-25T19:31:00.458Z","repository":{"id":56902344,"uuid":"418493297","full_name":"artemisia-absynthium/arachne","owner":"artemisia-absynthium","description":"Networking layer for apps using Swift Concurrency.","archived":false,"fork":false,"pushed_at":"2024-06-17T20:38:50.000Z","size":962,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-10-01T05:22:35.325Z","etag":null,"topics":["async-await","hacktoberfest","ios","macos","networking","swift","swift-concurrency","swift-package-manager","tvos","visionos","watchos"],"latest_commit_sha":null,"homepage":"https://arachne.netlify.app","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/artemisia-absynthium.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,"publiccode":null,"codemeta":null}},"created_at":"2021-10-18T12:34:03.000Z","updated_at":"2024-06-17T20:37:14.000Z","dependencies_parsed_at":"2024-06-16T21:45:13.407Z","dependency_job_id":"d9b1a851-04e1-48b8-a191-bca53260f131","html_url":"https://github.com/artemisia-absynthium/arachne","commit_stats":{"total_commits":50,"total_committers":1,"mean_commits":50.0,"dds":0.0,"last_synced_commit":"92ab7342b229d114cede6efe3b2fad9776dc723d"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artemisia-absynthium%2Farachne","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artemisia-absynthium%2Farachne/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artemisia-absynthium%2Farachne/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/artemisia-absynthium%2Farachne/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/artemisia-absynthium","download_url":"https://codeload.github.com/artemisia-absynthium/arachne/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219865149,"owners_count":16555931,"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":["async-await","hacktoberfest","ios","macos","networking","swift","swift-concurrency","swift-package-manager","tvos","visionos","watchos"],"created_at":"2024-07-29T19:22:07.422Z","updated_at":"2025-10-25T19:31:00.450Z","avatar_url":"https://github.com/artemisia-absynthium.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Arachne\n\n[![Swift](https://github.com/artemisia-absynthium/arachne/actions/workflows/swift.yml/badge.svg)](https://github.com/artemisia-absynthium/arachne/actions/workflows/swift.yml?branch=main)\n[![CodeQL](https://github.com/artemisia-absynthium/arachne/actions/workflows/github-code-scanning/codeql/badge.svg?branch=main)](https://github.com/artemisia-absynthium/arachne/actions/workflows/github-code-scanning/codeql)\n[![codecov](https://codecov.io/github/artemisia-absynthium/arachne/branch/main/graph/badge.svg?token=SE49QJW0M3)](https://codecov.io/github/artemisia-absynthium/arachne)\n\nArachne is a lightweight, minimalistic, zero dependencies networking layer for apps using [Swift Concurrency (async/await)](https://docs.swift.org/swift-book/LanguageGuide/Concurrency.html) or [Combine](https://developer.apple.com/documentation/combine) (support to Combine will be discontinued) developed in Swift, that provides an opinionated abstraction layer to remove boilerplate code.\n\nArachne aims to backport async/await `URLSession` tasks to macOS 10.15, iOS 13, iPadOS 13, tvOS 13 and watchOS 7, while the availability of their native counterpart in the Foundation framework is iOS 15.0+, iPadOS 15.0+, macOS 12.0+, Mac Catalyst 15.0+, tvOS 15.0+, watchOS 8.0+.\n\nThis library's design was inspired by [Moya](https://github.com/Moya/Moya), but differently from Moya, Arachne uses only the standard [Foundation framework](https://developer.apple.com/documentation/foundation/url_loading_system) (e.g. `URLSession`).\n\nThis makes Arachne suitable for recently created or migrated apps that make use of Apple's frameworks, instead of third party ones, like [Alamofire](https://github.com/Alamofire/Alamofire) or [RxSwift](https://github.com/ReactiveX/RxSwift).\n\n## Usage\n\nYou start by defining your APIs like this\n\n```swift\nimport Foundation\nimport Arachne\n\nenum MyAPIService {\n    case info\n    case userProfile(username: String)\n    case postEndpoint(body: MyCodableObject, limit: Int)\n}\n\nextension MyAPIService: ArachneService {\n    var baseUrl: String {\n        \"https://myapiservice.com\"\n    }\n\n    var path: String {\n        switch self {\n        case .info:\n            \"/info\"\n        case .userProfile(let username):\n            \"/users/\\(username)\"\n        case .postEndpoint:\n            \"/postendpoint\"\n        }\n    }\n\n    var queryStringItems: [URLQueryItem]? {\n        switch self {\n        case .postEndpoint(_, let limit):\n            [URLQueryItem(name: \"limit\", value: \"\\(limit)\")]\n        default:\n            nil\n        }\n    }\n\n    var method: HttpMethod {\n        switch self {\n        case .postEndpoint:\n            .post\n        default:\n            .get\n        }\n    }\n\n    var body: Data? {\n        switch self{\n        case .postEndpoint(let myCodableObject, _):\n            try? JSONEncoder().encode(myCodableObject)\n        default:\n            nil\n        }\n    }\n\n    var headers: [String : String]? {\n        switch self {\n        case .postEndpoint:\n            nil\n        default:\n            [\"Accept\": \"application/json\"]\n        }\n    }\n}\n```\n\nThen you can use them like this\n\nDeclare your provider\n\n```swift\nlet provider = ArachneProvider\u003cMyAPIService\u003e()\n```\n\nGet data from your endpoint\n\n```swift\nlet (data, _) = try await provider.data(.info)\n```\n\nLet's see it assembled in an extract of a SwiftUI app\n\n```swift\nimport SwiftUI\nimport Arachne\nimport os\n\nstruct Info: Codable {\n    let name: String\n}\n\nclass MyApiClient {\n    private let provider = ArachneProvider\u003cMyAPIService\u003e()\n\n    func loadInfo() async throws -\u003e Info {\n        let (data, _) = try await provider.data(.info)\n        return try JSONDecoder().decode(Info.self, from: data)\n    }\n}\n\n@Observable\nclass MyState {\n    private let apiClient = MyApiClient()\n    private let logger = Logger(subsystem: \"Arachne\", category: \"MyInteractor\")\n\n    var info: Info?\n\n    func getInfo() async {\n        do {\n            self.info = try await apiClient.loadInfo()\n        } catch {\n            logger.error(\"Error: \\(error.localizedDescription)\")\n        }\n    }\n}\n\nstruct MyView: View {\n    @State var state = MyState()\n\n    var body: some View {\n        Text(interactor.info?.name ?? \"No name\")\n            .task {\n                await interactor.getInfo()\n            }\n    }\n}\n```\n\n## Migrate from 0.3.0 to 0.4.0+\n\nA function using a `Combine` publisher, for example:\n\n```swift\nfunc getInfo() {\n    apiClient.loadInfo()\n        .sink { completion in\n            switch completion {\n            case .finished:\n                break\n            case .failure(let error):\n                // Handle error\n            }\n        } receiveValue: { info in\n            self.info = info\n        }\n        .store(in: \u0026cancellables)\n}\n```\n\ncan be easily migrated like this\n\n```swift\nfunc getInfo() async {\n    do {\n        self.info = try await apiClient.loadInfo()\n    } catch {\n        // Handle error\n    }\n}\n```\n\nor if you cannot make your function async\n\n```swift\nfunc getInfo() {\n    Task {\n        do {\n            self.info = try await apiClient.loadInfo()\n        } catch {\n            // Handle error\n        }\n    }\n}\n```\n\n## Installation\n\n### Swift Package Manager\n\n#### Using Xcode UI\n\nGo to your Project Settings \u003e Swift Packages and add Arachne by entering `https://github.com/artemisia-absynthium/arachne.git` in the search field.\n\n#### Not using Xcode UI\n\nAdd the following as a dependency to your `Package.swift`:\n\n```swift\n.package(url: \"https://github.com/artemisia-absynthium/arachne.git\", .upToNextMajor(from: \"0.6.1\"))\n```\n\nand then specify \"Arachne\" as a dependency of the Target in which you wish to use it.\n\n### Cocoapods\n\nSupport for CocoaPods has been discontinued since version 0.5.0, in order to install the latest version please use Swift Package Manager.\n\n## Roadmap\n\nCurrently supported tasks are\n\n* `bytes`\n* `data`\n* `download`\n* `upload`\n* Resumable download and download progress updates\n\nNext steps will be 🚧\n\n* Add support for resumable upload\n* Upload progress updates\n* Unit test specially for resumable download\n\n## Contributing\n\nContributions are welcome!\nNo special steps are required to get up and running developing this project, just clone and open in Xcode, the only requirement is for each PR to have proper unit tests and that all tests pass.\n\n## License\n\nThis project is released under the [MIT License](https://github.com/artemisia-absynthium/arachne/blob/main/LICENSE).\n\n## Project status\n\nThis project recently experienced a shift of goal, while the initial goal of this library was to provide Combine publishers for tasks that didn't have one, the introduction of async/await in Swift suddenly made using Combine for network requests look cumbersome and this is why no new Combine tasks will be added.\nThe current goal of this library is to backport async/await `URLSession` tasks to macOS 10.15, iOS 13, iPadOS 13, tvOS 13 and watchOS 7, while the availability of their native counterpart in the Foundation framework is iOS 15.0+, iPadOS 15.0+, macOS 12.0+, Mac Catalyst 15.0+, tvOS 15.0+, watchOS 8.0+ and to provide an opinionated abstraction layer to remove boilerplate code.\nIn the future, with the progressive drop of platform versions before iOS 15.0+, iPadOS 15.0+, macOS 12.0+, Mac Catalyst 15.0+, tvOS 15.0+, watchOS 8.0+ from the community, the goal of this library will be only to provide an opinionated abstraction layer to remove boilerplate code.\n\n## Why Arachne\n\nThinking about networking my mind immediately went to the best \"networkers\" in nature: spiders.\n\nSince I come from classical studies background I liked to use the Greek word for spider: Arachne (ᾰ̓ρᾰ́χνη). Arachne is also the name of the protagonist, a very talented weaver, of [a tale in Greek mythology](https://en.wikipedia.org/wiki/Arachne) and I felt it was really appropriate.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fartemisia-absynthium%2Farachne","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fartemisia-absynthium%2Farachne","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fartemisia-absynthium%2Farachne/lists"}