Hi! It’s Renat from Apphud – the best tool to analyze iOS subscriptions. As you know Apple at WWDC 2019 has announced their new SwiftUI framework which supposed to replace (or not?) existing UIKit. SwiftUI lets developers to write code in declarative way. That shortens amount of code a lot!
Apple already has published a great series of articles about SwiftUI with code and examples. I, instead, will try to make this article in Question-Answer format. Alright, let’s go.
Before you begin
To work with SwiftUI you need to have latest Xcode 11. You have to be a registered Apple Developer as well. Having latest macOS Catalina is not required but recommended (Canvas will not work without it).
Okay, now in Xcode 11 create a new project and make sure “Use SwiftUI” is checked.
Questions and answers
Where has Interface Builder gone?
In SwiftUI you don’t need Interface Builder anymore – there’s new Canvas to replace it, an interactive interface editor which has very close connection with code. While you write the code Canvas automatically generates a visual interpretation and vice versa. Very useful and safe. For example, your app won’t crash if you forgot to remove or update @IBoutlet
connection on some changed property. In this article I will skip Canvas explanations and focus only on code.
Are there any changes in app launch with SwiftUI?
Yes, now top-level object in view hierarchy is not UIWindow
, but new UIScene
(or it’s child UIWindowScene
). Window is being added to the scene now and these changes are related not to SwiftUI but to iOS 13 itself.
After creating the project you will notice files AppDelegate
, SceneDelegate
and ContentView
. SceneDelegate
– is a delegate class of UIScene
object which supposed to manage scenes in the app. Methods look very similar to AppDelegate’s
methods, aren’t they?

In delegate method (scene: willConnectTo: options:
) a new window is being created with UIHostingController
as it’s root controller. You will see that UIHostingController
is being initialized with ContentView
as it’s root view.
So ContentView
– is our “home” page. And we will code in this file.
What are the differences between View
and UIView
?
Let’s explain ContentView
contents word by word. First, ContentView
is a struct
. Very good beginning! This expands developing opportunities. Next, View
is a protocol now! And a very simple protocol. The only method that needs to be implemented – is a body
property. All your subviews must be inside body
and they must have their own body
as well.
struct ContentView: View { var body: some View { Text("Hello, world!") } }
What is “body”?
Body
– is our primary container which includes all our subviews. You can think it’s like a body
in an html page where ContentView
is a page itself. However in this case our body
must have just one some View
– any class that conforms to View
protocol.
Opaque return types and what is some
keyword?
some TypeName
– is a new thing in Swift 5.1 which is called opaque return type. Opaque return types are used in cases where you need to return an object with no matter which class but conformable to given type. In our case we have to return something that conforms to View
protocol. This can be Text
, Image
or our custom class. But again: just one instance of it. Otherwise, compiler will throw an error.

What is the syntax inside {} braces and where is addSubview
?
In Swift 5.1 there’s a new feature called Function Builders which allows grouping objects in a declarative way for a certain operations. This looks like a closure that returns an array but without any commas and return
keyword.
This mechanism has become a primary feature in SwiftUI. A special builder which creates and adds subviews in a declarative way has been called ViewBuilder
. All the hard thing is being processed behind the scenes – you just write your views inside a closure block with a new line. SwiftUI automatically adds them to a view hierarchy and makes optimization for you.
// Announcing ViewBuilder in SwiftUI header file @available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *) @_functionBuilder public struct ViewBuilder { /// Builds an empty view from an block containing no statements, `{ }`. public static func buildBlock() -> EmptyView /// Passes a single view written as a child view (e..g, `{ Text("Hello") }`) through /// unmodified. public static func buildBlock<Content>(_ content: Content) -> Content where Content : View }
How to add Views and controls to view hierarchy?
Creating views is super easy: you write them inside braces and modify appearance with functions. These functions are always return view
so you can make a chain of functions separated with dots. The hard thing is to learn new syntax and the list of available modifiers and controls. And of course Xcode 11 Beta is buggy (as usual) so syntax highlighting might not work sometimes.
var body: some View { VStack{ Text("World Time").font(.system(size: 30)) Text("Yet another subtitle").font(.system(size: 20)) } }
Not all the views have their analogs in SwiftUI. Some of them must be implemented in other way. Here is a list of views with their analogs:
UITableView
→List
UICollectionView
doesn’t have an analog ?UILabel
→Text
UITextField
→TextField
UIImageView
→Image
UINavigationController
→NavigationView
UIButton
→Button
UIStackView
→HStack
/VStack
UISwitch
→Toggle
UISlider
→Slider
UITextView
doesn’t have an analog ?UIAlertController
→Alert
/ActionSheet
UISegmentedControl
→SegmentedControl
UIStepper
→Stepper
UIDatePicker
→DatePicker
How to navigate between screens?
Instead of navigation controller you should use NavigationView
. Wrap your code inside the braces and add an action to push the new view. This may be a tap on the list’s row (ex UITableView
) or a button tap.
// An example of pushing a DetailView var body: some View { NavigationView { Text("World Time").font(.system(size: 30)) NavigationLink(destination: DetailView() { Text("Go Detail") } } }
To present a screen modally you should use .sheet
construction like this:
Button(action: { print("Button Pushed") self.show_modal = true }) { Text("Present Modal") }.sheet(isPresented: self.$show_modal) { ModalView() }
As being said, your body
must return some view, which can be any class. It means that you can even push a Text
or an Image
!
How to position views on screen?
Generally, you should use stacks. All the views depend on each other now. You can align your views horizontally (HStack
), vertically (VStack
) or above each other (ZStack
). You can also use ScrollView
and ListView
, add padding
and even set frame
. However, in SwiftUI frame
works in a different manner. This will be explained in the next article.
By combining all these containers you can make quite a big tree of views. So you may ask about performance in this case. Don’t worry: SwiftUI is optimized in such way that the use of stacks doesn’t hit performance. It is said in this video from WWDC (starting at 15:32).
var body: some View { NavigationView { VStack { NavigationLink(destination: LargeView(timeString: subtitle)) { Text("See Fullscreen") } Text("World Time").font(.system(size: 30)) } } }
How to show a navigation bar?
Wrapping your view hierarchy in a NavigationView{}
won’t be enough. To show a navigation bar you should add a navigation bar title.
NavigationView { VStack{}.navigationBarTitle(Text("World Time"), displayMode: .inline) }
Keep in mind that navigationBarTitle
modifier is added not to NavigationView
but to it’s child. DisplayMode is a parameter that controls navigation bar’s style: large or default.
Is there analog to viewDidLoad
?
Yes, it is. You can use onAppear{}
modifier and add your code inside the closure. This is similar to Javascript. This modifier can be added to any view. In the example below an http-request is being sent:
struct ContentView : View { @State var statusString : String = "World Time" var body: some View { NavigationView { VStack { NavigationLink(destination:DetailView()) { Text("Go Detail") } Text(statusString).font(.system(size: 30)) }.onAppear { self.loadTime() } } } func loadTime(){ NetworkService().getTime { (time) in if let aTime = time { self.statusString = "\(aTime.date())" } } } }
We have written a
loadTime
function which sends an http-request to get the time from a server. We won’t focus onNetworkService
class, you can download the source code from the link at the end of this article.
You will notice var statusString
property which has @State
parameter. What does it mean?
Property wrappers or what is @State
?
Swift 5.1 has introduced a new feature called property wrappers (or property delegates). This is a set of special property attributes which add some amazing functionality to our properties. In SwiftUI property wrappers are used to update or bind one of your view’s state with your property. For example, a Toggle
’s value. By using property wrappers you will be able to get changed values of your views without having to write protocols, functions, or even notification center!
@State
attribute lets us to read some view’s value without additional code. In an example above we are updating a text on the screen automatically when statusString
changes.
In a new example below we bind properties between Toggle
’s state and our own property by using $
sign.
// When Toggle’s value changes it also changes our property value. struct DetailsView: View { @State var changeToggle: Bool var body: some View { Toggle(isOn: $changeToggle) { Text("Change Toggle") } } }
Property wrappers are very important in SwiftUI and in Swift 5.1 itself. It is quite a big theme so it will need a separate article. You can watch these nice videos from WWDC: this one (starting at 37th minute), this (starting at 12th minute), and this (starting at 19th minute).
Can I add views at a runtime?
Not exactly. You can’t add a subview at anytime because SwiftUI is declarative framework. And it renders a whole view. But you may add conditions inside a body and reload the view when these conditions change. In this example we use @State — if
paired with isTimeLoaded
property.
struct ContentView : View { @State var statusString : String = "World Time" @State var isTimeLoaded : Bool = false var body: some View { NavigationView { VStack { if isTimeLoaded { addNavigationLink() } Text(statusString).font(.system(size: 30)).lineLimit(nil) }.navigationBarTitle(Text("World Time"), displayMode: .inline) }.onAppear { self.loadTime() } } func addNavigationLink() -> some View { NavigationLink(destination: Text("124!!!")) { Text("Go Detail") } } func loadTime(){ NetworkService().getTime { (time) in if let aTime = time { self.statusString = "\(aTime.date().description(with: Locale.current))" self.isTimeLoaded = true } } } } struct DetailView : View { var timeString : String var body : some View { Text(timeString).font(.system(size: 40)).lineLimit(nil) } }
BTW in addNavigationLink
you may notice there is no return
keyword. It’s a new feature in Swift 5.1. You can now omit the return
keyword in a single expression functions.
Conclusion
I have covered just a small piece of questions about SwiftUI. Hope this article will help a beginners to understand a new framework. And one last question: should I learn UIKit? Of course, yes. UIKit is still a primary framework for iOS and it continues to improve. Furthermore, many SwiftUI classes are just wrappers around UIKit. And there are no libraries, pods for SwiftUI yet. All has to be done by yourself.
So better to learn both UIKit and SwiftUI – thus you will be a more valuable developer.
Project source code can be downloaded here.
After approval
What to do after successful approval? Add Apphud SDK to your app and find out how much you earn in real-time with many more features.
Apphud is iOS subscriptions analytics tool. One of its main features is sending subscriptions-based notifications to your favorite analytics tool. Apphud fills the gap in sending events from Apple, practicing a hybrid approach: we use both Subscriptions Status Polling and Apple Subscriptions Notifications to collect the most accurate information. All you need if to integrate our lightweight SDK and setup integrations. Apphud will do the rest.