{"id":25448081,"url":"https://github.com/hudishkin/vvsi","last_synced_at":"2025-05-16T07:09:13.708Z","repository":{"id":277865045,"uuid":"933751878","full_name":"hudishkin/vvsi","owner":"hudishkin","description":"Lightweight architecture for SwiftUI application. Something between TCA and MVVM.","archived":false,"fork":false,"pushed_at":"2025-05-05T10:27:50.000Z","size":52,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-05T11:38:16.010Z","etag":null,"topics":["architecture","ios-architecture","mvvm","swift","swiftui","tca"],"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/hudishkin.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,"zenodo":null}},"created_at":"2025-02-16T16:04:29.000Z","updated_at":"2025-05-05T10:27:52.000Z","dependencies_parsed_at":"2025-03-18T14:29:21.112Z","dependency_job_id":"f5e2ed37-ee5e-4345-a768-824f43500719","html_url":"https://github.com/hudishkin/vvsi","commit_stats":null,"previous_names":["hudishkin/vvsi"],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hudishkin%2Fvvsi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hudishkin%2Fvvsi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hudishkin%2Fvvsi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hudishkin%2Fvvsi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hudishkin","download_url":"https://codeload.github.com/hudishkin/vvsi/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254485051,"owners_count":22078767,"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":["architecture","ios-architecture","mvvm","swift","swiftui","tca"],"created_at":"2025-02-17T19:18:45.842Z","updated_at":"2025-05-16T07:09:13.700Z","avatar_url":"https://github.com/hudishkin.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# View - ViewState - Interactor (VVSI)\n\nVVSI is an experimental (very) simple architecture for SwiftUI applications, representing something between [TCA](https://github.com/pointfreeco/swift-composable-architecture) (The Composable Architecture) and [MVVM](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel). The library offers a structured and predictable approach to managing state and business logic, while maintaining ease of use.\n\n## 📊 Architecture Diagram\n\n![VVSI Architecture Diagram](scheme.png)\n\n## 🌟 Benefits\n\n- **Separation of concerns** — clear separation of UI, state, and business logic\n- **Predictable data flow** — unidirectional data flow makes the application more stable\n- **Testability** — isolated components are easy to test\n- **Out-of-the-box asynchronicity** — support for asynchronous operations using Swift Concurrency\n- **Native SwiftUI integration** — works naturally with the SwiftUI system\n\n## 📦 Installation\n\n### Swift Package Manager\n\n```swift\ndependencies: [\n    .package(url: \"https://github.com/username/VVSI.git\", from: \"0.0.5\")\n]\n```\n\n## 🚀 How to Use\n\nVVSI consists of three main components:\n\n1. **View** — responsible for UI and state display\n2. **ViewState** — stores the current state and processes actions\n3. **Interactor** — contains business logic and handles side effects\n\n### Example: Simple List\n\n#### 1. Create state and action components\n\n```swift\n// ListView+State.swift\n\nextension ListView {\n\n    struct Options {\n        let count: Int\n        let length: Int\n    }\n\n    struct VState: StateProtocol {\n        var items: [String] = []\n    }\n\n    enum VAction: ActionProtocol {\n        case add\n        case remove\n        case random(Options)\n    }\n\n    enum VNotification: NotificationProtocol {\n        case error(String)\n    }\n}\n```\n\n#### 2. Implement the interactor to handle business logic\n\n```swift\n// ListView+Interactor.swift\n\nextension ListView {\n\n    final class Interactor: ViewStateInteractorProtocol {\n\n        typealias S = VState\n        typealias A = VAction\n        typealias N = VNotification\n\n        let notifications: PassthroughSubject\u003cN, Never\u003e = .init()\n        let service: Dependencies.Service\n\n        init(dependencies: Dependencies = .shared) {\n            service = dependencies.service\n        }\n\n        @MainActor\n        func execute(\n            _ state: @escaping CurrentState\u003cS\u003e,\n            _ action: VAction,\n            _ updater: @escaping StateUpdater\u003cS\u003e\n        ) {\n            switch action {\n            case .add:\n                Task.detached { [weak self] in\n                    guard await state().items.count \u003c 5 else {\n                        self?.notifications.send(.error(\"Max items count is 5\"))\n                        return\n                    }\n\n                    await updater { state in\n                        state.items.append(\"New item\")\n                    }\n                }\n\n            case .remove:\n                Task.detached {\n                    await updater { state in\n                        if !state.items.isEmpty {\n                            state.items.removeLast()\n                        }\n                    }\n                }\n            case .random(let opt):\n                Task {\n                    let strings = (0..\u003copt.count).map { _ in randomString(length: opt.length) }\n                    await updater { state in\n                        state.items = strings\n                    }\n                }\n            }\n        }\n    }\n}\n```\n\n#### 3. Create a view that uses ViewState\n\n```swift\n// ListView.swift\n\nstruct ListView: View {\n\n    enum AlertType: Identifiable {\n        var id: String { \"\\(self)\" }\n        case error(String)\n    }\n\n    @StateObject\n    var viewState = ViewState(.init(), Interactor())\n\n    @State\n    private var alertType: AlertType? = nil\n\n    var body: some View {\n        ForEach(viewState.state.items, id: \\.self) { item in\n            Text(item)\n        }\n\n        HStack {\n            Button {\n                viewState.trigger(.add)\n            } label: {\n                Text(\"Add\")\n            }\n\n            Button {\n                viewState.trigger(.remove)\n            } label: {\n                Text(\"Remove\")\n            }\n            Button {\n                viewState.trigger(.random(.init(count: Int.random(in: 1..\u003c10), length: Int.random(in: 1..\u003c5))))\n            } label: {\n                Text(\"Random\")\n            }\n        }\n        .alert(item: $alertType) { item in\n            switch item {\n            case .error(let error):\n                Alert(\n                    title: Text(\"Error\"),\n                    message: Text(error),\n                    dismissButton: .default(Text(\"Ok\"))\n                )\n            }\n        }\n        .onReceive(viewState.notifications) { notification in\n            switch notification {\n            case .error(let message):\n                alertType = .error(message)\n            }\n        }\n    }\n}\n```\n\n## 🧩 Core Concepts\n\n- **StateProtocol** — protocol for state structures\n- **ActionProtocol** — protocol for action enumerations\n- **NotificationProtocol** — protocol for notifications from interactor to view\n- **ViewStateInteractorProtocol** — protocol for interactors handling business logic\n\n## 📚 Complete Example\n\nA complete implementation example is available in the [VVSIExample](/VVSIExample) directory.\n\n## 📄 License\n\nThis project is released under the MIT license. See [LICENSE](/LICENSE) for details.\n\n## 👥 Contributors\n\nWe welcome your suggestions and contributions to the project! Create Issues and Pull Requests.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhudishkin%2Fvvsi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhudishkin%2Fvvsi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhudishkin%2Fvvsi/lists"}