식별자 (identifier) 를 포함한 비교가능한 데이터 소스 (diffable data source) 를 사용하여 콜렉션 뷰의 데이터를 표시하고 업데이트 하는데 간소화 합니다.
콜렉션 뷰 는 섹션 (section) 과 아이템 (item) 의 형식으로 데이터를 표시하고 콜렉션 뷰로 데이터를 나타내는 앱은 콜렉션 뷰에 섹션과 아이템을 삽입할 수 있습니다. 앱은 섹션과 아이템을 삭제 또는 이동에 대한 처리가 필요할 수도 있습니다. 예를 들어 샘플 프로젝트에 있는 앱은 콜렉션 뷰로 레시피를 나타내고 앱을 이용하여 레시피를 추가 및 삭제할 수 있고 레시피를 즐겨찾기로 표시할 수 있습니다. 이러한 동작을 위해 샘플 앱은 콜렉션 뷰에서 데이터 삽입, 삭제, 이동, 그리고 업데이트를 처리합니다.
앱에서 콜렉션 뷰를 사용할 때, UICollectionViewDataSource
프로토콜을 채택한 커스텀한 데이터 소스를 생성할 수 있습니다. 현재 콜렉션 뷰에서 정보를 최신으로 유지하기 위해 무슨 데이터가 변경되었는지 확인하고 변경사항을 기반으로 업데이트를 수행해야 하는데 이러한 프로세스는 삽입, 삭제, 그리고 이동을 신중하게 다뤄야 합니다.
이러한 복잡한 프로세스를 피하기위해 샘플 앱은 UICollectionViewDiffableDataSource
객체를 사용합니다. 비교가능한 데이터 소스 (diffable data source) 는 콜렉션 뷰에서 각 섹션과 아이템을 식별할 수 있는 식별자를 저장합니다. 이런 식별자는 안전하고, 변경되지 않음을 의미합니다. 반대로 UICollectionViewDataSource
를 준수하는 커스텀한 데이터 소스는 안전하지 않은 인덱스 (indice) 와 인덱스 경로 (index path) 를 사용합니다. 데이터 소스가 콜렉션 뷰의 콘텐츠를 추가, 삭제, 그리고 재정렬에 의해 변경될 수 있는 섹션과 아이템의 위치를 나타냅니다. 그러나 식별자를 가진 비교가능한 데이터 소스 (diffable data source) 는 콜렉션 뷰 내에 위치에 대한 정보가 없어도 섹션 또는 아이템을 참조할 수 있습니다.
NOTE 이 샘플은 데이터를 나타내기 위해 콜렉션 뷰를 사용하지만 테이블 뷰 또한 동일한 컨셉입니다. 테이블 뷰로 비교가능한 데이터 소스를 사용하는 것에 대한 자세한 내용은
UITableViewDiffableDataSource
를 참조하세요.
식별자로 값을 사용하려면 데이터 타입은 Hashable
프로토콜을 준수해야 합니다. 해시를 사용하면 빠르고 효과적인 조회를 제공하는 Set
, Dictionary
, 그리고 스냅샷 (snapshot) (NSDiffableDataSourceSnapshot
과 NSDiffableDataSourceSectionSnapshot
의 인스턴스) 과 같은 키로 값을 사용하는 데이터 콜렉션을 허용합니다. Hashable
타입은 Equatable
프로토콜도 준수하기 때문에 식별자는 동등성에 대한 구현도 필요합니다. 더 자세한 내용은 Equatable
을 참조하세요.
식별자는 Hashable
과 Equatable
을 준수하기 때문에 비교가능한 데이터 소스 (diffable data source) 는 현재 스냅샷 (snapshot) 과 다른 스냅샷 (snapshot) 을 비교할 수 있습니다. 이러면 콜렉션 뷰 내에서 섹션과 아이템의 차이점을 기반으로 삽입, 삭제, 그리고 이동이 가능하므로 일괄 업데이트 하는 코든느 필요하지 않습니다.
Important 동일한 두 식별자는 항상 같은 해시값을 가져야 합니다. 그러나 그 반대는 같을 필요가 없습니다. 동일한 해시 값을 가진 두 값은 반드시 같을 필요가 없습니다. 이러한 상황을 해시 충돌 (hash collision) 이라고 합니다. 효율성을 증가시키려면 동일하지 않은 식별자는 다른 해시 값을 가지도록 해야 합니다. 피할 수 없는 해시 충돌 (hash collision) 은 괜찮지만 최소한의 충돌 수를 유지해야 합니다. 그렇지 않으면 데이터 콜렉션에서 조회의 성능은 저하됩니다.
샘플 프로젝트에서 RecipeListViewController
는 콜렉션 뷰로 레시피의 목록을 나타냅니다. 컨트롤러는 레시피를 나타내기 전에 비교가능한 데이터 소스 (diffable data source) 에 저장하기 위한 인스턴스 변수 (instance variable) 에 정의합니다.
private var recipeListDataSource: UICollectionViewDiffableDataSource<RecipeListSection, Recipe.ID>!
RecipeListViewController
는 섹션 식별자 타입으로 RecipeListSection
, 그리고 아이템 식별자 타입으로 Recipe.ID
로 recipeListDataSource
로 선언합니다. 이러한 식별자 타입은 값이 포함된 타입을 데이터 소스에게 알려줍니다.
recipeListDataSource
는 RecipeListSection
을 사용하는 섹션 식별자 타입은 Int
타입의 원시값을 가지는 열거형 (enumeration) 입니다 (Swift 에서 Int
는 hashable 입니다). 각 열거형 케이스는 콜렉션 뷰의 섹션을 나타냅니다. 샘플에서 레시피의 목록을 나타내는 main
인 하나의 섹션만 있습니다.
private enum RecipeListSection: Int {
case main
}
아이템 식별자 타입은 recipeListDataSource
에서 Recipe.ID
를 사용합니다. 이 타입은 Recipe
구조체에 있으며, 아래와 같이 정의되어 있습니다:
struct Recipe: Identifiable, Codable {
var id: Int
var title: String
var prepTime: Int // In seconds.
var cookTime: Int // In seconds.
var servings: String
var ingredients: String
var directions: String
var isFavorite: Bool
var collections: [String]
fileprivate var addedOn: Date? = Date()
fileprivate var imageNames: [String]
}
이 구조체는 id
프로퍼티를 포함해야 하는 구조체를 요구하는 Identifiable
프로토콜을 준수합니다. Identifiable
을 준수함으로써, Recipe
구조체는 구조체에서 id
프로퍼티의 구현을 기반으로 연관된 타입 ID
를 자동으로 사용할 수 있습니다. 그리고 이 타입은 hashable 이기 때문에 샘플 앱은 아이템 식별자 타입으로 Recipe.ID
를 사용할 수 있습니다.
NOTE
Recipe
구조체는Hashable
프로토콜을 준수하지 않습니다. 레시피 구조체는 완벽하지 않고 비교가능한 데이터 소스 (diffable data source) 와 스냅샷 (snapshot) 에 저장된 아이템은 레시피 식별자(identifier) (각 레시피를 제공하기 위한Recipe.ID
값) 이므로 hashable 하지 않아도 됩니다.
recipeListDataSource
에 대해 아이템 식별자 타입으로 Recipe.ID
를 사용하는 것은 데이터 소스와 데이터 소스에 적용 된 모든 스냅샷 (snapshot) 에 완벽한 레시피 데이터가 아닌 Recipe.ID
값만 포함된다는 의미입니다. 이러한 접근 방식은 식별자 타입은 간단하고 hashable 타입이므로 콜렉션 뷰에서 레시피를 나타낼 때 최상의 성능을 위해 비교가능한 데이터 소스 (diffable data source) 를 최적화 합니다.