I am following Apple's Sample code 'ExampleappusingPhotosframework'.
In the subject of PHAssets/PHAssetCollection, I noticed there is the possibility of 'Folders' being present. These can appear from being created be Mac's Photo's App, or an older iPhoto app, ect....
There are possibilities of a folder being present amount PHAssetCollection's and that folder can contain unknown levels of nested folders, ending with an Album(s) at the end.
So, even above sample project crashes if I have nested folders in the photo library. Most app's just seem to ignore and not show these folders at all.
For an example lets say this example exists:
Folder Level 1 > Folder Level 2 > Custom Album > Image1, Image2
How iOS Photos App Handles it:
In the Root Album list, under user albums, "Folder Level 1" should be seen. When that's tapped, I want to list all the images from all the sub folders regardless of sub levels.....
viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
let addButton = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(self.addAlbum))
self.navigationItem.rightBarButtonItem = addButton
// Create a PHFetchResult object for each section in the table view.
let allPhotosOptions = PHFetchOptions()
allPhotosOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: true)]
allPhotos = PHAsset.fetchAssets(with: allPhotosOptions)
smartAlbums = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .albumRegular, options: nil)
userCollections = PHCollectionList.fetchTopLevelUserCollections(with: nil)
PHPhotoLibrary.shared().register(self)
}
cellForRowAt:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch Section(rawValue: indexPath.section)! {
case .allPhotos:
let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier.allPhotos.rawValue, for: indexPath)
cell.textLabel!.text = NSLocalizedString("All Photos", comment: "")
return cell
case .smartAlbums:
let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier.collection.rawValue, for: indexPath)
let collection = smartAlbums.object(at: indexPath.row)
cell.textLabel!.text = collection.localizedTitle
return cell
case .userCollections:
let cell = tableView.dequeueReusableCell(withIdentifier: CellIdentifier.collection.rawValue, for: indexPath)
let collection = userCollections.object(at: indexPath.row)
cell.textLabel!.text = collection.localizedTitle
return cell
}
}
Segue:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let destination = (segue.destination as? UINavigationController)?.topViewController as? AssetGridViewController
else { fatalError("unexpected view controller for segue") }
let cell = sender as! UITableViewCell
destination.title = cell.textLabel?.text
switch SegueIdentifier(rawValue: segue.identifier!)! {
case .showAllPhotos:
destination.fetchResult = allPhotos
case .showCollection:
// get the asset collection for the selected row
let indexPath = tableView.indexPath(for: cell)!
let collection: PHCollection
switch Section(rawValue: indexPath.section)! {
case .smartAlbums:
collection = smartAlbums.object(at: indexPath.row)
case .userCollections:
collection = userCollections.object(at: indexPath.row)
default: return // not reached; all photos section already handled by other segue
}
// configure the view controller with the asset collection
guard let assetCollection = collection as? PHAssetCollection
else { fatalError("expected asset collection") }
destination.fetchResult = PHAsset.fetchAssets(in: assetCollection, options: nil)
destination.assetCollection = assetCollection
}
}
assetCollection?.canContainCollections seems to be the deciding factor. However, how would I fetch all the containing PHAssets within the folder, regardless of the sub-levels/sub-folders? (This cannot be impossible since even the stock app has "All Photos" inside level 1)
With the current developer APIs (PhotoKit) there is no easy way (like with a single request) to list all assets in a folder (and its subfolders/albums). However, you can of recursively enumerate all albums in the folder and it's subfolders and then put the results into a single array. This however can be slow depending on the number of levels and assets contained in the nested structure. Please note, that a folder on any level can also contain an album (e.g. on "Folder Level 1" there could also be an album in addition to the folder "Folder Level 2"). Apple's Photos App is not using PhotoKit - so I assume they have a way to do this with a single request.