{"id":18060658,"url":"https://github.com/0xleif/waiter","last_synced_at":"2026-02-24T03:07:58.111Z","repository":{"id":171801288,"uuid":"648428107","full_name":"0xLeif/Waiter","owner":"0xLeif","description":"⌛ Asynchronous Waiting Made Easy","archived":false,"fork":false,"pushed_at":"2024-09-27T18:08:52.000Z","size":23,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-21T22:07:40.132Z","etag":null,"topics":["async","await","ios","macos","swift","testing","tests","tvos","wait","waitable","waiter","watchos"],"latest_commit_sha":null,"homepage":"https://0xleif.github.io/Waiter/","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/0xLeif.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":"2023-06-02T00:31:53.000Z","updated_at":"2024-09-27T18:08:55.000Z","dependencies_parsed_at":null,"dependency_job_id":"84757c48-76a0-44cc-be63-4fc29c3ee9ab","html_url":"https://github.com/0xLeif/Waiter","commit_stats":null,"previous_names":["0xleif/waiter"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/0xLeif/Waiter","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xLeif%2FWaiter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xLeif%2FWaiter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xLeif%2FWaiter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xLeif%2FWaiter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/0xLeif","download_url":"https://codeload.github.com/0xLeif/Waiter/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xLeif%2FWaiter/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29770362,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-24T03:07:52.931Z","status":"ssl_error","status_checked_at":"2026-02-24T03:07:49.415Z","response_time":75,"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":["async","await","ios","macos","swift","testing","tests","tvos","wait","waitable","waiter","watchos"],"created_at":"2024-10-31T04:10:06.374Z","updated_at":"2026-02-24T03:07:58.099Z","avatar_url":"https://github.com/0xLeif.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Waiter\n\n[![macOS Build](https://img.shields.io/github/actions/workflow/status/0xLeif/Waiter/macOS.yml?label=macOS\u0026branch=main)](https://github.com/0xLeif/Waiter/actions/workflows/macOS.yml)\n[![Ubuntu Build](https://img.shields.io/github/actions/workflow/status/0xLeif/Waiter/ubuntu.yml?label=Ubuntu\u0026branch=main)](https://github.com/0xLeif/Waiter/actions/workflows/ubuntu.yml)\n[![License](https://img.shields.io/github/license/0xLeif/Waiter)](https://github.com/0xLeif/Waiter/blob/main/LICENSE)\n[![Version](https://img.shields.io/github/v/release/0xLeif/Waiter)](https://github.com/0xLeif/Waiter/releases)\n\n⌛ *Asynchronous Waiting Made Easy*\n\nWaiter is a Swift library that provides convenient global functions for asynchronous waiting. It allows you to wait for values to satisfy specific conditions or become equal to expected values. Additionally, Waiter includes a Waitable protocol that you can adopt in your own types to enable waiting functionality.\n\n## Usage\n\n### Wait Example\n\n```swift\nimport Waiter\n\nclass Counter {\n    var value: Int = 0\n}\n\nlet counter = Counter()\n\nTask {\n    // Asynchronously increment the counter after a delay\n    await Task.sleep(1)\n    counter.value += 1\n}\n\ndo {\n    // Wait for the counter value to become equal to 1\n    let finalValue = try await wait(\n        on: counter,\n        for: \\.value,\n        expecting: 1\n    )\n\n    print(\"Counter value: \\(finalValue)\") // Output: Counter value: 1\n} catch {\n    print(\"Timeout Error: \\(error)\")\n}\n```\n\n### Waitable Example\n\n```swift\nimport Waiter\n\nclass Counter: Waitable {\n    var value: Int = 0\n}\n\nlet counter = Counter()\n\nTask {\n    // Asynchronously increment the counter after a delay\n    await Task.sleep(1)\n    counter.value += 1\n}\n\ndo {\n    // Wait for the counter value to become equal to 1\n    let finalValue = try await counter.wait(for: \\.value, expecting: 1)\n\n    print(\"Counter value: \\(finalValue)\") // Output: Counter value: 1\n} catch {\n    print(\"Timeout Error: \\(error)\")\n}\n```\n\n### Wait for a Value to Satisfy a Condition\n\nTo wait for a value of a specified key path to satisfy a condition, use the global `wait` function:\n\n```swift\n@discardableResult\npublic func wait\u003cObject: AnyObject, Value\u003e(\n    on object: Object,\n    for keyPath: KeyPath\u003cObject, Value\u003e,\n    duration: TimeInterval = 3,\n    interval: TimeInterval = 0.1,\n    expecting: @escaping (Value) -\u003e Bool\n) async throws -\u003e Value\n```\n\n- object: The object to wait on.\n- keyPath: The key path of the value to wait for.\n- duration: The maximum amount of time to wait for the value to change (default: 3 seconds).\n- interval: The interval at which to check the value (default: 0.1 seconds).\n- expecting: A closure that determines whether the value satisfies the condition.\n\n### Wait for a Value to Become Equal to a Specific Value\n\nTo wait for a value of a specified key path to become equal to a specific value, use the global `wait` function:\n\n```swift\n@discardableResult\npublic func wait\u003cObject: AnyObject, Value: Equatable\u003e(\n    on object: Object,\n    for keyPath: KeyPath\u003cObject, Value\u003e,\n    duration: TimeInterval = 3,\n    interval: TimeInterval = 0.1,\n    expecting: Value\n) async throws -\u003e Value\n```\n\n- object: The object to wait on.\n- keyPath: The key path of the value to wait for.\n- duration: The maximum amount of time to wait for the value to change (default: 3 seconds).\n- interval: The interval at which to check the value (default: 0.1 seconds).\n- expecting: The expected value to wait for.\n\n### Implementing Waitable Protocol\n\nYou can make your own types waitable by adopting the `Waitable` protocol. The protocol provides two default implementations for the wait functions, allowing you to use them directly or customize them based on your needs.\n\n### Testing with XCTest\n\nWaiter seamlessly integrates with XCTest, allowing you to test asynchronous behavior. Here's an example of a test case using Waiter:\n\n```swift\nimport Waiter\nimport XCTest\n@testable import YourProject\n\nfinal class YourTests: XCTestCase, Waitable {\n    func testYourFunctionality() async throws {\n        // Test setup\n\n        // Perform asynchronous operations\n\n        // Wait for a value to satisfy a condition\n        try await wait(on: object, for: keyPath, expecting: expectedValue)\n\n        // Assertions and test verification\n    }\n}\n```\n\n### Error Handling\n\nThe `wait` function can throw errors if the wait times out. If an error occurs, it means the wait duration exceeded the specified timeout and the condition was not satisfied within the given time frame. You can handle the error by displaying an appropriate message, retrying the operation, or taking any other desired action.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0xleif%2Fwaiter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F0xleif%2Fwaiter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0xleif%2Fwaiter/lists"}