6

I have a view and a viewModel that should update the ListView when users are added to the user array. I can verify that users are being added, yet the ObservedObject is not updating.

I have a search bar that lets you search users and then updates user array in the ViewModel which is supposed to update the View but it doesn't.

ViewModel

class UsersViewModel: ObservableObject {
    @Published var users: [User] = []
    @Published var isLoading = false
    var searchText: String = ""
    
    func searchTextDidChange() {
        isLoading = true
        API.User.searchUser(text: searchText) { (users) in
            self.isLoading = false
            self.users = users
        }
        // confirmed that users has data now at this point
    }
}

View

struct UsersView: View {
    @ObservedObject var usersViewModel = UsersViewModel()
    
    var body: some View {
        VStack() {
            SearchBarView(text: $usersViewModel.searchText, onSearchButtonChanged: usersViewModel.searchTextDidChange)
            
            // Not getting called after usersViewModel.users gets data
            if (usersViewModel.users.count > 0) {
                Text(usersViewModel.users[0].username)
            }
        }
    }
}

3 Answers 3

11

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.

Sign up to request clarification or add additional context in comments.

9 Comments

I don't think this is it. It seems to be a problem with the async data. If I create a button in the view and then just have it toggle a value in the ViewModel, that works fine. But if I run it through my asynchronous API call, it doesn't work (even though the async call returns data and updates the Published variable)
If my theory is correct, you can check by putting a breakpoint in UsersViewModel.init. If it fires more than once, then you're having this problem (even if it isn't the cause of this specific bug).
You are indeed correct. It's getting initialized a bunch. SO you are saying I should be creating the ViewModel prior to instantiating the View and then just pass the ViewModel in?
Yep, that's what you want to do (at least in iOS 13)
Appreciate your help. I've gotten it working by instantiating the ViewModel in View1 and then passing the ViewModel to my NavigationLink in View1 which instantiates View2. Just seems wild that I have to instantiate the ViewModel in a totally separate view that doesn't use it and may not ever even open View2 unless someone clicks on the NavigationLink
|
3

Try to update on main queue

API.User.searchUser(text: searchText) { (users) in
   DispatchQueue.main.async {
      self.isLoading = false
      self.users = users
   }
}

1 Comment

Nope. Nothing still
2

If your view is inside another view and you are not injecting the view model, consider using @StateObject.

This will not cause the object to be renewed every time the view is re-rendered.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.