Introduction

With the continuous evolution of iPadOS, the iPad has transformed from a consumption device into a powerful productivity tool. One of the most impactful features introduced in iPadOS 13 is multi-window support, which allows apps to display multiple independent instances or views side by side. For users who juggle documents, emails, research, and note‑taking simultaneously, this capability drastically reduces context switching and boosts efficiency. Implementing multi-window functionality is no longer optional for professional-grade applications — it’s an expected standard. This guide provides a comprehensive, step‑by‑step approach to adding multi-window support to your iPadOS apps, covering configuration, scene management, data synchronisation, and adaptive UI design. By the end, you’ll be equipped to deliver a seamless multitasking experience that leverages the full potential of the iPad’s large screen.

Understanding Multi-Window Support in iPadOS

Multi-window support in iPadOS is built on Apple’s scene‑based architecture, introduced alongside UIKit’s scene delegate. A scene represents one instance of your app’s user interface — equivalent to a window in earlier versions of iOS — but with greater flexibility. In iPadOS, a single app can have multiple scenes running concurrently, each with its own lifecycle, state, and view controller hierarchy. This design mirrors how macOS handles multiple windows, yet it is optimised for touch, drag‑and‑drop, and split‑screen interactions.

Unlike the traditional app delegate model where the entire app had one window, the scene delegate model separates the app’s shared data layer (handled by the app delegate) from the UI instances (handled by scene delegates). This separation is crucial because each scene can be created, destroyed, or moved to the background independently. For example, a user could have two notes open in your note‑taking app — one in full‑screen mode and another in Slide Over — each receiving updates from the same Core Data store.

Real‑world use cases are abundant: a document editor that lets users compare two files side by side, a project management tool that shows a dashboard in one window and a task list in another, or a browser that keeps multiple web pages open in separate scenes. Apple’s own apps — such as Safari, Notes, and Mail — showcase these patterns, and third‑party developers are expected to follow suit to remain competitive.

Configuring Your Project for Multi-Window Support

Setting the Deployment Target

Before writing any code, verify that your app’s deployment target is set to iPadOS 13.0 or later. This version introduced the `UISceneDelegate` and the underlying scene session infrastructure. In Xcode, navigate to your project’s target settings, select the “General” tab, and set the “Minimum Deployments” to 13.0 for iPad. If you need to support earlier iOS versions (e.g., iOS 12 on iPhones), you can conditionally compile scene‑related code, but for an iPad‑only app targeting modern devices, iOS 13 is the baseline.

Info.plist Configuration

The first concrete step is to modify your app’s Info.plist to declare how scenes should be created. Add the key UISceneConfigurations (shown as “Application Scene Manifest” in Xcode’s plist editor). Under this key, you define an array of scene configurations. The most common entry is for the default session role: UIWindowSceneSessionRoleApplication. Each configuration contains:

  • Scene Class Name: Typically your custom scene delegate class (e.g., $(PRODUCT_MODULE_NAME).MySceneDelegate).
  • Delegate Class Name: The class that conforms to UISceneDelegate. This can be the same as the scene class.
  • Storyboard Name (optional): If using storyboards, specify the initial storyboard for a new scene. If you create windows programmatically, you can omit this.

A minimal Info.plist configuration looks like this in raw XML:

<key>UIApplicationSceneManifest</key>
<dict>
    <key>UISceneConfigurations</key>
    <dict>
        <key>UIWindowSceneSessionRoleApplication</key>
        <array>
            <dict>
                <key>UISceneConfigurationName</key>
                <string>Default Configuration</string>
                <key>UISceneDelegateClassName</key>
                <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
            </dict>
        </array>
    </dict>
</dict>

If your app supports external displays (e.g., via AirPlay or HDMI), you can similarly configure a configuration for UIWindowSceneSessionRoleExternalDisplay. For most iPad‑only apps, the default application role suffices.

Transitioning from App Delegate to Scene Delegate

If your existing app uses a single‑window model with the old app delegate, you must refactor your scene‑lifecycle code. Remove any UIApplicationDelegate methods related to window management and move them to your SceneDelegate. Specifically:

  • Instead of application(_:didFinishLaunchingWithOptions:), use scene(_:willConnectTo:options:) to set up the window and its root view controller.
  • Instead of applicationDidEnterBackground(_:) / applicationWillEnterForeground(_:), use the corresponding scene delegate methods.

Important: The app delegate still controls global resources (e.g., Core Data stack, network configuration), while the scene delegate manages per‑window UI. Make sure your app delegate does not assume ownership of a single window.

Managing Multiple Windows Programmatically

Creating a New Window

In iPadOS, users can create new windows via the system‑provided menus or through drag‑and‑drop gestures. However, you can also programmatically request a new scene session. The primary API is:

let userActivity = NSUserActivity(activityType: "com.example.app.newDocument")
userActivity.userInfo = ["documentID" : someDocumentID]

UIApplication.shared.requestSceneSessionActivation(
    nil,                              // Use a nil session to create a new one
    userActivity: userActivity,
    options: nil,
    errorHandler: nil
)

This call tells UIKit to create a new scene session and connect it to your scene delegate. The userActivity is forwarded to the new scene’s scene(_:willConnectTo:options:) method, where you can extract the payload (e.g., a document identifier) and configure the window’s content accordingly.

You can also request that an existing scene session is brought to the foreground by passing its UISceneSession object as the first parameter. This is useful when implementing “open in new window” from context menus or detail views.

Handling Scene State Transitions

Each scene has its own state: foreground active, foreground inactive, background. Implement the following delegate methods to manage resources and save state:

  • sceneDidBecomeActive(_:) – Resume paused activities, start animations, refresh UI.
  • sceneWillResignActive(_:) – Pause ongoing tasks, save incrementally.
  • sceneDidEnterBackground(_:) – Save full state, release large cached objects.
  • sceneWillEnterForeground(_:) – Restore from saved state.

Because multiple scenes may transition independently, do not assume that when one scene enters the background the entire app is going to the background. Only the app delegate’s applicationDidEnterBackground(_:) signals that all scenes are backgrounded. Use scene‑specific methods for per‑window tasks.

Passing Data Between Windows

Since each scene has its own view controller hierarchy, you need a strategy to share data across windows. The most robust approach is to keep a shared data layer (e.g., Core Data, CloudKit, or a central ObservableObject in SwiftUI) that all scenes read from. When a user edits a document in one window, the change should immediately reflect in another window showing the same document. This can be achieved with Combine publishers, Core Data’s persistent history, or by using NotificationCenter for lighter synchronisation.

NSUserActivity is another powerful tool. It can be used not only to open new windows but also to hand off context between windows. For example, if a user selects a row in a table view in one window, you can broadcast an NSUserActivity that other scenes observe, updating their selection automatically. Apple’s own LinkPresentation and Drag and Drop frameworks also rely on activities to transfer data.

UI Considerations for Multi-Window Apps

Adaptive Layout with Size Classes

iPad windows can appear in various sizes: full screen, one‑third of the screen (Slide Over), or half‑screen (Split View on a 12.9‑inch iPad). Your UI must adapt gracefully to all possible widths. Use size classes (UIUserInterfaceSizeClass) to detect when a window is compact (narrow) versus regular (wide). For example, in a Split View scenario one window may have a compact width while the other is regular. Implement trait‑collection‑based layout changes: use a master‑detail pattern for regular width and a navigation‑based stack for compact width. SwiftUI’s @Environment(\.horizontalSizeClass) makes this straightforward.

Test each window configuration on simulators or real devices. Pay special attention to the window’s safe area insets, especially when the window is in Slide Over mode or when the keyboard is displayed.

Supporting Split View and Slide Over

By enabling multiple windows, your app automatically gains support for Split View and Slide Over — as long as you respect the scene’s size constraints. However, you should explicitly test the following:

  • Dragging a window to the left or right edge to create a Split View pair.
  • Dragging a window to the centre to create a floating Slide Over window.
  • Resizing the divider to adjust the split ratio.

If your app uses a fixed‑width layout, it may become unusable in a narrow Slide Over pane. Adopt a responsive design that collapses sidebars, reduces font sizes, and hides optional content when the window width falls below a threshold (e.g., 400 points).

Drag and Drop Across Windows

Multi‑window apps are a natural fit for drag‑and‑drop interactions. Users expect to drag a photo from one window into another, or to drag a link from a browser into a note. Implement UIDragInteraction on source views and UIDropInteraction on destination views. Because each scene is isolated, drag sessions are managed system‑wide — UIKit handles the transfer via pasteboards or NSItemProvider. Ensure that your drag items carry sufficient metadata so that the receiving scene can reconstruct the context (e.g., a file URL and its type).

// Example: providing a drag item
let itemProvider = NSItemProvider(object: myURL as NSURL)
let dragItem = UIDragItem(itemProvider: itemProvider)
dragItem.localObject = myModelObject   // optional for in‑app drags

For interoperability with other apps, use standard UTIs (e.g., kUTTypePlainText, kUTTypeImage).

Best Practices and Pitfalls

State Restoration

iPadOS can terminate a scene at any time — especially when memory is low or the user has many windows open. Implement state restoration to preserve the user’s place in each window. Use NSUserActivity (set to scene.session.stateRestorationActivity) to store lightweight state. For heavier data, rely on your persistent store. Always test by killing the app from the multitasker and relaunching — each restored window should appear exactly as the user left it.

Avoiding Resource Conflicts

When multiple scenes run concurrently, they share the same process and memory space. Avoid blocking the main thread with long‑running synchronous operations. Offload work to background queues and use os_unfair_lock or actors (Swift Concurrency) to protect shared mutable state. Also, be mindful of file access: if two scenes try to write to the same file simultaneously, you may encounter corruption. Use Core Data’s persistent history or coordinate writes via file coordination (NSFileCoordinator).

Testing on Different iPad Models

Not all iPads behave the same. For example, the 12.9‑inch iPad Pro can display two side‑by‑side windows in regular width, while the 9.7‑inch iPad may force one window into compact mode. Additionally, older iPads (e.g., iPad 6th generation) have less RAM, so having multiple windows may cause performance degradation. Test on a variety of physical devices and simulators. Use the Xcode “Simulate Background App” option to verify state restoration and memory warnings.

Conclusion

Multi-window support is not just a bullet point on a feature list — it is a fundamental shift in how iPad users interact with your app. By embracing the scene‑based architecture introduced in iPadOS 13, you empower users to work the way they want: comparing documents, keeping reference material visible, and never losing context. The steps outlined here — from Info.plist configuration to advanced data synchronisation and adaptive layout — give you a solid foundation for building a multi‑window experience that feels native and polished.

As the iPad continues to evolve with each new OS release (iPadOS 17 and beyond), the importance of multitasking only grows. Apple’s own documentation on scenes and the WWDC 2022 session “Make your app great for iPad” provide deeper insights into advanced patterns. For practical code examples, the Hacking with Swift tutorial series offers hands‑on guidance. Start by enabling a single additional window, then iterate — your users will reward you with increased engagement and satisfaction.