MacKuba

Kuba Suder's blog on Mac & iOS development

WWDC 19

SwiftUI On All Devices

Categories: SwiftUI 0 comments Watch the video

(*) – marks APIs that were changed since the video was published

SwiftUI is the shortest path to building great apps on every device

SwiftUI is designed to accomodate many UI paradigms – it can be used with a mouse, touch, digital crown, remote, or assistive technologies

It has great support for any specific platform’s conventions out of the box

It was built with the help of experts from all devices, to make sure that on each platform it feels right at home

"Learn once and use anywhere"

Common elements that render in an appropriate way on every device

Common layout system that works the same way everywhere

More advanced elements like Picker, which renders as a scrollable wheel or a button that navigates to a subpage on iOS, and as a popup button or a set of radio buttons on macOS

There’s no such thing as a “one size fits all app” – if you stick to the common denominator across all devices, you’ll be missing out on the things that make each device great, so you have to decide what the right design is for your app on each device and apply platform-specific customizations

But you can share skills and tools and some code *where it makes sense*

SwiftUI really helps you to reuse some parts of view code between platforms by making it very easy to refactor code, extracting parts of the view code into small reusable components that compose together

When porting an app to a new platform, take a design-first approach: don’t just look at how to make the code run on the other platform, think about what is the right expression for your app on this device

tvOS:

Biggest, boldest screen, viewed from a larger distance across the room

Longer periods of use than on mobile

Often used together with friends

Used with Siri Remote, entire interface must be navigable using focus

Streamlined navigation

Rich, immersive experience – media, photos, videos, not performing tasks

Carefully consider which experiences make sense when viewed on a large screen

Here in the Landmarks app, we want to include:

  • beautiful photos of landmarks
  • adding/removing of favorites
  • basic tourism information

We don’t want:

  • lengthy historical information (don't show long pieces of text)
  • advanced sorting & filtering (we don't want too many controls)
  • anything location-based, since people don't carry their TVs around

Focus:

SwiftUI supports focus by default for built-in controls

For more custom views, you can use the .focusable modifier if the view should be focusable and react to focus:

.focusable(canBecomeFocused) { isFocused … }

Siri Remote buttons:

Handling play/pause and menu buttons on the Siri Remote:

.onPlayPauseCommand { }
.onExitCommand { }

Navigation & design:

Don’t use long, vertically scrolled and nested lists of text, use a navigation UI that emphasizes content (pictures)

Take advantage of the big screen

Use horizontal stack views to create vertical lists of horizontally scrolled sections like e.g. in App Store apps, allowing the user to browse different categories while staying on a single page

On iOS, when using both a navigation controller and tab controller, the tab controller is at the top level and a navigation controller may be used in one or more tabs, so that when you navigate deeper the tab bar is still visible

On tvOS, the tab controller should be added *inside* the navigation controller, so that you see tabs at the top on the root level, but when you select something from the list, the tabs disappear to leave more space for the content

macOS:

High information density – a large screen with relatively small fonts, so you can show a lot of information

Higher tolerance for large amounts of text

Precision pointing device allows smaller click targets and denser controls (within reason!)

Multiple window support

Keyboard shortcuts

Touch Bar

In the Landmarks app on the Mac, we use a master-detail view with a list that shows detailed, condensed content, and detailed information about the selected landmark (including longer text) on the right

The app also supports opening multiple landmarks in separate windows by double-clicking rows in the list

SwiftUI automatically adjusts padding/spacing to be appropriate for the Mac

Use .controlSize() for more compact controls

Menu item commands: (*)

view.onCommand(#selector(showExplore:)) { ... }

ℹ️ Previously this modifier accepted a Command object in which you had to wrap the selector

Touch Bar:

view.touchBar(
  TouchBar {
    Button(action: show) { Image(…) }
    Button(action: delete) { Text(…) }
  }
)

Customizing parts of the UI:

To reuse the same container view between platforms but customize one of its subviews, e.g. the row view here used for each landmark within the list, you can make the container view generic, parametrized with the row type, and add a “row provider” closure that can be passed to the constructor:

struct SharedLandmarksList<LandmarkRowType: View>: View {
  var rowProvider: (Landmark) -> LandmarkRowType

  var body: some View {
    List(selection: $selectedLandmark) {
      ForEach(landmarks) { landmark in
        self.rowProvider(landmark: landmark).tag(landmark)
      }
    }
  }
}

Then, use the generic view in a Mac-specific view, passing a closure that gives it a Mac-specific row view for a given landmark:

struct MacLandmarksList: View {
  var body: some View {
    SharedLandmarksList() { landmark in
      return MacLandmarkRow(landmark: landmark)
    }
  }
}

Handling double-click: (*)

.onTapGesture(count: 2) { … }

ℹ️ Previously this modifier was named .tapAction

watchOS:

A good rule is to aim for important actions to be reachable with 3 taps or less

Don't just shrink your iPhone app, bring only the most relevant parts of it to the user's wrist

In the Watch version of the Landmarks app, we show only the most important information about the landmarks in the list, and include buttons for marking as favorite, calling the park's booking service, and showing navigation directions

We also include a toggle to only show favorite landmarks in order to limit the length of the list

The Watch app also includes interactive notifications when a new event related to a favorite landmark is added

.digitalCrownRotation modifier – handling digital crown events

.listStyle(.carousel) (*) – a scrolling list that focuses on each cell

ℹ️ After the initial betas, all control style modifiers like .listStyle required manual instantion of a style type object, like: .listStyle(CarouselListStyle()). This was changed again to the simplified version like above in Xcode 15 thanks to some changes in Swift generics.