Give your simulator superpowers

RocketSim: An Essential Developer Tool
as recommended by Apple

Identifiable protocol in SwiftUI explained with code examples

The Identifiable protocol in SwiftUI allows you to add a unique object identity. The protocol requires a single ID property of any hashable type, making it a flexible protocol for all kinds of instances.

While it’s a relatively simple protocol, a few possible edge cases can cause unexpected bugs in your SwiftUI code. Therefore, it’s important to understand how to use the protocol correctly.

Conforming your object to Identifiable

When creating new instances in Swift, you’re likely not considering adding Identifiable protocol conformance immediately. It’s more common to run into SwiftUI-related build errors like:

Referencing initializer ‘init(_:content:)’ on ‘ForEach’ requires that ‘Article’ conform to ‘Identifiable’

This build error occurs when you’re using a non-identifiable collection of objects inside a ForEach element:

The objects need to be identifiable to iterate over a collection using ForEach.
The objects need to be identifiable to iterate over a collection using ForEach.

SwiftUI requires an identity for each object since it would otherwise not be able to determine whether it’s necessary to redraw a view after the collection changes. In other words, adding identity impacts the behavior of your views.

We can demonstrate this by adding Identifiable protocol conformance to the article structure:

struct Article {
    let title: String
    let url: URL
}

extension Article: Identifiable {
    var id: String {
        url.absoluteString
    }
}

At first glance, this code seems perfectly fine as we can successfully compile and run our project. However, as soon as we encounter two articles with the same URL, the resulting output becomes unexpected:

Even though we've conformed our article structure to Identifiable, the outcome view shows unexpected results.
Even though we’ve conformed our article structure to Identifiable, the outcome view shows unexpected results.

While both articles have a unique title, the URL remains the same. The output view shows the same title twice since it thinks both articles are identical based on the identity. This demonstrates why you should carefully add identity to objects and consider whether the ID used is unique in all cases.

In this example, we can properly add identity support by introducing a new unique post ID property:

struct Article {
    let postID: Int
    let title: String
    let url: URL
}

extension Article: Identifiable {
    var id: Int {
        postID
    }
}

Stay updated with the latest in SwiftUI

The 2nd largest newsletter in the Apple development community with 19,429 developers. Don't miss out – Join today:

You can always unsubscribe, no hard feelings.

Relying on the default implementation for classes

You might have noticed you’re not required to add any ID property to classes to which you’ve added Identifiable conformance. The Identifiable protocol provides a default implementation for class types, which are only guaranteed to remain unique for the lifetime of an object.

The default ID type will be ObjectIdentifier, a unique identifier for a class instance or metatype. We can demonstrates this behavior by creating a class version of our article:

class ArticleClass: Identifiable {
    let postID: Int
    let title: String
    let url: URL
    
    init(postID: Int, title: String, url: URL) {
        self.postID = postID
        self.title = title
        self.url = url
    }
}

There’s no requirement to add a new ID property and printing out the default implementation shows something like:

print("\(articleClass.id)")
/// Prints: ObjectIdentifier(0x0000600000be0810)

The ID equals the object pointer, so it’s only guaranteed to be unique during the object’s lifetime.

Note: it’s essential to realize you should use a struct for the articles in this example. It might look like a solution to change all your types to classes, but this is wrong. You can learn more about this in my article Struct vs classes in Swift: The differences explained.

Conclusion

The Identifiable protocol allows you to iterate over a collection of objects inside a ForEach SwiftUI element. While it looks like a simple protocol with just a single property, it’s easy to make a common mistake that could lead to unexpected behavior in your view. Classes can rely on a default implementation, but you should not change all your structs to classes just to make them conform to Identifiable.

If you want to improve your SwiftUI knowledge, even more, check out the SwiftUI category page. Feel free to contact me or tweet me on Twitter if you have any additional tips or feedback.

Thanks!