Search
⛓️

ForEach

안녕하세요 iOS 개발자 루크입니다!
오늘은 SwiftUI 의 뷰를 구성함에 있어 빠질 수 가 없는 ForEach 문에 대해서 알아보려고 합니다 ㅎ

ForEach 란?

View 를 구성할 때 사용하는 반복문 같은 놈
Collection의 데이터를 기반으로 View 를 계산하는 구조체

Collection 의 조건

1. Identifiable 을 만족해야함 ← id 프로퍼티가 필요함

Identifiable 이란?
밸류 타입 또는 클래스에게 ID 속성을 부여하는 프로토콜로, 이를 활용해 인스턴스가 항상 유일함을 보장한다. 각 데이터가 각자를 식별할 수 있는 무언가를 가지고 있는 경우에 충족한다
Identifiable 프로토콜을 사용해서 클래스 또는 밸류 타입에 ID 속성을 부여하시먄 됩니다.
ObjectIdentifier 타입을 사용해서 id 프로퍼티를 정의함으로써 Identifiable 프로토콜을 준수할 수 있습니다.
그렇다면 extension 을 활용해서 id 프로퍼티를 추가하고 인스턴스를 생성할 때마다 id 를 넣어주어야할까요?
이는 너무 번거롭기 때문에 ForEach 문에는 Array 의 각 요소에 id 를 부여할 수 있는 방법을 제공한다네요!
ForEach(array, id: \.self)  를 활용하면 ForEach 문 내에서 각 item 에 대해 id를 정해줄 수 있다고 합니다. 위와 같이하면 Array 의 내부요소가 가지고 있는 값 자체를 id 로 가지도록 만들어줍니다.
따라서 ForEach 문에는 Int 같이 Identifiable 하지 않은 타입도 고유한 존재로 만들어서 사용이 가능합니다.
신기하게도 0..<10 같은 (Open) Range<Int>는 가능하지만, 0...10 같은 ClosedRange<Int>는 불가능한데요. 이는 ForEach의 init 구문 중에서 범위를 지원하는 것은 Range<Int> 밖에 없기 때문이라고 하네요 참고하시면 좋을 듯 합니다

2. ID가 Hashable 을 만족해야함

Hashable 이란?
정수 Hash 값을 제공하는 타입임을 보장하는 프로토콜
그 자체로 유일하게 표현이 가능한 방법을 제공해야 만족됩니다
func hash(into:) 이라는 메서드를 구현함으로써 충족이 가능한데요, Int, String, Float, Boolean 같은 경우 Hashable 을 기본적으로 만족하고 있습니다.
하지만 Hashable 함이 나타내어져 있지 않은( Hashable 프로토콜을 준수하지 않은) 타입으로 이루어진 ArrayForEach 문에 사용하려고 하면 에러가 발생하겠죠!
이럴때는 건네주고자 하는 타입이 Hashable 프로토콜을 준수하도록 해주면 됩니다. 보통은 타입이 가지고 있는 프로퍼티가 모두 Hashable 을 이미 준수하는 타입으로만 이루어져 있다면, 이름 옆에 Hashable 키워드를 작성해주는 것만으로도 Hashable 을 준수할 수 있습니다.

조건이 존재하는 이유?

ForEach에서 이런 식으로 Identifiable, Hashable을 지키도록 하는 것은 결국 각 item을 제대로 구분하기 위해서라고 합니다.
주의해야할 점은
이런 프로토콜을 다 지키더라도 값이 아예 같으면 SwiftUI 입장에서 당황할 수 밖에 없다는 점입니다. 이렇게 되면 렌더링은 문제없이 되나 의도치 않은 결과가 발생할 수 있어요
그래서 완전히 같은 값이 존재하거나, Hashable하게 데이터를 만들기 힘들 경우.
ForEach 문을 사용하기 위한 두가지 방법이 존재합니다.

1. ForEach 의 id 값을 조정한다

id: \.self.프로퍼티명 id 로 사용할만한 확실한 정보가 있을 경우 이것만을 활용해 객체들을 구분해줄 수 있습니다.
타입의 프로퍼티 전부를 Hashable 하게 할 필요 없이 타입의 한 프로퍼티 만으로 각 인스턴스를 구분하도록 하는 것이죠!

2. indices 를 사용해 인덱스를 통해 접근하기

Collection 을 사용해 데이터를 다룰 것이기 때문에 각 값에는 무조건 인덱스가 있기 마련이죠
struct ContentView: View { let array: [Info] = [Info(0), Info(1)] var body: some View { ForEach(array.indices, id:\.self) { idx in Text("index : \(array[idx].num") } } }
Swift
복사
이런 식으로 indces 를 사용하면 array 의 인덱스를 담은 Collection 을 얻어낼 수 있고 이를 통해 원본 데이터 접근이 가능해요 ㅎ
이렇게 되면 Hashable 하지 않아도 의도대로 ForEach 문을 사용할 수 있답니다!
오늘은 SwiftUI 를 사용해 반복되는 View 객체를 생성하는 방법과 그에 따라 자주 등장하는 Hashable Identifiable 경고문에 대해서 이해해보는 시간을 가졌습니다.
해결책을 정리해두고 나니 한결 마음이 수월하네요
오늘 포스팅은 여기서 마무리 하겠습니다