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

Supporting multiple windows on iPad

At a glance

Item Summary
Purpose Open, restore, and reactivate gallery and photo-inspector scenes using NSUserActivity payloads.
App architecture AppDelegate selects a scene configuration from an activity type, GalleryViewController requests an inspector for a selected Photo, and InspectorSceneDelegate reuses a matching session or creates and restores one.
Main patterns Activity-routed scenes, Session reuse before creation, Restorable activity payload
Project style Nine Swift files; storyboard-based UIKit scenes with gallery, detail, and inspector feature slices.

Project structure

Source bundle/
├── Gallery/
│   ├── AppDelegate.swift
│   ├── InspectorSceneDelegate.swift
│   ├── GalleryViewController.swift
│   ├── InspectorViewController.swift
│   ├── PhotoDetailViewController.swift
│   ├── SceneDelegate.swift
│   ├── Photo.swift
│   ├── GalleryViewController+Toolbar.swift
│   └── PhotoCell.swift
├── Configuration/
│   └── SampleCode.xcconfig
└── Gallery.xcodeproj/
    ├── .xcodesamplecode.plist
    └── project.pbxproj

Structure observations

  • The app delegate owns configuration routing only; gallery and inspector controllers own feature presentation.
  • Photo converts domain identity into detail/inspector activities, keeping payload keys out of controllers.
  • Inspector activation searches existing sessions first, preventing duplicate windows for the same photo.

Overall architecture

Reference code

Gallery/AppDelegate.swift:18 — activity-based scene configuration

    func application(_ application: UIApplication,
                     configurationForConnecting connectingSceneSession: UISceneSession,
                     options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        let configurationName: String
        switch options.userActivities.first?.activityType {
        case UserActivity.GalleryOpenInspectorActivityType:
            configurationName = "Inspector Configuration"
        default:
            configurationName = "Default Configuration"
        }
        return UISceneConfiguration(name: configurationName,
                                    sessionRole: connectingSceneSession.role)
    }

Interpretation

Scene creation is routed by an activity contract rather than a direct controller call. The gallery asks the inspector scene coordinator to open a photo, and the inspector coordinator owns session identity, activation, configuration, and restoration.

Ownership and state

Ownership evidence

Gallery/GalleryViewController.swift:13 — gallery model and collection state

    let photos = PhotoManager.shared.photos

    enum Section {
        case main
    }

    var dataSource: UICollectionViewDiffableDataSource<Section, Int>! = nil
    var collectionView: UICollectionView! = nil
Owner Object or state Relationship Mutation authority
PhotoManager.shared Immutable sample photo catalog Static singleton value It defines the bundled gallery data.
GalleryViewController Photo array, collection view, diffable data source Reads shared data and creates the UIKit presentation objects It selects photos and requests detail/inspector presentation.
Each Photo value Activity user-info and target-content identity Computes new activities on demand The model defines how its identity crosses a scene boundary.
InspectorSceneDelegate Inspector window/session restoration state UIKit supplies the window; the delegate annotates the scene session It configures, restores, finds, and activates inspector sessions.

Composition denotes source-visible construction and retained value state. Aggregation denotes a stored or injected collaborator whose exclusive lifetime the source does not prove.

Class and protocol design

Gallery/InspectorSceneDelegate.swift:89 — reuse-before-create scene policy

    class func activeInspectorSceneSessionForPhoto(_ photoAsset: String) -> UISceneSession? {
        for session in UIApplication.shared.openSessions
            where session.configuration.delegateClass == InspectorSceneDelegate.self {
            if let userInfo = session.userInfo,
               userInfo[InspectorSceneDelegate.inspectorScenePhotoAsset] as? String == photoAsset {
                return session
            }
        }
        return nil
    }
Type Responsibility Depends on or conforms to
AppDelegate Maps activity types to named scene configurations UIApplicationDelegate
SceneDelegate Restores the default gallery/detail scene UIWindowSceneDelegate
InspectorSceneDelegate Configures inspector content and manages inspector sessions UIWindowSceneDelegate, NSUserActivityDelegate
GalleryViewController Displays photos and emits detail/inspector commands Collection view data source, drag, and delegate roles
Photo Photo identity plus activity-payload factory Value type

Scene, collection, drag, and user-activity protocols are UIKit/Foundation callbacks. The sample defines no local protocol abstraction; its cross-scene contract is the NSUserActivity type and user-info schema.

Access control

Symbol Access Verified effect Design reason
Photo public type The type is visible outside the module, but its stored properties remain internal The explicit modifier is broader than the rest of this app sample; it does not make members public automatically.
Photo.assetName, title, activity properties implicit internal Usable only within the app module Scene files collaborate without exporting a complete public model API.
GalleryViewController.makeTargetedPreview private Callable only inside the controller’s lexical scope Context-menu preview construction is a view implementation detail.
Scene delegates and manager implicit internal Available to the application target UIKit resolves configured classes without a library-level public surface.

Reference code

Gallery/Photo.swift:20 — public type with internal members

public struct Photo {
    let assetName: String
    let title: String

    var inspectorUserActivity: NSUserActivity {
        let userActivity = NSUserActivity(
            activityType: UserActivity.GalleryOpenInspectorActivityType)
        userActivity.userInfo = activityUserInfo
        return userActivity
    }
}

Swift access is per declaration: public struct Photo does not promote unmodified properties to public. No open class hierarchy or fileprivate boundary appears in this feature.

Logic ownership and placement

Logic Owning type or file Why it lives there
Scene configuration selection AppDelegate UIKit asks it which named scene configuration should connect.
Photo selection, collection snapshots, inspector command GalleryViewController These operations share the gallery’s UI and selection state.
Cross-scene payload construction Photo Payload identity is domain data, not presentation mechanics.
Session lookup, activation, restoration InspectorSceneDelegate It owns inspector-scene lifecycle and session metadata.

Design patterns

Pattern Source evidence Purpose or tradeoff
Activity-routed scenes Gallery/AppDelegate.swift:18 Selects a concrete scene role from a stable activity type.
Session reuse before creation Gallery/InspectorSceneDelegate.swift:113 Avoids opening duplicate inspectors for one photo.
Restorable activity payload Gallery/Photo.swift:49 Carries photo identity through activation, suspension, and restoration.

Naming conventions

  • Scene roles are explicit (SceneDelegate, InspectorSceneDelegate) and configuration names match those roles.
  • Activity constants include the use case (GalleryOpenDetailActivityType, GalleryOpenInspectorActivityType).
  • Commands state lifecycle intent (openInspectorSceneSessionForPhoto, activeInspectorSceneSessionForPhoto).
  • Controller suffixes describe presentation roles; PhotoManager identifies the data owner.

Architecture takeaways

  • Use an activity type and payload as the contract between a feature controller and a new scene.
  • Let the scene delegate own reuse, activation, configuration, and restoration policy.
  • Read access per declaration: a public container can still have internal members.

Source map

Source file Architectural role
Gallery/AppDelegate.swift Scene-configuration routing
Gallery/GalleryViewController.swift Gallery composition, selection, and inspector requests
Gallery/InspectorSceneDelegate.swift Inspector configuration, session lookup, activation, and restoration
Gallery/SceneDelegate.swift Default scene restoration
Gallery/Photo.swift Photo catalog and activity payloads
Gallery/InspectorViewController.swift / Gallery/PhotoDetailViewController.swift Inspector and in-scene detail presentation