Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Swee Sen
Swee Sen

Posted on • Edited on

     

SwiftUI: How to use List with ObservableObject

In SwiftUI, it is very easy to construct a list of item. For example, let say we have an array containing the names of our users, we can display these names in a list by the following code:

structContentView:View{letusers=["John","Peter","Jane"]varbody:someView{List(users){userinText(user)}}}
Enter fullscreen modeExit fullscreen mode

In this case, we can make a list which consists of the names of all the users. However, this list of names is static, meaning that any changes to the users array will not cause the List to refresh and be updated to the latest values. To make the List to refresh whenever there is changes to the users array, we can make use of the@State variable. With the use of@State in front of a variable, SwiftUI will listen for any changes to that variable, and automatically render theView again with the latest values.

structContentView:View{@Statevarusers=["John","Peter","Jane"]varbody:someView{List(users){userinText(user)}}}
Enter fullscreen modeExit fullscreen mode

To see how this might be useful, let's say that we have a button that makes a network call to fetch the users information from our server, we can easily render the List with the network call result easily with the following code:

structContentView:View{@Statevarusers=["John","Peter","Jane"]varbody:someView{VStack{Button(action:{self.loadFromServer()}){Text("Fetch From Server")}List(users,id:\.self){userinText(user)}}}privatefuncloadFromServer(){//do some network call and assign the result to self.users}}
Enter fullscreen modeExit fullscreen mode

With the code above, when the result from the network call is ready and then assigned to self.users, SwiftUI detects changes to the variable and will redraw theList with the updated values.

While the code above works, it is not a good idea to put our networking code in theView Struct.View is only responsible for rendering the UI elements onto the screen. Having networking code inside aView Struct violates the Single Responsibility Principle. Hence, we should move the code related to networking out into separate class. However, moving networking code out of theView Struct also means that now we are unable to directly access the@State users variable to update the list. The solution :ObservableObject.

We can start by creating a struct calledUser with two properties:name andid. The reason for the need for id is to allowList in SwiftUI to be able to unique identify and keep track of the changes to the data source.

structUser:Identifiable{letid=UUID()letname:String}
Enter fullscreen modeExit fullscreen mode

Next, we can then create a class called UserContainer, and comform it toObservableObject.

classUsersContainer:ObservableObject{@Publishedvarusers=[User]()}
Enter fullscreen modeExit fullscreen mode

By marking the users variable as@Published, it means that whenever there is any changes to the users variable, the instances of the class that are "subscribed" to it will be notified, prompting SwiftUI to re-render theView. The final step is to connect the data source to ourView:

To do this, we will create an instance of the UsersContainer Class in the ContentView. In order to "subscribe" to be notified of any changes to the data, we have to mark it with@ObservedObject:

structContentView:View{@ObservedObjectvarusersContainer=UsersContainer()varbody:someView{VStack{List(usersContainer.users,id:\.id){userinText(user.name)}}}}
Enter fullscreen modeExit fullscreen mode

Now with the current arrangement, supposed that we want to make a network call to the server to retrieve the list of users, instead of doing it in the ContentView, we can now do the networking logic in the UsersContainer Class

classUsersContainer:ObservableObject{@Publishedvarusers=[User]()funcfetchFromServer(){//assign the result back to self.users}}
Enter fullscreen modeExit fullscreen mode

We can now add a button in ourContentView to trigger the fetchFromServer() call like this:

structContentView:View{@ObservedObjectvarusersContainer=UsersContainer()varbody:someView{VStack{Button(action:{self.usersContainer.fetchFromServer()}){Text("Fetch From Server")}List(usersContainer.users,id:\.id){userinText(user.name)}}}}
Enter fullscreen modeExit fullscreen mode

Top comments(2)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
john_lahut_9ae848e06e624c profile image
John Lahut
  • Joined
• Edited on• Edited

How do you have the list view update when a property of an object in the list changes but not the list itself?

If another view updated user.name, how does the view update if there are no Published properties on the User object itself?

CollapseExpand
 
enzogla profile image
Enzo Gladiadis
  • Joined

This helped me so so so much!!
Thank you!!!

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

  • Location
    Singapore
  • Joined

More fromSwee Sen

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp