> ## Content Index
> Fetch the complete content index at: https://somekindofcode.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Custom Identifier vs Identifiable.ID
- URL: https://somekindofcode.com/custom-identifier-vs-identifiable-id/
- Published: 2026-08-05T14:50:00.000Z
- Updated: 2026-08-05T14:50:56.000Z
- Description: While Identifiable.ID is a valid approach, it can still cause unexpected behaviour. Let's find an easy workaround by creating a small, generic type.
- Author: Christopher Beloch

The `Identifiable` protocol has been introduced to the Swift Standard Library with Swift 5.1 and even before has been a part of SwiftUI and is commonly used with all our data models since then. And some of you may even used some similar protocols created by the community or your own. I still prefer a custom `Identifier` type to `Identifiable.ID` and I want to show you why.

For this example I'll use two simple models:

```swift
struct Author: Identifiable {
  let id: String
  let name: String
}

struct Post: Identifiable {
  let id: String
  let title: String
  let content: String
  let author: Author
}
```

Now let's imagine a function that'll grab all posts of an author:

```swift
func getAllPosts(by authorId: Author.ID) -> [Post]
```

With this definition, the following code will compile without issues:

```
let post: Post // Currently viewed post

getAllPosts(by: post.author.id) // this will return the desired result
getAllPosts(by: post.id) // This will return unexpected results
```

This works, because `Author.ID` and `Post.ID` in this example are both of the type `String` and the `.ID` is only a `typealias` and not a separate type itself. If the author ID would've been an `Int`, the compiler would've thrown an error, because the types don't match.

But lets solve this with a custom `Identifier` object.

## Custom Identifier

No, I don't want to create a separate object for each main model. Generics will help us here.

```swift
struct Identifier<T: Identifiable> {
  let rawValue: Value.ID
  init(rawValue: Value.ID) {
    self.rawValue = rawValue
  }
}
```

and a little extension to all `Identifiable` objects:

```swift
extension Identifiable {
  var identifier: Identifier<Self> {
    return .init(rawValue: self.id)
  }
}
```

 What we can do now is update our function from above:

```swift
// Old: func getAllPosts(by authorId: Author.ID) -> [Post]
func getAllPosts(by authorId: Identifier<Author>) -> [Post]
```

And now change the usage of the code:

```swift
let post: Post // Currently viewed post

getAllPosts(by: post.author.identifier) // this is fine
getAllPosts(by: post.author.id) // this will cause a compile error
getAllPosts(by: post.identifier) // this will cause a compile error
```

To get easy access to the `Identifier` of a model, we use the extension to `Identifiable` from above. The regular `.id` won't work here as this will keep returning the `String` value instead of the desired `Identifier<Author>`.

With this, you get much safer code in the same way Swift itself prevents us humans (and AI) from doing to many mistakes that end up in unexpected results and unnecessary debugging sessions.