Reload View When @Published Is Changed

Reload view when @Published is changed

The problem is that all of your views create their own ScannedCode instances. You need to create it only on the type that creates all other views and inject the same instance into each one of them.

From your code snippets it isn't really clear which view is the parent of which, so it's hard to give you a definitive answer with a code example, but in general, you should never create an @ObservableObject in a view itself, since that object will be recreated as soon as the view is reloaded. Instead, you should be creating your @ObservableObject on the view model or the parent view and injecting it into the child view that needs reloading.

Using below code, whenever scannedCode on the ParentView is updated, it reloads its ChildView with the updated ScannedCode.

struct ParentView: View {
@Published var scannedCode = ScannedCode()

var body: some View {
ChildView(scannedCode: scannedCode)
}
}

struct ChildView: View {
@ObservedObject var scannedCode: ScannedCode

var body: some View {
Text(scannedCode.barCode)
}
}

@Published variable doesn't reload my view after API call?

First, I advise you to move your getMenItems() call to the init() method of ProductViewModel.

Then, you can remove the .onAppear(perform: self.productVM.getMenItems) in your MenProducts view and mark the method private in the ProductViewModel as no outside class/struct will be calling it.

I would also recommend you to not explicitly call the background queue with DispatchQueue.main.async as URLSession data task operations are asynchronous already.

You can read more about JSON decoding to Models in this great article: https://www.avanderlee.com/swift/json-parsing-decoding/

SwiftUI Published updates not refreshing

@Published publishes value changes, so if you would replace the User object it would update. Since User is a class (Reference type) it will not update the value of GlobalState.user when User.isLoggedIn changes.

Fix for this is this package called NestedPublished which sends the nested ObservableObject.willChange to its parent.

SwiftUI: View does not update when state variable changes

The cause of this is using @State for your CNMutableContact.

@State works best with value types -- whenever a new value is assigned to the property, it tells the View to re-render. In your case, though, CNMutableContact is a reference type. So, you're not setting a truly new value, you're modifying an already existing value. In this case, the View only updates when name changes, which then triggers your onChange, so there's no update after the contact changes and you're always left one step behind.

But, you need something like @State because otherwise you can't mutate the contact.

There are a couple of solutions to this. I think the simplest one is to wrap your CNMutableContact in an ObservableObject and call objectWillChange.send() explicitly when you change a property on it. That way, the View will be re-rendered (even though there aren't any @Published properties on it).

class ContactViewModel : ObservableObject {
var contact = CNMutableContact()

func changeGivenName(_ newValue : String) {
contact.givenName = newValue
self.objectWillChange.send()
}
}

struct ContentView: View {
@State var name: String = ""
@StateObject private var contactVM = ContactViewModel()

var body: some View {
TextField("name", text: $name)
.onChange(of: name) { newValue in
contactVM.changeGivenName(newValue)
print("contact.givenName = \(contactVM.contact.givenName)")
}
Text("contact.givenName = \(contactVM.contact.givenName)")
}
}

Another option is moving name to the view model and using Combine to observe the changes. This works without objectWillChange because the sink updates contact on the same run loop as name gets changed, so the @Published property wrapper signals the View to update after the change to contact has been made.

import Combine
import SwiftUI
import Contacts

class ContactViewModel : ObservableObject {
@Published var name: String = ""
var contact = CNMutableContact()

private var cancellable : AnyCancellable?

init() {
cancellable = $name.sink {
self.contact.givenName = $0
}
}
}

struct ContentView: View {
@StateObject private var contactVM = ContactViewModel()

var body: some View {
TextField("name", text: $contactVM.name)
Text("contact.givenName = \(contactVM.contact.givenName)")
}
}

Reload Image if value in EnvironmentObject changes

It would be helpful to see RecipeViewModel, but I'm going to make an assumption that it looks something like this:

struct Recipe {
var id : UUID
}

class RecipeViewModel : ObservableObject {
@Published var recipe : Recipe = Recipe(id: UUID())
}

(Keep in mind that recipe has to be @Published for this to work`)

In that case, the you can listen for changes on recipe by doing something like this in your ImageLoaderView:

.onReceive(recipeVM.$recipe) { _ in
loadImageFromFirebase()
}

This can replace your onAppear, since it'll get called with the initial value and then again any time it changes.

If you wanted to, you could even refactor this to send the specific recipe into the loadImageFromFirebase function:

.onReceive(recipeVM.$recipe) { recipe in
loadImageFromFirebase(recipe: recipe)
}

//....

func loadImageFromFirebase(recipe: Recipe) {
let storage = Storage.storage().reference(withPath: "images/\(recipe.id ?? "").jpg")
storage.downloadURL { url, error in
if error != nil {
print((error?.localizedDescription)!)
return
}
self.imageURL = url!
}
}


Related Topics



Leave a reply



Submit