Warning: Photos.framework may unleash Zalgo
For the most part the new Photos.framework is a refreshing replacement for the old Asset Library interface. It was so clearly built to handle the massive (and every increasing) size of photo libraries.
Sore Spots
There are a few sore spots. The first is annoying but minor: The title that Photos displays for an individual photo is not available; it's usually a location name but the only thing you get on PHAsset
is the CLLocation2D
. Even though Photos has the title, you have to use the reverse geocoder to look it up yourself. Not a deal-breaker by any measure.
The second is also minor: If you submit a lot of thumbnail cache requests then attempt to cache cancel them Photos.framework will deadlock. This was triggered by a bug in my own code which I solved by making sure I limit the number of cache requests. It is slightly worrying because it means there is some amount of non-thread-safe logic in the framework.
Zalgo
The last issue is much more serious. Photos.framework will unleash Zalgo.
The documentation on PHImageRequestOptions
has the money quote:
Photos may call the resultHandler block (that you specified in the requestImageForAsset:targetSize:contentMode:options:resultHandler: method more than once. Photos may call your result handler once to provide a low-quality image suitable for displaying temporarily while it prepares a high-quality image. If low-quality image data is immediately available, this first call may occur before the requestImageForAsset:targetSize:contentMode:options:resultHandler: method returns. When the high-quality image is ready, Photos calls your result handler again to provide it. If the image manager has already cached the requested image, Photos calls your result handler only once.
Did you catch that? Photos.framework may sometimes invoke your callback before the initial call returns, in other situations it will be invoked later.
Abandoning Reason
The basic problem is that you can't properly reason about the application's state if the callback may or may not observe the side-effects surrounding the parent function call. This is the exact scenario I ran into... I've been using ReSwift on a personal project and when the callback is invoked synchronously the callback dispatches an action attempting to follow-up on another action that hasn't been dispatched yet (and which can't be because it requires the request id... which I can't get because the call to get it hasn't returned yet!)
You might be tempted to think this behavior is OK so long as photos dispatches to a different queue but that still leaves a race condition if you need the request ID.
This can be worked around by funneling all callback invocations (and all side-effects of the thread making the request) through a serial queue (or the main queue). dispatch_async
from the callback and you can ensure a globally consistent ordering and a҉v͡oi͟d̴ ̷the ̷wr̷a̕th ͟of́ Z̝̬̮͔ͥ̈́͋̽͐̔a͔̻̫͉͐ͣ͊͛l͇̞̻̼̈́͐ͣ̐̕g͆͜o̵̰̫͍͊͑̓̏͊ͧ.
This blog represents my own personal opinion and is not endorsed by my employer.