SwiftUI Identifiers Example

SwiftUI identifiers are used to uniquely identify and track data elements within a view hierarchy. They provide a way to differentiate between different data items and enable efficient updates and animations when the underlying data changes. SwiftUI introduces the concept of identifiers to facilitate smooth and performant data-driven UI updates.

Identifiers in SwiftUI are typically used in conjunction with the List or ForEach views to render collections of data. They ensure that SwiftUI can efficiently update the views when the data changes by identifying individual data elements and tracking their state.

Identifier Stability

When creating SwiftUI identifiers, stability should be the foremost consideration. Identifier stability has a significant impact on various aspects, including the view lifetime, performance, and state stability.

Maintaining identifier stability ensures that views can be reliably identified and tracked throughout their lifetime. When identifiers remain consistent, SwiftUI can effectively manage view updates, animations, and transitions, resulting in a smoother user experience.

Additionally, identifier stability plays a crucial role in optimizing performance. By accurately identifying and tracking views, SwiftUI can minimize unnecessary updates and only modify the relevant parts of the user interface. This efficient handling of view updates leads to improved performance and responsiveness.

Furthermore, identifier stability contributes to state stability within SwiftUI applications. When views are associated with stable identifiers, the framework can preserve and restore their state accurately. This is particularly important when dealing with complex view hierarchies or when persisting and restoring user interface state.

To illustrate the significance of identifier stability, let’s dive into an example:

struct Student {
    let name: String
    let age: Int
}

struct ContentView: View {
    @State var students:[Student] = [
        Student(name: "Peter", age: 21),
        Student(name: "Jane", age: 20),
        Student(name: "Peter", age: 22)
    ]
    var body: some View {
        ScrollView {
            VStack {
                Button {
                    withAnimation(.easeInOut(duration: 1.0)) {
                        students.insert(Student(name: "Emma", age: 19), at: 0)
                    }
                    
                } label: {
                    Text("Add Emma")
                }
                
                ForEach(students, id: \.name) {student in
                    HStack {
                        Spacer()
                        Text(student.name)
                        Text(String(student.age))
                        Spacer()
                    }.padding()
                }
            }
        }.frame(width: 200, height: 400)
            .padding()
    }
}

In this example, we aim to showcase a list of students. The student list is defined on line 7, and we proceed to display it on line 24.

One crucial detail to highlight is our usage of the ForEach loop. Within it, we pass the name property of the Student struct as the id parameter. By doing so, we ensure that each student is uniquely identified.

When we run this code, the result is as follows:

SwiftUI Identifiers Example

In students array we have two students with the same name (Peter). One student is 21 years old while the other is 22. Since the name property of a Student struct is not unique, and we are using it as an id, we are getting 21 years old Peter printed twice.

Introducing Identifiable Protocol

To solve the struct id issue, we are implementing Identifiable protocol. Struct using Identifiable will have to implement id property.

struct Student: Identifiable {
    var id: UUID { UUID() }
    let name: String
    let age: Int
}

struct ContentView: View {
    @State var students:[Student] = [
        Student(name: "Peter", age: 21),
        Student(name: "Jane", age: 20),
        Student(name: "Peter", age: 22)
    ]
    var body: some View {
        ScrollView {
            VStack {
                Button {
                    withAnimation(.easeInOut(duration: 1.0)) {
                        students.insert(Student(name: "Emma", age: 19), at: 0)
                    }
                    
                } label: {
                    Text("Add Emma")
                }
                
                ForEach(students) {student in
                    HStack {
                        Spacer()
                        Text(student.name)
                        Text(String(student.age))
                        Spacer()
                    }.padding()
                }
            }
        }.frame(width: 200, height: 400)
            .padding()
    }
}

Let’s take a look at the updated code with highlighted changes. On lines 1 and 2, we have implemented the Identifiable protocol for the Student struct, utilizing UUID as the id property. This modification ensures that each student now possesses a unique identifier.

The benefit of conforming to the Identifiable protocol is that on line 25, we no longer need to explicitly specify the id parameter. SwiftUI automatically recognizes the id property within the Student struct, simplifying the code.

When we run this updated code, we observe both Peters being printed out correctly, and everything appears to be in order.

SwiftUI Identifiers Example

But what will happen when we add a new student to the list but tapping Add Emma button?

SwiftUI Identifiers Example

As you can see, the animation is being applied to the entire list instead of exclusively animating the updates made to the new student.

To understand why this occurs, let’s direct our attention to line 2:

 var id: UUID { UUID() }

On every view update, students will have new id. Therefore each user will be seen as a completely new element.

To fix this we need id of a student to be unique and stable. There are many ways to achieve stability and, in this case, one of them is having a default id value. So here’s how line 2 would looks like after this final update:

var id: UUID = UUID()

And finally we are getting the correct list update animation:

SwiftUI Identifiers Example

Leave a Reply

Your email address will not be published. Required fields are marked *