[Swift]Identifiable, Equatable, Hashable Protocols

๐Ÿ‘‰๐Ÿป ์ด์ „ ํฌ์ŠคํŠธ์—์„œ randomuser api ๋ฐ์ดํ„ฐ๋ฅผ struct๊ตฌ์กฐ๋กœ ๋ณ€ํ™˜ํ•ด์„œ ์•ฑ์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œํ˜„ํ•˜๋„๋ก ๊ตฌํ˜„ํ•ด๋ดค์Šต๋‹ˆ๋‹ค.
In the previous post, I converted the randomuser API data into a struct structure and implemented it to represent the data in the app.

์ด struct๊ตฌ์กฐ์—์„œ ๋งŽ์ด ์‚ฌ์šฉ๋˜๋Š” ํ”„๋กœํ† ์ฝœ์ด Identifiable,Equatable,Hashable์ž…๋‹ˆ๋‹ค.
The protocols that are widely used in this struct structure are Identifiable, Equatable, and Hashable.

https://www.freelifemakers.org/wordpress/index.php/2025/12/25/swift-infinitescreoo-refresh-loading/

๐Ÿ‘‰๐Ÿป ์•„๋ž˜๋Š” ์ด ํ”„๋กœํ† ์ฝœ๋“ค์˜ ๊ธฐ๋Šฅ์„ ์ดํ•ดํ•˜๊ธฐ ์œ„ํ•œ ๋Œ€ํ‘œ์ธ ๊ธฐ๋Šฅ๋งŒ ๊ตฌํ˜„ํ–ˆ์Šต๋‹ˆ๋‹ค.
Below, only representative functions are implemented to help you understand the functionality of these protocols.

๐Ÿ‘‰๐ŸปIdentifiable , Hashable

Swift์—์„œ ์œ„์˜ ์˜ˆ์ œ์ฒ˜๋Ÿผ api๋ฐ์ดํ„ฐ๋ฅผ struct๊ตฌ์กฐ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ์•ฑ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ตฌ์กฐ๋“ฑ์—์„œ Identifiable,Equatable,Hashable์ด ๋งŽ์ด ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
In Swift, Identifiable, Equatable, and Hashable are widely used in structures used in apps by converting API data into struct structures, as in the example above.

struct๋‚˜ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉ ํ• ๊ฒฝ์šฐ ์ธ์Šคํ„ด์Šค(๊ฐ์ฒด)๋ฅผ ์ƒ์„ฑํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
When using a struct or class, an instance (object) is created.

์ด ๋•Œ ์•„๋ž˜์ฒ˜๋Ÿผ ๋™์ผํ•œ ๊ฐ’์„ ๋Œ€์ž…ํ•˜์—ฌ ์ธ์Šคํ„ด์Šค๋ฅผ ์ƒ์„ฑํ•˜๋ฉด ๋ฐ˜๋ณต๋ฌธ์—์„œ ์ถœ๋ ฅ์‹œ ๋ฐ˜๋ณต๋ฌธ ๋‚ด์—์„œ ๊ฐ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ตฌ๋ถ„์ง“๊ธฐ๊ฐ€ ํž˜๋“ค์–ด์ง‘๋‹ˆ๋‹ค.
At this time, if you create an instance by substituting the same value as below, it becomes difficult to distinguish each instance within the loop when outputting it.

๊ทธ๋ž˜์„œ Identifiable์ด๋‚˜,Hashable ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•ด์„œ ๊ฐ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ตฌ๋ถ„์ง€์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.
So, we can use the Identifiable or Hashable protocol to distinguish each instance.

Identifiable์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ id๋ฅผ ์ƒ์„ฑํ•ด์„œ ๊ตฌ๋ถ„ ์ง“๋Š” ๋ฐฉ๋ฒ•์ด๊ณ  Hashable ํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜๋ฉด ์ž๋™์ ์œผ๋กœ ๋‚ด๋ถ€์— hash๊ฐ’์ด ์ƒ์„ฑ๋˜์–ด ์ธ์Šคํ„ด์Šค๋ฅผ ๊ตฌ๋ถ„์ง€์„ ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.
Identifiable is a method in which the user creates an id to distinguish them, and if the Hashable protocol is used, a hash value is automatically generated internally to distinguish instances.

์ธ์Šคํ„ด์Šค๋ฅผ ๊ตฌ๋ถ„์ง€์–ด์•ผํ•˜๋Š” ์ด์œ ๋Š” ๋‚˜์ค‘์— ๋ฐ์ดํ„ฐ์˜ ์ถ”๊ฐ€ ์‚ญ์ œ ๋ณ€๊ฒฝ์ด ๋ฐœ์ƒํ• ๋•Œ ํ•„์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.
The reason we need to distinguish between instances is because we need to do additional deletions and changes to the data later.

โœ”๏ธ Identifiable์€ ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
Identifiable is used like this:

Identifiable์ด ๋งŒ์กฑ๋˜๋ฉด SwiftUI์˜ List, ForEach ๋“ฑ์—์„œ ๋ฐ”๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
Once Identifiable is satisfied, it can be used directly in SwiftUI’s List, ForEach, etc.

struct Person: Identifiable {
    let id = UUID()  // ๊ณ ์œ  UUID ์ž๋™ ์ƒ์„ฑ
    let name: String
    let age: Int
}

List,ForEach์—์„œ ์‚ฌ์šฉ๋ฐฉ๋ฒ•
How to use List, ForEach

import SwiftUI

struct Person: Identifiable {
// ๊ณ ์œ  UUID ์ž๋™ ์ƒ์„ฑ / Automatically generate unique UUID
    let id = UUID()          
    let name: String
    let age: Int
}

struct PersonListView: View {
    let people: [Person] = [
        Person(name: "Alice", age: 30),
        Person(name: "Bob", age: 35),
        Person(name: "Charlie", age: 25),
        Person(name: "Diana", age: 28) 
    ]
    
    var body: some View {
        List(people) { person in
            HStack {
                Text(person.name)
                    .font(.headline)
                Spacer()
                Text("\(person.age) years old")
                    .foregroundColor(.secondary)
            }
        }
        .navigationTitle("People List")
    }
}

โœ”๏ธ Hashable์€ ์ด๋ ‡๊ฒŒ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.
This is how Hashable is used:

Dictionary์˜ ํ‚ค๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค.
Allows you to use it as a key in a dictionary.

struct Product: Hashable {
    let productID: Int
    let name: String
}

Hashable์ด ์ ์šฉ๋œ struct๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
A struct with Hashable applied can be used as follows:

// prices๋”•์…”๋„ˆ๋ฆฌ๋ฅผ Product์™€ Doubleํƒ€์ž…์œผ๋กœ ์ƒ์„ฑ
// Create a prices dictionary with Product and Double types
var prices: [Product: Double] = [:]

// ๊ฐ’์ด ๊ฐ™์€ ์ธ์Šคํ„ด์Šค1 / Instance 1 with the same value
let iPhone = Product(productID: 1, name: "iPhone 15") 
let iPad = Product(productID: 2, name: "iPad Pro")

// 1500000์™€ ๊ฐ™์€๊ฐ’, ์–ธ๋”์Šค์ฝ”์–ด๋Š” ๋ฌด์‹œ๋จ.
// Same value as 1500000, underscores are ignored.
prices[iPhone] = 1_500_000 
prices[iPad] = 1_200_000

// ๊ฐ™์€ ์ œํ’ˆ์œผ๋กœ ๊ฐ€๊ฒฉ ์กฐํšŒ / Check price for the same product
// ๊ฐ’์ด ๊ฐ™์€ ์ธ์Šคํ„ด์Šค2 / Instance 2 with the same value
let sameiPhone = Product(productID: 1, name: "iPhone 15") 
print(prices[sameiPhone] ?? 0)  // 1500000.0

// ๊ฐ€๊ฒฉ ์—…๋ฐ์ดํŠธ / price update
prices[sameiPhone] = 1_450_000  
print(prices[iPhone]!)  // 1450000.0

๐Ÿ‘‰๐Ÿป Equatable

โœ”๏ธ ์ƒ์„ฑ๋œ ์ธ์Šคํ„ด์Šค(๊ฐ์ฒด)์˜ ๋‚ด์šฉ(๊ฐ’)์„ ๋น„๊ตํ•˜๊ธฐ ์œ„ํ•ด์„œ ์‚ฌ์šฉ๋˜๋Š” ํ”„๋กœํ† ์ฝœ์ž…๋‹ˆ๋‹ค.
This is a protocol used to compare the contents (values) of created instances (objects).

โœ”๏ธ Equatable์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
Equatable is used like this:

struct Person: Equatable { 
    let name: String
    let age: Int
    let email: String
}

let p1 = Person(name: "๊น€์ฒ ์ˆ˜", age: 30, email: "kim@example.com")
let p2 = Person(name: "๊น€์ฒ ์ˆ˜", age: 30, email: "kim@example.com")

print(p1 == p2)  // true โ† Swift๊ฐ€ ์ž๋™์œผ๋กœ ๋ชจ๋“  ํ”„๋กœํผํ‹ฐ ๋น„๊ตํ•ด์คŒ

โœ”๏ธ Swift ์ดˆ๊ธฐ ๋ฒ„์ „(์˜ˆ: Swift 3~4.0) ์Šคํƒ€์ผ๋กœ, Equatable์„ ์ง์ ‘ ๊ตฌํ˜„ํ•œ ํ˜•ํƒœ์ž…๋‹ˆ๋‹ค.
This is a direct implementation of Equatable in the style of early Swift versions (e.g. Swift 3-4.0).

โœ”๏ธ ์›๋ž˜๋Š” static func == ์ด๋ ‡๊ฒŒ == ์ด๋ฆ„์ธ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ์ฃผ๊ณ  == ์ด ์—ฐ์‚ฐ์ž๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด
static func == ์ด ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜๋Š” ํ˜•ํƒœ์ž…๋‹ˆ๋‹ค.
Originally, a function named == was created like this: static func == , and when the == operator was called,
static func == This function was called.

โœ”๏ธ ํ•˜์ง€๋งŒ swift4.1๋ถ€ํ„ฐ๋Š” Equtableํ”„๋กœํ† ์ฝœ์„ ์‚ฌ์šฉํ•˜๊ณ  == ์—ฐ์‚ฐ์ž๋งŒ ํ˜ธ์ถœํ•˜๋ฉด swift๊ฐ€ ์ž๋™์œผ๋กœ ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด ์ค๋‹ˆ๋‹ค.
However, starting from Swift 4.1, if you use the Equtable protocol and only call the == operator, Swift will automatically create a function.

struct Person: Equatable {
    let name: String
    let age: Int
    let email: String
    
    static func == (lhs: Person, rhs: Person) -> Bool {
        return lhs.name == rhs.name && lhs.age == rhs.age
        // email์€ ๋น„๊ต์— ํฌํ•จ ์•ˆ ๋จ
    }
}

๐Ÿ‘‰๐Ÿป ๊ฒฐ๋ก ์€ identifiable๊ณผ Hashable์€ ๋™์ผํ•œ ๊ฐ’์„ ๊ฐ€์ง„ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฐ˜๋ณต๋ฌธ์ด๋‚˜ ์ฝœ๋ ‰์…˜์—์„œ ๊ตฌ๋ถ„์ง“๊ธฐ ์œ„ํ•ด์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ฑฐ๊ณ  Equatable์€ ์ธ์Šคํ„ด์Šค์˜ ๊ฐ’์„ ์‰ฝ๊ฒŒ ๋น„๊ตํ•  ์ˆ˜์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ํ”„๋กœํ† ์ฝœ ์ž…๋‹ˆ๋‹ค.
In conclusion, identifiable and Hashable are used to distinguish instances with the same value in loops or collections, and Equatable is a protocol that allows for easy comparison of instance values.

Leave a Reply