Sample CodeiOS, iPadOS, Mac CatalystReviewed 2026-07-21View on Apple Developer

Building high-performance lists and collection views

At a glance

Item Summary
Purpose Keeps a destination grid responsive by prefetching, preparing, caching, and canceling large image work.
App architecture The scene injects model/asset stores into a grid controller; the controller coordinates diffable UI and request lifetimes; AssetStore owns a multi-tier image pipeline.
Main patterns Dependency injection, diffable data source, prefetch/cancel, memory + file caches, request sharing, and protocol-backed stores.
Project style Programmatic UIKit feature grouped into Stores, Utilities, and Views, with Combine used for cancellable pipelines.

Project structure

CollectionViewSample/
├── AppDelegate.swift
├── SceneDelegate.swift
├── DestinationPost.swift
├── PostGridViewController.swift
├── Stores/
│   ├── SampleData.swift
│   ├── ModelStore.swift
│   └── AssetStore.swift
├── Utilities/
│   ├── MemoryLimitedCache.swift
│   ├── FileBasedCache.swift
│   ├── PlaceholderStore.swift
│   ├── UnfairLock.swift
│   └── URLSession+DownloadTaskPublisher.swift
└── Views/
    ├── DestinationPostCell.swift
    ├── DestinationPostPropertiesView.swift
    └── SectionBackgroundView.swift

Structure observations

  • Stores own lookup and asset preparation; view files own drawing/layout and cell request lifetime.
  • Utility caches separate memory pressure, disk I/O, downsampling, and locking concerns.
  • SceneDelegate is the composition root; AppDelegate only supplies scene lifecycle configuration.

Overall architecture

Reference code

CollectionViewSample/PostGridViewController.swift:53 — cell registration joins model lookup, placeholder delivery, asynchronous loading, cancellation-token transfer, and item reconfiguration.

let cellRegistration =
    UICollectionView.CellRegistration<DestinationPostCell, DestinationPost.ID> {
        [weak self] cell, indexPath, postID in
        guard let self = self else { return }

        let post = self.postsStore.fetchByID(postID)
        let asset = self.assetsStore.fetchByID(post.assetID)
        var token = self.prefetchingIndexPathOperations
            .removeValue(forKey: indexPath) ?? cell.assetToken

        if asset.isPlaceholder && token == nil {
            token = self.assetsStore.loadAssetByID(post.assetID) {
                [weak self] in self?.setPostNeedsUpdate(postID)
            }
        }
        cell.assetToken = token
        cell.configureFor(post, using: asset)
    }

Interpretation

The controller owns UI scheduling, not image-processing details. It asks the asset store for the best immediately available representation, carries a cancellable from prefetch to the visible cell, and reconfigures only the affected identifier when the prepared image arrives.

Ownership and state

Ownership evidence

CollectionViewSample/Stores/AssetStore.swift:100 — one shared publisher is retained per asset ID and removed when the pipeline completes.

private var requestsCache:
    [Asset.ID: Publishers.Share<AnyPublisher<Asset, Never>>] = [:]

func prepareAssetIfNeeded(
    id: Asset.ID
) -> Publishers.Share<AnyPublisher<Asset, Never>> {
    if let request = requestsCache[id] {
        return request
    }

    let publisher = prepareAsset(id: id)
        .subscribe(on: Self.networkingQueue)
        .receive(on: DispatchQueue.main)
        .handleEvents(receiveCompletion: { [weak self] _ in
            self?.requestsCache.removeValue(forKey: id)
        })
        .assertNoFailure("Downloading Asset (id: \(id)")
        .eraseToAnyPublisher()
        .share()
    requestsCache[id] = publisher
    return publisher
}
Owner Object or state Relationship Mutation authority
SceneDelegate Window and root grid Constructs feature graph Scene connection callback
PostGridViewController Collection view, diffable source, injected stores, prefetch-token dictionary Retains UI/session dependencies Controller setup, cell registration, prefetch callbacks
DestinationPostCell Image/properties views and current asset token Owns visible request/assertion lifetime Configuration, reuse, deinit
AssetStore Prepared cache, disk caches, placeholder queue, in-flight publisher map Owns image delivery pipeline Fetch/load/prepare methods
MemoryLimitedCache Assets, memory usage, assertion counts, memory-warning subscription Owns memory residency policy Lock-protected cache operations
FileBasedCache / PlaceholderStore Cache directory and on-disk image representation Own disk-layer behavior Fetch/move/downsample operations

Class and protocol design

Type or protocol Responsibility Depends on or conforms to
ModelStore Synchronous lookup of an Identifiable model by ID Associated type
AnyModelStore<Model> Dictionary-backed concrete model lookup ModelStore
Cache Optional cached lookup by model ID Associated type
MemoryLimitedCache Prepared-image cache with assertions and pressure purging Cache, UnfairLock
FileBasedCache Load/store assets by deterministic file URL Cache, FileManager
PlaceholderStore Downsample downloaded originals into small future placeholders FileBasedCache, ImageIO
AssetStore Resolve best current image and coordinate prepare/download/cache stages ModelStore, Combine
PostGridViewController Build adaptive layout/diffable snapshots and bridge prefetch to assets UICollectionViewDataSourcePrefetching

The local protocols capture two narrow lookup capabilities. Despite its name, AnyModelStore is not a general closure-based type eraser; it is a concrete generic dictionary store that satisfies ModelStore.

Access control

Symbol Access Verified effect Why it fits this design
Grid stores, collection view, and data source implicit internal Other target code can inspect or replace them. Keeps the teaching sample simple, though production code could narrow setters.
Prefetch dictionary and section-kind string fileprivate Shared by the controller’s same-file extensions, unavailable to other files. The file is split into setup, prefetch, and layout extensions.
Configuration/layout/update helpers private Stay behind controller lifecycle and callbacks. Prevents other types from driving partial UI setup.
AssetStore cache layers and request map private Callers use fetch/load rather than manipulating cache policy. Protects the multi-tier pipeline.
placeholderStore Declared public let Read-only reference outside AssetStore; effective reach is still limited by the internal class/app module. Exposes the sample’s generated placeholder store without allowing replacement.
MemoryLimitedCache.currentMemoryUsage private(set) Readable for diagnostics; only the cache updates accounting. Preserves memory-usage invariants.
Cache asset dictionaries, lock, assertion counts private Mutation must pass through lock-aware methods. Avoids unsynchronized state.
DestinationPostCell.assetToken / configureFor Declared public Available to controller code, but effectively internal with the internal cell type. Makes the cell’s small integration surface explicit.

Reference code

CollectionViewSample/Utilities/MemoryLimitedCache.swift:26 — externally readable memory accounting surrounds private, lock-guarded residency state.

class MemoryLimitedCache: Cache {
    let memoryLimit: Measurement<UnitInformationStorage>
    private(set) var currentMemoryUsage =
        Measurement<UnitInformationStorage>.zero

    private let accessLock = UnfairLock()
    private var assets: [Asset.ID: Asset] = [:]
    private var protectedAssetIDs: [Asset.ID: Int] = [:]
    private var cancellation = [AnyCancellable]()
}

Logic ownership and placement

Logic Owning type or file Placement rationale
Root dependency construction SceneDelegate Scene lifetime determines the root controller graph.
Snapshot/layout/cell/prefetch coordination PostGridViewController Requires collection-view identifiers and visibility context.
Request sharing, cache ordering, image preparation AssetStore One service enforces the asset-delivery policy.
Memory accounting, assertions, purging MemoryLimitedCache Residency policy needs one lock-protected owner.
Disk URL and image decoding FileBasedCache Independent persistence concern.
Placeholder downsampling PlaceholderStore Specialized disk-cache behavior, not controller work.
Request cancellation on reuse DestinationPostCell Cell lifetime is the exact boundary for visible asset use.

Design patterns

Pattern Source evidence Purpose or tradeoff
Constructor injection CollectionViewSample/SceneDelegate.swift:17 Makes the grid’s store dependencies explicit and test-replaceable at construction.
Diffable data source CollectionViewSample/PostGridViewController.swift:76 Applies identity-based snapshots and targeted reconfiguration.
Prefetch and cancellation CollectionViewSample/PostGridViewController.swift:106 Couples speculative work to collection-view callbacks.
Cell-scoped resource token CollectionViewSample/Views/DestinationPostCell.swift:24 Cancels or releases the asset assertion on reuse/deinit.
Multi-tier cache CollectionViewSample/Stores/AssetStore.swift:41 Serves prepared, placeholder, local, then downloaded representations.
Shared publisher CollectionViewSample/Stores/AssetStore.swift:100 Deduplicates concurrent work for the same asset ID.
Lock-protected cache CollectionViewSample/Utilities/MemoryLimitedCache.swift:31 Keeps accounting and eviction state coherent across access.
Protocol-backed store CollectionViewSample/Stores/ModelStore.swift:10 Defines lookup capability separately from dictionary storage.

Naming conventions

  • Storage roles use Store or Cache: AssetStore, AnyModelStore, PlaceholderStore, and MemoryLimitedCache.
  • Pipeline methods describe phases: fetchByID, loadAssetByID, prepareAssetIfNeeded, and makeDownloadRequest.
  • DestinationPostCell and DestinationPostPropertiesView name the domain element plus UI role.
  • Boolean/state names reveal policy: isPlaceholder, currentMemoryUsage, protectedAssetIDs, and prefetchingIndexPathOperations.

Architecture takeaways

  • Inject stores at the scene/controller boundary and keep cache mechanics out of cells and view controllers.
  • Tie asynchronous work to UI lifetime with cancellables that move from prefetch ownership to cell ownership.
  • Deduplicate work by stable asset ID and reconfigure diffable items by identity.
  • Separate prepared-memory, original-disk, and placeholder-disk policies.
  • Use private(set) and private dictionaries to protect accounting and concurrency invariants.

Source map

Source file Relevant symbols
CollectionViewSample/SceneDelegate.swift Composition root and store injection
CollectionViewSample/PostGridViewController.swift Diffable UI, layout, prefetch/cancel
CollectionViewSample/Stores/ModelStore.swift ModelStore and dictionary-backed store
CollectionViewSample/Stores/AssetStore.swift Asset pipeline and shared requests
CollectionViewSample/Utilities/MemoryLimitedCache.swift Locked memory residency and assertions
CollectionViewSample/Utilities/FileBasedCache.swift Disk cache
CollectionViewSample/Utilities/PlaceholderStore.swift Placeholder downsampling
CollectionViewSample/Views/DestinationPostCell.swift Cell layout and resource-token lifetime