-
Modernize your UIKit app
Discover the latest updates to UIKit. Learn how to update your iPhone app layouts to work great when resized with iPhone Mirroring and on iPad. Explore new APIs for tab and navigation bars, find out how to prepare your app for new Apple Intelligence capabilities, and get introduced to a skill for your coding agent of choice that helps modernize your codebase.
Chapters
- 0:00 - Introduction
- 0:34 - App adaptivity
- 2:10 - Legacy API: App lifecycle
- 2:51 - Legacy API: Main screen
- 5:46 - Full-screen mode for games
- 6:17 - Legacy API: User interface idiom
- 7:06 - Legacy API: Interface orientation
- 7:55 - UIView Body protocols for motion & location
- 8:19 - Test your resizable iPhone app
- 9:18 - Tab bars and sidebars
- 10:52 - Navigation bars
- 12:37 - Menus
- 13:01 - Integrate with Apple Intelligence
- 14:07 - Agentic coding
- 15:32 - Next steps
Resources
- TN3208: Preparing your app’s launch screen to meet App Store requirements
- TN3210: Optimizing your app for iPhone Mirroring
- Make your UIKit app more flexible
- Adapting your app when traits change
- Transitioning to the UIKit scene-based life cycle
- Automatic trait tracking
- Human Interface Guidelines: Menus
Related Videos
WWDC26
- Explore advanced App Intents features for Siri and Apple Intelligence
- Get the most out of Device Hub
WWDC25
WWDC24
-
Search this video…
Hi, I'm Michael Ochs, an engineering manager on the UI Frameworks team.
And today I'll tell you how to modernize your UIKit app. In this video I'll tell you about some big changes to app adaptivity requirements, and how iPhone apps are fully resizable.
Next, I'll present some new APIs in tab bars, navigation bars and menus.
Then, I'll talk about how to support Apple Intelligence and some changes you should be aware of.
And finally I'll cover a new skill that helps an agent to handle most of this modernization work automatically.
Let's get started with app adaptivity.
Your app can already appear in many different environments. There are different screen sizes, and apps can run side by side - with other apps and even themselves. On iPad, people expect apps to resize and move to external displays seamlessly. On iPhone, people expect apps to work in both portrait and landscape orientations. They also expect to mirror their iPhone to the Mac. In iOS and macOS 27 this experience has been improved and apps running in these environments are now exposed to many of the dynamic changes any app native to the platform needs to support.
When people use iPhone Mirroring on the Mac, they can fully resize the iPhone window, allowing your apps to resize and adapt. Likewise, an iPhone-only app running on iPad will be fully resizable like any other iPad app. Because of this, it is important that your app dynamically adjusts to any available scene size at runtime. If your app is a universal binary, you are off to a great start. But there is more to do to make your app fully adaptive and have it dynamically adjust to the environment it is running in. I will cover the most important steps and common issues you might encounter when making your app more adaptive: verifying your app is no longer using app lifecycle, and instead has adopted scene lifecycle, the basis for any adaptive app, making sure your app is not using main screen references, and how to handle user interface idiom and interface orientation checks.
The first step is to move from app lifecycle to scene lifecycle.
Most apps are already using scene lifecycle. It is the basis for an adaptive app and for many of the other tasks I'm going to show you in this video. UIScene lifecycle is now required when building with the latest SDKs. Without it, your application will no longer launch. Verify that your app is using a UISceneDelegate which is the basis for scene lifecycle.
If you have not yet migrated to scene lifecycle, check out the video "Make your UIKit app more flexible" from WWDC25 and read "Transitioning to the UIKit scene-based lifecycle" in the documentation. Next, when your iPhone app is mirrored on a Mac, or when a person moves your app to an external display on the iPad, the screen associated with your scene changes. That means that any reference to the main screen in your code will provide incorrect information for the environment your app is running in.
It is important that you do not reference the main screen in your app. Instead, access the screen dynamically from a window's window scene.
When a view or view controller is not available in the immediate context, pass in a screen reference to any method that needs it.
Even better than fetching the correct screen is to remove screen references all together. There are a lot of other APIs that are better suited for an adaptive app. I'll take you through two common patterns.
Accessing the scale of the screen should be replaced with the trait collection's displayScale.
Views and view controllers automatically update their trait collections and provide reasonable fallbacks even when they are not part of the visible hierarchy. A lot of common override points are also tracked, meaning that you do not need to explicitly monitor for changes. Instead, the system tracks which trait collection properties are being used inside common layout and drawing methods, like layoutSubviews, updateProperties, drawRect, and many others.
If a change to a tracked trait is detected, the system will ensure these methods are called again so your UI automatically updates accordingly. Check out "Automatic trait tracking" in the documentation to learn more. In places where automatic trait tracking is not available, registerForTraitChanges can be used to observe changes.
This method allows you to set up a closure that is called when a specific trait in the receiving view changes.
Use this to invalidate caches or update other data related to that view.
Check out "Adapting your app when traits change" in the documentation to learn more. Another common use of the screen is to check its bounds to get the amount of space available to your app.
However, in an adaptive environment, the space your scene has available is not always the full screen. So if you still have any references to screen bounds in your app, now is the time to remove these.
The window scene's effective geometry provides information on how much space your app has available. If you need to monitor the effective geometry for changes, implement windowScene:didUpdateEffectiveGeometry: in your scene delegate.
In your views and view controllers, instead of referencing the scene bounds, use the available size of your view controller's view or your view's superview to determine how much space is available to it.
This makes your UI less dependent on how it is presented. It will adjust better when your view controller appears in other contexts, for example inside of a split view controller.
For games, resizing can be challenging. Due to this, UIRequiresFullscreen is honored on iPhone in resizable environments starting in iOS 27. Its behavior has also been updated and no longer opts your app fully out of resizing. Instead, it enables discrete resizing that honors your supported interface orientations.
In discrete resizing, every time a person changes the scene size, the system transitions the scene to a new screen configuration matching that size, so your game always renders at full quality in the available space. If your app is using the User Interface idiom trait, be aware that this trait is no longer meaningful for any kind of layout decision.
Your app is expected to use the additional space in a meaningful way, regardless of whether it is running in the phone or pad user interface idiom.
When your iPhone app is running on an iPad or in iPhone Mirroring on the Mac, it will be fully resizable, but it will still run under the phone user interface idiom. Stop checking the user interface idiom for any layout decisions in your code.
Use size classes instead to handle sizing constraints, such as collapsing menus and updating your app's layout for the available space. If you need finer control, use the surrounding view's size like I mentioned earlier.
Interface orientation also is no longer useful for layout decisions.
In iOS 27, an app's supported interface orientation is a preference provided to the system. It will be ignored when your app is running in a resizable environment. You should not consider interface orientation for any layout calculations. In iPhone Mirroring on the Mac, your apps will always be running in the portrait interface orientation, regardless of the aspect ratio of your app's scene. Any interface orientation checks in your app should also be updated to use size classes. This conceptual shift was introduced in iOS 8.
At WWDC2014, Bruce Nilo said: "A device rotation is only an animated bounds change." With an array of device sizes, resizable windows on iPad, and resizable iPhone apps on Mac, today this insight is more relevant than ever. And speaking of interface orientation: in iOS 27 UIView also conforms to the new Body protocols from CoreMotion and CoreLocation.
This makes it much easier to configure your motion and location managers. Connect them to the view that visualizes the motion data, such as a compass or a map view. This ensures the data is always in the right coordinate space, regardless of interface orientation.
That covers the main adaptivity changes. Now here is how to test these changes in your app.
Xcode 27 brings new ways to iterate on your app's behavior across different screen sizes, without having to install your app on multiple separate simulators or devices.
In the new Device Hub app as well as in Xcode Previews, click the "enter resize mode" icon. Then, drag the edges of the device to resize it freely. This allows you to iterate on your changes quicker. Once you are satisfied with the results, make sure to test iPhone Mirroring and iPad with real devices.
To learn more about the Device Hub app and its tools, watch "Get the most out of Device Hub".
And that's adaptivity. Before I show you a new agentic coding skill that can help you with the work necessary to make your app fully adaptive, there are two more topics to cover. First up: bars and menus.
On iPad, tab bars can expand into a full sidebar representation to surface more sections of the app hierarchy when the current environment supports a sidebar. On iPhone, the bottom tab bar is shown across all sizes by default.
New in iOS 27, iPhone apps can also opt into sidebars by setting the tab bar controller's sidebar.preferredPlacement to .sidebar. Note that in contrast to the iPad, this is an app choice. If your app opts into the sidebar representation, there is no way to toggle between a sidebar and tab bar layout in the UI.
Instead, the system determines if there is enough space to show a sidebar, for example when the horizontal size class is regular.
To determine if the tab bar's sidebar representation can be shown, use the sidebar's isAvailable property.
If a sidebar is not currently available, surface the UI behind nested tabs in other parts of your app. To learn more about managing tab groups, watch "Make your UIKit app more flexible" from WWDC25, and to learn more about tab bars and its integration with sidebars, watch "Elevate your tab and sidebar experience in iPadOS" from WWDC24.
UITabBarController also lets you customize the prominent tab. The prominent tab is always visible, even when the tab bar collapses during scrolling. In iOS 27, you have the option to make any tab prominent by setting the prominentTabIdentifier.
Okay, that's tab bars. Now let's talk about navigation bars.
Navigation bars can interactively slide away as people scroll. This provides more room on the screen for your app's content. By default, navigation bars minimize in certain conditions defined by the system.
You can force this behavior, one way or the other, by setting the barMinimizationBehavior property on your navigation item to .always or .never. If you handle safe area avoidance yourself, set barMinimizationSafeAreaAdjustment to .never so bar minimization doesn't update insets automatically.
Another change during scroll interactions is an updated appearance for the scroll edge effects. As such, you should review your design, especially where you have previously overridden the defaults provided by the OS.
In particular, the .automatic style no longer switches between the existing soft and hard styles but provides its own visuals for additional clarity. If you have overridden the style from .automatic previously, that decision should be re-evaluated, especially when set to .soft, as that no longer matches the default system appearance.
With the refined look of Liquid Glass, images you set on menu elements may not be shown by default in some contexts, such as in the menu bars on iPadOS and macOS.
If you still need an image to be visible, set the preferredImageVisibility property to override the default system behavior.
Review the updated Human Interface Guidelines for when to include visible images in a menu element.
And that's bars and menus. Now, how to support Apple Intelligence in your app.
Menus in iOS 27 feature an Ask Siri button, to allow people to start a conversation with Siri right from your app. This is a powerful entry point that allows people to interact with the context they care about.
Menus will automatically display this item when there's content relevant for Siri. To provide more relevant information specific to your app, use the new View Annotations API. With it you can annotate specific views with AppEntities. Check out "Explore advanced App Intents features for Siri and Apple Intelligence" to learn more.
If your app supports drag and drop, Siri can load resources from your application's drag handlers.
When Apple Intelligence is invoked from context menus, the system will call available drag delegate methods to load the content. Avoid performing animations or presenting modal UI from sessionWillBegin. Drag sessions can be initiated without a user gesture. If your app has a stateful UI that shows up when a user initiates a drag, put that code in sessionDidMove instead.
If the adaptivity changes I outlined sound like a lot of work, I've got you covered. Let's talk about what's on everyone's mind: agentic coding! New in Xcode 27 is an app modernization skill. It has a deep understanding of the adaptivity tasks I outlined. And with the context of your project, it can make a lot of these changes automatically. Use Xcode's intelligence features and ask an agent to make your app more adaptable.
It will automatically convert main screen calls to traitCollection or scene bounds checks, adding invalidation logic if necessary.
It will also replace interface orientation checks with size class checks. It will even convert your app to use scene lifecycle. For more complex tasks, it will ask clarifying questions. And for tasks too large to handle in a single session, it will add comments to help you keep track of the remaining work.
And to use skills in other tools, you can export the ones Xcode uses with "xcrun agent skills export". This will create markdown files that you can then import in your workflows.
Skills like this one are a powerful way to prepare your app for resizable environments.
And… that's it! These are the most important things to modernize your app.
Build your app with the iOS 27 SDK, and try out the resizable simulator in the new Device Hub app and test your app in iPhone Mirroring on macOS 27. Identify areas in your app that need to be a little more flexible. And if you like agentic coding, give the new skill a try and discover how much it can do automatically.
Thanks for joining. I cannot wait to resize your apps.
-
-
3:24 - Use local screen references
// Use local screen references // Access the correct screen through a windowScene let screen = window?.windowScene?.screen // Pass in local screen references func generateThumbnail(_ image: UIImage, screen: UIScreen) -> UIImage { // existing code, replacing main screen with local screen reference // ... } -
3:49 - Replace screen scale with displayScale
// Replace the screen's scale with trait collection's displayScale override func layoutSubviews() { super.layoutSubviews() // layoutSubviews will be called again automatically when displayScale changes let displayScale = traitCollection.displayScale // ... } -
4:36 - Register for trait changes
// Manually register for trait changes let displayScaleTrait: [UITrait] = [UITraitDisplayScale.self] registerForTraitChanges(displayScaleTrait) { (view: GalleryView, previousTraitCollection: UITraitCollection) in view.cache.invalidate() } -
5:19 - Monitor effective geometry changes
// UIWindowSceneDelegate func windowScene( _ windowScene: UIWindowScene, didUpdateEffectiveGeometry previousEffectiveGeometry: UIWindowScene.Geometry ) { let geometry = windowScene.effectiveGeometry let availableSpace = geometry.coordinateSpace.bounds // ... } -
5:35 - Check available space using view bounds
// Checking available space override func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() let availableSpace = view.bounds.size // ... } -
8:12 - Configure motion and location body
// Configure motion and heading bodies override func viewDidLoad() { super.viewDidLoad() motionManager.deviceMotionBody = view locationManager.headingBody = view } -
9:51 - Opt into sidebar layout
tabBarController.sidebar.preferredPlacement = .sidebar -
10:22 - Check sidebar availability
tabBarController.sidebar.isAvailable -
10:53 - Set prominent tab identifier
// Set the prominent tab let tabs = [ // ... ] let tabBarController = UITabBarController(tabs: tabs) tabBarController.prominentTabIdentifier = "cart" -
11:30 - Customize bar minimization behavior
// Customize bar minimization behavior override init( nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle? ) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) navigationItem.barMinimizationBehavior = .always navigationItem.barMinimizationSafeAreaAdjustment = .never } -
15:05 - Export Xcode skills for use in other tools
xcrun agent skills export
-
-
- 0:00 - Introduction
A modern UIKit app dynamically adjusts to the environment it runs in. iOS 27 introduces new requirements and APIs to help you get there. Discover new APIs in tab bars, navigation bars, and menus; Apple Intelligence support; and a new skill that helps an agent handle much of this modernization work automatically.
- 0:34 - App adaptivity
iPhone apps are now fully resizable in iPhone Mirroring on Mac and when running on iPad. Your app needs to dynamically adjust to any available scene size at runtime. The four areas to audit are scene lifecycle adoption, main screen references, user interface idiom checks, and interface orientation checks.
- 2:10 - Legacy API: App lifecycle
UIScene lifecycle is now required when building with the latest SDKs; apps without it will no longer launch. If you haven't migrated from app lifecycle yet, start here.
- 2:51 - Legacy API: Main screen
UIScreen.main always refers to the device's main screen, but in resizable environments your scene may be running on a different screen entirely. Replace it with screen references from the window scene, trait collections for scale, and effective geometry for available space.
- 5:46 - Full-screen mode for games
UIRequiresFullscreen is now honored on iPhone in resizable environments, enabling discrete resizing that respects your supported interface orientations so games always render at full quality.
- 6:17 - Legacy API: User interface idiom
The user interface idiom trait is no longer meaningful for layout. An iPhone app running on iPad stays in the phone idiom but is fully resizable. Use size classes instead.
- 7:06 - Legacy API: Interface orientation
Supported interface orientations are treated as a preference only and are ignored in resizable environments, including iPhone Mirroring which always reports portrait. Replace orientation checks with size classes.
- 7:55 - UIView Body protocols for motion & location
UIView now conforms to the new Body protocols from CoreMotion and CoreLocation in iOS 27, making it straightforward to keep motion and location data in the correct coordinate space regardless of interface orientation.
- 8:19 - Test your resizable iPhone app
Xcode 27's Device Hub app and Xcode Previews let you freely drag the edges of the simulator to test any screen size without needing multiple devices. Test with real devices for iPhone Mirroring and iPad.
- 9:18 - Tab bars and sidebars
iPhone apps can now opt into a sidebar layout by setting tabBarController.sidebar.preferredPlacement to .sidebar, with the system deciding when there's enough space to show it. Check tabBarController.sidebar.isAvailable to determine if the system can show the sidebar, and use tabBarController.prominentTabIdentifier to pin any tab so it stays visible even when the tab bar collapses during scroll.
- 10:52 - Navigation bars
Navigation bars can now slide away during scroll to give people more room. The system determines when this happens by default; override it with barMinimizationBehavior. The .automatic scroll edge effect style has new visuals; apps that previously overrode it to .soft should re-evaluate their design.
- 12:37 - Menus
Menu element images may no longer appear by default in some contexts like menu bars on iPadOS and macOS. Use preferredImageVisibility to override this behavior where needed.
- 13:01 - Integrate with Apple Intelligence
Menus automatically display an Ask Siri button when there's content relevant for Siri. If your app supports drag and drop, Siri can load resources from your drag handlers. Drag sessions can be initiated without a user gesture, so avoid triggering animations or presenting modal UI from sessionWillBegin.
- 14:07 - Agentic coding
Xcode 27's app modernization skill can automatically convert mainScreen calls, replace orientation checks with size class checks, and migrate your app to scene lifecycle. Skills can also be exported for use in other tools.
- 15:32 - Next steps
Build with the iOS 27 SDK, test in Device Hub and iPhone Mirroring on macOS 27, identify flexibility gaps in your app, and try the agentic modernization skill.