{"id":13489439,"url":"https://github.com/tcldr/Entwine","last_synced_at":"2025-03-28T04:31:11.534Z","repository":{"id":48310662,"uuid":"191013066","full_name":"tcldr/Entwine","owner":"tcldr","description":"Testing tools and utilities for Apple's Combine framework.","archived":false,"fork":false,"pushed_at":"2023-12-04T08:02:09.000Z","size":7711,"stargazers_count":442,"open_issues_count":2,"forks_count":26,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-05-22T15:32:53.465Z","etag":null,"topics":["apple","combine","combine-framework","ios","reactive","reactive-programming","reactive-streams","swift","swiftui","test","tvos","unit-test","xctest"],"latest_commit_sha":null,"homepage":"","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/tcldr.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}},"created_at":"2019-06-09T13:58:11.000Z","updated_at":"2024-05-15T15:04:57.000Z","dependencies_parsed_at":"2024-01-13T01:38:32.548Z","dependency_job_id":"751ade7b-d0c6-49c8-9e4e-8b689ff59ae4","html_url":"https://github.com/tcldr/Entwine","commit_stats":{"total_commits":40,"total_committers":6,"mean_commits":6.666666666666667,"dds":"0.17500000000000004","last_synced_commit":"72a73955f03b5888199aeccc58067dc365381244"},"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tcldr%2FEntwine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tcldr%2FEntwine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tcldr%2FEntwine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tcldr%2FEntwine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tcldr","download_url":"https://codeload.github.com/tcldr/Entwine/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245970531,"owners_count":20702435,"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":["apple","combine","combine-framework","ios","reactive","reactive-programming","reactive-streams","swift","swiftui","test","tvos","unit-test","xctest"],"created_at":"2024-07-31T19:00:27.672Z","updated_at":"2025-03-28T04:31:11.189Z","avatar_url":"https://github.com/tcldr.png","language":"Swift","readme":"\n\n# [Entwine](https://github.com/tcldr/Entwine)\n![CI](https://github.com/tcldr/Entwine/workflows/CI/badge.svg)\n[![@tcldr1](https://img.shields.io/static/v1?label=\u0026message=@tcldr1\u0026color=1DA1F2\u0026logo=twitter\u0026labelColor=24292e)](https://twitter.com/tcldr1)\n\nAccessories for [Apple's Combine Framework](https://developer.apple.com/documentation/combine).\n\n---\n\n## About\nEntwine consists of three libraries (over two repositories) to be used in conjuction with Apple's Combine framework:\n- The [_Entwine Utilities library_](https://github.com/tcldr/Entwine/blob/master/Assets/Entwine/README.md) includes additional operators, subjects and utilities for working with Combine sequences.\nThe package currently includes a `ReplaySubject`, a `withLatest(from:)` operator and a `Publishers.Factory` for rapidly defining publishers inline from any source.\n\n    **[View the README for the Entwine Utilities Library](https://github.com/tcldr/Entwine/blob/master/Assets/Entwine/README.md)**\n    \n- The [_EntwineTest library_](https://github.com/tcldr/Entwine/blob/master/Assets/EntwineTest/README.md) consists of tools for verifying expected behavior of Combine sequences. It houses\na `TestScheduler` that uses virtual time, a `TestablePublisher` that schedules a user-defined sequence of\nelements in absolute or relative time, and a `TestableSubscriber` that record a time-stamped list of events that can be compared against expected behavior.\n\n    **[View the README for the EntwineTest Library](https://github.com/tcldr/Entwine/blob/master/Assets/EntwineTest/README.md)**\n\n- The [_EntwineRx library_](https://github.com/tcldr/EntwineRx/blob/master/README.md) is a small library maintained under a [separate repository](https://github.com/tcldr/EntwineRx) that contains bridging operators from RxSwift to Combine and vice versa\nmaking _RxSwift_ and _Combine_ work together seamlessly.\n\n    **[View the README for the EntwineRx Library](https://github.com/tcldr/EntwineRx)**\n\n\n\n_Note: EntwineRx is maintained as a separate Swift package to minimize the SPM dependency graph_.\n\n\n---\n\n## Quick start guide\n### Create _Combine_ publishers from any source\nUse the [`Publishers.Factory`](https://tcldr.github.io/Entwine/EntwineDocs/Extensions/Publishers/Factory.html) publisher from the _Entwine_ package to effortlessly create a publisher that\nmeets Combine's backpressure requirements from any source. [Find out more about the _Entwine Utilities_ library.](https://github.com/tcldr/Entwine/blob/master/Assets/Entwine/README.md)\n\n_Inline publisher creation for PhotoKit authorization status:_\n```swift\n\nimport Entwine\n\nlet photoKitAuthorizationStatus = Publishers.Factory\u003cPHAuthorizationStatus, Never\u003e { dispatcher in\n    let status = PHPhotoLibrary.authorizationStatus()\n    dispatcher.forward(status)\n    switch status {\n    case .notDetermined:\n        PHPhotoLibrary.requestAuthorization { newStatus in\n            dispatcher.forward(newStatus)\n            dispatcher.forward(completion: .finished)\n        }\n    default:\n        dispatcher.forward(completion: .finished)\n    }\n    return AnyCancellable {}\n}\n```\n### Unit test _Combine_ publisher sequences\nUse the `TestScheduler`, `TestablePublisher` and `TestableSubscriber` to simulate _Combine_ sequences and test against expected output. [Find out more about the _EntwineTest_ library](https://github.com/tcldr/Entwine/blob/master/Assets/EntwineTest/README.md)\n\n_Testing Combine's `map(_:)` operator:_\n\n```swift\n\nimport XCTest\nimport EntwineTest\n    \nfunc testMap() {\n    \n    let testScheduler = TestScheduler(initialClock: 0)\n    \n    // creates a publisher that will schedule it's elements relatively, at the point of subscription\n    let testablePublisher: TestablePublisher\u003cString, Never\u003e = testScheduler.createRelativeTestablePublisher([\n        (100, .input(\"a\")),\n        (200, .input(\"b\")),\n        (300, .input(\"c\")),\n    ])\n    \n    let subjectUnderTest = testablePublisher.map { $0.uppercased() }\n    \n    // schedules a subscription at 200, to be cancelled at 900\n    let results = testScheduler.start { subjectUnderTest }\n    \n    XCTAssertEqual(results.recordedOutput, [\n        (200, .subscription),           // subscribed at 200\n        (300, .input(\"A\")),             // received uppercased input @ 100 + subscription time\n        (400, .input(\"B\")),             // received uppercased input @ 200 + subscription time\n        (500, .input(\"C\")),             // received uppercased input @ 300 + subscription time\n    ])\n}\n```\n\n### Bridge your _RxSwift_ view models to _Combine_ and use with _SwiftUI_\nFirst, make sure you add the [_EntwineRx Swift Package_](https://github.com/tcldr/EntwineRx) (located in an external repo) as a dependency to your project.\n\n_Example coming soon_\n\n---\n\n## Requirements\nEntwine sits on top of Apple's Combine framework and therefore requires _Xcode 11_ and is has minimum deployment targets of _macOS 10.15_, _iOS 13_, _tvOS 13_ or _watchOS 6_.\n\n---\n\n## Installation\nEntwine is delivered via a Swift Package and can be installed either as a dependency to another Swift Package by adding it to the dependencies section of a `Package.swift`  file\nor to an Xcode 11 project by via the `File -\u003e Swift Packages -\u003e Add package dependency...` menu in Xcode 11. \n\n---\n\n## Documentation\nDocumentation for each package is available at:\n- [Entwine Documentation](https://tcldr.github.io/Entwine/EntwineDocs/) (Operators, Publishers and Accessories)\n- [EntwineTest Documentation](https://tcldr.github.io/Entwine/EntwineTestDocs/) (Tools for testing _Combine_ sequence behavior)\n- [EntwineRx Documentation](https://tcldr.github.io/Entwine/EntwineRxDocs/) (Bridging operators for _RxSwift_)\n\n---\n\n## Copyright and license\n\nThis project is released under the [MIT license](https://github.com/tcldr/Entwine/blob/master/LICENSE)\n\n---\n\n","funding_links":[],"categories":["Open Source","Swift","Content"],"sub_categories":["Libraries"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftcldr%2FEntwine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftcldr%2FEntwine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftcldr%2FEntwine/lists"}