{"$schema": "https://raw.githubusercontent.com/guitaripod/wwdc-sessions/master/schema/transcript.schema.json", "id": "wwdc2026-303", "language": "eng", "source": "https://developer.apple.com/videos/play/wwdc2026/303", "wordCount": 2754, "segments": [{"start": 7.0, "text": "Hello, my name is Jake - I'm an engineer on the Camera Performance Team."}, {"start": 13.0, "text": "Welcome to \"Build a responsive camera app that launches quickly\"."}, {"start": 18.0, "text": "When launch is slow, people notice."}, {"start": 22.0, "text": "From years of optimizing the native Camera.app,"}, {"start": 25.0, "text": "I've learned that the single most important factor"}, {"start": 28.0, "text": "in making a camera launch feel fast"}, {"start": 31.0, "text": "is how quickly the preview frame appears on the display."}, {"start": 36.0, "text": "I want to capture a cool shot of my dominoes"}, {"start": 39.0, "text": "but I forgot to launch my camera"}, {"start": 41.0, "text": "before the dominoes were already falling."}, {"start": 44.0, "text": "I've placed a red domino in the middle, so it's essential that I launch and capture"}, {"start": 50.0, "text": "the moment just before the red domino falls."}, {"start": 58.0, "text": "As the app launches, there's a period of blank preview."}, {"start": 62.0, "text": "By the time preview begins rendering,"}, {"start": 65.0, "text": "I've already missed the red domino tumbling over."}, {"start": 69.0, "text": "Having the preview render shortly after the app has launched"}, {"start": 73.0, "text": "allows customers to capture a quick action shot,"}, {"start": 76.0, "text": "ensuring they don't miss the moment."}, {"start": 79.0, "text": "I'll help you build a camera app designed for performance."}, {"start": 83.0, "text": "In this video, I'll talk about 4 main topics to enhance performance."}, {"start": 89.0, "text": "First, I'll discuss how to accelerate a camera launch experience,"}, {"start": 94.0, "text": "so preview is up and running without a hitch."}, {"start": 98.0, "text": "Then, I'll talk about best practices for rendering preview,"}, {"start": 102.0, "text": "so no frames are dropped."}, {"start": 105.0, "text": "Third, I'll touch on APIs that help sustain performance,"}, {"start": 109.0, "text": "even in challenging environments."}, {"start": 112.0, "text": "Lastly, I'll introduce a new API,"}, {"start": 116.0, "text": "designed to offer deterministic file-write performance"}, {"start": 119.0, "text": "for high data rate video captures."}, {"start": 122.0, "text": "I'll start with fast launch."}, {"start": 126.0, "text": "There are 4 stages of a camera app launch sequence."}, {"start": 130.0, "text": "First, the app launches."}, {"start": 133.0, "text": "This covers the time for the linker to load the binary, run static initializers,"}, {"start": 139.0, "text": "and create UIScenes,"}, {"start": 142.0, "text": "plus anything else the app does before creating a capture session."}, {"start": 147.0, "text": "Second the session is configured and started."}, {"start": 151.0, "text": "Initializing the capture session, committing the configuration,"}, {"start": 156.0, "text": "and starting the session all take time and system resources."}, {"start": 162.0, "text": "Third, once the session is started, all AVCaptureOutput objects initialize."}, {"start": 169.0, "text": "This time varies with the number of outputs and their quality settings."}, {"start": 175.0, "text": "Finally, preview begins streaming and frames start flowing to the app."}, {"start": 181.0, "text": "I'll walk through specific optimizations for each of these stages."}, {"start": 186.0, "text": "The app's UI plays an important role in the camera launch experience."}, {"start": 191.0, "text": "When designing a launch flow, split the work into two phases:"}, {"start": 196.0, "text": "resources critical for launching and displaying preview"}, {"start": 201.0, "text": "and resources that can be created after preview is running."}, {"start": 206.0, "text": "Take AVCam for example — the classic sample camera app for AVFoundation ."}, {"start": 213.0, "text": "There are several UI elements: a camera preview, a shutter button,"}, {"start": 219.0, "text": "an image well and a mode picker."}, {"start": 222.0, "text": "The camera preview is the most critical UI element for someone"}, {"start": 226.0, "text": "the moment they launch the app"}, {"start": 228.0, "text": "because this is what makes the camera feel like its ready to use."}, {"start": 232.0, "text": "The image well and the mode picker aren't needed before preview renders,"}, {"start": 237.0, "text": "so this work should wait until after preview starts."}, {"start": 242.0, "text": "UI elements aren't the only factor in launch time."}, {"start": 246.0, "text": "Any resource created before preview has rendered will influence launch time."}, {"start": 252.0, "text": "Applying these 2 phases to AVCam, I create the shutter button and preview on launch,"}, {"start": 259.0, "text": "but fade-in all other UI elements after launch finishes."}, {"start": 264.0, "text": "Now that the app's impact to launch is reduced,"}, {"start": 267.0, "text": "I'll focus on how AVCaptureSession and its related objects impact the next stage —"}, {"start": 274.0, "text": "session configuration."}, {"start": 277.0, "text": "Configuring and starting AVCaptureSession"}, {"start": 281.0, "text": "takes a lot of system resources and allocations"}, {"start": 284.0, "text": "that directly impact app launch."}, {"start": 288.0, "text": "A typical AVCaptureSession consists of an AVCaptureDeviceInput,"}, {"start": 293.0, "text": "usually the Camera or Microphone."}, {"start": 296.0, "text": "AVCaptureConnection wires the capture device to the outputs."}, {"start": 302.0, "text": "In this example, I want two outputs - one for preview and one for capture."}, {"start": 309.0, "text": "The AVCaptureVideoPreviewLayer is the output for displaying the preview,"}, {"start": 315.0, "text": "while the AVCapturePhotoOutput serves as the output for image captures."}, {"start": 321.0, "text": "These objects together power an app's camera experience."}, {"start": 327.0, "text": "Because AVCaptureSession coordinates all the capture objects,"}, {"start": 332.0, "text": "I want to create it first, as soon as the main thread finishes UI setup."}, {"start": 338.0, "text": "Creating AVCaptureSession blocks the main thread."}, {"start": 342.0, "text": "To avoid a hang, create it in parallel with UI initialization."}, {"start": 348.0, "text": "When displaying preview on launch, dispatch AVCaptureSession creation"}, {"start": 353.0, "text": "off the main thread."}, {"start": 355.0, "text": "This allows the session's setup to run in the background"}, {"start": 359.0, "text": "while the app's UIScene is being created."}, {"start": 363.0, "text": "Committing multiple configurations extends launch time."}, {"start": 367.0, "text": "Commit a single configuration up front to avoid lengthy reconfigurations"}, {"start": 372.0, "text": "during launch."}, {"start": 374.0, "text": "startRunning and stopRunning on AVCaptureSession are blocking calls."}, {"start": 380.0, "text": "Don't call them on the main thread, or the app will hang."}, {"start": 384.0, "text": "Next, I'll cover the most expensive part of camera launch —"}, {"start": 389.0, "text": "initializing AVCaptureOutputs."}, {"start": 393.0, "text": "Initializing AVCaptureOutputs noticeably slows down launch."}, {"start": 399.0, "text": "To render preview, the app only needs a preview layer or one output initialized."}, {"start": 406.0, "text": "Outputs like the MovieFileOutput and PhotoOutput aren't needed for preview."}, {"start": 412.0, "text": "To reduce time spent initializing outputs, adopt the Deferred start API,"}, {"start": 418.0, "text": "available in iOS 26 and later."}, {"start": 422.0, "text": "Deferred start lets apps put off output initialization until launch has finished."}, {"start": 428.0, "text": "In this launch sequence, all AVCaptureOutputs initialize"}, {"start": 433.0, "text": "before the first preview frame renders."}, {"start": 437.0, "text": "The idea with deferred start is to postpone any output that isn't needed"}, {"start": 442.0, "text": "for launch until after preview has started."}, {"start": 446.0, "text": "With deferred start, the launch sequence changes."}, {"start": 450.0, "text": "The app launches, configures the session and starts it."}, {"start": 455.0, "text": "Now only the preview output initializes before the first frame displays."}, {"start": 461.0, "text": "The system then either runs the deferred initialization automatically,"}, {"start": 466.0, "text": "when conditions allow, or waits for the app to signal when it's a good time."}, {"start": 473.0, "text": "Every AVCaptureOutput and AVCaptureVideoPreviewLayer"}, {"start": 478.0, "text": "has an isDeferredStartEnabled property."}, {"start": 482.0, "text": "Set it to true to defer that output."}, {"start": 485.0, "text": "To optimize for launch, defer all outputs except the output used to render preview."}, {"start": 493.0, "text": "There are two ways to specify when deferred start runs:"}, {"start": 498.0, "text": "automatic start and manual start."}, {"start": 501.0, "text": "Apps recompiled against the iOS 26 and later SDKs"}, {"start": 506.0, "text": "use automatic mode by default."}, {"start": 509.0, "text": "The automaticallyRunsDeferredStart property is set to true when in this mode."}, {"start": 515.0, "text": "In automatic mode, the system picks the best time to initialize"}, {"start": 520.0, "text": "the deferred outputs."}, {"start": 522.0, "text": "This happens shortly after preview appears on the device."}, {"start": 528.0, "text": "The session sends two delegate callbacks"}, {"start": 531.0, "text": "so the app knows when deferred start begins and ends."}, {"start": 536.0, "text": "SessionWillRunDeferredStart fires before output initialization begins"}, {"start": 542.0, "text": "and SessionDidRunDeferredStart fires after it completes."}, {"start": 548.0, "text": "Now, I'll show how to adopt this."}, {"start": 553.0, "text": "First, I'll create a class that handles the delegate callbacks"}, {"start": 557.0, "text": "from the deferred start API."}, {"start": 560.0, "text": "SessionWillRunDeferredStart is called before deferred start begins."}, {"start": 566.0, "text": "This is a good place to create any background resources the app needs."}, {"start": 572.0, "text": "SessionDidRunDeferredStart is called after deferred start completes."}, {"start": 579.0, "text": "At this point, all capture outputs are initialized and ready to use."}, {"start": 584.0, "text": "Now, I'll add deferred start to the captureSession."}, {"start": 589.0, "text": "During configuration, set automaticallyRunsDeferredStart to true"}, {"start": 594.0, "text": "on AVCaptureSession."}, {"start": 597.0, "text": "Remember: if your app recompiled against iOS 26 and later,"}, {"start": 602.0, "text": "this is automatically set to true for you."}, {"start": 605.0, "text": "Next, enable deferred start on every output that isn't required for launch."}, {"start": 612.0, "text": "Here, I defer the photo capture output and use the video preview layer"}, {"start": 617.0, "text": "to render preview."}, {"start": 620.0, "text": "Then, I'll attach the delegate callback class"}, {"start": 623.0, "text": "from earlier to the capture session."}, {"start": 627.0, "text": "The session is now configured, so I'll commit the configuration"}, {"start": 631.0, "text": "and call startRunning."}, {"start": 634.0, "text": "For apps that want finer control, the deferred start API"}, {"start": 639.0, "text": "also offers a manual mode, with runDeferredStartWhenNeeded."}, {"start": 645.0, "text": "In manual mode, the app tells the system when to begin deferred start."}, {"start": 651.0, "text": "This is useful for apps that want to read preferences or setup UI"}, {"start": 656.0, "text": "before the heavy initialization begins"}, {"start": 659.0, "text": "or for apps using VideoDataOutput to render preview,"}, {"start": 663.0, "text": "which I'll discuss in more detail, later in this video."}, {"start": 668.0, "text": "With manual mode, the sequence changes."}, {"start": 673.0, "text": "Once the app finishes start up work,"}, {"start": 676.0, "text": "such as creating non-critical resources, call runDeferredStartWhenNeeded"}, {"start": 683.0, "text": "on the capture session."}, {"start": 685.0, "text": "This tells the system it can run deferred start."}, {"start": 689.0, "text": "To opt into manual mode, set automaticallyRunsDeferredStart"}, {"start": 694.0, "text": "on AVCaptureSession to false."}, {"start": 698.0, "text": "In this example, I want to render preview myself,"}, {"start": 701.0, "text": "using AVCaptureVideoDataOutput."}, {"start": 704.0, "text": "So I'll disable deferred start on this output."}, {"start": 707.0, "text": "I'll leave the rest of the code the same as the previous example."}, {"start": 712.0, "text": "Next, I need to decide when to run deferred start on the deferred output."}, {"start": 718.0, "text": "To do that, I'll track whether the first frame has been presented."}, {"start": 724.0, "text": "Here, I'm using a CAMetalLayer."}, {"start": 728.0, "text": "Once the first frame is presented, I'll setup any non-critical UI elements"}, {"start": 734.0, "text": "and tell AVCaptureSession to run deferred start on the postponed outputs."}, {"start": 739.0, "text": "After the first frame is presented, no special handling is needed."}, {"start": 745.0, "text": "To verify the launch is faster with deferred start,"}, {"start": 748.0, "text": "I set up a light board in the lab."}, {"start": 751.0, "text": "My goal is to compare the difference in position of the LED pattern in preview."}, {"start": 757.0, "text": "The phone on the right has deferred start enabled;"}, {"start": 760.0, "text": "the one on the left doesn't."}, {"start": 763.0, "text": "I want to capture the pattern when both the red and green LEDs are on screen."}, {"start": 770.0, "text": "I screenshotted the moment when one device successfully shows preview."}, {"start": 775.0, "text": "The deferred start phone on the right"}, {"start": 778.0, "text": "is clearly able to capture the expanding pattern."}, {"start": 783.0, "text": "By the time the phone without deferred start finishes launching,"}, {"start": 787.0, "text": "the green LEDs have nearly faded out, missing that clear separation."}, {"start": 794.0, "text": "I also timed the launch sequence on both phones."}, {"start": 798.0, "text": "Without deferred start, the app launch was close to a second."}, {"start": 803.0, "text": "With deferred start, launch is cut in half!"}, {"start": 807.0, "text": "That's a 2 times faster launch!"}, {"start": 810.0, "text": "This is a massive step forward in launch times!"}, {"start": 814.0, "text": "Preview is up and running faster than ever!"}, {"start": 817.0, "text": "For complex capture sessions, apps may see an even bigger improvement!"}, {"start": 823.0, "text": "Deferring AVCapturePhotoOutput does have a catch."}, {"start": 828.0, "text": "Preview starts much sooner,"}, {"start": 830.0, "text": "but the time to the first capture stays the same."}, {"start": 834.0, "text": "Because the photo output is deferred, the system has to finish initializing it"}, {"start": 840.0, "text": "before a capture can begin."}, {"start": 843.0, "text": "Preview is up quickly, but someone can still miss the shot."}, {"start": 847.0, "text": "To solve this problem, set \"isResponsiveCaptureEnabled\" to true"}, {"start": 853.0, "text": "on AVCapturePhotoOutput."}, {"start": 856.0, "text": "This property adds buffering between starting a capture"}, {"start": 861.0, "text": "and when processing begins,"}, {"start": 863.0, "text": "so people can capture the moment even if the photo output isn't fully ready yet."}, {"start": 869.0, "text": "The green phone enables responsive capture in conjunction with deferred start."}, {"start": 875.0, "text": "As the dominoes fall, I quickly launch and take a picture."}, {"start": 883.0, "text": "The green phone allowed me to get a perfect shot of the dominoes,"}, {"start": 886.0, "text": "while the purple phone missed the moment."}, {"start": 891.0, "text": "To learn more about how to use responsive capture"}, {"start": 895.0, "text": "and how to capture stunning, high resolution images,"}, {"start": 899.0, "text": "watch \"Implement high resolution photo capture\" from WWDC26."}, {"start": 905.0, "text": "Once preview is running, keeping a steady frame rate and cadence is essential —"}, {"start": 911.0, "text": "otherwise the camera feels laggy."}, {"start": 914.0, "text": "Next, I'll share best practices for rendering preview."}, {"start": 919.0, "text": "Revisiting the session architecture from earlier,"}, {"start": 923.0, "text": "the easiest way to render preview"}, {"start": 926.0, "text": "is with AVCaptureVideoPreviewLayer."}, {"start": 929.0, "text": "It shows exactly what the camera sees,"}, {"start": 932.0, "text": "directly in the app's UI."}, {"start": 935.0, "text": "AVCaptureVideoPreviewLayer is optimized for rendering preview."}, {"start": 940.0, "text": "No need to process video frames in the app."}, {"start": 944.0, "text": "AVCaptureVideoPreviewLayer does this automatically, handling tricky situations"}, {"start": 950.0, "text": "such as HDR tone-mapping."}, {"start": 953.0, "text": "It also keeps CPU and GPU overhead low,"}, {"start": 957.0, "text": "which saves power and leaves more headroom for the UI."}, {"start": 962.0, "text": "And it's tuned for low-latency preview, so the app shows what the camera sees"}, {"start": 968.0, "text": "with very little delay."}, {"start": 970.0, "text": "As a trade-off for simplicity, AVCaptureVideoPreviewLayer does not allow"}, {"start": 976.0, "text": "for per-frame access."}, {"start": 978.0, "text": "For apps that want more control over preview rendering,"}, {"start": 982.0, "text": "then AVCaptureVideoDataOutput is the better choice."}, {"start": 986.0, "text": "AVCaptureVideoDataOutput takes the place of AVCaptureVideoPreviewLayer"}, {"start": 992.0, "text": "in the session architecture, and becomes the primary output for displaying frames"}, {"start": 998.0, "text": "on the device."}, {"start": 1001.0, "text": "AVCaptureVideoDataOutput gives more control over the flow of preview,"}, {"start": 1007.0, "text": "enabling apps to process individual frames."}, {"start": 1011.0, "text": "It also lets the app apply a custom UI overlay on each frame."}, {"start": 1018.0, "text": "And per-frame processing makes it easier to integrate with Metal"}, {"start": 1022.0, "text": "and to analyze frame data."}, {"start": 1025.0, "text": "Use AVCaptureVideoPreviewLayer when you just need to show the camera feed."}, {"start": 1030.0, "text": "And remember, apps using AVCaptureVideoPreviewLayer"}, {"start": 1034.0, "text": "are opted into automatic deferred start when recompiled against iOS 26 and later."}, {"start": 1041.0, "text": "Use AVCaptureVideoDataOutput when per-frame processing is the priority."}, {"start": 1048.0, "text": "Deferred start doesn't apply automatically with AVCaptureVideoDataOutput,"}, {"start": 1054.0, "text": "so adopt manual deferred start to get the same launch gains."}, {"start": 1059.0, "text": "When rendering preview, keep per-frame work short."}, {"start": 1064.0, "text": "This helps avoid frame drops and keeps the experience fluid."}, {"start": 1069.0, "text": "As the device heats up, performance gets harder to maintain,"}, {"start": 1073.0, "text": "because the system throttles to adapt."}, {"start": 1077.0, "text": "Monitor the session's performance"}, {"start": 1079.0, "text": "and adjust to system conditions for a sustainable experience."}, {"start": 1084.0, "text": "Next, I'll cover APIs that let your app monitor performance"}, {"start": 1088.0, "text": "and adapt to system conditions."}, {"start": 1091.0, "text": "Revisiting the architecture from earlier, there's a capture session,"}, {"start": 1096.0, "text": "a photo output, and a preview layer."}, {"start": 1100.0, "text": "This is a fairly basic setup, but it grows in complexity as an app adds more cameras"}, {"start": 1106.0, "text": "or input devices."}, {"start": 1109.0, "text": "As complexity grows, so does the performance cost."}, {"start": 1113.0, "text": "Understanding the capture session's cost"}, {"start": 1116.0, "text": "helps you design for a sustainable experience."}, {"start": 1120.0, "text": "The hardwareCost API returns a value between 0 and 1."}, {"start": 1125.0, "text": "It tells you what share of the session's hardware is actively in use."}, {"start": 1130.0, "text": "A value above 1 means the system can't support the configuration."}, {"start": 1136.0, "text": "Several things contribute to this cost."}, {"start": 1139.0, "text": "The number of cameras used."}, {"start": 1141.0, "text": "The active formats of the source devices, such as using 1080p or 4K."}, {"start": 1148.0, "text": "The frame rate of the source device's formats."}, {"start": 1152.0, "text": "hardwareCost assumes the format's max frame rate,"}, {"start": 1156.0, "text": "so if you're running at a lower frame rate,"}, {"start": 1158.0, "text": "like 30 frames per second instead of 60 frames per second,"}, {"start": 1162.0, "text": "use the frame-rate override property to reduce the cost."}, {"start": 1167.0, "text": "And lastly, the use of binned formats."}, {"start": 1170.0, "text": "Binned formats use less hardware bandwidth since these formats group pixels."}, {"start": 1177.0, "text": "The systemPressureCost API also returns a value between 0 and 1."}, {"start": 1184.0, "text": "It represents the cost of the session's current configuration."}, {"start": 1188.0, "text": "When it goes above 1, the configuration is unsustainable."}, {"start": 1193.0, "text": "To adjust to the current system state,"}, {"start": 1196.0, "text": "monitor the systemPressureState property of AVCaptureDevice."}, {"start": 1202.0, "text": "As the system pressure state increases,"}, {"start": 1205.0, "text": "consider reducing the capture device's frame rate,"}, {"start": 1209.0, "text": "or throttling any use of the GPU or Apple Neural Engine,"}, {"start": 1213.0, "text": "or minimizing UI work."}, {"start": 1216.0, "text": "Use the hardwareCost and systemPressureState API"}, {"start": 1220.0, "text": "after initial session setup."}, {"start": 1224.0, "text": "After committing the configuration, check that the hardwareCost doesn't exceed"}, {"start": 1229.0, "text": "the device's capabilities."}, {"start": 1232.0, "text": "Once hardwareCost is at or below 1,"}, {"start": 1235.0, "text": "observe AVCaptureDevice's systemPressureState"}, {"start": 1239.0, "text": "and register a handler for state changes."}, {"start": 1243.0, "text": "Use this handler to adapt using the techniques I just covered."}, {"start": 1248.0, "text": "Video capture is also sensitive to performance issues"}, {"start": 1252.0, "text": "once the device enters a pressured state."}, {"start": 1256.0, "text": "Traditional file-system input/output is variable,"}, {"start": 1260.0, "text": "because the system is juggling competing operations,"}, {"start": 1264.0, "text": "memory fragmentation, and device storage wear."}, {"start": 1268.0, "text": "This means file input/output behavior is non-deterministic."}, {"start": 1274.0, "text": "High data-rate video captures, like ProRes,"}, {"start": 1277.0, "text": "need sustained high-bandwidth input/output to record smoothly without dropping frames."}, {"start": 1284.0, "text": "To address this challenge, use AVProVideoStorage, new in iOS 27!"}, {"start": 1292.0, "text": "This class tracks and manages pre-allocated storage"}, {"start": 1297.0, "text": "for high data rate video captures."}, {"start": 1300.0, "text": "It's a system-wide resource that all apps share."}, {"start": 1305.0, "text": "AVProVideoStorage works with the existing movie-recording APIs."}, {"start": 1311.0, "text": "Applications opt in by setting usesProVideoStorage"}, {"start": 1316.0, "text": "on AVCaptureMovieFileOutput"}, {"start": 1319.0, "text": "or on AVAssetWriter"}, {"start": 1322.0, "text": "when using AVCaptureVideoDataOutput to record content."}, {"start": 1327.0, "text": "The system handles allocation and file input / output"}, {"start": 1331.0, "text": "so write performance stays consistent for high data-rate codecs."}, {"start": 1337.0, "text": "Camera Settings is updated so people can control how much storage to allocate."}, {"start": 1343.0, "text": "The remainingCapacity method reports how much storage is left."}, {"start": 1348.0, "text": "That value decreases during a recording and stops decreasing"}, {"start": 1354.0, "text": "when the recording stops."}, {"start": 1357.0, "text": "Use the openSettings method"}, {"start": 1359.0, "text": "to take someone from your app to the settings UI."}, {"start": 1364.0, "text": "To use AVProVideoStorage, first check that the storage is supported."}, {"start": 1370.0, "text": "AVProVideoStorage is a singleton, so use the shared method"}, {"start": 1374.0, "text": "to obtain the instance of this object."}, {"start": 1378.0, "text": "Next, create the MovieFileOutput, AVCaptureSession, AVCaptureConnections"}, {"start": 1385.0, "text": "and select the format for recording."}, {"start": 1389.0, "text": "Use the new isProVideoStorageSupported method on AVCaptureMovieFileOutput"}, {"start": 1395.0, "text": "to check for compatibility."}, {"start": 1398.0, "text": "Before recording, confirm the storage is not busy resizing"}, {"start": 1402.0, "text": "or servicing file creation or deletion requests."}, {"start": 1406.0, "text": "Finally, turn on ProVideoStorage on the movieOutput and start recording."}, {"start": 1413.0, "text": "During capture, the recording is written to the pre-allocated storage,"}, {"start": 1418.0, "text": "and then moved to the specified location once the capture finishes."}, {"start": 1424.0, "text": "As I mentioned before, this feature also works great with AVAssetWriter."}, {"start": 1432.0, "text": "I covered ways to optimize a camera app for launch,"}, {"start": 1436.0, "text": "best practices for rendering preview,"}, {"start": 1438.0, "text": "APIs for sustained performance,"}, {"start": 1441.0, "text": "and how to get deterministic file-write speeds for ProRes captures."}, {"start": 1447.0, "text": "Adopt deferred start with the quality photo output —"}, {"start": 1450.0, "text": "you'll keep launch fast and get gorgeous image quality too."}, {"start": 1455.0, "text": "Analyze performance in other parts of your camera app."}, {"start": 1459.0, "text": "Use Instruments and Xcode"}, {"start": 1462.0, "text": "to measure, identify and fix performance issues."}, {"start": 1466.0, "text": "And remember: most of the time you're developing your app at a desk"}, {"start": 1471.0, "text": "or in a controlled environment,"}, {"start": 1474.0, "text": "but people use your app in the real world."}, {"start": 1477.0, "text": "Test and measure performance in all conditions —"}, {"start": 1480.0, "text": "like on a hot, sunny day."}, {"start": 1482.0, "text": "Lastly, watch \"Create a more responsive camera experience\" from WWDC23"}, {"start": 1489.0, "text": "and \"Implement high resolution photo capture\" from WWDC26"}, {"start": 1494.0, "text": "to learn how to integrate capture responsiveness into your app."}, {"start": 1501.0, "text": "Performance isn't just a feature,"}, {"start": 1503.0, "text": "it's the foundation of a great camera experience."}, {"start": 1507.0, "text": "Keep optimizing and keep capturing."}, {"start": 1510.0, "text": "Thanks for watching!"}]}