Using Xcode in SwiftUI Mode

When creating a new project, Xcode now provides a choice of creating either a Storyboard or SwiftUI based user interface for the project. When creating a SwiftUI project, Xcode appears and behaves significantly differently when designing the user interface for an app project compared to the UIKit Storyboard mode.

When working in SwiftUI mode, most of your time as an app developer will be spent in the code editor and preview canvas, both of which will be explored in detail in this chapter.

1.1 Starting Xcode 11

As with all iOS examples in this book, the development of our example will take place within the Xcode 11 development environment. If you have not already installed this tool together with the latest iOS SDK refer first to the Installing Xcode 11 and the iOS 13 SDK chapter of this book. Assuming the installation is complete, launch Xcode either by clicking on the icon on the dock (assuming you created one) or use the macOS Finder to locate Xcode in the Applications folder of your system.

When launched for the first time, and until you turn off the Show this window when Xcode launches toggle, the screen illustrated in Figure 17‑1 will appear by default:

Figure 17‑1

If you do not see this window, simply select the Window -> Welcome to Xcode menu option to display it. From within this window, click on the option to Create a new Xcode project.

1.2 Creating a SwiftUI Project

When creating a new project, the project options screen includes an option to select how the user interface is to be implemented. To use SwiftUI, simply change the menu option highlighted in Figure 17‑2 to SwiftUI:

Figure 17‑2

Once a new project has been created with SwiftUI selected, the main Xcode panel will appear with the default layout for SwiftUI development displayed.

1.3 Xcode in SwiftUI Mode

Before beginning work on a SwiftUI user interface, it is worth taking some time to gain familiarity with how Xcode works in SwiftUI mode. A newly created project includes a single SwiftUI View file named ContentView.swift which, when selected from the project navigation panel, will appear within Xcode as shown in Figure 17‑3 below:

Figure 17‑3

Located to the right of the project navigator (A) is the code editor (B). To the right of this is the preview canvas (C) where any changes made to the SwiftUI layout declaration will appear in real-time.

Selecting a view in the canvas will automatically select and highlight the corresponding entry in the code editor, and vice versa. Attributes for the currently selected item will appear in the attributes inspector panel (D).

During debugging, the debug panel (E) will appear containing debug output from both the iOS frameworks and any diagnostic print statements you have included in your code.

The three panels (A, D and E) can be displayed and hidden using the three buttons located on the right-hand side of the toolbar as shown in Figure 17‑4:

Figure 17‑4

1.4 The Preview Canvas

The preview canvas provides both a visual representation of the user interface design and a tool for adding and modifying views within the layout design. The canvas may also be used to perform live testing of the running app without the need to launch an iOS simulator. Figure 17‑5 illustrates a typical preview canvas for a newly created project:

Figure 17‑5

If the canvas is not visible it can be displayed using the Xcode Editor -> Canvas menu option.

The main canvas area (A) represents the current view as it will appear when running on a physical device. When changes are made to the code in the editor, those changes are reflected within the preview canvas. To avoid continually updating the canvas, and depending on the nature of the changes being made, the preview will occasionally pause live updates. When this happens, the Resume button (B) will appear which, when clicked, will once again begin updating the preview.

By default, the preview displays a static representation of the user interface. To test the user interface in a running version of the app, simply click on the Live Preview button (C). Xcode will then build the app and run it within the preview canvas where you can interact with it as you would in a simulator or on a physical device. When in Live Preview mode, the button changes to a stop button which can be used to exit live mode.

The current version of the app may also be previewed on an attached physical device by clicking on the Preview on Device button (D). As with the preview canvas, the running app on the device will update dynamically as changes are made to the code in the editor.

Right-clicking on either the Live Preview or Preview on Device buttons will provide the option to run in debug mode, attaching the process to the debugger and allowing diagnostic output to appear in the debug area (marked E in Figure 17‑3 above).

Figure 17‑6

1.5 Preview Pinning

When building an app in Xcode it is likely that it will consist of several SwiftUI View files in addition to the default ContentView.swift file. When a SwiftUI View file is selected from the project navigator, both the code editor and preview canvas will change to reflect the currently selected file. Sometimes you may want the user interface layout for one SwiftUI file to appear in the preview canvas while editing the code in a different file. This can be particularly useful if the layout from one file is dependent on or embedded in another view. The pin button (labelled F in Figure 17‑5 above) pins the current preview to the canvas so that it remains visible on the canvas after navigating to a different view. The view to which you have navigated will appear beneath the pinned view in the canvas and can be viewed by scrolling.

Finally, the size buttons (E) can be used to zoom in and out of the canvas.

1.6 Modifying the Design

Working with SwiftUI primarily involves adding additional views, customizing those views using modifiers, adding logic and interacting with state and other data instance bindings. All of these tasks can be performed exclusively by modifying the structure in the code editor. The font used to display the “Hello World” Text view, for example, can be changed by adding the appropriate modifier in the editor:

Text("Hello World")

An alternative to this is to make changes to the SwiftUI views by dragging and dropping items from the Library panel. The Library panel is displayed by clicking on the toolbar button highlighted in Figure 17‑7:

Figure 17‑7

When displayed, the Library panel will appear as shown in Figure 17‑8:

Figure 17‑8

When launched in this way, the Library panel is transient and will disappear either after a selection has been made, or a click is performed outside of the panel. To keep the panel displayed, hold down the Option key when clicking on the Library button.

When first opened, the panel displays a list of views available for inclusion in the user interface design. The list can be browsed, or the search bar used to narrow the list to specific views. The toolbar (highlighted in the above figure) can be used to switch to other categories such as modifiers, commonly used code snippets, images and color resources.

An item within the library can be applied to the user interface design in a number of ways. To apply a font modifier to the “Hello World” Text view, one option is to select the view in either the code or preview canvas, locate the font modifier in the Library panel and double-click on it. Xcode will then automatically apply the font modifier.

Another option is to locate the Library item and then drag and drop it onto the desired location either in the code editor or the preview canvas. In Figure 17‑9 below, for example, the font modifier is being dragged to the Text view within the editor:

Figure 17‑9

The same result can be achieved by dragging an item from the library onto the preview canvas. In the case of Figure 17‑10, a Button view is being added to the layout beneath the existing Text view:

Figure 17‑10

In this example, the side along which the view will be placed if released highlights and the preview canvas displays a notification that the Button and existing Text view will automatically be placed in a Vertical Stack container view (stacks will be covered later in the chapter entitled SwiftUI Stacks and Frames).

Once a view or modifier has been added to the SwiftUI view file it is highly likely that some customization will be necessary, such as specifying the color for a foreground modifier. One option is, of course, to simply make the changes within the editor, for example:

Text("Hello World")

Another option is to select the view in either the editor or preview panel and then make the necessary changes within the Attributes inspector panel:

Figure 17‑11

The Attributes inspector will provide the option to make changes to any modifiers already applied to the selected view.

Before moving on to the next topic, it is also worth noting that the Attributes inspector provides yet another way to add modifiers to a view via the Add Modifier menu located at the bottom of the panel. When clicked, this menu will display a long list of modifiers available for the current view type. To apply a modifier, simply select it from the menu. An entry for the new modifier will subsequently appear in the inspector where it can be configured with the required properties.

1.7 Editor Context Menu

Holding down the Command key while clicking on the item in the code editor will display the menu shown in Figure 17‑12:

Figure 17‑12

This menu provides a list of options which will vary depending on the type of item selected. Options typically include a shortcut to a popup version of the Attributes inspector for the current view, together with options to embed the current view in a stack or list container. This menu is also useful for extracting part of a view into its own self-contained subview. Creating subviews is strongly encouraged to promote reuse, improve performance and unclutter complex design structures.

1.8 Previewing on Multiple Device Configurations

Every newly created SwiftUI View file includes an additional declaration at the bottom of the file that resembles the following:

struct ContentView_Previews: PreviewProvider {

    static var previews: some View {




This structure, which conforms to the PreviewProvider protocol, returns an instance of the primary view within the file. This instructs Xcode to display the preview for that view within the preview canvas (without this declaration, nothing will appear in the canvas).

By default, the preview canvas shows the user interface on a single device based on the current selection in the run target menu to the right of the run and stop button in the Xcode toolbar. To preview on other device models, one option is to simply change the run target and wait for the preview canvas to change.

A better option, however, is to modify the previews structure to specify a different device. In the following example, the canvas previews the user interface on an iPhone SE:

struct ContentView_Previews: PreviewProvider {

    static var previews: some View {


              .previewDevice(PreviewDevice(rawValue: "iPhone SE"))

              .previewDisplayName("iPhone SE")


In fact, it is possible using this technique to preview multiple device types simultaneously by placing them into a Group view as follows:

struct ContentView_Previews: PreviewProvider {

    static var previews: some View {

        Group {
              .previewDevice(PreviewDevice(rawValue: "iPhone SE"))
              .previewDisplayName("iPhone SE")
              .previewDevice(PreviewDevice(rawValue: "iPhone 11"))
              .previewDisplayName("iPhone 11")

When multiple devices are previewed, they appear in a scrollable list within the preview canvas as shown in Figure 17‑13:

Figure 17‑13

The environment modifier may also be used to preview the layout in other configurations, for example, to preview in dark mode:

    .previewDevice(PreviewDevice(rawValue: "iPhone SE"))
    .previewDisplayName("iPhone SE")
         .environment(\.colorScheme, .dark)

This preview structure is also useful for passing sample data into the enclosing view for testing purposes within the preview canvas, a technique that will be used in later chapters. For example:

struct ContentView_Previews: PreviewProvider {

    static var previews: some View {
        ContentView(sampleData: mySampleData)                 

1.9 Running the App on a Simulator

Although much can be achieved using the preview canvas, there is no substitute for running the app on physical devices and simulators during testing.

Within the main Xcode project window, the menu located in the top left-hand corner of the window (marked C in Figure 17‑14) is used to choose a target simulator. This menu will include both simulators that have been configured and any physical devices connected to the development system:

Figure 17‑14

Clicking on the Run toolbar button (A) will compile the code and run the app on the selected target. The small panel in the center of the Xcode toolbar (D) will report the progress of the build process together with any problems or errors that cause the build process to fail. Once the app is built, the simulator will start and the app will run. Clicking on the stop button (B) will terminate the running app.

The simulator includes a number of options not available in the live preview for testing different aspects of the app. The Hardware and Debug menus, for example, include options for rotating the simulator through portrait and landscape orientations, testing Face ID authentication and simulating geographical location changes for navigation and map-based apps.

1.10 Running the App on a Physical iOS Device

Although the Simulator environment provides a useful way to test an app on a variety of different iOS device models, it is important to also test on a physical iOS device.

If you have entered your Apple ID in the Xcode preferences screen as outlined in the Joining the Apple Developer Program chapter and selected a development team for the project, it is possible to run the app on a physical device simply by connecting it to the development Mac system with a USB cable and selecting it as the run target within Xcode.

With a device connected to the development system, and an application ready for testing, refer to the device menu located in the Xcode toolbar. There is a reasonable chance that this will have defaulted to one of the iOS Simulator configurations. Switch to the physical device by selecting this menu and changing it to the device name as shown in Figure 17‑5:

Figure 17‑15

With the target device selected, make sure the device is unlocked and click on the run button at which point Xcode will install and launch the app on the device. If you have not yet joined the Apple Developer Program, the following dialog may appear within Xcode indicating that you need to configure your device to trust the developer certificate used to build the app:

Figure 17‑16

Following the instructions in the dialog, open the Settings app on the device, navigate to General -> Profiles and Device Management and select the developer certificate on the resulting screen:

Figure 17‑17

In the subsequent certificate screen, tap the Trust “Apple Development: <email address>” button followed by the Trust button in the confirmation dialog:

Figure 17‑18

Once the certificate is trusted, it should be possible to install and run the app on the device.

As will be discussed later in this chapter, a physical device may also be configured for network testing, whereby apps are installed and tested on the device via a network connection without the need to have the device connected by a USB cable.

1.11 Managing Devices and Simulators

Currently connected iOS devices and the simulators configured for use with Xcode can be viewed and managed using the Xcode Devices window which is accessed via the Window -> Devices and Simulators menu option. Figure 17‑19 for example, shows a typical Device screen on a system where an iPhone has been detected:

Figure 17‑19

A wide range of simulator configurations are set up within Xcode by default and can be viewed by selecting the Simulators tab at the top of the dialog. Other simulator configurations can be added by clicking on the + button located in the bottom left-hand corner of the window. Once selected, a dialog will appear allowing the simulator to be configured in terms of device, iOS version and name.

1.12 Enabling Network Testing

In addition to testing an app on a physical device connected to the development system via a USB cable, Xcode also supports testing via a network connection. This option is enabled on a per device basis within the Devices and Simulators dialog introduced in the previous section. With the device connected via the USB cable, display this dialog, select the device from the list and enable the Connect via network option as highlighted in Figure 17‑20:

Figure 17‑20

Once the setting has been enabled, the device may continue to be used as the run target for the app even when the USB cable is disconnected. The only requirement being that both the device and development computer be connected to the same Wi-Fi network. Assuming this requirement has been met, clicking on the run button with the device selected in the run menu will install and launch the app over the network connection.

1.13 Dealing with Build Errors

If for any reason a build fails, the status window in the Xcode toolbar will report that an error has been detected by displaying “Build” together with the number of errors detected and any warnings. In addition, the left-hand panel of the Xcode window will update with a list of the errors. Selecting an error from this list will take you to the location in the code where corrective action needs to be taken.

1.14 Monitoring Application Performance

Another useful feature of Xcode is the ability to monitor the performance of an application while it is running, either on a device or simulator or within the live preview canvas. This information is accessed by displaying the Debug Navigator.

When Xcode is launched, the project navigator is displayed in the left-hand panel by default. Along the top of this panel is a bar with a range of other options. The seventh option from the left displays the debug navigator when selected as illustrated in Figure 17‑21. When displayed, this panel shows a number of real-time statistics relating to the performance of the currently running application such as memory, CPU usage, disk access, energy efficiency, network activity and iCloud storage access.

Figure 17‑21

When one of these categories is selected, the main panel (Figure 17‑22) updates to provide additional information about that particular aspect of the application’s performance:

Figure 17‑22

Yet more information can be obtained by clicking on the Profile in Instruments button in the top right-hand corner of the panel.

1.15 Exploring the User Interface Layout Hierarchy

Xcode also provides an option to break the user interface layout out into a rotatable 3D view that shows how the view hierarchy for a user interface is constructed. This can be particularly useful for identifying situations where one view instance is obscured by another appearing on top of it or a layout is not appearing as intended. This is also useful for learning how SwiftUI works behind the scenes to construct a SwiftUI layout, if only to appreciate how much work SwiftUI is saving us from having to do.

To access the view hierarchy in this mode, begin by previewing the view in debug mode as illustrated in Figure 17‑6 above. Once the preview is live, click on the Debug View Hierarchy button indicated in Figure 17‑23:

Figure 17‑23

Once activated, a 3D “exploded” view of the layout will appear. Clicking and dragging within the view will rotate the hierarchy allowing the layers of views that make up the user interface to be inspected:

Figure 17‑24

Moving the slider in the bottom left-hand corner of the panel will adjust the spacing between the different views in the hierarchy. The two markers in the right-hand slider (Figure 17‑25) may also be used to narrow the range of views visible in the rendering. This can be useful, for example, to focus on a subset of views located in the middle of the hierarchy tree:

Figure 17‑25

While the hierarchy is being debugged, the left-hand panel will display the entire view hierarchy tree for the layout as shown in Figure 17‑26 below:

Figure 17‑26

Selecting an object in the hierarchy tree will highlight the corresponding item in the 3D rendering and vice versa. The far right-hand panel will also display the attributes of the selected object. If the panel is not currently visible it can be displayed by clicking on the toolbar button indicated in Figure 17‑27:

Figure 17‑27

Figure 17‑28, for example, shows the inspector panel while a Text view is selected within the view hierarchy.

Figure 17‑28

1.16 Summary

When creating a new project, Xcode provides the option to use either UIKit Storyboards or SwiftUI as the basis of the user interface of the app. When in SwiftUI mode, most of the work involved in developing an app takes place in the code editor and the preview canvas. New views can be added to the user interface layout and configured either by typing into the code editor or dragging and dropping components from the Library either onto the editor or the preview canvas.

The preview canvas will usually update in real time to reflect code changes as they are typed into the code editor, though will frequently pause updates in response to larger changes. When in the paused state, clicking the Resume button will restart updates. The Attribute inspector allows the properties of a selected view to be changed and new modifiers added. Holding down the Command key while clicking on a view in the editor or canvas displays the context menu containing a range of options such as embedding the view in a container or extracting the selection to a subview.

The preview structure at the end of the SwiftUI View file allows previewing to be performed on multiple device models simultaneously and with different environment settings.

An Overview of SwiftUI

Now that Xcode has been installed and the basics of the Swift programming language covered, it is time to start introducing SwiftUI.

First announced at Apple’s Worldwide Developer Conference in 2019, SwiftUI is an entirely new approach to developing apps for all Apple operating system platforms. The basic goals of SwiftUI are to make app development easier, faster and less prone to the types of bugs that typically appear when developing software projects. These elements have been combined with SwiftUI specific additions to Xcode that allow SwiftUI projects to be tested in near real-time using a live preview of the app during the development process.

Many of the advantages of SwiftUI originate from the fact that it is both declarative and data driven, topics which will be explained in this chapter.

The discussion in this chapter is intended as a high-level overview of SwiftUI and does not cover the practical aspects of implementation within a project. Implementation and practical examples will be covered in detail in the remainder of the book.

1.1 UIKit and Interface Builder

To understand the meaning and advantages of SwiftUI’s declarative syntax, it helps to understand how user interface layouts were designed before the introduction of SwiftUI. Up until the introduction of SwiftUI, iOS apps were built entirely using UIKit together with a collection of associated frameworks that make up the iOS Software Development Kit (SDK).

To aid in the design of the user interface layouts that make up the screens of an app, Xcode includes a tool called Interface Builder. Interface Builder is a powerful tool that allows storyboards to be created which contain the individual scenes that make up an app (with a scene typically representing a single app screen).

The user interface layout of a scene is designed within Interface Builder by dragging components (such as buttons, labels, text fields and sliders) from a library panel to the desired location on the scene canvas. Selecting a component in a scene provides access to a range of inspector panels where the attributes of the components can be changed.

The layout behavior of the scene (in other words how it reacts to different device screen sizes and changes to device orientation between portrait and landscape) is defined by configuring a range of constraints that dictate how each component is positioned and sized in relation to both the containing window and the other components in the layout.

Finally, any components that need to respond to user events (such as a button tap or slider motion) are connected to methods in the app source code where the event is handled.

At various points during this development process, it is necessary to compile and run the app on a simulator or device to test that everything is working as expected.

1.2 SwiftUI Declarative Syntax

SwiftUI introduces a declarative syntax that provides an entirely different way of implementing user interface layouts and behavior from the UIKit and Interface Builder approach. Instead of manually designing the intricate details of the layout and appearance of components that make up a scene, SwiftUI allows the scenes to be described using a simple and intuitive syntax. In other words, SwiftUI allows layouts to be created by declaring how the user interface should appear without having to worry about the complexity of how the layout is actually built.

This essentially involves declaring the components to be included in the layout, stating the kind of layout manager in which they to be contained (vertical stack, horizontal stack, form, list etc.) and using modifiers to set attributes such as the text on a button, the foreground color of a label, or the method to be called in the event of a tap gesture. Having made these declarations, all the intricate and complicated details of how to position, constrain and render the layout are handled automatically by SwiftUI.

SwiftUI declarations are structured hierarchically, which also makes it easy to create complex views by composing together small, re-usable custom subviews.

While the view layout is being declared and tested, Xcode provides a preview canvas which changes in real-time to reflect the appearance of the layout. Xcode also includes a live preview mode which allows the app to be launched within the preview canvas and fully tested without the need to build and run on a simulator or device.

Coverage of the SwiftUI declaration syntax begins with the chapter entitled Creating Custom Views with SwiftUI.

1.3 SwiftUI is Data Driven

When we say that SwiftUI is data driven, this is not to say that it is no longer necessary to handle events generated by the user (in other words the interaction between the user and the app user interface). It is still necessary, for example, to know when the user taps a button and to react in some app specific way. Being data driven relates more to the relationship between the underlying app data and the user interface and logic of the app.

Prior to the introduction of SwiftUI, an iOS app would contain code responsible for checking the current values of data within the app. If data is likely to change over time, code has to be written to ensure that the user interface always reflects the latest state of the data (perhaps by writing code to frequently check for changes to the data, or by providing a refresh option for the user to request a data update). Similar problems arise when keeping the user interface state consistent and making sure issues like toggle button settings are stored appropriately. Requirements such as these can become increasingly complex when multiple areas of an app depend on the same data sources.

SwiftUI addresses this complexity by providing several ways to bind the data model of an app to the user interface components and logic that provide the app functionality.

When implemented, the data model publishes data variables to which other parts of the app can then subscribe. Using this approach, changes to the published data are automatically reported to all subscribers. If the binding is made from a user interface component, any data changes will automatically be reflected within the user interface by SwiftUI without the need to write any additional code.

1.4 SwiftUI vs. UIKit

With the choice of using UIKit and SwiftUI now available, the obvious question arises as to which is the best option. When making this decision it is important to understand that SwiftUI and UIKit are not mutually exclusive. In fact, several integration solutions are available (a topic area covered starting with the chapter entitled Integrating UIViews with SwiftUI).

The first factor to take into consideration during the decision process is that any app that includes SwiftUI-based code will only run on devices running iOS 13 or later. This means, for example, that your app will only be available to users with the following iPhone models:

  • iPhone 11
  • iPhone 11 Pro
  • iPhone 11 Pro Max
  • iPhone Xs
  • iPhone Xs Max
  • iPhone XR
  • iPhone X
  • iPhone 8
  • iPhone 8 Plus
  • iPhone 7
  • iPhone 7 Plus
  • iPhone 6s
  • iPhone 6s Plus
  • iPhone SE

Apple reported on October 15, 2019 that, based on App Store measurements, 50% of all iPhone devices were running iOS 13, a percentage that will continue to increase with the passage of time.

If supporting devices running older versions of iOS is not of concern and you are starting a new project, it makes sense to use SwiftUI wherever possible. Not only does SwiftUI provide a faster, more efficient app development environment, it also makes it easier to make the same app available on multiple Apple platforms (iOS, iPadOS, macOS, watchOS and tvOS) without making significant code changes.

If you have an existing app developed using UIKit there is no easy migration path to convert that code to SwiftUI, so it probably makes sense to keep using UIKit for that part of the project. UIKit will continue to be a valuable part of the app development toolset and will be extended, supported and enhanced by Apple for the foreseeable future. When adding new features to an existing project, however, consider doing so using SwiftUI and integrating it into the existing UIKit codebase.

When adopting SwiftUI for new projects, it will probably not be possible to avoid using UIKit entirely. Although SwiftUI comes with a wide array of user interface components, it will still be necessary to use UIKit for certain functionality such as map and web view integration.

In addition, for extremely complex user interface layout designs, it may also be necessary to use Interface Builder in situations where layout needs cannot be satisfied using the SwiftUI layout container views.

1.5 Summary

SwiftUI introduces a different approach to app development than that offered by UIKit and Interface Builder. Rather than directly implement the way in which a user interface is to be rendered, SwiftUI allows the user interface to be declared in descriptive terms and then does all the work of deciding the best way to perform the rendering when the app runs.

SwiftUI is also data driven in that data change drives the behavior and appearance of the app. This is achieved through a publisher and subscriber model. This chapter has provided a very high-level view of SwiftUI. The remainder of this book will explore SwiftUI in greater depth.

SwiftUI Essentials – iOS Edition

  1. An Introduction to Swift Structures
  2. An Introduction to Swift Property Wrappers
  3. An Overview of SwiftUI
  4. Using Xcode in SwiftUI Mode
  5. The Anatomy of a Basic SwiftUI Project
  6. Creating Custom Views with SwiftUI
  7. SwiftUI Stacks and Frames
  8. Working with SwiftUI State, Observable and Environment Objects
  9. A SwiftUI Example Tutorial
  10. SwiftUI Observable and Environment Objects – A Tutorial
  11. SwiftUI Stack Alignment and Alignment Guides
  12. SwiftUI Lists and Navigation
  13. A SwiftUI List and Navigation Tutorial
  14. Building Tabbed Views in SwiftUI
  15. Building Context Menus in SwiftUI
  16. Basic SwiftUI Graphics Drawing
  17. SwiftUI Animation and Transitions
  18. Working with Gesture Recognizers in SwiftUI
  19. Integrating UIViews with SwiftUI
  20. Integrating SwiftUI with UIKit