Heads up... You're reading this book for free, with parts of this chapter shown beyond this point astext.
You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.
More than a framework, SwiftUI is a new paradigm for creating apps in the Apple ecosystem. These are exciting times. We’re in the middle of a journey towards a post Cocoa, Swift-native world. Until now, you didn’t need to diverge from Apple’s Cocoa Objective-C based patterns and Model-View-Controller, MVC, architecture. This was true even if you started to write
UIKit apps using Swift. SwiftUI brings a new set of building blocks, paradigms and patterns that are native to Swift. These new tools will help you, and other developers, be drastically more productive. And, more productive developers means better apps for users.
Note: This chapter assumes familiarity with SwiftUI and Combine concepts and terminology. To get hands on practice with SwiftUI and Combine, check out SwiftUI by Tutorials and Combine: Asynchronous Programming with Swift raywenderlich.com books.
This chapter will help you get your app’s architecture ready for SwiftUI. You’ll explore what SwiftUI means for app architecture and how SwiftUI is different than
UIKit. You’ll also get some advice for practicing, preparing and planning SwiftUI integration. You’ll walk through things you can change in your codebase today, even if you don’t plan on integrating SwiftUI for a while. After reading this chapter, you’ll be able to decide how and when you should start incorporating SwiftUI into your existing iOS apps.
What SwiftUI means for app architecture
Architecting features with SwiftUI is new, fun and exciting. SwiftUI brings new powerful patterns and tools for you to use when architecting the Swift code behind your app’s UI. You end up writing less architectural boilerplate and more domain specific logic. And, you get a lot of behaviors, such as
Publisher subscription, for free. SwiftUI enables you to break down your UI into small and reusable pieces that are much lighter than
UIViewController. You can decompose large portions of UI code into small encapsulated components without paying a performance penalty. All to say, SwiftUI helps you build well architected UI systems.
Note: SwiftUI is quite new, so establishing architecture best practices will take some time. The content in this edition is based on initial explorations with SwiftUI. Advice and best practices will evolve over time. I recommend keeping an eye on SwiftUI content written by developers that have had a chance to ship production apps using SwiftUI.
The architectural patterns that SwiftUI enables are based on some of the industry’s latest best practices and paradigms. This is great news, especially if you’ve already invested time in learning any of these paradigms such as functional reactive programming. You can now use these paradigms without fighting the UI framework. If you’ve ever tried applying non-Apple paradigms to
UIKit based iOS apps, you know how difficult it is to program against the framework. With SwiftUI, you won’t be fighting the UI framework when applying the latest thinking in UI architecture.
SwiftUI goes even further than the industry’s latest approaches. SwiftUI was clearly designed with UI architecture in mind. For example, view dependencies in SwiftUI are explicit. This makes architecting for SwiftUI incredibly satisfying because you can easily see all of a view’s dependencies when opening a SwiftUI file.
Today, SwiftUI is not a complete application framework. Currently, SwiftUI does not have application level APIs. Because of this, even when using SwiftUI, you’re still architecting within the context of
UIKit. You still use a
UIApplicationDelegate and you still need to give your root
UIWindow a root
UIViewController. This means if you want to use SwiftUI, you’ll be building mixed
UIKit and SwiftUI apps for the foreseeable future. Fortunately, incorporating SwiftUI into a
UIKit architecture is incredibly easy. This is awesome because you’ll be able to gradually adopt SwiftUI over time.
Because most developers who are interested in using SwiftUI will be working from an existing
UIKit iOS app, this chapter focuses on how to get an existing app’s architecture ready for SwiftUI.
Architecting with SwiftUI versus UIKit
SwiftUI expects and enables a different kind of architecture than
UIKit favors an Objective-C object-oriented imperative MVC approach where every type is a reference and every view is mutable. This is different from SwiftUI’s reactive functional approach where immutable value types are the norm. Architecting features using SwiftUI might feel strange at first. Don’t let this discourage you! Once you get the hang of it, architecting with SwiftUI is delightful. You’ll be able to build features much faster using SwiftUI compared to
When to start building with SwiftUI
SwiftUI is available on iOS 13 and above. Therefore, in order to use SwiftUI you’ll need to require your users to upgrade to iOS 13. Or, you’ll need to build the same features in both
UIKit and SwiftUI so that you can ship your app to pre-iOS 13 devices. You can use
@available to fork control flow depending on SwiftUI’s availability. Because SwiftUI requires iOS 13, most teams won’t adopt SwiftUI extensively until the majority of their user base has upgraded to iOS 13.
Because architecting with SwiftUI is so different than architecting with
UIKit, it’s worth experimenting and practicing with SwiftUI in order to become familiar with SwiftUI’s mechanics. This section covers some of the architecture skills you can practice to gain that familiarity.
First, practice breaking down large SwiftUI views into smaller reusable views. Take an existing screen in one of your apps and build the UI using SwiftUI. Build the screen in one SwiftUI
View. Don’t worry about hooking your practice SwiftUI view into networking and persistence subsystems. Use
@State for any mutable UI state. And, for your first practice views, avoid
@EnvironmentObject because they add unnecessary complexity that isn’t critical to learning the architectural foundations of SwiftUI.
Understanding the view lifecycle
Once you’re comfortable breaking views down, you can get familiar with the
View lifecycle, i.e. when
struct values are created, destroyed and recreated. A great way to do this is to place log statements in
View initializers and in
var body computed property closures. Do this at all levels of the view hierarchy and notice what views get re-created in response to state changes. This will help you understand which views are best suited for holding onto references to longer lived objects such as dependency containers.
Connecting UIKit to SwiftUI
It’s also worth exploring how to bridge between
UIKit and SwiftUI. You can practice incorporating SwiftUI views through
UIViewController presentation APIs and through
UIViewController containment APIs. Try creating and providing an
@ObservableObject to a
View. You can also practice injecting
@Environment values and
@EnvironmentObjects by calling modifiers on a
View. This will help you build intuition for how to provide values and objects from
UIKit into SwiftUI.
Even if you’re not ready to ship features built with SwiftUI, there are changes you can make today to your existing codebase to prepare for SwiftUI. Here are some ideas.
Migrating to value types
Take a look at your app’s data model types. SwiftUI works best when data models are designed as value types. As a matter of fact,
@State can only store values. If your data models are designed as reference types, consider refactoring them into
Moving state to state containers
While data model types themselves should be value types, you’ll most likely need a state container to hold values. State containers are just objects with value type properties. In
UIKit, you can use a
UIViewController, a view model or a Redux store as a state container. Once you’re ready to start building features with SwiftUI, you’ll need to decide whether you want to store your data model values in a
@State container or in an
@ObservableObject container. Refactoring your data model types to value types will help you easily use your existing data model within SwiftUI.
You’ll most likely be gradually adopting SwiftUI into your current codebase. You might not have enough time to ease SwiftUI integration by making broad sweeping changes across your entire codebase. In addition, SwiftUI does not provide all the functionality available in
UIKit. So it’s a good idea to plan your codebase’s SwiftUI adoption ahead of time.
Starting with new screens
When looking for things to build using SwiftUI, consider building new screens or components. You can incorporate SwiftUI components into existing view controllers via
UIViewController containment APIs and you can incorporate SwiftUI screens via
UIViewController presentation APIs. It’s a good idea to start here because building a single screen or component limits the amount of concepts you’ll need to be familiar with to build a well architected SwiftUI feature. You won’t have to design for things like navigation and dependency scopes. The lifetime of a screen or component is relatively easy to manage compared to a SwiftUI
View that can present and dismiss many screens.
Selecting a state management strategy
Once you know what to build with SwiftUI, you can start thinking about state management. If your SwiftUI screen or component is immutable and does not need to observe changes in state, you don’t need to worry about state management. You can simply use properties on the root
View and subviews to hold onto data model values. Or, if the data is global, you can extend
EnvironmentValues and provide the values as
@Environment values. Make sure to only do this for truly global values because any SwiftUI
View will be able to request any custom values added to
EnvironmentValues. If you do need to manage mutable state, you can use
Migrating existing screens
If you’re done shipping new screens or if you don’t have anything new to build, you can start planning how to migrate existing screens from
UIKit to SwiftUI. But first, you might be wondering whether you should re-write existing
UIKit code rather than plugging your existing
UIViews into SwiftUI via
UIViewRepresentable. This depends on your migration strategy. You could either take a bottom up approach or a top down approach.
- SwiftUI is available starting on iOS 13 and it requires a deployment target of iOS 13 or above.
- Even when using SwiftUI, you’re still architecting within the context of
- Because architecting with SwiftUI is so different than architecting with
UIKit, it’s worth experimenting and practicing with SwiftUI in order to become familiar with its mechanics.
- Even if you’re not ready to ship features built with SwiftUI, there are changes you can make today to your existing codebase to prepare for the future, such as migrating to value types and moving state to state containers.
- You’ll most likely be gradually adopting SwiftUI into your current codebase.
- When looking for things to build using SwiftUI, consider building new screens or components.
- You can take a bottom up or top down approach to migrating an app from
- If you can’t wait and would like to see some SwiftUI architecture code you can check out the source on Github at https://github.com/raywenderlich/swiftui-example-app-koober.
Where to go from here?
That wraps up getting started with SwiftUI architecture. But, there’s one more thing… We are in the process of rebuilding this book’s example app, Koober, entirely in SwiftUI. If you can’t wait and would like to see some SwiftUI architecture code you can check out the source on Github at https://github.com/raywenderlich/swiftui-example-app-koober. The example is not finished at the time of writing. However, you can follow along as we work towards completing the examples.