View in English

  • Apple Developer
    • Get Started

    Explore Get Started

    • Overview
    • Learn
    • Apple Developer Program

    Stay Updated

    • Latest News
    • Hello Developer
    • Platforms

    Explore Platforms

    • Apple Platforms
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    • App Store

    Featured

    • Design
    • Distribution
    • Games
    • Accessories
    • Web
    • Home
    • CarPlay
    • Technologies

    Explore Technologies

    • Overview
    • Xcode
    • Swift
    • SwiftUI

    Featured

    • Accessibility
    • App Intents
    • Apple Intelligence
    • Games
    • Machine Learning & AI
    • Security
    • Xcode Cloud
    • Community

    Explore Community

    • Overview
    • Meet with Apple events
    • Community-driven events
    • Developer Forums
    • Open Source

    Featured

    • WWDC
    • Swift Student Challenge
    • Developer Stories
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Centers
    • Documentation

    Explore Documentation

    • Documentation Library
    • Technology Overviews
    • Sample Code
    • Human Interface Guidelines
    • Videos

    Release Notes

    • Featured Updates
    • iOS
    • iPadOS
    • macOS
    • watchOS
    • visionOS
    • tvOS
    • Xcode
    • Downloads

    Explore Downloads

    • All Downloads
    • Operating Systems
    • Applications
    • Design Resources

    Featured

    • Xcode
    • TestFlight
    • Fonts
    • SF Symbols
    • Icon Composer
    • Support

    Explore Support

    • Overview
    • Help Guides
    • Developer Forums
    • Feedback Assistant
    • Contact Us

    Featured

    • Account Help
    • App Review Guidelines
    • App Store Connect Help
    • Upcoming Requirements
    • Agreements and Guidelines
    • System Status
  • Quick Links

    • Events
    • News
    • Forums
    • Sample Code
    • Videos
 

Videos

Open Menu Close Menu
  • Collections
  • All Videos
  • About

More Videos

  • About
  • Summary
  • Transcript
  • Code
  • What’s new in SwiftUI

    Explore the latest additions to SwiftUI and discover how they can improve your apps. We'll introduce a new Document protocol with direct disk access and snapshot-based diffing for building high-performance apps; new APIs for reordering content in lists, grids, and sections; and toolbar enhancements including visibility priority and auto-minimizing behavior. We'll also cover expanded presentation APIs — including swipe actions on any view — plus AsyncImage caching improvements and lazy state initialization for Observable types.

    Chapters

    • 0:00 - Introduction
    • 2:12 - Refreshed look and feel
    • 8:06 - Document-based apps
    • 15:18 - Presentation and interaction
    • 19:58 - Data flow and performance
    • 27:25 - Next steps

    Resources

    • TN3211: Resolving SwiftUI source incompatibilities for State and ContentBuilder
    • State()
    • ContentBuilder
    • Swift Collections on GitHub
      • HD Video
      • SD Video

    Related Videos

    WWDC26

    • Code-along: Build powerful drag and drop in SwiftUI
    • Modernize your UIKit app
  • Search this video…

    Hi, I'm Steven, and I work on UI Frameworks! My name is Julia, and I'm also a UI Frameworks engineer. We're excited to talk to you about what's new in SwiftUI! SwiftUI has gained some major upgrades. From a refined look and feel, to performance improvements, new ways to interact with your apps, and a powerful new document API, we have a lot of great new things to share.

    But first… I love stickers. I have my laptop covered with them. I love stickers too! But there are only so many places in the real world where we're allowed to stick them. So we came up with a way to unlock unlimited sticker potential: an app! Meet our sticker app! I'll start by picking a photo… I really like this photo of Steven and me at Apple Park! Now… I wish we could bring our pets to work. They would absolutely love it here! So I'm gonna drag this sticker of my dog Pretzel into the scene... and resize her to match the size of her personality. And I'll add my cat, Kishka! She's got an even bigger personality! Now this is what I call an ideal workday! I wish we could do this all day, but there's a lot to cover, so it's important we stick… to the script! Our app takes advantage of lots of new enhancements to SwiftUI that have helped us build a first class user experience that looks great and also has great performance. I'll start by taking you through the beautiful new look apps gain on the 2027 releases, along with ways to optimize toolbar content for resizability. Julia will tell you about new APIs that unlock powerful document features in your apps along with improvements to presentation and interaction. And finally, I'll talk about how to keep your apps running smoothly with enhancements to performance, and data flow. Let's get started with the refreshed look and feel for apps on the 2027 releases. When I build and run our app, the Liquid Glass design automatically takes on its updated appearance. Apps gain this look without having to change a single line of code! Liquid Glass has a refined look and automatically responds to the new Liquid Glass slider to adjust its tint.

    On macOS, like on iOS, you can mark Liquid Glass custom elements as "interactive" so they respond more fluidly to user's clicks. And this is optimized to work great with the mouse pointer, so it feels right at home on the Mac.

    And just like Mac, our iPad app automatically takes on a distinct appearance when inactive, with the icons and text dimming to reinforce which window is active; like here, when I tap to switch between our app and the Files app.

    I love how these improvements look in our app. And even more so since the app's look was refreshed without having to make any code changes! But there are also ways to fine tune our app's appearance to get things… just right.

    Our custom account button in the sidebar dims along with the rest of the tab labels, using the appearsActive environment value to conditionally reduce the button's opacity when the window isn't active. iPad and Mac Menu Bars now have a minimal set of icons by default, reserving them for key actions. However, I can apply the labelStyle titleAndIcon modifier to our Store menu item to show its icon, so that one will stand out.

    As I accumulate more stickers, I really appreciate the resizability of our app on Mac and iPad. And on iOS 27, our iPhone app becomes resizable too.

    In Xcode 27, Live Previews now have resize handles that allow you to test how your app responds to being interactively resized! This allows you to instantly preview how an app will behave when using iPhone Mirroring, or when running it as an iPhone app on iPad.

    This is already working great, especially since we made sure our app resizes well on iPad and Mac.

    Apps built with SwiftUI gain a lot of this functionality automatically, but if your app uses both UIKit and SwiftUI, there may be some additional things to consider. Things like how to correctly determine screen geometry, using size classes instead of idiom for sizing your views, and responding to interface orientation changes. To learn more about getting ready for resizability for apps that use both UIKit and SwiftUI, check out "Modernize your UIKit app".

    Our app also has a full store experience that allows people to download new sticker packs! My personal favorite is the WWDC26 sticker pack! The store view has a few tabs, including one for the shopping cart. The shopping cart tab is displayed on the bottom trailing edge of the screen, distinguishing it from the other tabs, which contain store content. To enable this special tab placement, I'm using the new prominent tab role to make it stand out.

    With all of the features our app has, the toolbar is a great way to provide quick access to the most important actions. As I add more features to the app, the list of toolbar items will probably grow even bigger! And this becomes especially important when resizing the app.

    When I resize the app window, the toolbar items are automatically adjusted by the system.

    And some of the items that don't fit, end up becoming hidden! On iPhone, where there's even less horizontal space to work with, there isn't enough room for all my toolbar buttons. Important actions like Undo, Redo, and Share are hidden in the overflow menu! And that's where the new toolbar APIs come in! They allow me to specify which buttons stay visible when toolbar space is limited.

    The most important ToolbarItemGroup contains buttons for editing: Undo and Redo. But they're currently hidden! I want the system to know that it's important to keep them visible. I can do this by adding the new visibilityPriority modifier, and setting the priority to high. Now, the Undo and Redo buttons become visible! Some of the actions I'd prefer to keep in the overflow menu, since they aren't used as often, like the buttons for swapping out the photo, exporting the page as an image, and clearing the stickers. I choose to always place these buttons in the overflow menu by grouping them in the new ToolbarOverflowMenu container. And there they are in the menu, if I need them! Lastly, for my Share button, I want to make sure it's never hidden, so I don't forget to send these sticker pages to everyone I know. I can use the new topBarPinnedTrailing placement to make the Share button always visible in the trailing position.

    And now, the toolbar is set up perfectly, to access all the app's important features, no matter the window or screen size.

    There's one more toolbar enhancement I want to show you. I have lots of stickers in my collection, and when I'm scrolling through them, I want to have as much space as possible for all of them. So I add the new toolbarMinimizeBehavior modifier, and set it to "onScrollDown" for the navigationBar placement. Now, the system automatically moves the navigation bar out of the way when I scroll.

    I think the app looks and feels great, but there's a lot more going on under the surface. Our app can open and save sticker pages, and even has support for exporting the pages as images. This is all thanks to some powerful new features Julia will tell you about. The app has all these capabilities, and even more, thanks to the new SwiftUI Document API. I'll start by giving an overview of the new Document APIs, and how they serve as a base for building an app.

    For quite some time now, SwiftUI has had support for document-based apps via the FILE_DOCUMENT and REFERENCE_FILE_DOCUMENT protocols.

    In the 2027 releases, I'm happy to share expanded APIs that build on that foundation.

    You might be familiar with document-based apps like PixelMator PRO, or Pages, or the one we spend our days in: Xcode.

    They get a lot of functionality out of the box, including things like keyboard shortcuts, Command+N for new documents, and Command-O for opening documents, the edited indicator that tells you when a document has changes, a smart autosaving mechanism, and much more. And the Document API unlocks a number of improvements an app can make both under-the-hood, and in the UI. I'll cover three of these, including the document creation context, disk reading and writing performance improvements, and first-class support for direct document URL access.

    Here's how people create documents in our app.

    By default, the app lets you start with a blank sticker page. But I want to help people get going more quickly. So there's also a button to create a page from a photo! I use the new DocumentCreationSource API to declare two sources, blank and photo, and add a NewDocumentButton for each one to the launch scene. When choosing one of these buttons, SwiftUI passes the source to my document creation closure via the context parameter. I check the context in my initializer, and if the source is "photo", the document opens with the photo picker already presented! Now, I'm only one tap away from putting stickers on my photo.

    Document-based apps read and write a lot of data. They can also have complex UI that needs to update frequently. The new API provides great ways to optimize these operations and keep your app running smoothly.

    I opt our app into the document architecture by declaring a DocumentGroup as the first Scene in the app's body. My StickerDocument class describes the document type. It provides data to the views and describes how to read data from disk and write it back. The Document API works in conjunction with the modern Observation framework, so I am using the Observable macro. This alone gives me a performance boost: the views will update only when a property they depend on changes. My goal is to make reading and writing as fast and efficient as possible. Let me take you through the optimization points in the new API.

    For writing, I conform the document to the writable document protocol. It has three requirements. First, a list of formats the app can write.

    Our application supports a custom package format with a photo, and stickers inside. Second, the snapshot method that returns the current document content for writing. To represent the content, I use a custom PageSnapshot struct. It contains everything I need for writing — the background image, coordinates for the stickers, and the stickers themselves.

    It acts as a snapshot of the document at a single point in time.

    To satisfy the third requirement, I provide a Writer. The Writer conforms to the DocumentWriter protocol and knows how to write a document to disk in a specified format. I give it the requested content type, which for my app is "stickerDocument". The DocumentWriter protocol has a notion of Snapshot. And the PageSnapshot type fits perfectly here! DocumentWriter's only requirement is a method for writing. It offers multiple opportunities to optimize performance. First, the write method is nonisolated and asynchronous.

    This lets me perform expensive disk writing operations in the background, so the app stays responsive.

    I write only the parts of the package that actually need updating, by comparing the current and the previous snapshots. Even with all the optimizations, disk operations can take noticeable time, so SwiftUI provides a progress parameter that lets me report writing progress using the Foundation Subprogress API.

    To teach our document type how to read from disk, I conform the StickerDocument class to the ReadableDocument protocol. ReadableDocument is a twin to WritableDocument. Here's how they compare. Each protocol requires a list of supported content types. WritableDocument provides a snapshot and ReadableDocument knows how to apply it.

    WritableDocument has a friend protocol: DocumentWriter.

    And ReadableDocument's friend is DocumentReader, which does all the disk-related heavy lifting.

    Now, the Sticker app is ready to read and write files, like this page! I think I make a pretty good pirate! There's one more feature to add: saving pages as images, so I can share them with people who don't have the app.

    The Document API makes it possible to extend my writer to save pages in another format, like PNG, using Core Graphics.

    I return to the writable content types definition and add .PNG to the list. Now, to support an additional format, I revisit the write method I implemented earlier.

    Since the app can now handle multiple formats, I am adding content type checks for each type the app supports.

    For PNG, I use Core Graphics to flatten the stickers and background photo into a single image and write it to the URL.

    To add even more types, I could write the document in any format, using any framework, just by adding another content type! That's how our app is set up to save documents.

    Now, it's time to get to my sticker collection, where there are some great enhancements to presentation and interaction. Sometimes, our massive collection of stickers can feel a little chaotic. And that's where the reorderable container APIs come in! There are two different ways to browse the sticker collection in the app's inspector. The first is a "List" that displays each sticker along with its name. I'd like to keep these stickers organized by dragging them to rearrange their order. So I use the Reorderable API.

    I add the Reorderable modifier to ForEach and add a reorderContainer modifier to the List.

    Then, in the closure, I call this helper function I wrote, difference.apply, to update my array of stickers! Under the hood, my apply function uses the open source swift-collections package to commit the ordering changes.

    Visit swift.org for more details.

    SwiftUI automatically handles the drag interaction and animation for me! I can also organize my stickers in a grid, which makes it easier to browse more at a time. And the reorderable API works with any container, not just List! This means I can take the code from my list and repurpose it to use a Lazy_V_Grid! The code for reordering stays exactly the same.

    I get the same interactive reordering behavior on a completely different container, using the same code! And now these APIs also bring reordering capabilities to watchOS for the first time! When it comes to reordering, that's just the beginning! For a deep dive into all of the features of reorderable containers, check out the code-along session: "Build powerful drag and drop in SwiftUI".

    When I'm on the go, I love to customize my sticker pages on my iPhone! The sheet at the bottom of the UI keeps all my stickers right where I need them.

    I've even set up swipe actions for removing stickers from the list, by adding the swipeActions modifier to my list item, along with this Delete button.

    But I want some more flexibility to customize my list, so I've decided to switch to using a Lazy V Stack. And now, SwiftUI supports swipe actions on any view, not just List! I move my ForEach out of the List into a Lazy V Stack with my updated item style and add the swipeActionsContainer modifier, which coordinates the swipe actions across the items in this scroll view.

    Well, as I said before, I love stickers! So, it's no surprise that when I start decorating a photo, I'll admit it, I can get a little carried away. And sometimes that means I need to make space by deleting some stickers, even if I love them all.

    I've added a context menu to each sticker placed on the photo, giving me quick access to delete it with this Delete button.

    Tapping the button sets the stickerToDelete State variable to the current sticker.

    I've also added a confirmationDialog modifier. Now, confirmation dialogs support the same item-binding pattern that sheets use.

    I pass a stickerToDelete binding to the modifier.

    When I set stickerToDelete to a value by tapping the Delete button, the confirmation dialog appears.

    And this also works with alert! I've taken you through some great improvements to presentation and interaction, but that's not all! There are even more improvements in SwiftUI in the 2027 releases.

    Many of these improvements make the APIs you're already using better without any changes to your apps.

    Great performance is important for making an app feel responsive and polished. Being thoughtful about the way data flows through your app is one of the best ways to keep your app's performance in top shape.

    Steven is going to tell you about some big improvements to data flow and performance. Thanks, Julia! The sticker store you added to the app is really cool. I love how the sticker packs are downloadable, because that allows me to take advantage of improvements to AsyncImage! AsyncImage is a great way to load image assets from the Internet as they appear on screen.

    This works great when scrolling to reveal more of these adorable stickers of Kishka and Pretzel! But up until now, AsyncImage hasn't kept images in memory. When scrolling back up, they would reload when reappearing on screen. I'd prefer that these images show immediately when scrolling back to the top. And in the 2027 releases, they do! AsyncImage now supports standard HTTP caching, so images are cached by default, respecting the server's cache headers, without any changes to your code. This is enabled automatically for every app. And apps built with Xcode 27 can take advantage of new APIs to customize how downloads happen.

    For more control over how an image is downloaded I can construct my own URLRequest and pass it to AsyncImage. This allows a wide variety of per-request customizations, like specifying a cache policy, for instance. And if I need a longer-lived configuration, like a bigger cache, let's say, I can instantiate my own custom URLSession and configure a URLCache with whatever capacity I need and then use my session by passing it to the asyncImageURLSession modifier.

    Now, when I scroll back to the top, the images are automatically loaded from the cache! SwiftUI provides a variety of ways for apps to model and store their data, and to pass that data to views. One great way to store an app's data is using Observable classes, like I do here with the StickerStore class! When StickerStoreView is initialized, a new instance of my StickerStore class is created and assigned to the state variable. This instance stays around for the lifetime of the view. But what happens when the parent view updates, causing StickerStoreView to be initialized again? In prior releases, a new instance of StickerStore would be created on every initialization. But the original instance is still the one being stored in the State variable. And so the new one was just discarded. This would happen again for every reinitialization of the view even though the main stored instance of the class remained stable.

    In the 2027 releases, for the first time, classes initialized and stored using State properties are now lazy, which means they will only be initialized once. This is thanks to the conversion of State from a Dynamic Property to a macro! Now, when StickerStoreView is initialized for the first time, a new instance of my StickerStore class is created like before, but on future initializations, no new class instances are created.

    And this behavior has been back ported to the releases where @Observable was first introduced, starting with iOS 17, macOS 14, and aligned releases.

    In some cases, the introduction of the state macro can be a source-breaking change.

    For example, if you specify a default value for your @State variable, and then you assign a value to the same @State variable inside your init, Xcode will show an error about use before initialization. To resolve this error, remove the unnecessary default value assignment. For additional details about how the State macro may impact your code, check out the documentation.

    Maintaining good runtime performance is critical for keeping your app working smoothly. But there's another type of performance that can have a significant impact on your app development experience.

    If your app has complex, deeply nested views, you may have encountered this error: "The compiler is unable to type-check this expression in reasonable time". But why does this happen? This view has a Section, a Group, and a ForEach wrapping its content. To type check this expression, first the compiler has to select which overload of Section to use. Section can be initialized with a builder that produces either a View, or TableRowContent. To know which one to use, the compiler has to try both options. In my code, the Section builder returns a Group. The compiler can't know what type of content section produces until it figures out the type of the nested group's content. This time, there are even more options, and for the nested ForEach the compiler will have to try each one. And then, ForEach's builder has its own set of options that will also need to be checked.

    And we haven't even gotten to the content yet! Trying each of these paths makes type checking increasingly expensive. But I already know that Section, Group, and ForEach in my code are building views, so there's really only one valid path through this decision tree.

    Instead of this complex set of choices, what if these builders weren't constrained by the types they produce, and instead just assembled their content? In the 2027 releases, that's exactly what SwiftUI starts doing. The most common set of builders now share a single initializer, leaving just one, straightforward path.

    This is possible because multiple different builder types have been unified under a single builder: ContentBuilder! This is a step towards enabling unified builders across all of SwiftUI's APIs. And ContentBuilder can be used with any minimum deployment target, because under the hood, it's an evolution of the existing ViewBuilder.

    ContentBuilder provides a substantial improvement in type checking performance in SwiftUI when building using Xcode 27; whether you're targeting the 2027 releases, or previous releases as well. We're also excited to introduce new agent skills included with Xcode 27 to help you adopt the new features from the 2027 releases in your apps, and improve your app's performance and code correctness.

    The SwiftUI Specialist Skill can help you follow SwiftUI best practices in your apps. The What's New In SwiftUI Skill can guide you through adopting new APIs from the 2027 releases. Both of these skills can be accessed in the Coding Assistant in Xcode 27. And to use these skills with other tools, you can export them with the "xcrun agent skills export" command. This will create markdown files you can import in your workflows. We've covered some exciting new enhancements to SwiftUI. Now, it's your turn! Start by building your project in Xcode 27 for the 2027 releases and then check out your app's updated look and feel! If you have a document-based app, investigate how the new Document APIs can make it even better.

    And try out the SwiftUI agent skills in Xcode 27 to adopt new APIs and best practices.

    Well… as sad as we are to peel ourselves away, we have to adhere to the schedule. We hope you have as much fun adopting these improvements in your apps as we had with ours! Thanks for sticking with us!

    • 3:20 - appearsActive environment value

      struct SidebarFooterView: View {
          @Environment(\.appearsActive) private var appearsActive
      
          var body: some View {
              MyAccountView()
                  .opacity(appearsActive ? 1 : 0.5)
          }
      }
    • 3:34 - Menu icon visibility

      CommandMenu("Stickers") {
          Button { openStore() } label: {
              Label("Store", systemImage: "bag.fill")
                  .labelStyle(.titleAndIcon)
              }
          }
          // Other menu items
      }
    • 5:12 - Prominent tab role

      TabView {
          Tab { EventsTab() }
          Tab { HolidaysTab() }
          Tab { FunTab() }
      
          Tab(role: .prominent) {
              CartTab()
          }
      }
    • 6:15 - Toolbar item visibility and overflow menu

      // Toolbar item visibility priority
      
      StickerPageView()
          .toolbar {
              ToolbarItemGroup {
                  UndoButton()
                  RedoButton()
              }
              .visibilityPriority(.high)
              ToolbarOverflowMenu {
                  ChoosePhotoButton()
                  ExportAsImageButton()
                  ClearAllStickersButton()
              }
              ToolbarItem(placement: .topBarPinnedTrailing) {
                  ShareButton()
              }
          }
    • 7:37 - Minimize toolbar on scroll with toolbarMinimizeBehavior

      // Minimize toolbar when scrolling
      
      ScrollView {
          StickerListView()
      }
      .toolbarMinimizeBehavior(.onScrollDown, for: .navigationBar)
    • 9:47 - Document creation sources with context parameter

      // Use the context to create a document
      
      @main
      struct Stickers: App {
          var body: some Scene {
              DocumentGroupLaunchScene("Create a Sticker Page") {
                  NewDocumentButton("New Sticker Page", source: .blank)
                  NewDocumentButton("Sticker Page from Photo…", source: .photo)
              }
             
              DocumentGroup { /* ... */ }
          }
      }
        
      extension DocumentCreationSource {
          static let blank = Self(id: "blank")
          static let photo = Self(id: "photo")
      }
    • 10:01 - Use the context to create a document

      @main
      struct Stickers: App {
          var body: some Scene {
              DocumentGroupLaunchScene("Create a Sticker Page") {
                  NewDocumentButton("New Sticker Page", source: .blank)
                  NewDocumentButton("Sticker Page from Photo…", source: .photo)
              }
             
              DocumentGroup { document in
                  StickerPageDocumentView(document)
              } { configuration, context in
                  StickerPageDocument(configuration: configuration, context: context)
              }
          }
      }
    • 10:43 - Document app declaration

      @main
      struct Stickers: App {
          var body: some Scene {
              DocumentGroup { /* ... */ }
              WindowGroup { /* ... */ }
          }
      }
    • 11:25 - Implement document writing

      @Observable
      final class StickerDocument {
          // ...
      }
    • 11:34 - Implement document writing: list writable formats

      @Observable
      final class StickerDocument {
          
          static let writableDocumentTypes: [UTType] = [.stickerDocument]
        
          // ...
      }
      
      import UniformTypeIdentifiers
      
      extension UTType {
          static let stickerDocument = UTType(exportedAs: "stickerdocument")
      }
    • 11:45 - Implement document writing: provide snapshot

      @Observable
      final class StickerDocument {
          
          static let writableDocumentTypes: [UTType] = [.stickerDocument]
        
          @MainActor
          func snapshot(contentType: UTType) async throws -> sending PageSnapshot { /* ... */ }
            
          // ...
      }
    • 11:54 - Implement document writing: represent the snapshot

      struct PageSnapshot {
          var background: Image
          var metadata: StickerPlacements
          var stickers: [Image]
      }
      
      struct StickerPlacements { /* ... */ }
    • 12:13 - Implement document writing: provide a DocumentWriter

      @Observable
      final class StickerDocument {
          
          static let writableDocumentTypes: [UTType] = [.stickerDocument]
        
          @MainActor
          func snapshot(contentType: UTType) async throws -> sending PageSnapshot {
              makeSnapshot()
          }
            
          func writer(configuration: sending WriteConfiguration) -> sending Writer {
              Writer(contentType: configuration.contentType)
          }
      }
    • 12:33 - DocumentWriter: Snapshot

      struct Writer<Snapshot>: DocumentWriter {
          typealias Snapshot = PageSnapshot
          
          // ...
      }
    • 12:36 - DocumentWriter: PageSnapshot as Snapshot

      struct Writer<Snapshot>: DocumentWriter {
          typealias Snapshot = PageSnapshot
        
          let contentType: UTType
          
          // ...
      }
    • 12:42 - DocumentWriter protocol implementation

      struct Writer<Snapshot>: DocumentWriter {
          typealias Snapshot = PageSnapshot
        
          let contentType: UTType
          
          nonisolated func write(
              snapshot: sending PageSnapshot, to destination: URL,
              previous: sending PageSnapshot?, progress: consuming Subprogress
          ) async throws {
              // write .stickerDocument
          }
      }
    • 13:18 - Progress reporting during writing

      struct Writer<Snapshot>: DocumentWriter {
          typealias Snapshot = PageSnapshot
        
          let contentType: UTType
          
          nonisolated func write(
              snapshot: sending PageSnapshot, to destination: URL,
              previous: sending PageSnapshot?, progress: consuming Subprogress
          ) async throws {
              // report progress…
              // write .stickerDocument
          }
      }
    • 13:27 - Implement document reading with ReadableDocument protocol

      extension StickerDocument: ReadableDocument {
        
      }
    • 14:35 - Add PNG to supported formats list

      @Observable
      final class StickerDocument: WritableDocument {
        
          static let writableContentTypes: [UTType] = [.stickerDocument, .png]
      }
    • 14:48 - Add content type checks

      struct Writer<Snapshot>: DocumentWriter {
          typealias Snapshot = PageSnapshot
        
          let contentType: UTType
          
          nonisolated func write(
              snapshot: sending PageSnapshot, to destination: URL,
              previous: sending PageSnapshot?, progress: consuming Subprogress
          ) async throws {
              if contentType.conforms(to: .stickerDocument) {
                  // write .stickerDocument
              } else if contentType.conforms(to: .png)
              
          }
      }
    • 14:56 - Writing multiple formats including PNG

      struct Writer<Snapshot>: DocumentWriter {
          typealias Snapshot = PageSnapshot
      
          let contentType: UTType
      
          nonisolated func write(
              snapshot: sending PageSnapshot, to destination: URL, 
              previous: sending PageSnapshot?, progress: consuming Subprogress
          ) async throws {
              if contentType.conforms(to: .stickerDocument) {  
                  // write .stickerDocument
              } else if contentType.conforms(to: .png) {
                  let context = CGContext(/* ... */) 
                  context.draw(/* ... */)
              }
          }
      }
    • 15:58 - Reorderable list with reorderContainer

      List {
          ForEach(stickers) { sticker in
              StickerListItemView(sticker: sticker)
          }
          .reorderable()
      }
      .reorderContainer(for: Sticker.self) { difference in
          difference.apply(to: &stickers)
      }
    • 16:14 - Apply changes to a reorderable list's data source

      import OrderedCollections // from https://github.com/apple/swift-collections
      
      extension ReorderDifference where CollectionID == ReorderableSingleCollectionIdentifier {
          func apply(to values: inout [some Identifiable<ItemID>]) {
              var dictionary = OrderedDictionary(uniqueKeys: values.map { $0.id }, values: values)
              let destinationOffset: Int? = switch destination.position {
              case .before(let destination):
                  dictionary.keys.firstIndex(of: destination)
              case .end:
                  nil
              }
              dictionary.move(keys: sources, to: destinationOffset ?? values.endIndex)
              values = dictionary.values.elements
          }
      }
    • 16:48 - Reorderable grid with LazyVGrid

      LazyVGrid {
          ForEach(stickers) { sticker in
              StickerListItemView(sticker: sticker)
          }
          .reorderable()
      }
      .reorderContainer(for: Sticker.self) { difference in
          difference.apply(to: &stickers)
      }
    • 18:12 - Swipe actions on List

      List {
          ForEach(stickers) { sticker in
              StickerListItemView(sticker: sticker)
                  .swipeActions {
                      DeleteButton(sticker: sticker)
                  }
          }
      }
    • 18:15 - Swipe actions on any view

      ScrollView {
          LazyVStack {
              ForEach(stickers) { sticker in
                  StickerListItemView(sticker: sticker)
                      .swipeActions {
                          DeleteButton(sticker: sticker)
                      }
              }
          }
      }
      .swipeActionsContainer()
    • 18:54 - Confirmation dialog with item binding

      struct StickerCanvasView: View {
          var stickers: [Sticker]
          @State private var stickerToDelete: Sticker?
      
          var body: some View {
              ZStack {
                  ForEach(stickers) { sticker in
                      PlacedStickerView(sticker: sticker)
                          .contextMenu {
                              // ...
                          }
                  }
              }
              .confirmationDialog(
                  "Delete?", item: $stickerToDelete
              ) { sticker in
                  DeleteStickerButton(sticker)
              }   
          }
      }
    • 19:35 - Alert with item binding

      struct StickerCanvasView: View {
          var stickers: [Sticker]
          @State private var stickerToDelete: Sticker?
      
          var body: some View {
              ZStack {
                  ForEach(stickers) { sticker in
                      PlacedStickerView(sticker: sticker)
                          .contextMenu {
                              // ...
                          }
                  }
              }
              .alert(
                  "Delete?", item: $stickerToDelete
              ) { sticker in
                  DeleteStickerButton(sticker)
              }   
          }
      }
    • 21:18 - AsyncImage with URLRequest and custom URLSession

      @Observable class StickerStore {
          static let imageSession: URLSession = {
              let config = URLSessionConfiguration.default
              config.urlCache = URLCache(
                  memoryCapacity: 64 * 1024 * 1024,
                  diskCapacity: 256 * 1024 * 1024)
              return URLSession(configuration: config)
          }()
      }
      
      ForEach(pets) { pet in
          AsyncImage(request: URLRequest(
              url: pet.imageURL,
              cachePolicy: .returnCacheDataElseLoad)
          )
      }
      .asyncImageURLSession(StickerStore.imageSession)
    • 23:08 - @State converted to macro for lazy initialization

      @Observable class StickerStore { }
      
      struct StickerStoreView: View {
          // store is now lazily initialized, only
          // created once for the lifetime of the view
          @State private var store = StickerStore()
      
          var body: some View {
              // ...
          }
      }
    • 23:48 - @State macro init assignment error

      struct StickerPageView: View {
          @State private var page = StickerPage()
          let title: String
          
          init(title: String) {
              self.page = StickerPage(title: title) // Variable 'self.title' used before being initialized
              self.title = title
          }
          
          var body: some View {
              // ...
          }
      }
    • 24:02 - Fixed @State macro init assignment error

      struct StickerPageView: View {
          @State private var page: StickerPage // Removed default value to fix error
          let title: String
          
          init(title: String) {
              self.page = StickerPage(title: title)
              self.title = title
          }
          
          var body: some View {
              // ...
          }
      }
    • 26:07 - @ContentBuilder

      @ContentBuilder
      func stickerLibraryView() -> some View {
        // ...
      }
    • 0:00 - Introduction
    • This video covers the refreshed Liquid Glass look and feel, new document-based app APIs, presentation and interaction improvements, and data flow and performance enhancements.

    • 2:12 - Refreshed look and feel
    • Apps automatically adopt the updated Liquid Glass appearance on 2027 OS releases without code changes. Covers interactive Liquid Glass elements, the inactive window appearance on iPadOS, toolbar customization with overflow menus and pinned placements, minimize-on-scroll behavior, and guidance for building resizable apps using size classes.

    • 8:06 - Document-based apps
    • Expanded document APIs for SwiftUI apps, including the new DocumentCreationSource API for custom new-document flows, performance improvements for reading and writing large documents, and first-class support for direct document URL access via the FileDocument and ReferenceFileDocument protocols.

    • 15:18 - Presentation and interaction
    • New reorderable container APIs let users drag to reorder items in any container — List, LazyVGrid, and on watchOS for the first time. Also covers swipe actions on arbitrary views beyond List, and item binding-driven confirmation dialogs.

    • 19:58 - Data flow and performance
    • AsyncImage now supports standard HTTP caching by default, with new APIs for custom URLRequest and URLSession configurations. The @State property wrapper is converted to a macro, making class initialization lazy to prevent redundant allocations — back-ported to iOS 17, macOS 14, and aligned releases.

    • 27:25 - Next steps
    • Key takeaways and recommended next steps: build in Xcode 27 to see the updated Liquid Glass look in your app, adopt the new Document APIs for document-based apps, and explore the SwiftUI agent skills.

Developer Footer

  • Videos
  • WWDC26
  • What’s new in SwiftUI
  • Open Menu Close Menu
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    • App Store
    Open Menu Close Menu
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • Icon Composer
    • SF Symbols
    Open Menu Close Menu
    • Accessibility
    • Accessories
    • Apple Intelligence
    • Audio & Video
    • Augmented Reality
    • Business
    • Design
    • Distribution
    • Education
    • Games
    • Health & Fitness
    • In-App Purchase
    • Localization
    • Maps & Location
    • Machine Learning & AI
    • Security
    • Safari & Web
    Open Menu Close Menu
    • Documentation
    • Downloads
    • Sample Code
    • Videos
    Open Menu Close Menu
    • Help Guides & Articles
    • Contact Us
    • Forums
    • Feedback & Bug Reporting
    • System Status
    Open Menu Close Menu
    • Apple Developer
    • App Store Connect
    • Certificates, IDs, & Profiles
    • Feedback Assistant
    Open Menu Close Menu
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program
    • Mini Apps Partner Program
    • News Partner Program
    • Video Partner Program
    • Security Bounty Program
    • Security Research Device Program
    Open Menu Close Menu
    • Meet with Apple
    • Apple Developer Centers
    • App Store Awards
    • Apple Design Awards
    • Apple Developer Academies
    • WWDC
    Read the latest news.
    Get the Apple Developer app.
    Copyright © 2026 Apple Inc. All rights reserved.
    Terms of Use Privacy Policy Agreements and Guidelines