iOS 18 PhotoKit fetchAssetCollections Compatibility
iOS 18 PhotoKit fetchAssetCollections Compatibility

iOS 18 PhotoKit fetchAssetCollections Compatibility

In iOS app development, nearly all apps that access the user’s photo library rely on PhotoKit’s PHAssetCollection.fetchAssetCollections(with:subtype:options:) API.
However, with the release of iOS 18 SDK, many developers have noticed that the same code no longer returns the expected albums on the new system. This article explains the reason behind this change and provides the correct usage.

在 iOS 应用开发中,几乎所有需要访问用户相簿的 App 都会用到 PhotoKit 的接口 —— PHAssetCollection.fetchAssetCollections(with:subtype:options:)
然而,随着 iOS 18 SDK 的发布,不少开发者发现:同样的代码在新系统上不再返回预期的相簿。本文将详细解释这一变化的根源,并给出正确的写法。


1. Observed Issue

Developers migrating to iOS 18 SDK have reported that the following code no longer correctly fetches the “user library” or “custom albums”:

let result = PHAssetCollection.fetchAssetCollections(
    with: .album,
    subtype: .smartAlbumUserLibrary,
    options: nil
)

On older systems (like iOS 17), this code might have worked “by coincidence,” returning the user’s “All Photos” or custom albums.
On iOS 18, however, it often returns an empty collection or incomplete results.


2. Root Cause: Type and Subtype Mismatch

The root cause is that <strong>PHAssetCollectionType</strong> and <strong>PHAssetCollectionSubtype</strong> have a strict one-to-one correspondence.

Apple’s official Objective-C documentation clearly states:

PHAssetCollectionTypeAlbum (.album) should only be used with subtypes that represent user albums;
PHAssetCollectionTypeSmartAlbum (.smartAlbum) should only be used with subtypes that represent smart albums.

In other words:

  • .album corresponds to .albumRegular, .albumImported, etc.;
  • .smartAlbum corresponds to .smartAlbumUserLibrary, .smartAlbumFavorites, etc.

smartAlbumUserLibrary clearly belongs to Smart Albums, yet it was incorrectly paired with .album.
While older systems tolerated this mismatch internally, this behavior was never officially supported. With iOS 18, Apple likely removed this non-standard compatibility, causing older code to fail.


3. Incorrect vs. Correct Usage

❌ Incorrect Example (Mismatched)

// Type and subtype do not match
PHAssetCollection.fetchAssetCollections(
    with: .album,
    subtype: .smartAlbumUserLibrary,
    options: nil
)

✅ Correct Examples (Matching or Flexible)

// Option 1: Correct type/subtype pairing
PHAssetCollection.fetchAssetCollections(
    with: .smartAlbum,
    subtype: .smartAlbumUserLibrary,
    options: nil
)

// Option 2: Use `.any` if subtype is uncertain
PHAssetCollection.fetchAssetCollections(
    with: .album,
    subtype: .any,
    options: nil
)

If you want to enumerate all user-created albums, with: .album, subtype: .any is the safest approach.
For system albums like “All Photos” or “Recently Added,” use .smartAlbum as the type.


4. iOS 18 Changes and Compatibility Recommendations

iOS 18 introduces major UI and underlying data structure changes to the Photos app, including a single-scrollable view and more diversified smart album categories.
Consequently, PhotoKit now enforces stricter validation of type/subtype combinations in the SDK.

Recommendations:

  1. Audit all usages of fetchAssetCollections(with:subtype:options:);
  2. Ensure that type and subtype are correctly paired;
  3. For multi-version compatibility, prefer .any for subtype;
  4. For user-created albums only:PHAssetCollection.fetchAssetCollections(with: .album, subtype: .any, options: nil)and filter out system albums manually.

5. Summary

  • Starting with iOS 18, PhotoKit enforces stricter type/subtype validation;
  • .album should no longer be used with .smartAlbumUserLibrary;
  • The root cause of older code failing is a type/subtype mismatch;

#pragma mark - PHAssetCollection types

typedef NS_ENUM(NSInteger, PHAssetCollectionType) {
    PHAssetCollectionTypeAlbum           = 1,
    PHAssetCollectionTypeSmartAlbum      = 2,

    PHAssetCollectionTypeMoment     API_DEPRECATED("Will be removed in a future release", ios(8, 13), tvos(10, 13)) API_UNAVAILABLE(macos) = 3,

};

typedef NS_ENUM(NSInteger, PHAssetCollectionSubtype) {
    
    // PHAssetCollectionTypeAlbum regular subtypes
    PHAssetCollectionSubtypeAlbumRegular         = 2,
    PHAssetCollectionSubtypeAlbumSyncedEvent     = 3,
    PHAssetCollectionSubtypeAlbumSyncedFaces     = 4,
    PHAssetCollectionSubtypeAlbumSyncedAlbum     = 5,
    PHAssetCollectionSubtypeAlbumImported        = 6,
    
    // PHAssetCollectionTypeAlbum shared subtypes
    PHAssetCollectionSubtypeAlbumMyPhotoStream   = 100,
    PHAssetCollectionSubtypeAlbumCloudShared     = 101,
    
    // PHAssetCollectionTypeSmartAlbum subtypes
    PHAssetCollectionSubtypeSmartAlbumGeneric    = 200,
    PHAssetCollectionSubtypeSmartAlbumPanoramas  = 201,
    PHAssetCollectionSubtypeSmartAlbumVideos     = 202,
    PHAssetCollectionSubtypeSmartAlbumFavorites  = 203,
    PHAssetCollectionSubtypeSmartAlbumTimelapses = 204,
    PHAssetCollectionSubtypeSmartAlbumAllHidden  = 205,
    PHAssetCollectionSubtypeSmartAlbumRecentlyAdded = 206,
    PHAssetCollectionSubtypeSmartAlbumBursts     = 207,
    PHAssetCollectionSubtypeSmartAlbumSlomoVideos = 208,
    PHAssetCollectionSubtypeSmartAlbumUserLibrary = 209,
    PHAssetCollectionSubtypeSmartAlbumSelfPortraits API_AVAILABLE(ios(9)) = 210,
    PHAssetCollectionSubtypeSmartAlbumScreenshots API_AVAILABLE(ios(9)) = 211,
    PHAssetCollectionSubtypeSmartAlbumDepthEffect API_AVAILABLE(macos(10.13), ios(10.2), tvos(10.1)) = 212,
    PHAssetCollectionSubtypeSmartAlbumLivePhotos API_AVAILABLE(macos(10.13), ios(10.3), tvos(10.2)) = 213,
    PHAssetCollectionSubtypeSmartAlbumAnimated API_AVAILABLE(macos(10.15), ios(11), tvos(11)) = 214,
    PHAssetCollectionSubtypeSmartAlbumLongExposures API_AVAILABLE(macos(10.15), ios(11), tvos(11)) = 215,
    PHAssetCollectionSubtypeSmartAlbumUnableToUpload API_AVAILABLE(macos(10.15), ios(13), tvos(13)) = 216,
    PHAssetCollectionSubtypeSmartAlbumRAW API_AVAILABLE(macos(12), ios(15), tvos(15)) = 217,
    PHAssetCollectionSubtypeSmartAlbumCinematic API_AVAILABLE(macos(12), ios(15), tvos(15)) = 218,
    PHAssetCollectionSubtypeSmartAlbumSpatial API_AVAILABLE(macos(15), ios(18), tvos(18)) = 219,
    
    // Used for fetching, if you don't care about the exact subtype
    PHAssetCollectionSubtypeAny = NSIntegerMax
};

Leave a Reply

Your email address will not be published. Required fields are marked *