Every day, more photos are taken with the iPhone than any other camera. Displays on iOS devices get better every year, but even back in the pre-Retina era when the iPad was introduced , one of its killer uses was just displaying user photos and exploring the photo library. Since the camera is one of the iPhone's most important and popular features, there is a big demand for apps and utilities that make use of the wealth of users' photo libraries.
Until the summer of 2014, developers used the AssetsLibrary Framework to access the ever-growing photo libraries of users. Over the years, Camera.app and Photos.app have changed significantly, adding new features, including a way of organizing photos by moments. Meanwhile, the AssetsLibrary framework lagged behind.
With iOS 8, Apple has given us PhotoKit, a modern framework that's more performant than AssetsLibrary and provides features that allow applications to work seamlessly with a device's photo library.
We'll start with a bird's-eye view of the framework's object model : the entities and the relationships between them, fetching instances of those entities, and working with the fetch results.
Additionally, we'll cover the asset metadata that wasn't available to developers when using AssetsLibrary.
Then we'll discuss loading the assets' image data : the process itself, multitudes of available options, some gotchas, and edge cases.
PhotoKit Object Model
PhotoKit defines an entity graph that models the objects presented to the user in the stock Photos.app. These photo entities are lightweight and immutable. All the PhotoKit objects inherit from the abstract
base class, whose public interface only provides a
represents a single asset in the user's photo library, providing the metadata
for that asset.
Groups of assets are called asset collections and are represented by the
class. A single asset collection can be an album or a moment in the photo library, as well as one of the special "smart albums." These include collections of all videos, recently added items, user favorites, all burst photos, and more
is a subclass of
represents a group of
s. Since it is a
itself, a collection list can contain other collection lists, allowing for complex hierarchies of collections. In practice, this can be seen in the Moments tab in the Photos.app: Asset --- Moment --- Moment Cluster --- Moment Year.
Fetching Photo Entities
Fetching vs. Enumerating
Those familiar with the AssetsLibrary framework might remember that to be able to find assets with specific properties, one has to enumerate through the user's library and collect the matching assets. Granted, the API provided some ways of narrowing down the search domain , but it still remains quite unwieldy.
In contrast, PhotoKit entity instances are fetched . Those familiar with Core Data will recognize the approaches and concepts used and described here.
Fetches are made using the class methods of the entities described above. Which class/method to use depends on the problem domain and how you're representing and traversing the photo library. All of the fetch methods are named similarly:
class func fetchXXX(..., options: PHFetchOptions) -> PHFetchResult
parameter gives us a way of filtering and ordering the returned results, similar to
You may have noticed that these fetch methods aren't asynchronous. Instead, they return a
object, which allows access to the underlying collection of results with an interface similar to
. It will dynamically load its contents as needed and cache contents around the most recently requested value. This behavior is similar to the result array of an
with a set
property. There is no way to parametrize this behavior for
, but the documentation promises
“optimal performance even when handling a large number of results.”
s returned by the fetch methods will not be updated automatically if the photo library contents match the request change. Observing changes and processing updates for a given
are described in a later section
You might find that you have designed a component that operates on an asset collection, and yet you would like to be able to use it with an arbitrary set of assets. PhotoKit provides an easy way to do that using transient asset collections.
Transient asset collections are created explicitly by you from an array of
objects or from a
containing assets. This is done using the
factory methods on
. The objects vended by these methods can be used just like any other
. Despite that, these collections aren't saved to the user's photos library and thus aren't displayed in the Photos.app.
Similarly to asset collections, you can create transient collection lists by using the
factory methods on
This can turn out to be very useful when you need to combine results from two fetch requests.
As mentioned in the beginning of this article, PhotoKit provides some additional metadata about user assets that wasn't available (or at least not as easily available) in the past when using ALAssetsLibrary.
HDR and Panorama Photos
You can use a photo asset's
property to find out if the underlying image was captured with HDR enabled and whether or not it was shot in the Camera.app's Panorama mode.
Favorite and Hidden Assets
To find out if an asset was marked as favorite or was hidden by the user, just inspect the
properties of the
Burst Mode Photos
for assets that are representative of a burst photo sequence (multiple photos taken while the user holds down the shutter). It will also have a
value which can then be used to fetch the rest of the assets in that burst sequence via
The user can flag assets within a burst sequence; additionally, the system uses various heuristics to mark potential user picks automatically. This metadata is accessible via
property. This property is a bitmask with three defined constants:
for assets marked manually by the user,
for potential user picks, and
for unmarked assets.
The screenshot shows how Photos.app automatically marks potential user picks in a burst sequence.
Over the years of working with user photo libraries, developers created hundreds (if not thousands) of tiny pipelines for efficient photo loading and display. These pipelines dealt with request dispatching and cancelation, image resizing and cropping, caching, and more. PhotoKit provides a class that does all this with a convenient and modern API:
Image requests are dispatched using the
method. The method takes in a
, desired sizing of the image and other options (via the
parameter object), and a results handler. The returned value can be used to cancel the request if the requested data is no longer necessary.
Image Sizing and Cropping
Curiously, the parameters regarding the sizing and cropping of the result image are spread across two places. The
parameters are passed directly into the
method. The content mode describes whether the photo should be aspect-fitted or aspect-filled into the target size, similar to UIView's
. Note: If the photo should not be resized or cropped, pass
provides means of specifying how
the image manager should resize. The
property can be set to
(when the result image must match the target size),
(more efficient than .Exact, but the resulting image might differ from the target size), or
. Furthermore, the
property lets us specify how the image manager should crop the image. Note: If
is provided, set
Request Delivery and Progress
By default, the image manager will deliver a lower-quality version of your image before delivering the high-quality version if it decides that's the optimal strategy to use. You can control this behavior through the
property; the default behavior described above is
. Set it to
if you're only interested in the highest quality of the image available and if longer load times are acceptable. Use
to load the image faster while sacrificing the quality.
You can make the
method synchronous using the
. Note: When
is set to
property is ignored and considered to be set to
When setting these parameters, it is important to always consider that some of your users might have iCloud Photo Library enabled. The PhotoKit API doesn't necessarily distinguish photos available on-device from those available in the cloud — they are all loaded using the same
method. This means that every single one of your image requests may potentially be a slow network request over the cellular network. Keep this in mind when considering using
and/or making your requests synchronous. Note: If you want to make sure that the request doesn't hit the network, set
Another iCloud-related property is
. You can set it to a
block that will be called by the image manager when downloading the photo from iCloud.
PhotoKit allows apps to make non-destructive adjustments to photos. For edited photos, the system keeps a separate copy of the original image and the app-specific adjustment data. When fetching assets using the image manager, you can specify which version of the image asset should be delivered via the result handler. This is done by setting the
will deliver the image with all adjustments applied to it;
delivers the image before any adjustments are applied to it; and
delivers the image in its original, highest-quality format (e.g. the RAW data, while
would deliver a JPEG).
You can read more about this aspect of the framework in Sam Davies' article on Photo Extensions .
The result handler is a block that takes in a
dictionary. It can be called by the image manager multiple times throughout the lifetime of the request, depending on the parameters and the request options.
dictionary provides information about the current status of the request, such as:
Whether the image has to be requested from iCloud (in which case you're going to have to re-request the image if you initially set
Whether the currently delivered
UIImageis the degraded form of the final result. This lets you display a preview of the image to the user while the higher-quality image is being downloaded —
The request ID (convenience for canceling the request), and whether the request has already been canceled —
An error, if an image wasn't provided to the result handler —
These values let you update your UI to inform your user and, together with the
discussed above, hint at the loading state of their images.
At times it's useful to load some images into memory prior to the moment when they are going to be shown on the screen, for example when displaying a screen with a large number of asset thumbnails in a scrolling collection. PhotoKit provides a
subclass that deals with that specific use case –
provides a single key method –
. You pass in an array of
s, the request parameters and options that should match those you're going to use later when requesting individual images. Additionally, there are methods that you can use to inform the caching manager to stop caching images for a list of specific assets and to stop caching all images.
property lets you specify whether the image manager should prepare images at high quality. When caching a short and unchanging list of assets, the default
value should work just fine. When caching while quickly scrolling in a collection view, it is better to set it to
Note: In my experience, using the caching manager can be detrimental to scrolling performance when the user is scrolling extremely fast through a large asset collection. It is extremely important to tailor the caching behavior for the specific use case. The size of the caching window, when and how often to move the caching window, the value of the
property — these parameters should be carefully tuned and the behavior tested with a real photo library and on target hardware. Furthermore, consider setting these parameters dynamically based on the user's actions.
Requesting Image Data
Finally, in addition to requesting plain old
provides another method that returns the asset data as an
object, its universal type identifier, and the display orientation of the image. This method returns the largest available representation of the asset.
The Times They Are A-Changin'
We have discussed requesting metadata of assets in the user's photo library, but we haven't covered how to keep our fetched data up to date. The photo library is essentially a big bag of mutable state, and yet the photo entities covered in the first section are immutable. PhotoKit lets you receive notifications about changes to the photo library together with all the information you need to correctly update your cached state.
First, you need to register a change observer (conforming to the
protocol) with the shared
object using the
method. The change observer's
method will be called whenever another app or the user makes a change in the photo library that affects any assets or collections that you fetched prior to the change
. The method has a single parameter of type
, which you can use to find out if the changes are related to any of the fetched objects that you are interested in.
Updating Fetch Results
provides methods you can call with any
s whose changes you are interested in tracking –
. If there are no changes, these methods will return
, otherwise you will be vended a
provides a reference to an updated photo entity object, as well as boolean flags telling you whether the object's image data was changed and whether the object was deleted.
encapsulates information about changes to a
that you have previously received after a fetch.
is designed to make updates to a collection view or table view as simply as possible. Its properties map exactly to the information you need to provide in a typical collection view update handler. Note that to update
correctly, you must process the changes in the correct order: RICE
). Furthermore, the
property of the change details can be set to
, meaning that the old fetch result should just be replaced by the new value as a whole. You should call
in such cases.
Note: There is no need to make change processing centralized. If there are multiple components of your application that deal with photo entities, then each of them could have its own
. The components can then query the
objects on their own to find out if (and how) they need to update their own state.
Wind of Change
Now that we know how to observe changes made by the user and other applications, we should try making our own!
Changing Existing Objects
Performing changes on the photo library using PhotoKit boils down to creating a change request object linked to one of the assets or asset collections and setting relevant properties on the request object or calling appropriate methods describing the changes you want to commit. This has to happen within a block submitted to the shared
method. Note: You should be prepared to handle failure in the completion block passed to the
method. This approach provides safety and relative ease of use, while working with state that can be changed by multiple actors, such as your application, the user, other applications, and photo extensions.
To modify assets, create a
. You can then modify the creation date, the asset's location, and whether or not it should be hidden and considered a user's favorite. Additionally, you can delete the asset from the user's library.
Similarly, to modify asset collections or collection lists, create a
. You can then modify the collection title, add or remove members of the collection, or delete the collection altogether.
Note that before your changes are committed to the user's library, an alert might be shown to acquire explicit authorization from the user.
Creating New Objects
Creating new assets is done similarly to changing existing assets. Just use the appropriate
factory method when creating the change request, and pass the asset image data (or a URL) into it. If you need to make additional changes related to the newly created asset, you can use the creation change request's
property. It returns a placeholder that can be used in lieu of a reference to a "real"
We have discussed the basics of PhotoKit, but there is still a lot to be discovered. You should learn more by poking around the sample code , watching the WWDC session video , and just diving in and writing some code of your own! PhotoKit enabled a new world of possibilities for iOS developers, and we are sure to see more creative and clever products built on its foundation in the coming months and years.