{"id":13396592,"url":"https://github.com/slazyk/Observable-Swift","last_synced_at":"2025-03-13T23:31:33.827Z","repository":{"id":55140501,"uuid":"21072915","full_name":"slazyk/Observable-Swift","owner":"slazyk","description":"KVO for Swift - Value Observing and Events","archived":false,"fork":false,"pushed_at":"2019-03-27T16:17:25.000Z","size":106,"stargazers_count":1236,"open_issues_count":10,"forks_count":112,"subscribers_count":33,"default_branch":"master","last_synced_at":"2024-10-29T18:05:50.021Z","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/slazyk.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":"2014-06-21T16:25:26.000Z","updated_at":"2024-10-28T23:58:18.000Z","dependencies_parsed_at":"2022-08-14T13:10:57.371Z","dependency_job_id":null,"html_url":"https://github.com/slazyk/Observable-Swift","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slazyk%2FObservable-Swift","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slazyk%2FObservable-Swift/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slazyk%2FObservable-Swift/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/slazyk%2FObservable-Swift/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/slazyk","download_url":"https://codeload.github.com/slazyk/Observable-Swift/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243500090,"owners_count":20300743,"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":[],"created_at":"2024-07-30T18:00:57.616Z","updated_at":"2025-03-13T23:31:33.480Z","avatar_url":"https://github.com/slazyk.png","language":"Swift","funding_links":[],"categories":["Extensions","Architecture and State"],"sub_categories":[],"readme":"# Value Observing and Events for Swift\n\nSwift lacks the powerful Key Value Observing (KVO) from Objective-C. But thanks to closures, generics and property observers, in some cases it allows for far more elegant observing. You have to be explicit about what can be observed, though.\n\n## Overview\n\nObservable-Swift is a Swift library for value observing (via explicit usage of `Observable\u003cT\u003e`) and subscribable events (also explicit, using `Event\u003cT\u003e`). While it is not exactly \"KVO for Swift\" (it is explicit, there are no \"Keys\", ...) it is a catchy name so you can call it that if you want. The library is still under development, just as Swift is. Any contributions, both in terms of suggestions/ideas or actual code are welcome.\n\nObservable-Swift is brought to you by Leszek Ślażyński (slazyk), you can follow me on [twitter](https://twitter.com/slazyk) and [github](https://github.com/slazyk). \nAlso check out [SINQ](https://github.com/slazyk/SINQ) my other Swift library that makes working with collections a breeze.\n\n### Observables\n\nUsing `Observable\u003cT\u003e` and related classes you can implement wide range of patterns using value observing. Some of the features: \n\n- observable variables and properties\n- chaining of observables (a.k.a. key path observing)\n- short readable syntax using `+=`, `-=`, `\u003c-`/`^=`, `^`\n- alternative syntax for those who dislike custom operators\n- handlers for _before_ or _after_ the change\n- handlers for `{ oldValue:, newValue: }` `(oldValue, newValue)` or `(newValue)`\n- adding multiple handlers per observable\n- removing / invalidating handlers\n- handlers tied to observer lifetime\n- observable mutations of value types (structs, tuples, ...)\n- ~~conversions from observables to underlying type~~ (not available since Swift Beta 6)\n- observables combining other observables\n- observables as value types or reference types\n- ...\n\n### Events\n\nSometimes, you don’t want to observe for value change, but other significant events.\nUnder the hood `Observable\u003cT\u003e` uses `beforeChange` and `afterChange` of `EventReference\u003cValueChange\u003cT\u003e\u003e`. You can, however, use `Event\u003cT\u003e` or `EventReference\u003cT\u003e` directly and implement other events too.\n\n## Installation\n\nYou can use either [CocoaPods](https://cocoapods.org/) or [Carthage](https://github.com/Carthage/Carthage) to install Observable-Swift.\n\nOtherwise, the easiest option to use Observable-Swift in your project is to clone this repo and add Observable-Swift.xcodeproj to your project/workspace and then add Observable.framework to frameworks for your target.\n\nAfter that you just `import Observable`.\n\n## Examples\n`Observable\u003cT\u003e` is a simple `struct` allowing you to have observable variables.\n\n```swift\n// create a Observable\u003cInt\u003e variable\nvar x = Observable(0)\n\n// add a handler\nx.afterChange += { println(\"Changed x from \\($0) to \\($1)\") }\n// without operators: x.afterChange.add { ... }\n\n// change the value, prints \"Changed x from 0 to 42\"\nx \u003c- 42\n// alternativelyL x ^= 42, without operators: x.value = 42\n```\n\nYou can, of course, have observable properties in a `class` or a `struct`:\n\n```swift\nstruct Person {\n    let first: String\n    var last: Observable\u003cString\u003e\n    \n    init(first: String, last: String) {\n        self.first = first\n        self.last = Observable(last)\n    }\n}\n    \nvar ramsay = Person(first: \"Ramsay\", last: \"Snow\")\nramsay.last.afterChange += { println(\"Ramsay \\($0) is now Ramsay \\($1)\") }        \nramsay.last \u003c- \"Bolton\"\n```\nUp to Swift Beta 5 you could implicitly convert `Observable\u003cT\u003e` to `T`, and use it in places where `T` is expected. Unfortunately Beta 6 forbids defining implicit conversions:\n```swift\nlet x = Observable(20)\n// You can use the value property ...\nlet y1 = x.value + 22\n// ... or a postfix operator ...\nlet  y2 = x^ + 22\n/// ... which has the advantage of easy chaining\nlet y3 = obj.property^.whatever^.sthElse^\n/// ... you can also use ^= instead of \u003c- for consistency with the postfix ^\n```\n\nFor value types (such as `structs` or `tuples`) you can also observe their mutations:  \n*Since `Observable` is a `struct`, ramsay in example above gets mutated too. This means, you could observe ramsay as well.*\n\n```swift\nstruct Person {\n    let first: String\n    var last: String\n    var full: String { get { return \"\\(first) \\(last)\" } }\n}\n\nvar ramsay = Observable(Person(first: \"Ramsay\", last: \"Snow\"))\n// x += { ... } is the same as x.afterChange += { ... }\nramsay += { println(\"\\($0.full) is now \\($1.full)\") }\nramsay.value.last = \"Bolton\"\n```\n\nYou can remove observers by keeping the subscription object:\n\n```swift\nvar x = Observable(0)    \nlet subscr = x.afterChange += { (_,_) in println(\"changed\") }\n// ...\nx.afterChange -= subscr\n// without operators: x.afterChange.remove(subscr)\n```\n\nInvalidating it:\n\n```swift\nvar x = Observable(0)    \nlet subscr = x.afterChange += { (_,_) in println(\"changed\") }\n// ...\nsubscr.invalidate() // will be removed next time event fires\n```\n\nOr tie the subscription to object lifetime:\n\n```swift\nvar x = Observable(0)        \nfor _ in 0..1 {\n    let o = NSObject() // in real-world this would probably be self\n    x.afterChange.add(owner: o) { (oV, nV) in println(\"\\(oV) -\u003e \\(nV)\") }\n    x \u003c- 42 // handler called\n} // o deallocated, handler invalidated\nx \u003c- -1 // handler not called\n```\n\nYou can also chain observables (observe \"key paths\"):\n```swift\nclass Person {\n    let firstName: String\n    var lastName: Observable\u003cString\u003e\n    var friend: Observable\u003cPerson?\u003e = Observable(nil)\n\t// init(...) { ... }\n}\n\nlet me = Person()\nvar myFriendsName : String? = nil\n\n// we want to observe my current friend last name\n// and get notified with name when the friend or the name changes\nchain(me.friend).to{$0?.lastName}.afterChange += { (_, newName) in\n\tmyFriendsName = newName\n}\n\n// alternatively, we can do the same with '/' operator\n(me.friend / {$0?.lastName}).afterChange += { (_, newName) in\n\tmyFriendsName = newName\n}\n```\n\n`Event\u003cT\u003e` is a simple `struct` allowing you to define subscribable events. `Observable\u003cT\u003e` uses `EventReference\u003cValueChange\u003cT\u003e\u003e` for `afterChange` and `beforeChange`.\n\n```swift\nclass SomeClass {\n \t// defining an event someone might be interested in\n \tvar somethingChanged = Event\u003cString\u003e()\n \n \t// ...\n \n \tfunc doSomething() {\n \t\t// ...\n \t\t// fire the event and notify all observers\n \t\tsomethingChanged.notify(\"Hello!\")\n \t\t// ...\n \t}\n}\n\nvar obj = SomeClass()\n\n// subscribe to an event\nobj.somethingChanged += { println($0) }\n\nobj.doSomething()\n```\n\nMore examples can be found in tests in `ObservableTests.swift`\n\n## Advanced\n\nIf you require observables as reference types, you can use either `ObservableProxy` which is a reference type in between your code and the real `Observable` value type. You can also use `ObservableReference` which is a `ObservableProxy` to an `Observable` that it holds on a property.\n\nSame is true for `Event`, there is `EventReference` as well. Actually, `Observable` uses `EventReference` instead of `Event`, otherwise some use cases would be difficult to implement. This means, that if you want to unshare events and subscriptions you need to call `observable.unshare(removeSubscriptions:)`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslazyk%2FObservable-Swift","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fslazyk%2FObservable-Swift","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fslazyk%2FObservable-Swift/lists"}