wasi
wasi

Reputation: 11

How to use view and data framework in swift clean architecture

I'm developing an app with Swift and SwifUI where GoogleCast framework is needed only for one flavor.

Imagine the view where I instantiate the GoogleCastButton:

import SwiftUI
import GoogleCast

struct NavBarView: View {
    @ObservedObject private var viewModel: NavBarViewModel
    
    init(viewModel: NavBarViewModel) {
        self.viewModel = viewModel
    }
    
    var body: some View {
        HStack {
            Text("Home")

//other view stuff...

            if viewmodel.showCastButton {
                GCKUICastButton()
            }
        }
    }
}

With the following approach, I have to use conditional compilation (#if flavourA) in the view to reuse the rest of the view and is something I don't wanna do because the app can become very complicated

I need to find a way in which I can inject a view interface or something like that in my composition root depending on the flavor but I'm not sure if this is the correct approach using MVVM + clean architecture and if so I'm not sure on how to do it properly

On the other hand, if I follow the dependency injection approach that I mentioned I will need to implement a FakeGoogleCastButton: GoogleCastButton which returns an empty view no?

If yes I would also need to create a FakeGoogleCastService: CastService to use in the data layer?

I have the feeling that I am not understanding how to correctly apply the principles of clean architecture in this case.

Maybe there is another type of interface adapter for this situation?

I created a CastRepository and a CastService interfaces to use the Google Cast data in my use cases but I don't know how I should approach the UI

Upvotes: 0

Views: 285

Answers (2)

LiLi Kazine
LiLi Kazine

Reputation: 223

  1. Create a @State or @Observable struct/class as data source of your view
  2. Create an Interactor, maybe hide the implementation behind a protocol for mocking ability. The View should hold a this interactor either by DI or directly passing, and send actions(user inputs or life cycle events) and bindable reference of your data source to the interactor.
  3. Your interactor handle actions and make changes on the bindable data source, which would eventually case View updates. If the logic requires networking or db transation, those belong to Repository layer.

ref: clean-architecture-swiftui

Upvotes: -1

malhal
malhal

Reputation: 30746

In SwiftUI the View struct already is the view model, thus it already is MVVM and already is clean. SwiftUI automatically manages the UI layer (i.e. UIKit UIView objects) from these View struct lightweight descriptions and keeps them up to date. If you try to code view model objects on top you'll just run into major issues so it's best to just learn the View struct, and how body is called when ever any @State var, @Binding var or let changes. You'll also need to use computed vars to transform data as you pass it in to a child View. If you want to group related vars with testable logic you can make a @State custom struct but have to learn about mutating func.

For your service, usually those are EnvironmentKey structs, that way they can be easily replaced with a mock for Previews. Usually they have an async func you can call from .task that returns results you can store in @State. See Apple's @Environment(\.authorizationController) for an example.

Upvotes: 1

Related Questions