Crash in photolibraryd Process During Import in Photos.app & PHAsset.fetchAssets() Call

Problem:

While calling PHAsset.fetchAssets() and iterating over its results, if the Photos.app is simultaneously running an import operation (File | Import), the photolibraryd process crashes. I have already flagged this issue to Apple (FB13178379) but wanted to check if anyone else has encountered this behavior.

Steps to Reproduce:

  1. Initiate an import in the Photos.app.
  2. Run the following code snippet in the Playground:
import Photos

PHPhotoLibrary.requestAuthorization(for: .readWrite) { authorizationStatus in
    guard authorizationStatus == .authorized else { return }
    let fetchResult = PHAsset.fetchAssets(with: nil)
    print(fetchResult)
    for i in 0..<fetchResult.count {
        print(fetchResult[i])
    }
}

Upon doing this, I consistently receive the error: Connection to assetsd was interrupted - photolibraryd exited, died, or closed the photo library in the Console, causing my code to hang.

Environment:

  • macOS Version: 13.5.2 (22G91)
  • Xcode Version: Version 14.3.1 (14E300c)

Additional Info:

After the crash, a report pops up in the Console, and typically, the Photos.app import operation freezes too. I've noticed that after terminating all processes and/or rebooting, the Photos.app displays "Restoring from iCloud…" and the recovery process lasts overnight.

Seeking Suggestions:

I'm exploring potential workarounds for this issue. I've attempted to use fetchLimit to obtain assets in batches, but the problem persists. Is there a method to detect that the Photos.app is executing an import, allowing my process to wait until completion? Alternatively, can I catch the photolibraryd crash and delay until it's restored?

I'm operating in a batch processing mode for photos, so pausing and retrying later in the event of a Photos.app import isn't an issue.

Any guidance or shared experiences would be greatly appreciated!

Cheers and happy coding!

For future reference for anyone else that encountered this problem, my workaround is to wrap all calls to the PhotoKit library with a timeout handler like this:

enum TimeoutError: Error {
    case timedOut
}

func withTimeout<T>(_ timeout: TimeInterval, block: @escaping () -> T) throws -> T {
    let semaphore = DispatchSemaphore(value: 0)
    var result: T? = nil
    
    DispatchQueue.global().async {
        result = block()
        semaphore.signal()
    }
    
    if semaphore.wait(timeout: .now() + timeout) == .timedOut {
        // function did not finish in time
        throw TimeoutError.timedOut
    } else {
        // function finished in time
        return result!
    }
}

A timeout of 5 seconds seems to work well on my machine. If a timeout is thrown, my app will stop processing and try again later. The photolibraryd process still crashes sometimes, but the system seems to recover and the import process in the Photos.app will continue and when my app tries to fetch again it is usually successful.

Not a great solution, but the best I could come up with.

One problem is that sometimes the fetch function (represented by block() in the code above) never returns which results in an orphaned task. I don't think there is a way to kill this task so it will be orphaned as long as my app is running...

Crash in photolibraryd Process During Import in Photos.app &amp; PHAsset.fetchAssets() Call
 
 
Q