#WWDC19•

SwiftUI On All Devices
Jeff Nadeau, macOS Frameworks
Ada Turner, tvOS Frameworks
Meghna Sapre, watchOS Frameworks© 2019 Apple Inc. All rights reserved. Redistribution or public display not permitted without written permission from Apple.

The shortest path to building great
apps on every device

The shortest path to building great
apps on every device

The shortest path to building great
apps on every device

AppKitUIKitTVUIKit
UIKit

WatchKit

AppKitUIKitTVUIKit
UIKit

WatchKit

AppKitUIKitTVUIKit
UIKit

WatchKit

Designed for Every Platform

Designed to accommodate many UI paradigms
Platform support out of the box
A common toolkit that you can learn once and use anywhereCommon Elements

Basic controls

iOS

macOS

BluetoothBluetoothtvOS

Bluetooth

watchOS

BluetoothOn

Common Elements

Basic controls

Toggle(isOn: $btEnabled) {Text("Bluetooth")
}

iOS

macOS

BluetoothBluetoothtvOS

Bluetooth

watchOS

BluetoothOn

Common Elements

LayoutHStack {Text("Label")Spacer()Button(action: /* */) {Text("Delete")}
Button(action: /* */) {Text("Cancel")}}.padding()

Label

DeleteCancelCommon Elements

Advanced controls

Picker(selection: /* */, label: /* */) {macOS

ForEach(allFruits) { fruit in
Text(verbatim: fruit.name)
}
}

iOS

watchOS"ɛʺȔȁȔʛʛ˧

Common Elements

Advanced controls

Picker(selection: /* */, label: /* */) {macOS"ɛʺȔȁȔʛʛ˧

ForEach(allFruits) { fruit in
Text(verbatim: fruit.name)
}
}

iOS

watchOS

SwiftUI Essentials

WWDC 2019

Of course notOne Size Does Not Fit All

One Size Does Not Fit All

There’s no such thing as a “one size fits all app”

One Size Does Not Fit All

There’s no such thing as a “one size fits all app”
Make the most of each platform’s strengths

One Size Does Not Fit All

There’s no such thing as a “one size fits all app”
Make the most of each platform’s strengths
Share skillset and toolset

One Size Does Not Fit All

There’s no such thing as a “one size fits all app”
Make the most of each platform’s strengths
Share skillset and toolset
Share code where it makes senseWrite once, run anywhereWrite once, run anywhereLearn once, apply anywhereLearn once, apply anywhereWe built an app

We built four apps

Landmarks

Landmarks

Research and visit landmarks

Landmarks

Research and visit landmarks
Look at photos and visitor infoLandmarks

Research and visit landmarks
Look at photos and visitor info
Get maps and directions

Landmarks

Research and visit landmarks
Look at photos and visitor info
Get maps and directions
Mark favorites

Landmarks for Apple TV

Landmarks for Apple TV

Couch surfing for places to goLandmarks for Apple TV

Couch surfing for places to go
Together with family or friends

Landmarks for Apple TV

Couch surfing for places to go
Together with family or friends
Mark favorites to research later

Landmarks for Mac

Landmarks for Mac

Research many options in detailLandmarks for Mac

Research many options in detailCompare information

Landmarks for Mac

Research many options in detailCompare information
Sort and filter

Landmarks for Mac

Research many options in detailCompare information
Sort and filter
Read up on the details

Landmarks for iOS

Landmarks for iOS

Quick information about each landmark

Landmarks for iOS

Quick information about each landmark
Get driving directions

Landmarks for iOS

Quick information about each landmark
Get driving directions
Place a phone callLandmarks for Apple Watch

Landmarks for Apple Watch

Get at-a-glance info about the current location

Landmarks for Apple Watch

Get at-a-glance info about the current location
Receive important notifications

Learning ExperienceLearning Experience

New documentation series

Learning Experience

New documentation series
Build the app step-by-stepLearning Experience

New documentation series
Build the app step-by-step
Available for download today•

SwiftUI on Apple TV

Ada Turner, tvOS Frameworks

The biggest, boldest screen

The Biggest, Boldest Screen

The Biggest, Boldest Screen

10-foot experience

The Biggest, Boldest Screen

10-foot experience
Focus and the Siri Remote

The Biggest, Boldest Screen

10-foot experience
Focus and the Siri Remote
Streamlined navigation

What is a 10-foot experience?

10-Foot Experience10-Foot Experience

Large screen

10-Foot Experience

Large screenLong viewing distance10-Foot Experience

Large screenLong viewing distance
Extended periods of use

10-Foot Experience

Large screenLong viewing distance
Extended periods of use
Multiple viewers at the same timeLandmarks — What Makes the Cut?

Landmarks — What Makes the Cut?

Beautiful photos of landmarks

Landmarks — What Makes the Cut?

Beautiful photos of landmarks
Adding favorites

Landmarks — What Makes the Cut?

Beautiful photos of landmarks
Adding favorites
Basic tourism information

Landmarks — What Makes the Cut?

Beautiful photos of landmarks
Adding favorites
Basic tourism information

Lengthy historical information

Landmarks — What Makes the Cut?

Beautiful photos of landmarks

Lengthy historical information

Adding favorites

Advanced sorting and filtering

Basic tourism information

Landmarks — What Makes the Cut?

Beautiful photos of landmarks

Lengthy historical information

Adding favorites

Advanced sorting and filtering

Basic tourism information

Location-based notifications

Focus and the Siri Remote

Focus and the Siri Remote

Focus and the Siri Remote

Entire interface must support focus

Focus and the Siri Remote

Entire interface must support focus
SwiftUI supports focus by default

// Custom Focusable Views
struct MyFocusableView: View {let canBecomeFocused: Boolvar body: some View {return Text("Hello WWDC").focusable(canBecomeFocused) { isFocused in// Focus changed}.onPlayPauseCommand {// Play/pause button pressed}.onExitCommand {// Menu button pressed}}}// Custom Focusable Views
struct MyFocusableView: View {let canBecomeFocused: Boolvar body: some View {return Text("Hello WWDC").focusable(canBecomeFocused) { isFocused in// Focus changed}.onPlayPauseCommand {// Play/pause button pressed}.onExitCommand {// Menu button pressed}}}// Custom Focusable Views
struct MyFocusableView: View {let canBecomeFocused: Boolvar body: some View {return Text("Hello WWDC").focusable(canBecomeFocused) { isFocused in// Focus changed}.onPlayPauseCommand {// Play/pause button pressed}.onExitCommand {// Menu button pressed}}}// Custom Focusable Views
struct MyFocusableView: View {let canBecomeFocused: Boolvar body: some View {return Text("Hello WWDC").focusable(canBecomeFocused) { isFocused in// Focus changed}.onPlayPauseCommand {// Play/pause button pressed}.onExitCommand {// Menu button pressed}}}Streamlined navigation// TabbedView and NavigationView on tvOS
struct MainView: View {enum Tab { case explore, hikes, tours }@State var selection: Tab
var body: some View {return NavigationView {TabbedView(selection: $selection) {ExploreView().tabItemLabel(Text("Explore")).tag(Tab.explore)
HikesView().tabItemLabel(Text("Hikes")).tag(Tab.hikes)
ToursView().tabItemLabel(Text("Tours")).tag(Tab.tours)}}}}

// TabbedView and NavigationView on tvOS
struct MainView: View {enum Tab { case explore, hikes, tours }@State var selection: Tab
var body: some View {return NavigationView {TabbedView(selection: $selection) {ExploreView().tabItemLabel(Text("Explore")).tag(Tab.explore)
HikesView().tabItemLabel(Text("Hikes")).tag(Tab.hikes)
ToursView().tabItemLabel(Text("Tours")).tag(Tab.tours)}}}}

// TabbedView and NavigationView on tvOS
struct MainView: View {enum Tab { case explore, hikes, tours }@State var selection: Tab
var body: some View {return NavigationView {TabbedView(selection: $selection) {ExploreView().tabItemLabel(Text("Explore")).tag(Tab.explore)
HikesView().tabItemLabel(Text("Hikes")).tag(Tab.hikes)
ToursView().tabItemLabel(Text("Tours")).tag(Tab.tours)}}}}

// TabbedView and NavigationView on tvOS
struct MainView: View {enum Tab { case explore, hikes, tours }@State var selection: Tab
var body: some View {return NavigationView {TabbedView(selection: $selection) {ExploreView().tabItemLabel(Text("Explore")).tag(Tab.explore)
HikesView().tabItemLabel(Text("Hikes")).tag(Tab.hikes)
ToursView().tabItemLabel(Text("Tours")).tag(Tab.tours)}}}}

tvOS vs. iOS Navigation Hierarchies

tvOS vs. iOS Navigation Hierarchies

iOS
TabbedView

tvOS vs. iOS Navigation Hierarchies

iOS
TabbedView

NavigationView

Content

NavigationView

tvOS vs. iOS Navigation Hierarchies

iOS
TabbedView

NavigationView

Content

Content

NavigationView

Content

tvOS vs. iOS Navigation Hierarchies

iOS
TabbedView

NavigationView

Content

NavigationView

Content

Content

Content

Content

tvOS vs. iOS Navigation Hierarchies

iOS
TabbedView

NavigationView

Content

NavigationView

Content

Content

Content

Content

tvOS vs. iOS Navigation Hierarchies

NavigationView

iOS

tvOS

TabbedView

NavigationView

Content

NavigationView

Content

Content

Content

Content

tvOS vs. iOS Navigation Hierarchies

NavigationView

iOS

tvOS

TabbedView

NavigationView

Content

NavigationView

Content

Content

Content

Content

TabbedView

tvOS vs. iOS Navigation Hierarchies

NavigationView

iOS

tvOS

TabbedView

NavigationView

Content

NavigationView

Content

Content

Content

Content

TabbedView

Content

Content

Content

tvOS vs. iOS Navigation Hierarchies

NavigationView

iOS

tvOS

TabbedView

NavigationView

Content

NavigationView

TabbedView

Content

Content

Content

Content

Content

Content

Content

Content

Content

Content

tvOS vs. iOS Navigation Hierarchies
iOS
TabbedView(selection: $selection) {tvOS
NavigationView {NavigationView { ExploreView() }TabbedView(selection: $selection) {.tabItemLabel(Text("Explore"))ExploreView().tag(Tab.explore).tabItemLabel(Text("Explore"))NavigationView { HikesView() }.tag(Tab.explore).tabItemLabel(Text("Hikes"))

HikesView().tag(Tab.hikes).tabItemLabel(Text("Hikes"))

NavigationView { ToursView() }.tag(Tab.hikes).tabItemLabel(Text("Tours"))

ToursView().tag(Tab.tours).tabItemLabel(Text("Tours"))}.tag(Tab.tours)}}

tvOS vs. iOS Navigation Hierarchies
iOS
TabbedView(selection: $selection) {tvOS
NavigationView {NavigationView { ExploreView() }TabbedView(selection: $selection) {.tabItemLabel(Text("Explore"))ExploreView().tag(Tab.explore).tabItemLabel(Text("Explore"))NavigationView { HikesView() }.tag(Tab.explore).tabItemLabel(Text("Hikes"))

HikesView().tag(Tab.hikes).tabItemLabel(Text("Hikes"))

NavigationView { ToursView() }.tag(Tab.hikes).tabItemLabel(Text("Tours"))

ToursView().tag(Tab.tours).tabItemLabel(Text("Tours"))}.tag(Tab.tours)}}

tvOS vs. iOS Navigation Hierarchies
iOS
TabbedView(selection: $selection) {tvOS
NavigationView {NavigationView { ExploreView() }TabbedView(selection: $selection) {.tabItemLabel(Text("Explore"))ExploreView().tag(Tab.explore).tabItemLabel(Text("Explore"))NavigationView { HikesView() }.tag(Tab.explore).tabItemLabel(Text("Hikes"))

HikesView().tag(Tab.hikes).tabItemLabel(Text("Hikes"))

NavigationView { ToursView() }.tag(Tab.hikes).tabItemLabel(Text("Tours"))

ToursView().tag(Tab.tours).tabItemLabel(Text("Tours"))}.tag(Tab.tours)}}

tvOS vs. iOS Navigation Hierarchies
iOS
TabbedView(selection: $selection) {tvOS
NavigationView {NavigationView { ExploreView() }TabbedView(selection: $selection) {.tabItemLabel(Text("Explore"))ExploreView().tag(Tab.explore).tabItemLabel(Text("Explore"))NavigationView { HikesView() }.tag(Tab.explore).tabItemLabel(Text("Hikes"))

HikesView().tag(Tab.hikes).tabItemLabel(Text("Hikes"))

NavigationView { ToursView() }.tag(Tab.hikes).tabItemLabel(Text("Tours"))

ToursView().tag(Tab.tours).tabItemLabel(Text("Tours"))}.tag(Tab.tours)
}
}

•

Demo

•

SwiftUI on Mac

Jeff Nadeau, macOS Frameworks

•High information density•

Multiple windows•

Keyboard shortcuts•

Touch Bar•High information density•

Multiple windows•

Keyboard shortcuts•

Touch BarHigh Information Density

Provide more information at a glance

High Information Density

Provide more information at a glance
Precision pointing device allows smaller controls

High Information Density

Provide more information at a glance
Precision pointing device allows smaller controls

High Information Density

Provide more information at a glance
Precision pointing device allows smaller controls
Higher tolerance for large amounts of text

High Information Density

Provide more information at a glance
Precision pointing device allows smaller controls
Higher tolerance for large amounts of text

Lorem ipsum dolor sit amet, consectetur adipiscing
elit. Curabitur consectetur tincidunt mollis. Vivamus
ipsum nulla, sagittis id pellentesque ac, suscipit indui. Donec pretium suscipit blandit. Donec volutpat
mollis nulla. Proin a dapibus lorem. Morbi id tempornunc. Mauris tempus lacinia ligula in faucibus.
Quisque nec maximus augue. Nunc eu ornare nulla.
Suspendisse quam est, faucibus nec imperdiet sed,sagittis at purus.
Nam mollis, lorem sed pretium efficitur, mauris leo
luctus elit, volutpat consectetur turpis libero velante. Nulla non malesuada leo. Aenean ut interdum
turpis. Nunc sit amet euismod lorem, suscipit
venenatis nibh. Vestibulum euismod facilisis nisl nec
tincidunt. Integer massa ex, commodo vel finibus id,
cursus id erat. Vestibulum ante ipsum primis in
faucibus orci luctus et ultrices posuere cubilia Curae;
Maecenas ligula ante, facilisis id tristique nec,
eleifend vitae neque.

High Information Density

High Information Density

Spacing and padding automatically adapt

High Information Density

Spacing and padding automatically adapt
Use .controlSize() for compact controls

•High information density•

Multiple windows•

Keyboard shortcuts•

Touch Bar

Multiple Windows

Multiple Windows

Compare content across windows side-by-side

Multiple Windows

Compare content across windows side-by-side
Focus on a single item in its own window

Multiple Windows

Compare content across windows side-by-side
Focus on a single item in its own window
Spatially organize windows around the Desktop and Spaces

•High information density•

Multiple windows•

Keyboard shortcuts•

Touch Bar

Keyboard Shortcuts

Keyboard Shortcuts

Quick access to common actions

Keyboard Shortcuts

Quick access to common actions
Lightning-fast navigation

// Adding Keyboard Shortcuts
TabbedView(selection: $selectedTab) {ExploreView().tag(Tab.explore).tabItemLabel(Text("Explore"))HikesView().tag(Tab.hikes).tabItemLabel(Text(“Hikes"))
ToursView().tag(Tab.tours).tabItemLabel(Text(“Tours"))}

Keyboard Shortcuts

Keyboard Shortcuts

extension Command {static let showExplore = Command(Selector(("showExplore:")))
static let showHikes = Command(Selector(("showHikes:")))
static let showTours = Command(Selector(("showTours:")))}

// Adding Keyboard Shortcuts
TabbedView(selection: $selectedTab) {ExploreView().tag(Tab.explore).tabItemLabel(Text("Explore"))HikesView().tag(Tab.hikes).tabItemLabel(Text(“Hikes"))
ToursView().tag(Tab.tours).tabItemLabel(Text(“Tours"))}

// Adding Keyboard Shortcuts
TabbedView(selection: $selectedTab) {ExploreView().tag(Tab.explore).tabItemLabel(Text("Explore"))HikesView().tag(Tab.hikes).tabItemLabel(Text(“Hikes"))
ToursView().tag(Tab.tours).tabItemLabel(Text(“Tours"))}.onCommand(.showExplore) { self.selectedTab = .explore }.onCommand(.showHikes) { self.selectedTab = .hikes }.onCommand(.showTours) { self.selectedTab = .tours }// Adding Keyboard Shortcuts
TabbedView(selection: $selectedTab) {ExploreView().tag(Tab.explore).tabItemLabel(Text("Explore"))HikesView().tag(Tab.hikes).tabItemLabel(Text(“Hikes"))
ToursView().tag(Tab.tours).tabItemLabel(Text(“Tours"))}.onCommand(.showExplore) { self.selectedTab = .explore }.onCommand(.showHikes) { self.selectedTab = .hikes }.onCommand(.showTours) { self.selectedTab = .tours }// Adding Keyboard Shortcuts
TabbedView(selection: $selectedTab) {ExploreView().tag(Tab.explore).tabItemLabel(Text("Explore"))HikesView().tag(Tab.hikes).tabItemLabel(Text(“Hikes"))
ToursView().tag(Tab.tours).tabItemLabel(Text(“Tours"))}.onCommand(.showExplore) { self.selectedTab = .explore }.onCommand(.showHikes) { self.selectedTab = .hikes }.onCommand(.showTours) { self.selectedTab = .tours }Integrating SwiftUIWWDC 2019•High information density•

Multiple windows•

Keyboard shortcuts•

Touch Bar// Adding Touch Bar Support

TouchBar {}a// Adding Touch Bar Support

TouchBar {Button(action: toggleFavorite) {Image(isFavorite ? "favorite" : "favorite-empty")}a}a// Adding Touch Bar Support

TouchBar {Button(action: toggleFavorite) {Image(isFavorite ? "favorite" : "favorite-empty")}a
Button(action: placeCall) {Image("call")}a}a// Adding Touch Bar Support

TouchBar {Button(action: toggleFavorite) {Image(isFavorite ? "favorite" : "favorite-empty")}a
Button(action: placeCall) {Image("call")}a
Button(action: getDirections) {Text("Get Directions")}a}a// Adding Touch Bar Support

MyCustomView() .touchBar(
TouchBar {Button(action: toggleFavorite) {Image(isFavorite ? "favorite" : "favorite-empty")}a
Button(action: placeCall) {Image("call")}a
Button(action: getDirections) {Text("Get Directions")}a}a)// Adding Touch Bar Support

MyCustomView() .touchBar(
TouchBar {Button(action: toggleFavorite) {Image(isFavorite ? "favorite" : "favorite-empty")}a
Button(action: placeCall) {Image("call")}a
Button(action: getDirections) {Text("Get Directions")
}a
}a
)

•

Demo

•

SwiftUI on Apple Watch

Meghna Sapre, watchOS Frameworks

•

watchOS App•

Interactive Notifications•

Landmarks on Apple Watch

3
Taps or fewer

watchOS App

watchOS App

ScrollView

watchOS App

watchOS App.digitalCrownRotation

watchOS App.digitalCrownRotation

watchOS App

watchOS App

HStackVStackwatchOS App

watchOS App

List
Section•

watchOS App•

Interactive Notifications•

Landmarks on Apple Watch

Interactive Notifications

Interactive Notifications

Provide useful information

Interactive Notifications

Provide useful information
Offer intuitive actions

Interactive Notifications

Provide useful information
Offer intuitive actions
Deliver at the right time•watchOS App•

Interactive Notifications•

Landmarks on Apple Watch

Landmarks on Apple Watch

Landmarks on Apple Watch

Landmarks on Apple Watch

Landmarks on Apple Watch

Landmarks on Apple Watch
ForEach(landmarks) { landmark in}

WatchLandmarkDetails(landmark: landmark)Landmarks on Apple Watch// WatchLandmarkDetails// ImageLandmarkImage(landmark: landmark)// Details
NavigationButton(destination: WatchTourDetails(landmark:landmark)) {Text("Tour: 10:00 AM")}// Make a Call
Button(action: call) {Image(systemName: "phone.fill").foregroundColor(.green)}Landmarks on Apple Watch

Landmarks on Apple Watch

Landmarks on Apple Watch
var landmarks: [Landmark] {if showFavoritesOnly {return landmarkData.filter(isFavorite)}
return landmarkData}

Landmarks on Apple Watch
var landmarks: [Landmark] {if showFavoritesOnly {return landmarkData.filter(isFavorite)}
return landmarkData}

Button(action: self.toggleFavoritesOnly) {Text(self.userData.showFavoritesOnly ?
}"Show All" : "Show Favorites")Landmarks on Apple Watch

return landmarkList

Landmarks on Apple Watch

return landmarkList

Landmarks on Apple Watch

return landmarkList.listStyle(.carousel)

Interactive Notifications

Interactive Notifications

•

Demo

SummarySummary

Take a design-first approachSummary

Take a design-first approach
Effortlessly share model codeSummary

Take a design-first approach
Effortlessly share model code
Be judicious when sharing view codeSummary

Take a design-first approach
Effortlessly share model code
Be judicious when sharing view code
Learn once, apply anywhereMore Information
developer.apple.com/wwdc19/240