PhotoKit Limited Access and iCloud Photos: A Reliable Loading Workflow
- Author
- Ehsan Azish · 3NSOFTS
- Updated
- Read time
- 6 min read
- Level
- Intermediate
- Platform
- Swift, iOS app development, asynchronous loading
Implementation Notes
- ~/ What broke: A production edge case that generic tutorials skip.
- ~/ What to do: Ship the production fix with clear state, errors, and fallback behavior.
A photo feed needs to distinguish three questions: may the app read this asset, are its bytes available locally, and does this view still want the result? A spinner cannot answer all three.
This guide describes a loading contract for a read-only photo browser. It is an implementation pattern, not a claim that every app needs full-library access or background downloads.
1. Treat limited access as a usable library
Apple's limited-library documentation describes access to the items the person selects. Design the experience around that selection. Do not label limited authorization as an error or block the feed behind a request for everything.
Separate the following states in your view model:
- Permission not requested. What to display: Explain why photos are needed. Next action: Ask after the person chooses to continue.
- Selected-photo access. What to display: The accessible selection. Next action: Offer a way to manage that selection.
- Broader access. What to display: Accessible library results. Next action: Browse normally.
- Access denied or restricted. What to display: An explanation, not a blank feed. Next action: Appropriate settings guidance.
- Access granted, zero results. What to display: An empty-library message. Next action: Let the person change the selection.
Recheck access when returning from Settings. An asset disappearing from your fetch results is not proof that the original was deleted. Keep personal writing separate from the availability of a PhotoKit asset.
2. Give browsing and background work different network policies
For passive thumbnail preparation, request locally available media first. For an explicit tap to open an original, decide whether a download is appropriate and show progress or a retry state.
import Photos
let options = PHImageRequestOptions()
options.deliveryMode = .opportunistic
options.resizeMode = .fast
options.isNetworkAccessAllowed = false
This is request configuration, not a complete loader. Apple's network-access option defaults to false. A cloud-only result can be identified through the result information. Treat it as “needs download,” not “broken photo.” Enabling network access permits a request; it does not guarantee connectivity or completion.
Do not retry every visible cloud-only thumbnail with network access as a side effect of scrolling. That turns a small feed into an unpredictable download queue. Make your chosen policy consistent for photos and video playback, whose requests need separate handling.
3. Keep a request identity with each visible item
Use an asset identifier plus a request generation in the presentation layer. When a cell is reused or a new query replaces the feed, invalidate the old generation. Apply a callback only if both identifiers still match.
Store request IDs so obsolete requests can be cancelled. Apple documents using the request ID with cancellation. Cancellation saves work; an identity check also protects the UI from results already on their way.
Keep thumbnail and original-image states separate. A small preview can remain visible while a larger result is loading. Avoid replacing a useful preview with an empty placeholder on every retry.
4. Bound memory and work independently
Request a thumbnail sized for its displayed dimensions and screen scale, rather than an original for every card. Bound the number of simultaneous requests, and prioritize visible items before speculative work. Keep cache identity tied to size and content mode as well as asset identity.
Instrument an actual device with a mixed library. Record peak memory, time until the first useful image, and whether rapid scrolling lets outdated work replace current cards. Do not infer a smooth experience from a library containing six local screenshots.
A practical acceptance matrix
- Grant selected access to two photos; both should be usable without granting more.
- Remove one selection while its preview is loading; unrelated notes must survive.
- Open a cloud-only asset offline; show a recoverable state without an endless spinner.
- Scroll rapidly, then reverse direction; the image must still match its card.
- Change access in Settings and return; refresh the accessible results.
- Open a short cloud-backed video; distinguish buffering from missing access.
MemoryRemains is an example of the user-facing context: a private journal over a person's existing library. Its product page describes current behavior; the checklist above is broader engineering guidance. For the separate question of preserving journal data, read local storage, backup, and sync.