{"id":48469446,"url":"https://github.com/kylehughes/swift-async-result","last_synced_at":"2026-04-07T06:01:33.471Z","repository":{"id":349297437,"uuid":"1195771214","full_name":"kylehughes/swift-async-result","owner":"kylehughes","description":"Swift's Result type, extended with an in-progress case for asynchronous operations.","archived":false,"fork":false,"pushed_at":"2026-04-05T07:17:30.000Z","size":19,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-05T09:10:07.240Z","etag":null,"topics":["asynchronous-programming","structured-concurrency","swift"],"latest_commit_sha":null,"homepage":"https://kylehughes.github.io/swift-async-result/","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kylehughes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-30T03:36:32.000Z","updated_at":"2026-04-05T07:14:11.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/kylehughes/swift-async-result","commit_stats":null,"previous_names":["kylehughes/swift-async-result"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/kylehughes/swift-async-result","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kylehughes%2Fswift-async-result","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kylehughes%2Fswift-async-result/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kylehughes%2Fswift-async-result/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kylehughes%2Fswift-async-result/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kylehughes","download_url":"https://codeload.github.com/kylehughes/swift-async-result/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kylehughes%2Fswift-async-result/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31469968,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-06T08:36:52.050Z","status":"ssl_error","status_checked_at":"2026-04-06T08:36:51.267Z","response_time":112,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["asynchronous-programming","structured-concurrency","swift"],"created_at":"2026-04-07T06:01:30.570Z","updated_at":"2026-04-07T06:01:33.463Z","avatar_url":"https://github.com/kylehughes.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AsyncResult\n\n[![Platform Versions](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fkylehughes%2Fswift-async-result%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/kylehughes/swift-async-result)\n[![Swift Versions](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fkylehughes%2Fswift-async-result%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/kylehughes/swift-async-result)\n[![Test](https://github.com/kylehughes/swift-async-result/actions/workflows/test.yml/badge.svg)](https://github.com/kylehughes/swift-async-result/actions/workflows/test.yml)\n\n`Result`, extended with an in-progress case for asynchronous operations, e.g.\n\n```swift\nenum AsyncResult\u003cSuccess, Failure: Error\u003e {\n    case completed(Result\u003cSuccess, Failure\u003e)\n    case inProgress\n}\n```\n\n## About\n\nAsyncResult adds an `.inProgress` case to `Result` for representing loading, success, and failure states.\n\nAsyncResult has no dependencies. Tests cover 100% of lines and functions.\n\n### Capabilities\n\n* Combinators: `map`, `flatMap`, `mapError`, `flatMapError`, `tryMap`, `merge`, `zip`, `collect`.\n* Typed throws support across all throwing APIs.\n* `recover` with type-level proof (`AsyncResult\u003cSuccess, Never\u003e`) that recovery occurred.\n* `Failure == Never` specialization with `value`, `setFailureType(to:)`.\n* Optional interop: `init(optional:or:)`, `unwrap(or:)`.\n* Sync and async overloads for all combinators.\n* Swift 6 language mode support with strict concurrency.\n\n## Supported Platforms\n\n* iOS 13.0+\n* macOS 10.15+\n* tvOS 13.0+\n* visionOS 1.0+\n* watchOS 6.0+\n\n## Requirements\n\n* Swift 6.2+\n* Xcode 26.0+\n\n## Documentation\n\n[Documentation is available on GitHub Pages.](https://kylehughes.github.io/swift-async-result/)\n\n## Installation\n\n### Swift Package Manager\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/kylehughes/swift-async-result.git\", .upToNextMajor(from: \"1.0.0\")),\n]\n```\n\n## Getting Started\n\n`AsyncResult?` serves as view model state, where `nil` means idle:\n\n```swift\nimport AsyncResult\n\n@Observable\nfinal class UserViewModel {\n    var user: AsyncResult\u003cUser, any Error\u003e?\n\n    func load() async {\n        user = .inProgress\n        user = await AsyncResult { try await api.fetchUser() }\n    }\n}\n```\n\nChain transformations, including throwing ones, on the result:\n\n```swift\nlet displayName = user\n    .map(\\.profile)\n    .tryMap { try JSONDecoder().decode(Profile.self, from: $0) }\n    .map(\\.displayName)\n```\n\nRecover from errors with type-level proof:\n\n```swift\nlet safeResult: AsyncResult\u003cString, Never\u003e = user\n    .map(\\.name)\n    .recover { _ in \"Unknown\" }\n\n// nil only when in progress; failure is impossible\nlet name = safeResult.value\n```\n\nCombine multiple in-flight requests:\n\n```swift\nlet combined = profileResult.zip(with: settingsResult)\n// or collect a whole array:\nlet all = AsyncResult.collect(itemResults)\n```\n\n## Usage\n\n### State Modeling\n\n`AsyncResult` has two cases: `.inProgress` and `.completed(Result\u003cSuccess, Failure\u003e)`. Use `AsyncResult?` where `nil`\nrepresents idle, before any operation has been initiated.\n\n```swift\n@State private var result: AsyncResult\u003c[Item], any Error\u003e?\n\nvar body: some View {\n    switch result {\n    case nil: ContentUnavailableView(\"Tap to load\", ...)\n    case .inProgress: ProgressView()\n    case .completed(.success(let items)): ItemListView(items: items)\n    case .completed(.failure(let error)): ErrorView(error: error)\n    }\n}\n```\n\n### Throwing Transforms\n\n`tryMap` transforms the success value with a closure that can fail. It follows the same overload pattern as\n`init(catching:)`:\n\n```swift\n// Typed throws: the closure throws the Failure type directly\nresult.tryMap { (data: Data) throws(APIError) -\u003e User in\n    try decoder.decode(User.self, from: data)\n}\n\n// Untyped throws with error mapping\nresult.tryMap(\n    { try JSONDecoder().decode(User.self, from: $0) },\n    mapError: { _ in .decodingFailed }\n)\n```\n\n### Combining Results\n\n`merge`, `zip`, and `collect` all use the same priority: failure \u003e inProgress \u003e success.\n\n```swift\n// Zip two results into a tuple\nlet combined = profileResult.zip(with: avatarResult)\n\n// Merge with a custom transform\nlet summary = nameResult.merge(with: ageResult) { \"\\($0), \\($1)\" }\n\n// Collect an array of results\nlet allItems = AsyncResult.collect(itemResults)  // AsyncResult\u003c[Item], any Error\u003e\n```\n\n### Recovery and Never\n\n`recover` transforms failures into successes and returns `AsyncResult\u003cSuccess, Never\u003e`:\n\n```swift\nlet safe = result.recover { _ in fallbackValue }\nsafe.value  // nil only means in-progress\n```\n\n`setFailureType(to:)` composes infallible results with fallible ones:\n\n```swift\nlet infallible = AsyncResult\u003cInt, Never\u003e(42)\nlet fallible = AsyncResult\u003cString, MyError\u003e.completed(.success(\"hello\"))\nlet zipped = infallible.setFailureType(to: MyError.self).zip(with: fallible)\n```\n\n### Optional Interop\n\n```swift\n// Create from an optional\nlet result = AsyncResult(optional: cachedUser, or: CacheError.miss)\n\n// Unwrap an optional success value\nlet unwrapped: AsyncResult\u003cUser, any Error\u003e = result.unwrap(or: APIError.notFound)\n```\n\n## Important Behavior\n\n* `AsyncResult` does not have an idle case. Use `AsyncResult?` where `nil` represents the state before any operation\n  has been initiated.\n* `merge`, `zip`, and `collect` use failure \u003e inProgress \u003e success priority. A failure in any position is never hidden\n  by an in-progress state elsewhere.\n* `recover` returns `AsyncResult\u003cSuccess, Never\u003e`, which proves at the type level that error handling has occurred. The\n  `value` property on `Never`-failure results returns `nil` only for in-progress, never for failure.\n\n## Contributions\n\nAsyncResult is not accepting source contributions at this time. Bug reports will be considered.\n\n## Author\n\n[Kyle Hughes](https://kylehugh.es)\n\n[![Bluesky][bluesky_image]][bluesky_url]  \n[![LinkedIn][linkedin_image]][linkedin_url]  \n[![Mastodon][mastodon_image]][mastodon_url]\n\n[bluesky_image]: https://img.shields.io/badge/Bluesky-0285FF?logo=bluesky\u0026logoColor=fff\n[bluesky_url]: https://bsky.app/profile/kylehugh.es\n[linkedin_image]: https://img.shields.io/badge/LinkedIn-0A66C2?logo=linkedin\u0026logoColor=fff\n[linkedin_url]: https://www.linkedin.com/in/kyle-hughes\n[mastodon_image]: https://img.shields.io/mastodon/follow/109356914477272810?domain=https%3A%2F%2Fmister.computer\u0026style=social\n[mastodon_url]: https://mister.computer/@kyle\n\n## License\n\nAsyncResult is available under the MIT license.\n\nSee `LICENSE` for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkylehughes%2Fswift-async-result","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkylehughes%2Fswift-async-result","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkylehughes%2Fswift-async-result/lists"}