Multiplatform App Tutorial: SwiftUI and Xcode 12
Learn how to use Xcode 12’s multiplatform app template and SwiftUI to write a single app that runs on every Apple platform. By Renan Benatti Dias.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Multiplatform App Tutorial: SwiftUI and Xcode 12
25 mins
- Getting Started
- Considering the Project Structure
- Understanding the New App and Scene Protocol
- Running on macOS
- Understanding how SwiftUI Adapts to the Platform
- Polishing the macOS app
- Adding a minimum width to your list
- Adding a navigation title
- Working With Toolbars
- Understanding tab views on different platforms
- Optimizing the User Experience for Each Platform
- Updating the macOS UI
- Updating the iOS UI
- Understanding Independent Scenes and View States
- Adding Extra Functionality for macOS
- Creating the Preferences View
- Adding a Keyboard Shortcut
- Where to Go From Here?
Since Mac Catalyst was announced, Apple has been paving the way to let iOS developers bring their apps to the Mac. Expansions to SwiftUI — Apple’s simple, declarative approach to building user interfaces — now let you use it to build an entire app. These expansions, along with the multiplatform app template that’s new in Xcode 12, let you use one code base to build apps for every Apple platform.
In this tutorial, you’ll learn about:
- Xcode 12’s multiplatform app template
- The
App
protocol - How the
App
,Scene
s andView
s fit together - The way the same code adapts to each platform
- How to make a custom UI for each platform while reusing views
You’ll learn all of this by adding new features to RayGem — an app that displays information about different gemstones — for iOS, iPadOS and macOS.
Getting Started
Download the project materials by clicking the Download Materials button at the top or bottom of the tutorial. Open RayGem.xcodeproj inside the starter folder. Build and run.
RayGem is a simple multiplatform app that lists a collection of gems, which are precious or semiprecious stones. Users can read interesting facts about them and save their favorites.
You can already scroll and tap gems to read facts about each one. The app has the code to fetch and save favorite gems from Core Data, but it can neither save nor list favorites yet. You’ll add this feature during this tutorial.
Open the different views inside the starter project to become familiar with the app. The main view of the app is in GemList.swift, showing a list of rows found in GemRow.swift that are fetched from a Core Data store. By tapping a row, you navigate to a details view located in DetailsView.swift.
Considering the Project Structure
Before you start making any changes, take a look at the starter project. Notice how the groups are different than those in your usual iOS starter project.
When creating a new project, Xcode 12 has a new section called Multiplatform. In it, you’ll find the new template for multiplatform apps. The starter project for this tutorial was built using this template.
This template creates three groups:
- iOS: iOS-specific code
- macOS: macOS-specific code
- Shared: code for both platforms, including models, business logic and reusable views
SwiftUI lets you share UI code between platforms, and it automatically adapts the UI depending on the device. You can create views that are reusable on each platform, but some behaviors are better suited for certain platforms. As such, having a group for each platform allows you to write specific code for each while still reusing a lot of code.
Understanding the New App and Scene Protocol
Open AppMain.swift inside the Shared group.
@main
struct AppMain: App {
let persistenceController = PersistenceController.shared
var body: some Scene {
WindowGroup {
ContentView()
.environment(
\.managedObjectContext,
persistenceController.container.viewContext)
}
}
}
In iOS 14, Apple introduced the new App
protocol, which handles the app lifecycle and replaces the old AppDelegate.swift and SceneDelegate.swift.
Much like the View
protocol, the App
protocol requires you to implement a body by returning a Scene
. The body will be the root view of your app. Usually, you’ll return a WindowGroup
, a special type of Scene
made to be a container for your app’s view hierarchy. SwiftUI and WindowGroup
together take care of presenting your app differently on each platform. For instance, on macOS, WindowGroup
s will automatically have window management options in their menu bar and support gathering all of your app’s windows in tabs.
Swift 5.3 introduced the new @main
attribute to indicate the entry point of an app. By adding this attribute to a struct that implements App
, SwiftUI will use that struct as your app’s starting point.
Running on macOS
The app already has its basic functionality working on iOS, but how about macOS? Change the target from RayGem (iOS) to RayGem (macOS) and build and run again.
The app doesn’t feature any iOS or macOS specific code, it’s all built using plain old SwiftUI as you would build any other app. But still, you can already run your app on iOS and macOS! Isn’t that cool?
Using the new Multiplatform app template, Xcode creates two targets for your app: one for running on iOS and iPadOS and another for running on macOS. It uses these targets to run your app on each corresponding platform.
Understanding how SwiftUI Adapts to the Platform
Notice how SwiftUI adapted the UI for each platform. On iOS, it uses a navigation view with a list. When the user taps a row, it pushes the destination view of that row. On macOS, it uses a window with a side list and a content view. When the user clicks a row, the content view updates with the destination view of that row.
Now, switch the target back to RayGem (iOS) and build and run on an iPad simulator.
When the app runs on iPadOS, the list stays hidden on the left side of the view, and when the user selects a gem, the main view displays the destination view.
Polishing the macOS app
The UI already adapts to different device sizes, and even window resizing on macOS, but sometimes, you might want to add a few restrictions on some devices while keeping the behavior the same on others. Thankfully, SwiftUI includes a lot of modifiers to influence how it adapts your views to different platforms. You’ll make your app a better macOS citizen by telling SwiftUI what to do with your app.