I am trying to section user's Photo Library pictures in a Collection View based on their creation date in ascending order. I am using this method which obviously is painfully slow especially when the number of pictures is high.
First I fetch the PHAssets in sorted order:
let allPhotosOptions = PHFetchOptions()
allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
inFetchResult = PHAsset.fetchAssets(with: allPhotosOptions)
and then I copy the assets inside an array:
inFetchResult.enumerateObjects { (asset, index, stop) in
let yearComponent = Calendar.current.component(.year, from: asset.creationDate!)
let monthComponent = Calendar.current.component(.month, from: asset.creationDate!)
let monthName = DateFormatter().monthSymbols[monthComponent - 1]
var itemFound = false
for (index, _) in self.dateArray.enumerated() {
if self.dateArray[index].date == "\(monthName) \(yearComponent)" {
self.dateArray[index].assets.append(asset)
itemFound = true
break
} else {
continue
}
}
if !itemFound {
self.dateArray.append((date: "\(monthName) \(yearComponent)", assets: [asset]))
}
}
I then use this array as my data source.
Is there a better way to do this? I have tried dictionaries but they change the order of the objects. I also have considered finding a way to only add the assets to my dateArray when they are going to be displayed on view, however, collection view needs to know the total number of sections up front so I must go through all of the pictures and check their dates before loading the view.
You can fetch photo assets and do all the sorting logic in background thread like in the below code, once you will done with the heavy processing then you can access main Thread to update the UI.
let allPhotosOptions = PHFetchOptions()
allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
DispatchQueue.global(qos: .userInitiated).async {
let photos = PHAsset.fetchAssets(with: .image, options: nil)
var result = [Any]()
photos.enumerateObjects({ asset, _, _ in
// do the fetched photos logic in background thread
})
DispatchQueue.main.async {
// once you will get all the data, you can do UI related stuff here like
// reloading data, assigning data to UI elements etc.
}
}