Skip to main content

Blink/Model/PHFetchResultCollection.swift

1import Photos
2import SwiftUI
4/// Implement a RandomAccessCollection for a PHFetchResult.
5///
6/// This wrapper allows us to use a PHFetchResult in a SwiftUI ForEach loop,
7/// for example:
8///
9/// ```swift
10/// let fetchResult = PHAsset.fetchAssets(…)
11/// let collection = PHFetchResultCollection(fetchResult: fetchResult)
12///
13/// var body: some View {
14/// ForEach(collection, id: \.localIdentifier) {
15/// ...
16/// }
17/// }
18/// ```
19///
20/// This collection vends the IndexedPHAsset struct, which tells us both
21/// what asset we're on and where we are -- this is necessary for performance
22/// reasons. See the comment above the ForEach in PHAssetHStack.
23///
24/// This is based on code written by Slava Semeniuk on Stack Overflow:
25/// https://stackoverflow.com/q/62745595/1558022
27struct IndexedPHAsset {
28 var position: Int
29 var asset: PHAsset
32struct PHFetchResultCollection: RandomAccessCollection, Equatable {
33 typealias Element = IndexedPHAsset
34 typealias Index = Int
36 let fetchResult: PHFetchResult<PHAsset>
38 init(_ fetchResult: PHFetchResult<PHAsset>) {
39 self.fetchResult = fetchResult
40 }
42 var startIndex: Int { 0 }
43 var endIndex: Int { fetchResult.count }
45 subscript(position: Int) -> IndexedPHAsset {
46 IndexedPHAsset(
47 position: position,
48 asset: fetchResult.object(at: position)
49 )
50 }
53struct PHFetchResultCollection_Previews: PreviewProvider {
54 static var resultCollection: PHFetchResultCollection {
55 let options = PHFetchOptions()
56 options.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
57 options.fetchLimit = 3
59 let fetchResult: PHFetchResult<PHAsset> = PHAsset.fetchAssets(with: options)
61 return PHFetchResultCollection(fetchResult)
62 }
64 static var previews: some View {
65 VStack {
66 Text("These dates should be in descending order:")
68 ForEach(self.resultCollection, id: \.asset.localIdentifier) { indexedAsset in
69 Text("\(indexedAsset.asset.creationDate?.ISO8601Format() ?? "(unknown)")")
70 }
71 }
72 }