{"$schema": "https://raw.githubusercontent.com/guitaripod/wwdc-sessions/master/schema/transcript.schema.json", "id": "wwdc2026-269", "language": "eng", "source": "https://developer.apple.com/videos/play/wwdc2026/269", "wordCount": 3916, "segments": [{"start": 7.0, "text": "Hi, I'm Steven, and I work on UI Frameworks!"}, {"start": 10.0, "text": "My name is Julia, and I'm also a UI Frameworks engineer."}, {"start": 14.0, "text": "We're excited to talk to you about what's new in SwiftUI!"}, {"start": 18.0, "text": "SwiftUI has gained some major upgrades."}, {"start": 21.0, "text": "From a refined look and feel, to performance improvements,"}, {"start": 24.0, "text": "new ways to interact with your apps,"}, {"start": 26.0, "text": "and a powerful new document API,"}, {"start": 29.0, "text": "we have a lot of great new things to share."}, {"start": 32.0, "text": "But first… I love stickers."}, {"start": 36.0, "text": "I have my laptop covered with them."}, {"start": 39.0, "text": "I love stickers too!"}, {"start": 41.0, "text": "But there are only so many places in the real world"}, {"start": 43.0, "text": "where we're allowed to stick them."}, {"start": 45.0, "text": "So we came up with a way to unlock unlimited sticker potential:"}, {"start": 49.0, "text": "an app!"}, {"start": 51.0, "text": "Meet our sticker app!"}, {"start": 54.0, "text": "I'll start by picking a photo…"}, {"start": 56.0, "text": "I really like this photo of Steven and me at Apple Park!"}, {"start": 60.0, "text": "Now… I wish we could bring our pets to work."}, {"start": 64.0, "text": "They would absolutely love it here!"}, {"start": 67.0, "text": "So I'm gonna drag this sticker of my dog Pretzel into the scene..."}, {"start": 72.0, "text": "and resize her to match the size of her personality."}, {"start": 76.0, "text": "And I'll add my cat, Kishka!"}, {"start": 79.0, "text": "She's got an even bigger personality!"}, {"start": 83.0, "text": "Now this is what I call an ideal workday!"}, {"start": 87.0, "text": "I wish we could do this all day, but there's a lot to cover,"}, {"start": 91.0, "text": "so it's important we stick… to the script!"}, {"start": 94.0, "text": "Our app takes advantage of lots of new enhancements to SwiftUI that have helped us"}, {"start": 99.0, "text": "build a first class user experience that looks great and also has great performance."}, {"start": 105.0, "text": "I'll start by taking you through the beautiful new look apps gain"}, {"start": 108.0, "text": "on the 2027 releases, along with ways to optimize toolbar content for resizability."}, {"start": 115.0, "text": "Julia will tell you about new APIs"}, {"start": 117.0, "text": "that unlock powerful document features in your apps"}, {"start": 121.0, "text": "along with improvements to presentation and interaction."}, {"start": 125.0, "text": "And finally, I'll talk about how to keep your apps running smoothly"}, {"start": 128.0, "text": "with enhancements to performance, and data flow."}, {"start": 132.0, "text": "Let's get started with the refreshed look and feel for apps on the 2027 releases."}, {"start": 137.0, "text": "When I build and run our app, the Liquid Glass design"}, {"start": 140.0, "text": "automatically takes on its updated appearance."}, {"start": 144.0, "text": "Apps gain this look without having to change a single line of code!"}, {"start": 148.0, "text": "Liquid Glass has a refined look"}, {"start": 150.0, "text": "and automatically responds to the new Liquid Glass slider to adjust its tint."}, {"start": 155.0, "text": "On macOS, like on iOS, you can mark Liquid Glass custom elements"}, {"start": 160.0, "text": "as \"interactive\" so they respond more fluidly to user's clicks."}, {"start": 164.0, "text": "And this is optimized to work great with the mouse pointer,"}, {"start": 167.0, "text": "so it feels right at home on the Mac."}, {"start": 171.0, "text": "And just like Mac, our iPad app automatically takes on a distinct"}, {"start": 175.0, "text": "appearance when inactive, with the icons and text dimming to reinforce which window"}, {"start": 180.0, "text": "is active;"}, {"start": 181.0, "text": "like here, when I tap to switch between our app and the Files app."}, {"start": 186.0, "text": "I love how these improvements look in our app."}, {"start": 189.0, "text": "And even more so since the app's look was refreshed"}, {"start": 192.0, "text": "without having to make any code changes!"}, {"start": 194.0, "text": "But there are also ways to fine tune our app's appearance"}, {"start": 197.0, "text": "to get things… just right."}, {"start": 199.0, "text": "Our custom account button in the sidebar dims along with the rest of the tab labels,"}, {"start": 204.0, "text": "using the appearsActive environment value to conditionally reduce"}, {"start": 208.0, "text": "the button's opacity when the window isn't active."}, {"start": 212.0, "text": "iPad and Mac Menu Bars now have a minimal set of icons by default,"}, {"start": 217.0, "text": "reserving them for key actions."}, {"start": 219.0, "text": "However, I can apply the labelStyle titleAndIcon modifier to our Store menu item"}, {"start": 226.0, "text": "to show its icon, so that one will stand out."}, {"start": 229.0, "text": "As I accumulate more stickers, I really appreciate the resizability"}, {"start": 233.0, "text": "of our app on Mac and iPad."}, {"start": 236.0, "text": "And on iOS 27, our iPhone app becomes resizable too."}, {"start": 241.0, "text": "In Xcode 27, Live Previews now have resize handles that allow you to test"}, {"start": 246.0, "text": "how your app responds to being interactively resized!"}, {"start": 250.0, "text": "This allows you to instantly preview"}, {"start": 251.0, "text": "how an app will behave when using iPhone Mirroring,"}, {"start": 255.0, "text": "or when running it as an iPhone app on iPad."}, {"start": 258.0, "text": "This is already working great, especially since we made sure"}, {"start": 261.0, "text": "our app resizes well on iPad and Mac."}, {"start": 264.0, "text": "Apps built with SwiftUI gain a lot of this functionality automatically,"}, {"start": 269.0, "text": "but if your app uses both UIKit and SwiftUI,"}, {"start": 272.0, "text": "there may be some additional things to consider."}, {"start": 275.0, "text": "Things like how to correctly determine screen geometry,"}, {"start": 278.0, "text": "using size classes instead of idiom for sizing your views,"}, {"start": 282.0, "text": "and responding to interface orientation changes."}, {"start": 286.0, "text": "To learn more about getting ready for resizability for apps"}, {"start": 289.0, "text": "that use both UIKit and SwiftUI,"}, {"start": 292.0, "text": "check out \"Modernize your UIKit app\"."}, {"start": 296.0, "text": "Our app also has a full store experience"}, {"start": 298.0, "text": "that allows people to download new sticker packs!"}, {"start": 301.0, "text": "My personal favorite is the WWDC26 sticker pack!"}, {"start": 307.0, "text": "The store view has a few tabs, including one for the shopping cart."}, {"start": 312.0, "text": "The shopping cart tab is displayed on the bottom trailing edge of the screen,"}, {"start": 316.0, "text": "distinguishing it from the other tabs, which contain store content."}, {"start": 320.0, "text": "To enable this special tab placement,"}, {"start": 322.0, "text": "I'm using the new prominent tab role to make it stand out."}, {"start": 327.0, "text": "With all of the features our app has, the toolbar is a great way to provide"}, {"start": 331.0, "text": "quick access to the most important actions."}, {"start": 334.0, "text": "As I add more features to the app, the list of toolbar items"}, {"start": 337.0, "text": "will probably grow even bigger!"}, {"start": 340.0, "text": "And this becomes especially important when resizing the app."}, {"start": 343.0, "text": "When I resize the app window, the toolbar items are automatically adjusted"}, {"start": 348.0, "text": "by the system."}, {"start": 349.0, "text": "And some of the items that don't fit, end up becoming hidden!"}, {"start": 353.0, "text": "On iPhone, where there's even less horizontal space to work with,"}, {"start": 357.0, "text": "there isn't enough room for all my toolbar buttons."}, {"start": 360.0, "text": "Important actions like Undo, Redo, and Share are hidden in the overflow menu!"}, {"start": 366.0, "text": "And that's where the new toolbar APIs come in!"}, {"start": 369.0, "text": "They allow me to specify which buttons stay visible"}, {"start": 372.0, "text": "when toolbar space is limited."}, {"start": 375.0, "text": "The most important ToolbarItemGroup"}, {"start": 377.0, "text": "contains buttons for editing: Undo and Redo."}, {"start": 380.0, "text": "But they're currently hidden!"}, {"start": 382.0, "text": "I want the system to know that it's important to keep them visible."}, {"start": 385.0, "text": "I can do this by adding the new visibilityPriority modifier,"}, {"start": 390.0, "text": "and setting the priority to high."}, {"start": 392.0, "text": "Now, the Undo and Redo buttons become visible!"}, {"start": 397.0, "text": "Some of the actions I'd prefer to keep in the overflow menu,"}, {"start": 400.0, "text": "since they aren't used as often, like the buttons for swapping out the photo,"}, {"start": 404.0, "text": "exporting the page as an image, and clearing the stickers."}, {"start": 408.0, "text": "I choose to always place these buttons in the overflow menu"}, {"start": 412.0, "text": "by grouping them in the new ToolbarOverflowMenu container."}, {"start": 416.0, "text": "And there they are in the menu, if I need them!"}, {"start": 419.0, "text": "Lastly, for my Share button, I want to make sure it's never hidden,"}, {"start": 424.0, "text": "so I don't forget to send these sticker pages to everyone I know."}, {"start": 428.0, "text": "I can use the new topBarPinnedTrailing placement to make the Share button"}, {"start": 433.0, "text": "always visible in the trailing position."}, {"start": 437.0, "text": "And now, the toolbar is set up perfectly, to access all the app's important features,"}, {"start": 442.0, "text": "no matter the window or screen size."}, {"start": 446.0, "text": "There's one more toolbar enhancement I want to show you."}, {"start": 449.0, "text": "I have lots of stickers in my collection, and when I'm scrolling through them,"}, {"start": 453.0, "text": "I want to have as much space as possible for all of them."}, {"start": 456.0, "text": "So I add the new toolbarMinimizeBehavior modifier, and set it to \"onScrollDown\""}, {"start": 461.0, "text": "for the navigationBar placement."}, {"start": 464.0, "text": "Now, the system automatically moves the navigation bar"}, {"start": 467.0, "text": "out of the way when I scroll."}, {"start": 470.0, "text": "I think the app looks and feels great,"}, {"start": 472.0, "text": "but there's a lot more going on under the surface."}, {"start": 475.0, "text": "Our app can open and save sticker pages,"}, {"start": 478.0, "text": "and even has support for exporting the pages as images."}, {"start": 482.0, "text": "This is all thanks to some powerful new features Julia will tell you about."}, {"start": 486.0, "text": "The app has all these capabilities, and even more,"}, {"start": 489.0, "text": "thanks to the new SwiftUI Document API."}, {"start": 492.0, "text": "I'll start by giving an overview of the new Document APIs,"}, {"start": 496.0, "text": "and how they serve as a base for building an app."}, {"start": 500.0, "text": "For quite some time now, SwiftUI has had support for document-based apps"}, {"start": 504.0, "text": "via the FILE_DOCUMENT and REFERENCE_FILE_DOCUMENT protocols."}, {"start": 509.0, "text": "In the 2027 releases, I'm happy to share expanded APIs that build on that foundation."}, {"start": 516.0, "text": "You might be familiar with document-based apps like PixelMator PRO,"}, {"start": 522.0, "text": "or Pages, or the one we spend our days in: Xcode."}, {"start": 527.0, "text": "They get a lot of functionality out of the box,"}, {"start": 531.0, "text": "including things like keyboard shortcuts, Command+N for new documents,"}, {"start": 537.0, "text": "and Command-O for opening documents,"}, {"start": 540.0, "text": "the edited indicator that tells you when a document has changes,"}, {"start": 544.0, "text": "a smart autosaving mechanism, and much more."}, {"start": 548.0, "text": "And the Document API unlocks a number of improvements an app can make both"}, {"start": 553.0, "text": "under-the-hood, and in the UI."}, {"start": 557.0, "text": "I'll cover three of these,"}, {"start": 558.0, "text": "including the document creation context,"}, {"start": 561.0, "text": "disk reading and writing performance improvements,"}, {"start": 564.0, "text": "and first-class support for direct document URL access."}, {"start": 569.0, "text": "Here's how people create documents in our app."}, {"start": 573.0, "text": "By default, the app lets you start with a blank sticker page."}, {"start": 577.0, "text": "But I want to help people get going more quickly."}, {"start": 581.0, "text": "So there's also a button to create a page from a photo!"}, {"start": 586.0, "text": "I use the new DocumentCreationSource API to declare two sources, blank and photo,"}, {"start": 593.0, "text": "and add a NewDocumentButton for each one to the launch scene."}, {"start": 597.0, "text": "When choosing one of these buttons, SwiftUI passes the source to my document"}, {"start": 602.0, "text": "creation closure via the context parameter."}, {"start": 606.0, "text": "I check the context in my initializer, and if the source is \"photo\","}, {"start": 611.0, "text": "the document opens with the photo picker already presented!"}, {"start": 616.0, "text": "Now, I'm only one tap away from putting stickers on my photo."}, {"start": 623.0, "text": "Document-based apps read and write a lot of data."}, {"start": 627.0, "text": "They can also have complex UI that needs to update frequently."}, {"start": 632.0, "text": "The new API provides great ways to optimize these operations"}, {"start": 636.0, "text": "and keep your app running smoothly."}, {"start": 639.0, "text": "I opt our app into the document architecture by declaring a DocumentGroup"}, {"start": 645.0, "text": "as the first Scene in the app's body."}, {"start": 648.0, "text": "My StickerDocument class describes the document type."}, {"start": 651.0, "text": "It provides data to the views and describes how to read data from disk"}, {"start": 657.0, "text": "and write it back."}, {"start": 658.0, "text": "The Document API works in conjunction with the modern Observation framework,"}, {"start": 663.0, "text": "so I am using the Observable macro."}, {"start": 665.0, "text": "This alone gives me a performance boost: the views will update"}, {"start": 670.0, "text": "only when a property they depend on changes."}, {"start": 674.0, "text": "My goal is to make reading and writing as fast and efficient as possible."}, {"start": 681.0, "text": "Let me take you through the optimization points in the new API."}, {"start": 685.0, "text": "For writing, I conform the document to the writable document protocol."}, {"start": 691.0, "text": "It has three requirements."}, {"start": 694.0, "text": "First, a list of formats the app can write."}, {"start": 698.0, "text": "Our application supports a custom package format with a photo, and stickers inside."}, {"start": 704.0, "text": "Second, the snapshot method that returns the current document content for writing."}, {"start": 710.0, "text": "To represent the content, I use a custom PageSnapshot struct."}, {"start": 716.0, "text": "It contains everything I need for writing"}, {"start": 719.0, "text": "— the background image,"}, {"start": 721.0, "text": "coordinates for the stickers,"}, {"start": 723.0, "text": "and the stickers themselves."}, {"start": 725.0, "text": "It acts as a snapshot of the document at a single point in time."}, {"start": 731.0, "text": "To satisfy the third requirement, I provide a Writer."}, {"start": 735.0, "text": "The Writer conforms to the DocumentWriter protocol"}, {"start": 740.0, "text": "and knows how to write a document to disk in a specified format."}, {"start": 745.0, "text": "I give it the requested content type, which for my app is \"stickerDocument\"."}, {"start": 751.0, "text": "The DocumentWriter protocol has a notion of Snapshot."}, {"start": 755.0, "text": "And the PageSnapshot type fits perfectly here!"}, {"start": 759.0, "text": "DocumentWriter's only requirement is a method for writing."}, {"start": 763.0, "text": "It offers multiple opportunities to optimize performance."}, {"start": 768.0, "text": "First, the write method is nonisolated and asynchronous."}, {"start": 773.0, "text": "This lets me perform expensive disk writing operations in the background,"}, {"start": 778.0, "text": "so the app stays responsive."}, {"start": 782.0, "text": "I write only the parts of the package that actually need updating,"}, {"start": 786.0, "text": "by comparing the current and the previous snapshots."}, {"start": 791.0, "text": "Even with all the optimizations, disk operations can take noticeable time,"}, {"start": 796.0, "text": "so SwiftUI provides a progress parameter that lets me report writing progress"}, {"start": 803.0, "text": "using the Foundation Subprogress API."}, {"start": 806.0, "text": "To teach our document type how to read from disk,"}, {"start": 810.0, "text": "I conform the StickerDocument class to the ReadableDocument protocol."}, {"start": 815.0, "text": "ReadableDocument is a twin to WritableDocument."}, {"start": 818.0, "text": "Here's how they compare."}, {"start": 821.0, "text": "Each protocol requires a list of supported content types."}, {"start": 825.0, "text": "WritableDocument provides a snapshot"}, {"start": 829.0, "text": "and ReadableDocument knows how to apply it."}, {"start": 833.0, "text": "WritableDocument has a friend protocol: DocumentWriter."}, {"start": 838.0, "text": "And ReadableDocument's friend is DocumentReader,"}, {"start": 841.0, "text": "which does all the disk-related heavy lifting."}, {"start": 844.0, "text": "Now, the Sticker app is ready to read and write files, like this page!"}, {"start": 851.0, "text": "I think I make a pretty good pirate!"}, {"start": 855.0, "text": "There's one more feature to add:"}, {"start": 857.0, "text": "saving pages as images,"}, {"start": 859.0, "text": "so I can share them with people who don't have the app."}, {"start": 863.0, "text": "The Document API makes it possible to extend my writer"}, {"start": 867.0, "text": "to save pages in another format,"}, {"start": 869.0, "text": "like PNG, using Core Graphics."}, {"start": 873.0, "text": "I return to the writable content types definition and add .PNG to the list."}, {"start": 879.0, "text": "Now, to support an additional format,"}, {"start": 881.0, "text": "I revisit the write method I implemented earlier."}, {"start": 885.0, "text": "Since the app can now handle multiple formats, I am adding content type checks"}, {"start": 890.0, "text": "for each type the app supports."}, {"start": 894.0, "text": "For PNG, I use Core Graphics to flatten the stickers and background photo"}, {"start": 900.0, "text": "into a single image and write it to the URL."}, {"start": 904.0, "text": "To add even more types, I could write the document in any format,"}, {"start": 909.0, "text": "using any framework, just by adding another content type!"}, {"start": 914.0, "text": "That's how our app is set up to save documents."}, {"start": 918.0, "text": "Now, it's time to get to my sticker collection,"}, {"start": 921.0, "text": "where there are some great enhancements to presentation and interaction."}, {"start": 926.0, "text": "Sometimes, our massive collection of stickers can feel a little chaotic."}, {"start": 932.0, "text": "And that's where the reorderable container APIs come in!"}, {"start": 936.0, "text": "There are two different ways to browse"}, {"start": 939.0, "text": "the sticker collection in the app's inspector."}, {"start": 941.0, "text": "The first is a \"List\" that displays each sticker along with its name."}, {"start": 947.0, "text": "I'd like to keep these stickers organized by dragging them to rearrange their order."}, {"start": 953.0, "text": "So I use the Reorderable API."}, {"start": 958.0, "text": "I add the Reorderable modifier to ForEach"}, {"start": 962.0, "text": "and add a reorderContainer modifier to the List."}, {"start": 967.0, "text": "Then, in the closure, I call this helper function I wrote, difference.apply,"}, {"start": 973.0, "text": "to update my array of stickers!"}, {"start": 976.0, "text": "Under the hood, my apply function uses the open source swift-collections package"}, {"start": 982.0, "text": "to commit the ordering changes."}, {"start": 984.0, "text": "Visit swift.org for more details."}, {"start": 989.0, "text": "SwiftUI automatically handles the drag interaction and animation for me!"}, {"start": 996.0, "text": "I can also organize my stickers in a grid,"}, {"start": 1000.0, "text": "which makes it easier to browse more at a time."}, {"start": 1003.0, "text": "And the reorderable API works with any container, not just List!"}, {"start": 1008.0, "text": "This means I can take the code"}, {"start": 1010.0, "text": "from my list and repurpose it"}, {"start": 1013.0, "text": "to use a Lazy_V_Grid!"}, {"start": 1015.0, "text": "The code for reordering stays exactly the same."}, {"start": 1020.0, "text": "I get the same interactive reordering behavior"}, {"start": 1024.0, "text": "on a completely different container,"}, {"start": 1026.0, "text": "using the same code!"}, {"start": 1029.0, "text": "And now these APIs also bring reordering capabilities to watchOS for the first time!"}, {"start": 1037.0, "text": "When it comes to reordering, that's just the beginning!"}, {"start": 1040.0, "text": "For a deep dive into all of the features of reorderable containers,"}, {"start": 1045.0, "text": "check out the code-along session: \"Build powerful drag and drop in SwiftUI\"."}, {"start": 1051.0, "text": "When I'm on the go, I love to customize my sticker pages on my iPhone!"}, {"start": 1057.0, "text": "The sheet at the bottom of the UI keeps all my stickers right where I need them."}, {"start": 1064.0, "text": "I've even set up swipe actions for removing stickers from the list,"}, {"start": 1068.0, "text": "by adding the swipeActions modifier to my list item, along with this Delete button."}, {"start": 1076.0, "text": "But I want some more flexibility to customize my list,"}, {"start": 1080.0, "text": "so I've decided to switch to using a Lazy V Stack."}, {"start": 1084.0, "text": "And now, SwiftUI supports swipe actions on any view, not just List!"}, {"start": 1090.0, "text": "I move my ForEach out of the List into a Lazy V Stack with my updated item style"}, {"start": 1098.0, "text": "and add the swipeActionsContainer modifier,"}, {"start": 1102.0, "text": "which coordinates the swipe actions across the items in this scroll view."}, {"start": 1109.0, "text": "Well, as I said before, I love stickers!"}, {"start": 1115.0, "text": "So, it's no surprise that when I start decorating a photo,"}, {"start": 1120.0, "text": "I'll admit it, I can get a little carried away."}, {"start": 1125.0, "text": "And sometimes that means I need to make space by deleting some stickers,"}, {"start": 1130.0, "text": "even if I love them all."}, {"start": 1134.0, "text": "I've added a context menu to each sticker placed on the photo,"}, {"start": 1139.0, "text": "giving me quick access to delete it with this Delete button."}, {"start": 1145.0, "text": "Tapping the button sets the stickerToDelete State variable"}, {"start": 1149.0, "text": "to the current sticker."}, {"start": 1151.0, "text": "I've also added a confirmationDialog modifier."}, {"start": 1155.0, "text": "Now, confirmation dialogs support the same item-binding pattern that sheets use."}, {"start": 1161.0, "text": "I pass a stickerToDelete binding to the modifier."}, {"start": 1165.0, "text": "When I set stickerToDelete to a value by tapping the Delete button,"}, {"start": 1170.0, "text": "the confirmation dialog appears."}, {"start": 1174.0, "text": "And this also works with alert!"}, {"start": 1177.0, "text": "I've taken you through some great improvements to presentation"}, {"start": 1180.0, "text": "and interaction, but that's not all!"}, {"start": 1184.0, "text": "There are even more improvements in SwiftUI in the 2027 releases."}, {"start": 1190.0, "text": "Many of these improvements make the APIs you're already using better"}, {"start": 1195.0, "text": "without any changes to your apps."}, {"start": 1198.0, "text": "Great performance is important for making an app feel responsive and polished."}, {"start": 1204.0, "text": "Being thoughtful about the way data flows through your app is one of the best ways"}, {"start": 1209.0, "text": "to keep your app's performance in top shape."}, {"start": 1212.0, "text": "Steven is going to tell you about some big improvements to data flow and performance."}, {"start": 1217.0, "text": "Thanks, Julia!"}, {"start": 1218.0, "text": "The sticker store you added to the app is really cool."}, {"start": 1221.0, "text": "I love how the sticker packs are downloadable, because that allows me to"}, {"start": 1224.0, "text": "take advantage of improvements to AsyncImage!"}, {"start": 1228.0, "text": "AsyncImage is a great way to load image assets from the Internet"}, {"start": 1232.0, "text": "as they appear on screen."}, {"start": 1235.0, "text": "This works great when scrolling to reveal"}, {"start": 1237.0, "text": "more of these adorable stickers of Kishka and Pretzel!"}, {"start": 1241.0, "text": "But up until now, AsyncImage hasn't kept images in memory."}, {"start": 1245.0, "text": "When scrolling back up, they would reload when reappearing on screen."}, {"start": 1250.0, "text": "I'd prefer that these images show immediately"}, {"start": 1252.0, "text": "when scrolling back to the top."}, {"start": 1254.0, "text": "And in the 2027 releases, they do!"}, {"start": 1257.0, "text": "AsyncImage now supports standard HTTP caching, so images are cached by default,"}, {"start": 1263.0, "text": "respecting the server's cache headers, without any changes to your code."}, {"start": 1267.0, "text": "This is enabled automatically for every app."}, {"start": 1270.0, "text": "And apps built with Xcode 27 can take advantage of new APIs to customize"}, {"start": 1276.0, "text": "how downloads happen."}, {"start": 1277.0, "text": "For more control over how an image is downloaded I can construct"}, {"start": 1281.0, "text": "my own URLRequest and pass it to AsyncImage."}, {"start": 1285.0, "text": "This allows a wide variety of per-request customizations,"}, {"start": 1289.0, "text": "like specifying a cache policy, for instance."}, {"start": 1293.0, "text": "And if I need a longer-lived configuration, like a bigger cache, let's say,"}, {"start": 1298.0, "text": "I can instantiate my own custom URLSession"}, {"start": 1301.0, "text": "and configure a URLCache with whatever capacity I need"}, {"start": 1306.0, "text": "and then use my session by passing it to the asyncImageURLSession modifier."}, {"start": 1313.0, "text": "Now, when I scroll back to the top, the images are automatically loaded"}, {"start": 1317.0, "text": "from the cache!"}, {"start": 1320.0, "text": "SwiftUI provides a variety of ways for apps to model and store their data,"}, {"start": 1324.0, "text": "and to pass that data to views."}, {"start": 1327.0, "text": "One great way to store an app's data is using Observable classes,"}, {"start": 1331.0, "text": "like I do here with the StickerStore class!"}, {"start": 1334.0, "text": "When StickerStoreView is initialized, a new instance of my StickerStore class"}, {"start": 1339.0, "text": "is created and assigned to the state variable."}, {"start": 1342.0, "text": "This instance stays around for the lifetime of the view."}, {"start": 1345.0, "text": "But what happens when the parent view updates,"}, {"start": 1348.0, "text": "causing StickerStoreView to be initialized again?"}, {"start": 1352.0, "text": "In prior releases,"}, {"start": 1353.0, "text": "a new instance of StickerStore would be created on every initialization."}, {"start": 1358.0, "text": "But the original instance is still the one being stored in the State variable."}, {"start": 1363.0, "text": "And so the new one was just discarded."}, {"start": 1366.0, "text": "This would happen again for every reinitialization of the view"}, {"start": 1372.0, "text": "even though the main stored instance of the class remained stable."}, {"start": 1377.0, "text": "In the 2027 releases, for the first time,"}, {"start": 1381.0, "text": "classes initialized and stored using State properties"}, {"start": 1384.0, "text": "are now lazy, which means they will only be initialized once."}, {"start": 1388.0, "text": "This is thanks to the conversion of State"}, {"start": 1391.0, "text": "from a Dynamic Property"}, {"start": 1394.0, "text": "to a macro!"}, {"start": 1396.0, "text": "Now, when StickerStoreView is initialized for the first time,"}, {"start": 1400.0, "text": "a new instance of my StickerStore class is created like before,"}, {"start": 1405.0, "text": "but on future initializations, no new class instances are created."}, {"start": 1410.0, "text": "And this behavior has been back ported to the releases"}, {"start": 1413.0, "text": "where @Observable was first introduced,"}, {"start": 1416.0, "text": "starting with iOS 17, macOS 14, and aligned releases."}, {"start": 1422.0, "text": "In some cases, the introduction of the state macro"}, {"start": 1425.0, "text": "can be a source-breaking change."}, {"start": 1428.0, "text": "For example, if you specify a default value for your @State variable,"}, {"start": 1432.0, "text": "and then you assign a value to the same @State variable inside your init,"}, {"start": 1437.0, "text": "Xcode will show an error about use before initialization."}, {"start": 1441.0, "text": "To resolve this error, remove the unnecessary default value assignment."}, {"start": 1446.0, "text": "For additional details about how the State macro may impact your code,"}, {"start": 1450.0, "text": "check out the documentation."}, {"start": 1453.0, "text": "Maintaining good runtime performance is critical for keeping your app"}, {"start": 1456.0, "text": "working smoothly."}, {"start": 1458.0, "text": "But there's another type of performance"}, {"start": 1460.0, "text": "that can have a significant impact on your app development experience."}, {"start": 1464.0, "text": "If your app has complex, deeply nested views,"}, {"start": 1467.0, "text": "you may have encountered this error:"}, {"start": 1471.0, "text": "\"The compiler is unable to type-check this expression in reasonable time\"."}, {"start": 1476.0, "text": "But why does this happen?"}, {"start": 1478.0, "text": "This view has a Section, a Group, and a ForEach wrapping its content."}, {"start": 1483.0, "text": "To type check this expression,"}, {"start": 1485.0, "text": "first the compiler has to select which overload of Section to use."}, {"start": 1490.0, "text": "Section can be initialized with a builder that produces either a View,"}, {"start": 1494.0, "text": "or TableRowContent."}, {"start": 1496.0, "text": "To know which one to use, the compiler has to try both options."}, {"start": 1501.0, "text": "In my code, the Section builder returns a Group."}, {"start": 1504.0, "text": "The compiler can't know what type of content section produces"}, {"start": 1508.0, "text": "until it figures out the type of the nested group's content."}, {"start": 1512.0, "text": "This time, there are even more options,"}, {"start": 1515.0, "text": "and for the nested ForEach the compiler will have to try each one."}, {"start": 1520.0, "text": "And then, ForEach's builder has its own set of options"}, {"start": 1523.0, "text": "that will also need to be checked."}, {"start": 1526.0, "text": "And we haven't even gotten to the content yet!"}, {"start": 1529.0, "text": "Trying each of these paths makes type checking increasingly expensive."}, {"start": 1534.0, "text": "But I already know that Section, Group, and ForEach in my code are building views,"}, {"start": 1539.0, "text": "so there's really only one valid path through this decision tree."}, {"start": 1544.0, "text": "Instead of this complex set of choices, what if these builders weren't constrained"}, {"start": 1548.0, "text": "by the types they produce,"}, {"start": 1550.0, "text": "and instead just assembled their content?"}, {"start": 1553.0, "text": "In the 2027 releases, that's exactly what SwiftUI starts doing."}, {"start": 1559.0, "text": "The most common set of builders now share a single initializer,"}, {"start": 1563.0, "text": "leaving just one, straightforward path."}, {"start": 1566.0, "text": "This is possible because multiple different builder types have been unified"}, {"start": 1571.0, "text": "under a single builder: ContentBuilder!"}, {"start": 1574.0, "text": "This is a step towards enabling unified builders across all of SwiftUI's APIs."}, {"start": 1579.0, "text": "And ContentBuilder can be used with any minimum deployment target,"}, {"start": 1583.0, "text": "because under the hood, it's an evolution of the existing ViewBuilder."}, {"start": 1588.0, "text": "ContentBuilder provides a substantial improvement in type checking performance"}, {"start": 1591.0, "text": "in SwiftUI when building using Xcode 27;"}, {"start": 1595.0, "text": "whether you're targeting the 2027 releases, or previous releases as well."}, {"start": 1600.0, "text": "We're also excited to introduce new agent skills included with Xcode 27 to help you"}, {"start": 1606.0, "text": "adopt the new features from the 2027 releases in your apps,"}, {"start": 1610.0, "text": "and improve your app's performance and code correctness."}, {"start": 1614.0, "text": "The SwiftUI Specialist Skill can help you follow SwiftUI best practices in your apps."}, {"start": 1620.0, "text": "The What's New In SwiftUI Skill can guide you through adopting new APIs"}, {"start": 1625.0, "text": "from the 2027 releases."}, {"start": 1627.0, "text": "Both of these skills can be accessed in the Coding Assistant in Xcode 27."}, {"start": 1632.0, "text": "And to use these skills with other tools, you can export them"}, {"start": 1636.0, "text": "with the \"xcrun agent skills export\" command."}, {"start": 1640.0, "text": "This will create markdown files you can import in your workflows."}, {"start": 1645.0, "text": "We've covered some exciting new enhancements to SwiftUI."}, {"start": 1648.0, "text": "Now, it's your turn!"}, {"start": 1650.0, "text": "Start by building your project in Xcode 27 for the 2027 releases"}, {"start": 1656.0, "text": "and then check out your app's updated look and feel!"}, {"start": 1659.0, "text": "If you have a document-based app, investigate how the new Document APIs"}, {"start": 1663.0, "text": "can make it even better."}, {"start": 1665.0, "text": "And try out the SwiftUI agent skills in Xcode 27"}, {"start": 1668.0, "text": "to adopt new APIs and best practices."}, {"start": 1672.0, "text": "Well… as sad as we are to peel ourselves away, we have to adhere to the schedule."}, {"start": 1679.0, "text": "We hope you have as much fun adopting these improvements in your apps"}, {"start": 1683.0, "text": "as we had with ours!"}, {"start": 1684.0, "text": "Thanks for sticking with us!"}]}