{"id":22754760,"url":"https://github.com/klundberg/copyonwrite","last_synced_at":"2025-08-09T21:11:51.718Z","repository":{"id":56906631,"uuid":"82633712","full_name":"klundberg/CopyOnWrite","owner":"klundberg","description":"μframework encapsulating the `CopyOnWrite` type, to make implementing value semantics easy!","archived":false,"fork":false,"pushed_at":"2021-08-24T07:13:47.000Z","size":47,"stargazers_count":8,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-11-18T03:38:27.825Z","etag":null,"topics":["swift","value-semantics"],"latest_commit_sha":null,"homepage":null,"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/klundberg.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-02-21T03:53:18.000Z","updated_at":"2022-02-02T15:27:09.000Z","dependencies_parsed_at":"2022-08-21T03:20:54.165Z","dependency_job_id":null,"html_url":"https://github.com/klundberg/CopyOnWrite","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klundberg%2FCopyOnWrite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klundberg%2FCopyOnWrite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klundberg%2FCopyOnWrite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/klundberg%2FCopyOnWrite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/klundberg","download_url":"https://codeload.github.com/klundberg/CopyOnWrite/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":229181164,"owners_count":18032586,"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":["swift","value-semantics"],"created_at":"2024-12-11T06:18:58.439Z","updated_at":"2024-12-11T06:19:07.456Z","avatar_url":"https://github.com/klundberg.png","language":"Swift","readme":"# CopyOnWrite\nμframework encapsulating the `CopyOnWrite` type, to make implementing value semantics easy!\n\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fklundberg%2FCopyOnWrite%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/klundberg/CopyOnWrite)\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fklundberg%2FCopyOnWrite%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/klundberg/CopyOnWrite)\n\n[![codecov.io](https://img.shields.io/codecov/c/github/klundberg/CopyOnWrite.svg)](http://codecov.io/github/klundberg/CopyOnWrite)\n[![License](https://img.shields.io/cocoapods/l/CopyOnWrite.svg?style=flat)](http://cocoapods.org/pods/CopyOnWrite)\n\n\n## Why do you need it?\n\nCopyOnWrite is a μframework that provides an abstraction for implementing value-semantics in your structs that contain reference types.\n\nValue semantics is a concept that applies to types where you cannot affect the value of one variable by mutating another variable. Any attempts to change the state of some variable will remain local to that variable. For example, the `String` type is a value type (a struct) with value semantics:\n\n```swift\nvar s1 = \"Foo\"\nvar s2 = s1\ns1.append(\"Bar\")\nprint(s1) // FooBar\nprint(s2) // Foo\n```\n\nThe act of assigning `s1` to `s2` effectively makes a copy of the contents of `s1` and stores that copy into `s2` so that changes to one are isolated from the other, which is the critical detail that defines a type as having value semantics.\n\nHowever, just because `String` is a struct doesn't mean it gets value semantics for free. You must deliberately and intentionally implement this behavior if your struct types contain references inside them, like `String` does. Consider this example:\n\n```swift\nclass Foo {\n  var num: Int = 0\n}\n\nstruct Bar {\n  private var storage = Foo()\n\n  var num: Int {\n    return storage.num\n  }\n\n  func update(_ num: Int) {\n    storage.num = num\n  }\n}\n\nvar bar1 = Bar()\nvar bar2 = bar1\nbar1.update(100)\nprint(bar1.num) // 100\nprint(bar2.num) // 100\n```\n\nSince the `Bar` struct contains a reference type, the reference is copied when assigning bar1 to bar2, but the instance in memory remains the same, so altering the properties of that instance will affect all references to that instance, which may be unexpected behavior for those who use your types. For more in-depth details, read [this post](http://www.klundberg.com/blog/encapsulating-value-semantics) and check out the resources it references.\n\nYou can use this `CopyOnWrite` library to easily implement value semantics so that mutating reference types within your structs will keep those mutations local to the specific variable that struct is stored in, and not any other places the struct may have been stored.\n\n## How does it work?\n\nAltering the prior example in the following way will allow the `Bar` type to have value semantics:\n\n```swift\nclass Foo {\n  var num: Int = 0\n}\n\nstruct Bar {\n  private var storage = CopyOnWrite(reference: Foo(), copier: {\n    let new = Foo()\n    new.num = $0.num\n    return new\n  })\n\n  var num: Int {\n    return storage.reference.num\n  }\n\n  mutating func update(_ num: Int) {\n    storage.mutatingReference.num = num\n  }\n}\n\nvar bar1 = Bar()\nvar bar2 = bar1\nbar1.update(100)\nprint(bar1.num) // 100\nprint(bar2.num) // 0\n```\n\n`CopyOnWrite`'s primary initializer takes two parameters: the object to wrap, and a copier closure that is called only if it determines that it needs to make a copy to preserve value semantics. If you only have one reference that points to the internal reference wrapped by `CopyOnWrite`, the closure does not need to be called, and it will simply update the reference directly as an optimization. As soon as more than one value references the internal storage, `CopyOnWrite` will detect that fact and run the copy closure before mutating the reference:\n\n```swift\nvar bar3 = Bar()\nbar3.update(42) // only one reference, no copy made\nvar bar4 = bar3\nbar3.update(1024) // two references to Bar's internal storage in bar3 and bar4, so run the copy closure before making the change\nbar3.update(0) // bar3 has a unique reference now after the previous copy, so it will not copy again\n```\n\n## Usage\n\n### Reference access\n\nOnce you initialize a copy on write value, you can access the internal reference with two properties:\n\n* `reference` - intended for immutable operations: any property or method that does not change the observable state of your stored reference type can be safely called through this property.\n* `mutatingReference` - intended for mutable operations. Anything that does change the observable state of the reference must be called through this property.\n\nAs you can see in the example above, the `update` method on `Bar` was changed to be `mutating`. This is because simply accessing `mutatingReference` may cause the reference in `CopyOnWrite` to be reassigned, and so anything that references this property must also be marked mutating which helps guarantee that you cannot accidentally change a value within your reference type if your struct is stored in a `let` constant.\n\n### Caveats\n\nHowever, it is your responsibility to call the right property at the right time. There is nothing the compiler or this type can do to stop you from doing this:\n\n```swift\nfunc update(_ num: Int) {\n  storage.reference.num = num\n}\n```\n\nWhile this code will compile, it will cause the original issue to resurface, where calling `update` on one variable will affect the value stored in another.\n\nIt is also strongly recommended that your copy on write instance variables be private, so that external clients of your API don't do the wrong thing if they try to access it directly. Provide functions/properties that expose the functionality you want, backed by the referenced object instead.\n\n### Convenience initializers\n\nThis library also provides a protocol you can choose to conform to, in case you find yourself duplicating the same copy closure in many places throughout your types:\n\n```swift\npublic protocol Cloneable: class {\n  func clone() -\u003e Self\n}\n```\n\nIf your types can conform to that protocol, you can simply provide the reference to `CopyOnWrite` like so:\n\n```swift\nextension Foo: Cloneable {\n  func clone() -\u003e Self {\n    let new = self.init()\n    new.num = num\n    return new\n  }\n}\n\nstruct Bar {\n  private var storage = CopyOnWrite(reference: Foo())\n\n  // ...\n}\n```\n\nFor types that may conform to `NSCopying` or `NSMutableCopying`, there are convenience initializers for those as well:\n\n```swift\nCopyOnWrite(copyingReference: MyNSCopyingType())\nCopyOnWrite(mutableCopyingReference: MyNSMutableCopyingType())\n```\n\nTake care to use the correct initializer for the right type you want to store. If you use the `NSCopying` version for a type like `NSMutableString`, the `copy` method that gets called will actually create an immutable version, and when you try to call a mutating method on it you will crash your program.\n\n## Requirements\n\n* 1.0.0 is supported on Xcode 9/Swift 4\n* 0.9.0 is supported on Xcode 8.0+/Swift 3.0+\n* iOS 8+/OS X 10.9+/watchOS 2+/tvOS 9+\n\n## Installation\n\n### CocoaPods\n\n`CopyOnWrite` is available through [CocoaPods](https://cocoapods.org). To install it, simply add the following line to your Podfile:\n\n```ruby\npod 'CopyOnWrite', '~\u003e 1.0.0'\n```\n\n### Carthage\n\n`CopyOnWrite` can be integrated with [Carthage](https://github.com/Carthage/Carthage). Add the following to your `Cartfile` to use it:\n\n```\ngithub \"klundberg/CopyOnWrite\" ~\u003e 1.0.0\n```\n\n### Swift Package Manager\n\nAdd the following line to your dependencies list in your `Package.swift` file:\n\n```\n.package(url: \"https://github.com/klundberg/CopyOnWrite.git\", from: \"1.0.0\"),\n```\n\n### Manual Installation\n\nSimply copy the `CopyOnWrite.swift` file into your project.\n\n## Author\n\nKevin Lundberg, kevin at klundberg dot com\n\n## Contributions\n\nIf you have any changes you'd like to see, please feel free to open a pull request. Please include unit tests for any changes.\n\n## License\n\nCopyOnWrite is available under the MIT license. See the LICENSE file for more info.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fklundberg%2Fcopyonwrite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fklundberg%2Fcopyonwrite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fklundberg%2Fcopyonwrite/lists"}