ios – Utilizing @Binding in ViewModel – Completely different behaviour between @ObservedObject and @StateObject

0
4
ios – Utilizing @Binding in ViewModel – Completely different behaviour between @ObservedObject and @StateObject


Nonetheless being fairly new to SwiftUI I wrestle to grasp if to make use of @Binding in views solely or additionally in ViewModel lessons. Whereas there are a number of sources, which describe, that @Binding is meant for use solely in Views, there are a number of different sources and examples utilizing it in ViewModel lessons as nicely.

Which info is appropriate? Ought to a ViewModel use @Binding or solely @Revealed and the view handles relaying adjustments from/to the ViewModel?

If it may be utilized in, ought to the ViewModel be used as @ObservedObject or as @StateObject?


I’ve created the next instance which compares all circumstances:

  • BindingViewModel makes use of @Binding to retailer a textual content property
  • PublishedViewModel makes use of @Revealed to retailer a textual content property
  1. ObservedBindingTextView: BindingViewModel as @ObservedObject
  2. StateBindingTextView: BindingViewModel as @StateObject
  3. ObservedPublishedTextView: PublishedViewModel as @ObservedObject
  4. StatePublishedTextView: PublishedViewModel as @StateObject

enter image description here

Options 1, 3 and 4 appear all to work identically. Solely in Resolution 2 doesn’t deal with “incoming” textual content adjustments correctly. Right here each adjustments type inside and outdoors of this view are usually not utilized to its personal textual content view. Nevertheless adjustments from inside this view are appropriately utilized to all different views. Why?

Since Resolution 1, 3 and 4 work identically: Which one must be most popular? Is there an accurate resolution?

So far as I do know, the distinction between @ObservedObject and @StateObject is, that the later one is NOT destroyed / re-created everytime the view is re-drawn / re-created (appropriate?). Nevertheless, does this matter on this case?

struct SomeView: View {
    class SomeViewModel: ObservableObject {
        @Revealed var textual content = "Howdy, World!"
    }
    
    @ObservedObject personal var viewModel = SomeViewModel()
    
    var physique: some View {
        VStack {
            Textual content(viewModel.textual content)
            Button("Change Textual content") {
                viewModel.textual content += "."
            }
            .padding(.backside, 60)
            
            
            ObservedBindingTextView(textual content: $viewModel.textual content)
                .padding(.backside, 30)
            
            StateBindingTextView(textual content: $viewModel.textual content)
                .padding(.backside, 60)
            
            
            ObservedPublishedTextView(textual content: $viewModel.textual content)
                .padding(.backside, 30)
            
            StatePublishedTextView(textual content: $viewModel.textual content)
        }
    }
}


// Usung @Binding
class BindingViewModel: ObservableObject {
    @Binding var textual content: String
    
    init(textual content: Binding) {
        _text = textual content
    }
    
    func changeText() {
        textual content += "!"
    }
}

struct ObservedBindingTextView: View {
    @ObservedObject personal var viewModel: BindingViewModel
    
    init(textual content: Binding) {
        viewModel = .init(textual content: textual content)
    }
    
    var physique: some View {
        VStack {
            Textual content("Noticed Binding Textual content:n(viewModel.textual content)")
            
            Button("Change Noticed Binding Textual content") {
                viewModel.changeText()
            }
        }
    }
}

struct StateBindingTextView: View {
    @StateObject personal var viewModel: BindingViewModel
    
    init(textual content: Binding) {
        _viewModel = StateObject(wrappedValue: .init(textual content: textual content))
    }
    
    var physique: some View {
        VStack {
            Textual content("State Binding Textual content:n(viewModel.textual content)")
            
            Button("Change State Binding Textual content") {
                viewModel.changeText()
            }
        }
    }
}


// Utilizing @Revealed
class PublishedViewModel: ObservableObject {
    @Revealed var textual content: String
    
    init(textual content: String) {
        self.textual content = textual content
    }
    
    func changeText() {
        textual content += "?"
    }
}

struct ObservedPublishedTextView: View {
    @Binding var textual content: String
    @ObservedObject personal var viewModel: PublishedViewModel
    
    init(textual content: Binding) {
        _text = textual content
        viewModel = .init(textual content: textual content.wrappedValue)
    }
    
    var physique: some View {
        VStack {
            Textual content("Noticed Publishe Textual content:n(viewModel.textual content)")
            
            Button("Change Noticed Revealed Textual content") {
                viewModel.changeText()
            }
        }
        .onChange(of: textual content) { newText in
            viewModel.textual content = newText
        }
        .onChange(of: viewModel.textual content) { newText in
            textual content = newText
        }
    }
}

struct StatePublishedTextView: View {
    @Binding var textual content: String
    @StateObject personal var viewModel: PublishedViewModel
    
    init(textual content: Binding) {
        _text = textual content
        _viewModel = StateObject(wrappedValue: .init(textual content: textual content.wrappedValue))
    }
    
    var physique: some View {
        VStack {
            Textual content("State Revealed Textual content:n(viewModel.textual content)")
            
            Button("Change State Revealed Textual content") {
                viewModel.changeText()
            }
        }
        .onChange(of: textual content) { newText in
            viewModel.textual content = newText
        }
        .onChange(of: viewModel.textual content) { newText in
            textual content = newText
        }
    }
}

LEAVE A REPLY

Please enter your comment!
Please enter your name here