Identifiable is a protocol in Swift that forces types to include a unique identifier property. This identifier helps to differentiate individual instances of a type from each other within arrays or collections.
Let’s see with an example when the Identifiable protocol can be useful. If we have this code:
struct ContentView: View {
var body: some View {
var restaurants = [ Restaurant(name: "Cafe Deadend", image: "cafedeadend"),
Restaurant(name: "Homei", image: "homei"),
Restaurant(name: "Teakha", image: "teakha"),
Restaurant(name: "Cafe Loisl", image: "cafeloisl"),
Restaurant(name: "Teakha", image: "petiteoyster"),
Restaurant(name: "For Kee Restaurant", image: "forkeerestaurant"), Restaurant(name: "Po's Atelier", image: "posatelier"), Restaurant(name: "Bourke Street Bakery", image: "bourkestreetbakery"), Restaurant(name: "Haigh's Chocolate", image: "haighschocolate")]
var restaurantImages = ["cafedeadend", "homei", "teakha", "cafeloisl", "petiteoyster", "forkeerestaurant", "posatelier", "bourkestreetbakery", "haighschocolate"]
List(restaurants, id: \.name) { restaurant in
HStack {
Image(restaurant.image)
.resizable()
.frame(width: 40, height: 40)
.cornerRadius(5)
Text(restaurant.name)
}
}
.listStyle(.plain)
}
}
struct Restaurant {
var name: String
var image: String
}
Take a look at the Swift code and the parameters we passed to List. We passed the restaurants array and specified the name property as the identifier for each restaurant. List will iterate through the array and give us the current restaurant. Inside the HStack we specify how we want each restaurant row to be displayed. In this case we display an image and the name of the restaurant.
If you look closely there are two restaurants with the same name and as a result both will display the same image. And what we don’t want is for the same image to be displayed for both restaurants. In the following image you can see how both restaurants display the same name and the same image:
We can see how the restaurant “Teakha” displays the same image.
In the code we have told List to use the name property as the unique identifier. However when two restaurants have the same name, iOS considers the restaurants to be the same and reuses the view.
So how do we fix this?
We just need to provide each restaurant with a unique identifier. Let’s update the Restaurant struct by adding a property called id of type UUID. This will ensure that each restaurant has a unique identifier. Here is the updated code:
struct Restaurant {
var id = UUID()
var name: String
var image: String
}
In the code we added an id property to the Restaurant struct and initialized it with a unique identifier using the UUID() function. This ensures that each restaurant has a unique id. A UUID is made up of a 128-bit number, so theoretically the probability of having two identical IDs is almost zero.
To make everything work correctly we need to update the List view by changing the \.name parameter to .id. This tells List to use the id property of each restaurant as the identifier.
The code would look like this:
struct ContentView: View {
var body: some View {
var restaurants = [ Restaurant(name: "Cafe Deadend", image: "cafedeadend"),
Restaurant(name: "Homei", image: "homei"),
Restaurant(name: "Teakha", image: "teakha"),
Restaurant(name: "Cafe Loisl", image: "cafeloisl"),
Restaurant(name: "Teakha", image: "petiteoyster"),
Restaurant(name: "For Kee Restaurant", image: "forkeerestaurant"), Restaurant(name: "Po's Atelier", image: "posatelier"), Restaurant(name: "Bourke Street Bakery", image: "bourkestreetbakery"), Restaurant(name: "Haigh's Chocolate", image: "haighschocolate")]
var restaurantImages = ["cafedeadend", "homei", "teakha", "cafeloisl", "petiteoyster", "forkeerestaurant", "posatelier", "bourkestreetbakery", "haighschocolate"]
List(restaurants, id: \.id) { restaurant in
HStack {
Image(restaurant.image)
.resizable()
.frame(width: 40, height: 40)
.cornerRadius(5)
Text(restaurant.name)
}
}
.listStyle(.plain)
}
}
struct Restaurant {
var id = UUID()
var name: String
var image: String
}
In the following image you can see how now the second Teakha restaurant shows a different and correct image.
How to conform to protocol Identifiable
We can simplify the code by making the struct Restaurant conform to the Identifiable protocol, which has only one requirement: the type that implements the protocol has to have an id type as a unique identifier.
Let’s update the struct Restaurant to conform to the Identifiable protocol as follows:
struct Restaurant:Identifiable {
var id = UUID()
var name: String
var image: String
}
Since the Restaurant struct has a single id property, it conforms to the protocol requirement and we can thus initialize List without the need to explicitly specify an id parameter. This simplifies the code a lot. Here is the updated code:
struct ContentView: View {
var body: some View {
var restaurants = [ Restaurant(name: "Cafe Deadend", image: "cafedeadend"),
Restaurant(name: "Homei", image: "homei"),
Restaurant(name: "Teakha", image: "teakha"),
Restaurant(name: "Cafe Loisl", image: "cafeloisl"),
Restaurant(name: "Teakha", image: "petiteoyster"),
Restaurant(name: "For Kee Restaurant", image: "forkeerestaurant"), Restaurant(name: "Po's Atelier", image: "posatelier"), Restaurant(name: "Bourke Street Bakery", image: "bourkestreetbakery"), Restaurant(name: "Haigh's Chocolate", image: "haighschocolate")]
var restaurantImages = ["cafedeadend", "homei", "teakha", "cafeloisl", "petiteoyster", "forkeerestaurant", "posatelier", "bourkestreetbakery", "haighschocolate"]
List(restaurants) { restaurant in
HStack {
Image(restaurant.image)
.resizable()
.frame(width: 40, height: 40)
.cornerRadius(5)
Text(restaurant.name)
}
}
.listStyle(.plain)
}
}
struct Restaurant:Identifiable {
var id = UUID()
var name: String
var image: String
}