SwiftUI TextField with ViewModel not working

105 Views Asked by At

For a personal project, I am trying to set up an app according MVVM with Aviation Weather Data. I have a SearchBarView with a connected SearchBarViewModel. In this SearchBarViewModel, there is only one @Published var which is the Airport code (Two-way binded with the SearchBarView). I want to share this property with a class to make a network request. It seems that there is something wrong between the SearchBarView and the SearchBarViewModel, but I cannot find the problem...

import SwiftUI

struct SearchBarView: View {
    
    @ObservedObject var viewModel = SearchBarViewModel()
    
    var body: some View {
        HStack {
            Button {
                //Locate nearest airport with CoreLocation.
            } label: {
                Image(systemName: "location.circle")
                    .resizable()
                    .frame(width: 30, height: 30)
                    .foregroundColor(.primary)
            }
            
            TextField("Enter ICAO Code", text: $viewModel.airportCode)
                .padding(.horizontal)
                .padding(.vertical, 8)
                .background(Color.gray.opacity(0.2))
                .cornerRadius(10)
        }
    }
}

------------------------------------

import Foundation
import SwiftUI

final class SearchBarViewModel: ObservableObject {
    @Published var airportCode = ""
}

When I type something in the TextField in the ContentView, The SearchBarViewModel is not "updated" and does not publish anything...`

1

There are 1 best solutions below

0
On BEST ANSWER

SwiftUI is already MVVM you don't need your own objects, that is what @State is for. So it's just:

struct SearchBarView: View {
    @State var airportCode = ""

SwiftUI will take your hierarchy of View data structs, diff it, then use the difference in the data to generate the V layer automatically for you choosing the best UIKit, AppKit etc. objects depending on the platform/context.

When passing data down to child Views - pass down let for read access or @Binding var for read/write. In both cases body is called when the values change, think of body like an "onChange". The result is then diffed.

You can put your funcs for logic anywhere you like and make them return values you set on @State. Or you can group vars together into a custom struct and add a mutating func there. But since this is Swift just try to avoid classes when you can or you'll run into consistency issues/memory leaks since its tricky to get right, as you experienced.