Dunfey · Hotel WWDC as data, est. 1983
Front desk everything
Years
Topics

2024 SwiftUI & UI Frameworks

WWDC24 · 10 min · SwiftUI & UI Frameworks

Evolve your document launch experience

Make your document-based app stand out, and bring its unique identity into focus with the new document launch experience. Learn how to leverage the new API to customize the first screen people see when they launch your app. Utilize the new system-provided design, and amend it with custom actions, delightful decorative views, and impressive animations.

Watch at developer.apple.com ↗

Transcript all transcripts

Chapters

  • 0:00 — Introduction
  • 1:01 — Design overview
  • 2:18 — Getting started with SwiftUI
  • 3:13 — Getting started with UIKit
  • 4:11 — Customization
  • 6:42 — Adding template support to your app

Code shown on screen · 17 snippets

Document-based application swift · at 2:38 ↗
@main
struct WritingApp: App {
    var body: some Scene {
        DocumentGroup(newDocument: { StoryDocument() }) { file in
            StoryView(document: $file.document)
        }
    }
}
Presenting a document from the browser in iOS 17 swift · at 3:26 ↗
class DocumentViewController: UIDocumentViewController { ... }

let documentViewController = DocumentViewController()
let browserViewController = UIDocumentBrowserViewController(
    forOpening: [.plainText]
)
window.rootViewController = browserViewController
Presenting a document from the browser in iOS 17 swift · at 3:38 ↗
class DocumentViewController: UIDocumentViewController { ... }

let documentViewController = DocumentViewController()
let browserViewController = UIDocumentBrowserViewController(
    forOpening: [.plainText]
)
window.rootViewController = browserViewController
browserViewController.delegate = self
Presenting a document from the browser in iOS 17 swift · at 3:43 ↗
class DocumentViewController: UIDocumentViewController { ... }

let documentViewController = DocumentViewController()
let browserViewController = UIDocumentBrowserViewController(
    forOpening: [.plainText]
)
window.rootViewController = browserViewController
browserViewController.delegate = self

// MARK: UIDocumentBrowserViewControllerDelegate

func documentBrowser(
    _ browser: UIDocumentBrowserViewController, 
    didPickDocumentsAt documentURLs: [URL]
) {
    guard let url = documentURLs.first else { return }
    documentViewController.document = StoryDocument(fileURL: url)
    browser.present(documentViewController, animated: true)
}
Presenting a document from the browser in iOS 18 swift · at 3:56 ↗
class DocumentViewController: UIDocumentViewController { ... }

let documentViewController = DocumentViewController()
window.rootViewController = documentViewController
Customize the document launch experience: background swift · at 4:38 ↗
DocumentGroup(
    newDocument: { StoryDocument() }
) { file in
    StoryView(document: $file.document)
}

DocumentGroupLaunchScene {
...
} background: {
    Image(.pinkJungle)
        .resizable()
        .aspectRatio(contentMode: .fill)
}
Customize the document launch experience: new document button title swift · at 4:49 ↗
DocumentGroup(
    newDocument: { StoryDocument() }
) { file in
    StoryView(document: $file.document)
}
DocumentGroupLaunchScene {
    NewDocumentButton("Start Writing")
} background: {
    Image(.pinkJungle)
        .resizable()
        .aspectRatio(contentMode: .fill)
}
Customize the document launch experience: accessory views swift · at 5:29 ↗
DocumentGroupLaunchScene {
    NewDocumentButton("Start Writing")
} background: {
    Image(.pinkJungle)
        .resizable()
        .aspectRatio(contentMode: .fill)
} overlayAccessoryView: {

}
Position accessory views swift · at 5:44 ↗
DocumentGroupLaunchScene {
    NewDocumentButton("Start Writing")
} background: {
    Image(.pinkJungle)
        .resizable()
        .aspectRatio(contentMode: .fill)
} overlayAccessoryView: { geometry in

}
Position accessory views swift · at 5:53 ↗
DocumentGroupLaunchScene {
    NewDocumentButton("Start Writing")
} background: {
...
} overlayAccessoryView: { geometry in
    ZStack {
        Image(.robot)
            .position(
                x: geometry.titleViewFrame.minX, 
                y: geometry.titleViewFrame.minY
            )
        Image(.plant)
            .position(
                x: geometry.titleViewFrame.maxX, 
                y: geometry.titleViewFrame.maxY
            )
    }
}
Customize the document launch experience in a UIKit app swift · at 6:11 ↗
class DocumentViewController: UIDocumentViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Update the background
        launchOptions.background.image = UIImage(resource: .pinkJungle)

        // Add foreground accessories
        launchOptions.foregroundAccessoryView = ForegroundAccessoryView()
    }
}
Create a document from a template: add a button swift · at 7:31 ↗
DocumentGroupLaunchScene {
    NewDocumentButton("Start Writing")
    NewDocumentButton("Choose a Template", for: StoryDocument.self) {

    }
}
Create a document from a template: return document later swift · at 7:45 ↗
@State private var creationContinuation: CheckedContinuation<StoryDocument?, any Error>?

DocumentGroupLaunchScene {
    NewDocumentButton("Start Writing")
    NewDocumentButton("Choose a Template", for: StoryDocument.self) {
        try await withCheckedThrowingContinuation { continuation in
            self.creationContinuation = continuation
       }
    }
}
Create a document from a template: present a template picker swift · at 7:56 ↗
@State private var creationContinuation: CheckedContinuation<StoryDocument?, any Error>?
@State private var isTemplatePickerPresented = false

DocumentGroupLaunchScene {
    NewDocumentButton("Start Writing")
    NewDocumentButton("Choose a Template", for: StoryDocument.self) {
        try await withCheckedThrowingContinuation { continuation in
            self.creationContinuation = continuation
            self.isTemplatePickerPresented = true
        }
    }
    .sheet(isPresented: $isTemplatePickerPresented) {
        TemplatePicker(continuation: $creationContinuation
    }
}
Create a document from a template: template picker view swift · at 8:07 ↗
struct TemplatePicker: View {
    @Binding var creationContinuation: CheckedContinuation<StoryDocument?, any Error>?

    var body: some View {
        Button("Three Act Structure") {
            creationContinuation?.resume(returning: StoryDocument.threeActStructure())
            creationContinuation = nil
        }
    }
}

extension StoryDocument {
    static func threeActStructure() -> Self {
        Self.init(...)
    }
}
Create a document from a template in UIKit swift · at 8:20 ↗
extension UIDocument.CreationIntent {
    static let template = UIDocument.CreationIntent("template")
}
Create a document from a template in UIKit swift · at 8:29 ↗
launchOptions.secondaryAction = LaunchOptions.createDocumentAction(with: .template) 
launchOptions.browserViewController.delegate = self

// MARK: UIDocumentBrowserViewControllerDelegate

func documentBrowser(
    _ browser: UIDocumentBrowserViewController, 
    didRequestDocumentCreationWithHandler importHandler: @escaping (URL?, ImportMode) -> Void) 
{
    switch browser.activeDocumentCreationIntent {
    case .template: 
        presentTemplatePicker(with: importHandler)
    default:
        let newDocumentURL = // ...
        importHandler(newDocumentURL, .copy)
    }
}

Resources