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
iOS Swift - Custom Camera Overlay
How to Convert an Int into Nsdata in Swift
How to Change the Nstimeinterval of an Nstimer After X Seconds
How to Hide API Keys in Github for iOS (Swift) Projects
How to Check If a Nstimer Is Running or Not in Swift
Why am I Getting Com.Facebook.Sdk.Login Error 308
It Is Possible to Know If a String Is Encoded in Base64
Swift Core Data Sync with Web Server
Changing Font Size in a Label for Only iPhone 4S, Is This Possible
Swift: Protocol VS. Struct VS. Class
What am I Doing Wrong with Allowsfractionalunits on Nsdatecomponentsformatter
Where to Find a Clear Explanation About Swift Alert (Uialertcontroller)
How to Apply Multiple Masks to Uiview
Create Navbar Programmatically with Button and Title Swift
Read Logs Using the New Swift Os_Log API
Using Completionselector and Completiontarget with Uiimagewritetosavedphotosalbum