```swift class PhotoLibrary: ObservableObject { @Published var assets = [PHAsset]() @Published var imageCachingManager: PHCachingImageManager = PHCachingImageManager() func requestAuthorization() { PHPhotoLibrary.requestAuthorization { [weak self] (status) in guard let self = self else { return } switch status { case .authorized: self.getAllPhotos() case .denied: break case .notDetermined: break case .restricted: break case .limited: self.getAllPhotos() @unknown default: break } } } private func getAllPhotos() { imageCachingManager.allowsCachingHighQualityImages = false let allPhotosOptions = PHFetchOptions() allPhotosOptions.includeHiddenAssets = false allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)] let assets: PHFetchResult = PHAsset.fetchAssets(with: .image, options: allPhotosOptions) var _assets = [PHAsset]() assets.enumerateObjects({ asset, _, _ in _assets.append(asset) }) DispatchQueue.main.async { [weak self] in self?.assets = _assets } } func resetCache() { imageCachingManager.stopCachingImagesForAllAssets() assets = [] } func preCacheImages(assets: [PHAsset], size: CGSize) { DispatchQueue.global(qos: .userInitiated).async { [weak self] in guard let self = self else { return } let options = PHImageRequestOptions() options.deliveryMode = .opportunistic options.resizeMode = .fast options.isSynchronous = true options.isNetworkAccessAllowed = true self.imageCachingManager.startCachingImages(for: assets, targetSize: size, contentMode: .aspectFill, options: options) } } func fetchImage(index: Int, size: CGSize?) async -> UIImage? { guard let asset = assets[safe: index] else { return nil } let options = PHImageRequestOptions() options.deliveryMode = .opportunistic options.resizeMode = .fast options.isSynchronous = true options.isNetworkAccessAllowed = true return await withCheckedContinuation({ [weak self] (continuation: CheckedContinuation<UIImage?, Never>) in guard let self = self else { return } self.imageCachingManager.requestImage(for: asset, targetSize: size ?? PHImageManagerMaximumSize, contentMode: .aspectFill, options: options) {(image, info) in continuation.resume(returning: image) } }) } func removeFromCache(index: Int, size: CGSize) { DispatchQueue.global(qos: .userInitiated).async { [weak self] in guard let self = self else { return } guard let asset = self.assets[safe: index] else { return } let options = PHImageRequestOptions() options.deliveryMode = .opportunistic options.resizeMode = .fast options.isSynchronous = true options.isNetworkAccessAllowed = true self.imageCachingManager.stopCachingImages(for: [asset], targetSize: size, contentMode: .aspectFill, options: options) } } } ``` ```swift struct PhotoView: View { var index: Int let thumbnailSize: CGSize? weak var photoLibrary: PhotoLibrary? @State var image: UIImage? = nil var body: some View { HStack { if let image = image { Image(uiImage: image) .resizable() .square() } else { Color.label .aspectRatio(1.0, contentMode: .fill) } } .onDisappear(perform: { unload() }) .onAppear(perform: { Task { guard let photoLibrary = photoLibrary else { return } let image = await photoLibrary.fetchImage(index: index, size: thumbnailSize ?? PHImageManagerMaximumSize) DispatchQueue.main.async { self.image = image } } }) } private func unload() { self.image = nil } } ``` ```swift struct PickProfilePictureView: View { @ObservedObject var photoLibrary = PhotoLibrary() var body: some View { VStack(alignment: .leading) { GeometryReader { reader in let width = reader.size.width / 4 - 6 ScrollView { LazyVGrid(columns: [GridItem(), GridItem(), GridItem(), GridItem()]) { ForEach(photoLibrary.assets.indices, id: \.self) { index in PhotoView(index: index, thumbnailSize: CGSize(width: width, height: width), photoLibrary: photoLibrary) .contentShape(Rectangle()) .onDisappear(perform: { photoLibrary.removeFromCache(index: index, size: CGSize(width: width, height: width)) }) } } } } Spacer() } .ignoresSafeArea(.container, edges: .bottom) .onAppear { self.photoLibrary.requestAuthorization() } } } ```