{"id":19688554,"url":"https://github.com/maxvol/raspswift","last_synced_at":"2025-04-29T08:34:42.491Z","repository":{"id":89974805,"uuid":"94243242","full_name":"maxvol/RaspSwift","owner":"maxvol","description":"Reactive Aggregate State Pipeline (based on RxSwift)","archived":false,"fork":false,"pushed_at":"2021-06-30T14:01:49.000Z","size":1276,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-05T14:04:11.719Z","etag":null,"topics":["ios","macos","reactive","reactive-extensions","reactive-programming","rxswift","swift","unidirectional","unidirectional-data-flow","unidirectional-dataflow"],"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/maxvol.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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-06-13T18:12:12.000Z","updated_at":"2022-04-28T11:27:15.000Z","dependencies_parsed_at":"2023-05-30T18:00:12.457Z","dependency_job_id":null,"html_url":"https://github.com/maxvol/RaspSwift","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxvol%2FRaspSwift","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxvol%2FRaspSwift/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxvol%2FRaspSwift/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maxvol%2FRaspSwift/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maxvol","download_url":"https://codeload.github.com/maxvol/RaspSwift/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251465210,"owners_count":21593842,"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":["ios","macos","reactive","reactive-extensions","reactive-programming","rxswift","swift","unidirectional","unidirectional-data-flow","unidirectional-dataflow"],"created_at":"2024-11-11T18:38:19.447Z","updated_at":"2025-04-29T08:34:42.482Z","avatar_url":"https://github.com/maxvol.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# README #\n\nNB: refer to `rxswift` branch for a working version, this branch is under development.\n\n**RASP - Reactive Aggregate State Pipeline**\n\nThis Unidirectonal Data Flow framework is inspired by Redux and built on top op RxSwift framework.\nComparing to other similar frameworks, it is exceptionally simple by design and requires the very minimum of boilerplate code.\n\nhttps://medium.com/p/5a690383e18e\n\nRather than declaring plenty of Publish/BehaviorSubjects and grouping them ad hoc by *.combineLatest()*,\non many occasions it makes more sense to group state values together by domain, such as:\n\n* REST/HATEOAS state with recent links\n* geolocation state\n* UI navigation state\n* lifecycle state\n* motion state\n* data state\n* etc.\n\nThat way, at every value update you have a _complete snapshot_ of what is going on in particular domain.\nBesides, there is only one way to update domain state, namely by sending event to the state aggregator - this ensures _consistency_ of every state snapshot.\n\n![alt text](https://github.com/maxvol/RaspSwift/blob/master/rasp.png \"Diagram\")\n\nHow it works (per domain state):\n\n* events from different sources are gathered into a single stream with *.merge()* operator\n* on every event from the combined stream, it gets applied (reduced) to the current state via *.scan()* operator\n* consumers can subscribe to domain state as a whole or to a single field by using selector which is *.map().distinctUntilChanged()*\n\nFor example, combining independently updated heading and location into one consistent geolocation state snapshot would look as follows -\n\n![alt text](https://github.com/maxvol/RaspSwift/blob/master/rasp-geo.png \"Diagram\")\n\n...where events and state are defined as follows:\n\n**NB this documentation is for v1 using RxSwift, while v2 is using Combine** \n\n```swift\nstruct GeoEvent: RaspEvent {\n    case heading(heading)\n    case location(Location)\n}\n\nstruct GeoState: RaspState {\nvar heading: Heading\n    var location: Location\n\n    mutating func apply(event: RaspEvent) {\n        switch event {\n        case let geoEvent as GeoEvent:\n            switch event {\n            case .heading(let heading):\n                self.heading = heading\n            case .location(let location):\n                self.location = location\n            }\n        default:\n            break\n        }\n    }\n}\n```\nA more generic example of usage:\n\n```swift\nstruct MyRestState: RaspState {\n    var links: [String: String] = [:]\n\n    mutating func apply(event: RaspEvent) {\n        switch event {\n        case let event1 as Event1:\n            self.links[\"\\(event1.value)\"] = \"\\(event1.value)\"\n        default:\n        break\n        }\n    }\n}\n\nstruct Event1: RaspEvent {\n    let value: Int\n}\n\nlet scheduler = SerialDispatchQueueScheduler(qos: .default)\nlet event1 = Observable\u003cInt\u003e.interval(0.3, scheduler: scheduler).take(5).map { value in\n    return Event1(value: value)\n}\n\nlet scheduler2 = SerialDispatchQueueScheduler(qos: .default)\nlet event2 = Observable\u003cInt\u003e.interval(0.5, scheduler: scheduler2).take(5).map { value in\n    return Event1(value: value)\n}\n\nlet myRestAggregator = RaspAggregator\u003cMyRestState\u003e(initial: MyRestState(), sources: event1.asRaspEvent(), event2.asRaspEvent())\n\nmyRestAggregator.state.subscribe( onNext: { state in\n    print(\"\\(state)\")\n}).disposed(by: self.disposeBag)\n\nlet sel1 = RaspSelector\u003cMyRestState, Int\u003e { state -\u003e Int in\n    let value: Int = state.links.count\n    return value\n}\n\nlet one1 = myRestAggregator.select(selector: sel1)\n\none1.subscribe( onNext: { field in\n    print(\"\\(field)\")\n}).disposed(by: self.disposeBag)\n```\n\n* Version\n\n2.0.0\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxvol%2Fraspswift","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaxvol%2Fraspswift","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaxvol%2Fraspswift/lists"}