{"id":23392683,"url":"https://github.com/drewmccormack/Forked","last_synced_at":"2025-08-24T11:31:10.320Z","repository":{"id":267318141,"uuid":"834790149","full_name":"drewmccormack/Forked","owner":"drewmccormack","description":"It's like Git for your Swift data types.","archived":false,"fork":false,"pushed_at":"2025-07-20T14:21:05.000Z","size":46348,"stargazers_count":476,"open_issues_count":5,"forks_count":13,"subscribers_count":7,"default_branch":"main","last_synced_at":"2025-08-23T08:57:22.214Z","etag":null,"topics":[],"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/drewmccormack.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}},"created_at":"2024-07-28T11:21:01.000Z","updated_at":"2025-08-19T03:34:36.000Z","dependencies_parsed_at":"2025-05-16T00:14:24.419Z","dependency_job_id":null,"html_url":"https://github.com/drewmccormack/Forked","commit_stats":null,"previous_names":["drewmccormack/forked"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/drewmccormack/Forked","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drewmccormack%2FForked","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drewmccormack%2FForked/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drewmccormack%2FForked/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drewmccormack%2FForked/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/drewmccormack","download_url":"https://codeload.github.com/drewmccormack/Forked/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drewmccormack%2FForked/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271854475,"owners_count":24834453,"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-24T02:00:11.135Z","response_time":111,"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":"2024-12-22T05:02:09.678Z","updated_at":"2025-08-24T11:31:10.309Z","avatar_url":"https://github.com/drewmccormack.png","language":"Swift","funding_links":[],"categories":["Data and Storage","Misc","Swift","Recently Updated"],"sub_categories":["Vim","[Feb 04, 2025](/content/2025/02/04/README.md)"],"readme":"![Forked: Share Data with Confidence](https://raw.githubusercontent.com/drewmccormack/Forked/main/Sources/Forked/Documentation.docc/Resources/ForkedGardenBanner.png)\n\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fdrewmccormack%2FForked%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/drewmccormack/Forked)\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fdrewmccormack%2FForked%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/drewmccormack/Forked)\n\nForked provides a generalized approach to managing shared data in Swift applications, both on-device — avoiding race conditions — and across devices as a framework for offline-first and [local-first](https://www.inkandswitch.com/local-first/) software.\n\nForked can operate within a single iOS app, on a Swift server, or distributed across a network. The `ForkedCloudKit` package, for example, supports syncing of data across devices in just a few lines of code.\n\nIn short, what's forking stopping you?![^goodplace]\n\n## Quick Start\n\n### Try Before You Buy\n\nNobody wants to invest time in a framework without knowing if it's right for them, so we have uploaded the [Forkers](https://apps.apple.com/us/app/forkers/id6739265992) sample app to the App Store for you to try. (Note that it is unlisted, so use the link instead of searching.) The Forkers app is built on Forked, and the source code is right here. Try it out, and don't forget to test out the iCloud sync!\n\n### Installation\n\n#### Swift Package Manager\n\nAdd to your `Package.swift`:\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/drewmccormack/Forked.git\", from: \"0.1.0\")\n]\n```\n\n#### Xcode\n\n1. Select your project in the navigator\n2. Open the Package Dependencies tab\n3. Click + and enter: `https://github.com/drewmccormack/Forked.git`\n4. Add `Forked` and any of the subpackages you need\n\n## Key Features\n\n- **Safe**: Prevents data races and manages race conditions without locks, queues, or actors\n- **Swift Values**: Model data with `Sendable` value types that pass easily between threads and isolation domains\n- **Simple Setup**: 100% Swift, no complex configuration needed\n- **Smart Sync**: Git-inspired branching and merging lets you track changes on a single device or across many\n- **Supports Local-First**: [Local-first](https://www.inkandswitch.com/local-first/) apps are all the rage, and Forked makes it easy to get started\n- **Smashable**: Advanced 3-way merging algorithms (_eg_ CRDTs) intelligently handle conflicts\n- **Saveable**: Full `Codable` support for easy persistence to disk and cloud services\n- **Seamless iCloud**: Built-in CloudKit integration for effortless multi-device synchronization\n- **Scalable**: You can start using Forked with your own data types, and scale up to complete data models when it suits\n- **Self Service**: You can add custom storage for Forked data, and integrate with custom cloud services\n- **Succinct**: Unlike Git, Forked only keeps the bare essentials for merging, not a complete history of all changes\n\n## How it Works\n\nForked is based on a decentral model similar to Git. It tracks changes to a shared data resource, and resolves conflicts using 3-way merging. You are in control, and never lose any changes to your data.\n\nIn contrast to locks, queues, and actors, Forked doesn't serialize access to a resource. Instead, Forked provides a branching mechanism to systematically create copies of the data, which can be modified concurrently, and merged at a later time.\n\nForked takes care of all the logic involved in the branching process, including keeping a copy of the data at the point that branches (known as _forks_) diverge. This 'divergence' copy is known as the _common ancestor_, and it is important, because when it comes time to merge the forks again, Forked can use it to determine what was changed, and in which fork(s).\n\nYou can merge branches safely at any time with Forked — in any order — using powerful merging algorithms that go way beyond what is available in other data modeling frameworks. For example, Forked utilizes so-called Conflict-Free Replicated Data Types (CRDTs) to merge text in a way that would seem logical to people, rather than choosing a solution with results only a machine could love.\n\n## Show Me The Forking Code!\n\nReady to play? Let's learn about Forked by example.\n\n### A Simple Forking Example\n\nHere is your first fork:\n\n```swift\nimport Forked\nlet uiFork = Fork(name: \"ui\")\nlet intResource = QuickFork\u003cInt\u003e(initialValue: 0, forks: [uiFork])\n```\n\n`QuickFork` is a convenient way to create an in-memory `ForkedResource` holding a single value, in this case an `Int`.\n\nWe have also declared `uiFork`, which is a named fork. Aside from forks you create yourself, all `ForkedResource` instances have a central fork called `main`, which can be merged with any other fork.\n\nLet's update the `Int` on `uiFork`, and independently on the `main` fork, then merge to get the result:\n\n```swift\ntry intResource.update(uiFork, with: 1)\ntry intResource.update(.main, with: 2)\ntry intResource.mergeIntoMain(from: uiFork)\nlet resultInt = try intResource.value(in: .main)!\n```\n\nThe `resultInt` will be `2` in this case, because that was the value set most recently. \n\n### Controlled Forking\n\nFor an atomic type like an `Int`, the results of merging are not very interesting; the real power of Forked comes from its ability to merge complex data types. \n\nLet's start by defining a struct so we can control the merging behavior of the `Int`.\n\n```swift\nstruct AccumulatingInt: Mergeable {\n    var value: Int = 0\n    func merged(withSubordinate other: Self, commonAncestor: Self) throws -\u003e Self {\n        return AccumulatingInt(value: self.value + other.value - commonAncestor.value)\n    }\n}\n```\n\nBy conforming to the `Mergeable` protocol, `AccumulatingInt` has total control over how it is merged.\n\nThe `Mergeable` protocol requires the func `merged(withSubordinate:commonAncestor:)`. The `subordinate` is a conflicting value from another fork, and `commonAncestor` is the value at the point that the two forks diverged.\n\nThe merge algorithm of `AccumulatingInt` determines what has changed on each fork since the common ancestor was created, and tallies these changes up to produce a new value.\n\nIf we were to use an `AccumulatingInt` in the original example, instead of an `Int`, the result would be `3`, because the `uiFork` incremented by `1`, and the `main` fork incremented by `2`, giving a total of `3`.\n\n### Merging Algorithms\n\nSo you can come up with structs that can merge in any way that you choose, but those merging algorithms can quickly get complex. That's where the subpackage `ForkedMerge` comes in: it provides standard built-in merging algorithms.\n\nImagine we are developing a text editor with this oversimplified model:\n\n```swift\nimport Forked\nimport ForkedMerge\n\nstruct TextDocument: Mergeable {\n    var text: String = \"\"\n    func merged(withSubordinate other: Self, commonAncestor: Self) throws -\u003e Self {\n        let newText = try TextMerger().merge(\n            self.text, \n            withSubordinate: other.text, \n            commonAncestor: commonAncestor.text\n        )\n        return TextDocument(text: newText)\n    }\n}\n```\n\nIt doesn't look like much, but you've just created the model for a fully collaborative text editor. For example, if the model initially contains the text \"Fork Yeah\", and...\n\n1. One user changes this to \"Fork Yeah!!!\"\n2. Another changes it at the same time to \"Fork yeah\"\n3. `TextMerger` will merge to give \"Fork yeah!!!\"\n\n### Modeling Data\n\nHaving complete control over merging is great, and the merging algorithms provided by `ForkedMerge` make it much easier to piece things together, but wouldn't it be nice if `Forked` could just generate this code automatically? \n\nThat's exactly what `ForkedModel` is for. It uses Swift Macros to make defining a global data model almost trivial. \n\nLet's update `TextDocument` to use `ForkedModel`:\n\n```swift\nimport Forked\nimport ForkedModel\n\n@ForkedModel\nstruct TextDocument {\n    @Merged var text: String = \"\"\n}\n```\n\n\"Where's the rest?\" I hear you cry. There is no rest! That's the forking lot!\n\nThis code is equivalent to the code we wrote manually in the previous section. It could form the basis of a fully collaborative text editor, or simply a personal editor syncing via iCloud.\n\nAnd it doesn't stop there: each property in the struct gets merged independently. You can choose from a standard _atomic_ merge for simple types, to advanced merging algorithms for common Swift types like `String`, `Array`, `Dictionary`, and `Set`.\n\nTo demonstrate, here is a more complex example of `TextDocument`:\n\n```swift\nimport Forked\nimport ForkedModel\n\n@ForkedModel\nstruct TextDocument {\n    var id: UUID = UUID()\n    @Merged var text: String = \"\"\n    @Merged var tags: Set\u003cString\u003e = []\n    @Merged(using: .textMerge) var comment: String = \"\"\n    @Merged var editCount: AccumulatingInt = .init()\n    var cursorPosition: Int = 0\n}\n```\n\nThe `@Merged` attribute tells `ForkedModel` that the property is `Mergeable`, and it should use an appropriate merging algorithm. There are defaults for most common types, but you can override this by passing a different merge algorithm to the `using:` parameter. \n\nIf you have a custom `Mergeable` type, like `AccumulatingInt`, applying `@Merged` will cause it to merge using the `merged(withSubordinate:commonAncestor:)` method you provided. \n\nProperties without `@Merged` attached will be merged atomically, with a more recent change taking precedence over an older one. Properties will be merged in a property-wise manner, based on the most recent change to the property itself\n\n## Sample Code\n\nA good way to get started with `Forked` is to take a look at the sample apps provided. They range in difficulty from very basic, to a fully-functional iCloud-based Contacts app. \n\n##### [A Race of Actors](https://github.com/drewmccormack/Forked/tree/main/Samples/A%20Race%20of%20Actors)\nActors solve the problem of data races in Swift very well, but they don't help at all with race conditions, and can even give rise to new ones. This sample shows you can use a `ForkedResource` inside of an actor to deal with race conditions in a straightforward way.\n\n##### [Forked Model](https://github.com/drewmccormack/Forked/tree/main/Samples/Forked%20Model)\nSets up a simple mergeable model similar to the ones above. The UI allows you to change the values of text and a counter in two different forks, and pressing a button you see how they get merged.\n\n##### [Forking Simple iCloud](https://github.com/drewmccormack/Forked/tree/main/Samples/Forking%20Simple%20iCloud)\nThe model in this sample is extremely simple, and is secondary in importance to how you setup the `CloudKitExchange` to sync data with iCloud. The sample shows how you can use a `ForkedResource` for storage on disk, update a property for display in SwiftUI, and monitor changes to forks in order to refresh the UI when changes arrive from iCloud.\n\n##### [Forkers](https://github.com/drewmccormack/Forked/tree/main/Samples/Forkers)\nForkers is a contacts app for keeping track of your favorite forkers. The model is more complex than the other samples, showing how you can nest `Mergeable` types, in this case with an `Array` of your contacts. It also integrates with iCloud, giving a fully-functional, local-first contacts app.\n\n## Docs\n\nDocumentation is available for each subpackage.\n\n##### [Forked](https://drewmccormack.github.io/Forked/Forked/documentation/forked)\nThis is the core package, and needed to use any of the other packages. It provides `ForkedResource`, which is the basic building block of `Forked`.\n\n##### [ForkedMerge](https://drewmccormack.github.io/Forked/ForkedMerge/documentation/forkedmerge)\nThis package provides the standard merging algorithms for `Mergeable` types. It also includes a number of Conflict-Free Replicated Data Types (CRDTs).\n\n##### [ForkedModel](https://drewmccormack.github.io/Forked/ForkedModel/documentation/forkedmodel)\nThis package provides the `@ForkedModel` and `@Merged` macros, which allow you to define a global data model using value types.\n\n##### [ForkedCloudKit](https://drewmccormack.github.io/Forked/ForkedCloudKit/documentation/forkedcloudkit)\nThis provides the `CloudKitExchange` class, which automatically syncs a `ForkedResource` between devices with iCloud.\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a pull request or open an issue.\n\n## License\n\nForked is available under the MIT license. See the LICENCE file for more info.\n\n[^goodplace]: \"Forking\" jokes are inspired by [The Good Place](https://en.wikipedia.org/wiki/The_Good_Place). Go watch it!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrewmccormack%2FForked","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdrewmccormack%2FForked","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrewmccormack%2FForked/lists"}