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 ↗Chapters
Code shown on screen · 17 snippets
Document-based application
@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
class DocumentViewController: UIDocumentViewController { ... }
let documentViewController = DocumentViewController()
let browserViewController = UIDocumentBrowserViewController(
forOpening: [.plainText]
)
window.rootViewController = browserViewController Presenting a document from the browser in iOS 17
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
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
class DocumentViewController: UIDocumentViewController { ... }
let documentViewController = DocumentViewController()
window.rootViewController = documentViewController Customize the document launch experience: background
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
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
DocumentGroupLaunchScene {
NewDocumentButton("Start Writing")
} background: {
Image(.pinkJungle)
.resizable()
.aspectRatio(contentMode: .fill)
} overlayAccessoryView: {
} Position accessory views
DocumentGroupLaunchScene {
NewDocumentButton("Start Writing")
} background: {
Image(.pinkJungle)
.resizable()
.aspectRatio(contentMode: .fill)
} overlayAccessoryView: { geometry in
} Position accessory views
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
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
DocumentGroupLaunchScene {
NewDocumentButton("Start Writing")
NewDocumentButton("Choose a Template", for: StoryDocument.self) {
}
} Create a document from a template: return document later
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
private var creationContinuation: CheckedContinuation<StoryDocument?, any Error>?
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
struct TemplatePicker: View {
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
extension UIDocument.CreationIntent {
static let template = UIDocument.CreationIntent("template")
} Create a document from a template in UIKit
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
Related sessions
-
12 min -
14 min