Skip to content

Instantly share code, notes, and snippets.

@oliverfoggin
Last active December 7, 2021 22:10
Show Gist options
  • Save oliverfoggin/77116b970a621d6e89c855296b51084a to your computer and use it in GitHub Desktop.
Save oliverfoggin/77116b970a621d6e89c855296b51084a to your computer and use it in GitHub Desktop.
Type Safe Identifiers with automatic conformance to Identifiable
/// This was inspired by this... https://www.swiftbysundell.com/articles/type-safe-identifiers-in-swift/
/// I wanted to try and find a way to have a type safe identifier that also allowed conformance to Identifiable
import Foundation
struct Identifier<Subject: Identified>: Hashable {
let value: Subject.IdentifierType
init(_ value: Subject.IdentifierType) {
self.value = value
}
}
extension Identifier: CustomStringConvertible {
public var description: String {
"\(value)"
}
}
extension Identifier: ExpressibleByStringLiteral, ExpressibleByUnicodeScalarLiteral, ExpressibleByExtendedGraphemeClusterLiteral where Subject.IdentifierType == String {
init(stringLiteral value: String) {
self.value = value
}
init(unicodeScalarLiteral value: String) {
self.value = value
}
init(extendedGraphemeClusterLiteral value: String) {
self.value = value
}
}
extension Identifier: ExpressibleByIntegerLiteral where Subject.IdentifierType == Int {
init(integerLiteral value: Int) {
self.value = value
}
}
protocol Identified: Identifiable {
associatedtype IdentifierType: Hashable
var identifier: Identifier<Self> { get }
}
extension Identified {
var id: IdentifierType {
identifier.value
}
}
struct Person: Identified {
typealias IdentifierType = String
let identifier: Identifier<Self>
}
let person = Person(identifier: "3")
dump(person)
print(person.identifier)
print(person.id) // id comes "for free" from the automatic Identifiable conformance.
let person2 = Person(identifier: "5")
dump(person2)
print(person2.identifier)
print(person2.id)
let randomIdentifier: Identifier<Person> = 3 // Error because Identifier<Person> requires a String
print(randomIdentifier)
struct Account: Identified {
typealias IdentifierType = String
let identifier: Identifier<Self>
}
let account = Account(identifier: "Account1")
func doSomething(personID: Identifier<Person>) {
print("Hello, \(personID)")
}
doSomething(personID: person.identifier)
doSomething(personID: account.identifier) // Error because Identifier<Account> and Identifier<Person> are not compatible.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment