Swiftui Published Updates Not Refreshing

SwiftUI Observed Object not updating when published value changes

The ForEach does not detect that any of cards changes because it is uses Equatable which in your case uses only id.

Here is a fix:

struct Card: Identifiable, Equatable {
var isFaceUp: Bool = false
var isMatched: Bool = false
var content: CardContent

var id: Int

static func == (lhs: MemoryGame<CardContent>.Card, rhs: MemoryGame<CardContent>.Card) -> Bool {
lhs.id == rhs.id && lhs.isFaceUp == rhs.isFaceUp // << here !!
}
}

and also needed update for

mutating func choose(_ card: Card) {
let chosenIndex = cards.firstIndex{ $0.id == card.id } // << here !!
cards[chosenIndex!].isFaceUp.toggle()
}

Tested with Xcode 13.4 / iOS 15.5

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 ObservableObject not updating when value is Published

You are likely winding up with different UsersViewModel objects:

@ObservedObject var usersViewModel = UsersViewModel()

Since UsersView is a struct, this creates a new model every time the View is instantiated (which can happen very often, not just when the view appears). In iOS 14 there is @StateObject to combine State (which preserves information between View instantiations) with ObservedObject, but in iOS 13 I recommend passing in the ObservedObject if it's not a shared instance.

Published property not updating in SwiftUI

for loop will block the main queue so create a queue to run for i in 1...1000000 on another thread

    //
// ContentView.swift
// StackOverFlow
//
// Created by Mustafa T Mohammed on 12/31/21.
//

import SwiftUI

public class ViewSearch {
@ObservedObject var viewModel: ViewModel

/// Main initializer for instance.
/// - Parameter viewModel: The view model for searching.
public init(viewModel: ViewModel) {
self.viewModel = viewModel
}
func doSomething() {
let q = DispatchQueue.init(label: "doSomething")
// for loop will block the main queue so create a queue to run for i in 1...1000000
// on another thread
q.async { [weak self] in // weak self to prevent retain cycle
guard let self = self else { return }
for i in 1...1000000 {
if i % 250000 == 0 {
DispatchQueue.main.async { //update UI by coming back to main thread
self.viewModel.label = "value: \(i)"
}
}
}
DispatchQueue.main.async { //update UI by coming back to main thread
self.viewModel.label = "Done!"
}
}
}
}

public class ViewModel: ObservableObject {
@Published public var label = "initial value" {
didSet {
print("\(label)")
self.objectWillChange.send()
}
}
@Published public var searchText = ""

var search: ViewSearch? = nil
}

struct ContentView: View {
@ObservedObject var model: ViewModel

var body: some View {
TextField("Search", text: $model.searchText) { isEditing in
if isEditing {
model.label = "initial value"
}
} onCommit: {
if !model.searchText.isEmpty {
model.search = ViewSearch(viewModel: model)
model.search?.doSomething()
}
}

Text(model.label)
}
}

SwiftUI published variable not updating view

  1. You're using two different instances of DatabaseDelegate, one in the AppDelegate and one in the MainViewDelegate. The boolean is only updated in app delegate's instance.

Move your auth listener into your DatabaseDelegate.

class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
FirebaseApp.configure()
return true
}
}

class DatabaseDelegate: ObservableObject {
@Published var userDataLoaded = false

private var handle: AuthStateDidChangeListenerHandle?

init() {
self.handle = Auth.auth().addStateDidChangeListener { (auth, user) in
// .. etc
self.getUserInfo(...)
}
}

private func getUserInfo(UID: String, withCompletionHandler completionHandler: @escaping () -> Void) {
database.collection("Users").document(UID).getDocument { (document, error) in
// .. etc
self.userDataLoaded = true
}
}
}

  1. You need to use StateObject instead of ObservedObject since you are initializing it internally on the view, instead of injecting it from an external source.
struct MainViewDelegate: View {
@StateObject private var databaseDelegate = DatabaseDelegate()
}

If you want to use ObservedObject, you can create it externally and inject into the view like so:

var databaseDelegate = DatabaseDelegate()

MainViewDelegate(databaseDelegate: databaseDelegate)

struct MainViewDelegate: View {
@ObservedObject var databaseDelegate: DatabaseDelegate
}


Related Topics



Leave a reply



Submit