{"id":17338648,"url":"https://github.com/dreymonde/alba","last_synced_at":"2025-06-14T11:05:35.304Z","repository":{"id":64060158,"uuid":"74279324","full_name":"dreymonde/Alba","owner":"dreymonde","description":"🎙 Stateful event observing engine [DEPRECATED]","archived":false,"fork":false,"pushed_at":"2020-12-06T14:59:52.000Z","size":84,"stargazers_count":19,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-06-08T21:47:34.136Z","etag":null,"topics":["functional-reactive-programming","swift"],"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/dreymonde.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}},"created_at":"2016-11-20T14:22:43.000Z","updated_at":"2021-06-03T18:41:19.000Z","dependencies_parsed_at":"2022-12-01T20:09:48.018Z","dependency_job_id":null,"html_url":"https://github.com/dreymonde/Alba","commit_stats":null,"previous_names":[],"tags_count":36,"template":false,"template_full_name":null,"purl":"pkg:github/dreymonde/Alba","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dreymonde%2FAlba","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dreymonde%2FAlba/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dreymonde%2FAlba/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dreymonde%2FAlba/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dreymonde","download_url":"https://codeload.github.com/dreymonde/Alba/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dreymonde%2FAlba/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259804867,"owners_count":22913902,"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":["functional-reactive-programming","swift"],"created_at":"2024-10-15T15:38:56.287Z","updated_at":"2025-06-14T11:05:35.273Z","avatar_url":"https://github.com/dreymonde.png","language":"Swift","readme":"# Alba\n\n**Alba** is a tiny yet powerful library which allows you to create sophisticated, decoupled and complex architecture using functional-reactive paradigms. **Alba** is designed to work mostly with reference semantics instances (classes).\n\n## Usage\n\n#### Create publisher\n\n```swift\nlet publisher = Publisher\u003cUUID\u003e()\npublisher.publish(UUID())\n```\n\n#### Subscribing\n\nIn order to subscribe, you should use `Subscribe` instances. The easiest way to get them is by using `.proxy` property on publishers:\n\n```swift\nfinal class NumbersPrinter {\n    \n    init(numbersPublisher: Subscribe\u003cInt\u003e) {\n        numbersPublisher.subscribe(self, with: NumbersPrinter.print)\n    }\n    \n    func print(_ uuid: Int) {\n        print(uuid)\n    }\n    \n}\n\nlet printer = NumbersPrinter(numbersPublisher: publisher.proxy)\npublisher.publish(10) // prints \"10\"\n```\n\nIf you're surprised by how `NumbersPrinter.print` looks - that's because this allows **Alba** to do some interesting stuff with reference cycles. Check out the [implementation](https://github.com/dreymonde/Alba/blob/master/Sources/Proxy.swift#L52) for details.\n\n#### That functional things\n\nThe cool thing about publisher proxies is the ability to do interesting things on them, for example, filter and map:\n\n```swift\nlet stringPublisher = Publisher\u003cString\u003e()\n\nfinal class Listener {\n    \n    init(publisher: Subscribe\u003cString\u003e) {\n        publisher\n            .flatMap({ Int($0) })\n            .filter({ $0 \u003e 0 })\n            .subscribe(self, with: Listener.didReceive)\n    }\n    \n    func didReceive(positiveNumber: Int) {\n        print(positiveNumber)\n    }\n    \n}\n\nlet listener = Listener(publisher: stringPublisher.proxy)\nstringPublisher.publish(\"14aq\") // nothing\nstringPublisher.publish(\"-5\")   // nothing\nstringPublisher.publish(\"15\")   // prints \"15\"\n```\n\nCool, huh?\n\n#### Lightweight observing\n\n```swift\nlet publisher = Publisher\u003cInt\u003e()\npublisher.proxy.listen { (number) in\n    print(number)\n}\npublisher.publish(112) // prints \"112\"\n```\n\nBe careful with `listen`. Don't prefer it over `subscribe` as it can introduce memory leaks to your application.\n\n### Writing your own `Subscribe` extensions\n\nIf you want to write your own `Subscribe` extensions, you should use `rawModify` method:\n\n```swift\npublic func rawModify\u003cOtherEvent\u003e(subscribe: (ObjectIdentifier, EventHandler\u003cOtherEvent\u003e) -\u003e (), entry: @autoclosure @escaping ProxyPayload.Entry) -\u003e Subscribe\u003cOtherEvent\u003e\n```\n\nHere is, for example, how you can implement `map`:\n\n```swift\npublic extension Subscribe {\n    \n    public func map\u003cOtherEvent\u003e(_ transform: @escaping (Event) -\u003e OtherEvent) -\u003e Subscribe\u003cOtherEvent\u003e {\n        return rawModify(subscribe: { (identifier, handle) in\n            let handler: EventHandler\u003cEvent\u003e = { event in\n                handle(transform(event))\n            }\n            self._subscribe(identifier, handler)\n        }, entry: .transformation(label: \"mapped\", .transformed(fromType: Event.self, toType: OtherEvent.self)))\n    }\n    \n}\n```\n\n`entry` here is purely for debugging purposes -- you're describing the intention of your method.\n\n### Inform Bureau\n\nOne of the main drawbacks of the functional-reactive style is an elevated level of indirection -- you can't easily detect the information flow in your application. **Alba** aims to solve this problem with the help of a handy feature called *Inform Bureau*. Inform Bureau collects information about every subscription and publishing inside your application, so it's easy for you to detect what's actually going on (and to detect any problems easily, of course).\n\n#### Enabling Inform Bureau\n\nInform Bureau is an optional feature, so it should be enabled in your code in order to work. It's actually just one line of code -- make sure to put this in your `AppDelegate`'s `init` (`application(_:didFinishLaunchingWithOptions)` is too late):\n\n```swift\nAlba.InformBureau.isEnabled = true\n```\n\nJust this line will no have any immediate effect -- in order for Inform Bureau to become useful, you should also enable it's `Logger`:\n\n```swift\nAlba.InformBureau.Logger.enable()\n```\n\nAnd that's it! Now you're going to see beautiful messages like these in your output:\n\n```\n(S) ManagedObjectContextObserver.changes (Publisher\u003c(NSChangeSet, Int)\u003e)\r\n(S) --\u003e mapped from (NSChangeSet, Int) to NSSpecificChangeSet\u003cCleaningPoint\u003e\r\n(S) !-\u003e subscribed by PointsListViewController:4929411136\n```\n\n```\n(S) +AppDelegate.applicationWillTerminate (Publisher\u003cUIApplication\u003e)\r\n(S) --\u003e mapped from UIApplication to ()\r\n(S) merged with:\r\n(S)    +AppDelegate.applicationDidEnterBackground (Publisher\u003cUIApplication\u003e)\r\n(S)    --\u003e mapped from UIApplication to ()\r\n(S) !-\u003e subscribed by ContextSaver:6176536960\n```\n\n```\n(P) ContextSaver.didSaveContext (Publisher\u003c()\u003e) published ()\n```\n\n*Hint*: `(S)` are subscriptions events, and `(P)` are publications.\n\n#### Getting your code ready for Inform Bureau\n\nInform Bureau can be enabled with two lines of code. However, in order for it to be useful, there is a little amount of work required from you. First and foremost, you should create all your publishers with descriptive `label`:\n\n```swift\nlet didFailToSaveImage = Publisher\u003cError\u003e(label: \"ImageSaver.didFailToSaveImage\")\n```\n\nYou should name your publishers using the next convention: `[type_name].[publisher_name]`\n\nIf your publisher is declared as `static`, then add `+` to the beginning:\n\n```swift\nstatic let applicationWillTerminate = Publisher\u003cUIApplication\u003e(label: \"+AppDelegate.applicationWillTerminate\")\n```\n\n#### OSLogger\n\n**Alba**'s Inform Bureau takes full advantage of Apple's latest [Unified Logging][unified-logging-wwdc] system. The support for this system comes via `Alba.OSLogger` object. If you want your app to write **Alba** logs via `os_log`, enable it after enabling `InformBureau`:\n\n```swift\nAlba.InformBureau.isEnabled = true\nAlba.OSLogger.enable()\n```\n\nIn order for `os_log` to work, you should also do [this](http://stackoverflow.com/a/40744462/5569590).\n\nNow you can see **Alba** logs of your program in a **Console** application.\n\n## Installation\n\n**Alba** is available through [Carthage][carthage-url]. To install, just write into your Cartfile:\n\n```ruby\ngithub \"dreymonde/Alba\" ~\u003e 0.3.3\n```\n\nYou can also use SwiftPM. Just add to your `Package.swift`:\n\n```swift\nimport PackageDescription\n\nlet package = Package(\n    dependencies: [\n        .Package(url: \"https://github.com/dreymonde/Alba.git\", majorVersion: 0, minor: 3),\n    ]\n)\n```\n\n## Contributing\n\n**Alba** is in early stage of development and is opened for any ideas. If you want to contribute, you can:\n\n- Propose idea/bugfix in issues\n- Make a pull request\n- Review any other pull request (very appreciated!), since no change to this project is made without a PR.\n\nActually, any help is welcomed! Feel free to contact us, ask questions and propose new ideas. If you don't want to raise a public issue, you can reach me at [dreymonde@me.com](mailto:dreymonde@me.com).\n\n[carthage-url]: https://github.com/Carthage/Carthage\n[unified-logging-wwdc]: https://developer.apple.com/videos/play/wwdc2016/721/\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdreymonde%2Falba","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdreymonde%2Falba","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdreymonde%2Falba/lists"}