SwiftUI — How to setup a project
In this tutorial, we will take a small step back and when setting up a new project understand the entry point of our App, how all views are connected to each other and to our code and finally explain how SwiftUI works 😊
Also, you can watch the video tutorial for this article:
SwiftUI is the new UI framework developed by Apple. It provides a new API allowing you to write code that works on all Apple platforms. Yes you heard right! All Apple platforms: iOS, iPadOS, watchOS and tvOS!
Requirements
You need macOS Catalina and Xcode 11 Beta in order to have SwiftUI render the canvas (simulator) properly. → How To Install Catalina + Xcode 11 Beta
Writing our first SwiftUI code
Let me throw you into the cold water of that shiny new framework to give you a taste of its capabilities. How about we implement a TableView with SwiftUI?
import SwiftUIstruct Hero: Identifiable {
let id: UUID = UUID()
let name: String
}struct ContentView: View {
let heros = [
Hero(name: "Iron Man"),
Hero(name: "Thor")
Hero(name: "Spider-Man")
] var body: some View {
List(heros) { hero in
Text(hero.name)
}
}
}
Done. I’m not joking. There’s no hidden code or black magic happening. This is all it needs! And here’s what this code looks like:
Excited? But also a tiny bit intimidated? No worries it becomes intuitive pretty quick. First let‘s take a small step back and understand the entry point of the App, how all views are connected to each other and to our code and finally explain what this code here means 😊
Index
- Create a new project including SwiftUI
- Understand the entry point of the App
- Understand the canvas (Simulator) next to your code
- Define your SwiftUI View as the first View on App launch
- Where to go from here
1. Create a new project including SwiftUI
In Xcode-Beta just go ahead as usual and create a new project and select the Single View App:
Next, make sure that Use SwiftUI is checked before you finally determine where to create the project:
2. Understand the entry point of the App
What I like to do is to remove as much code and files as possible from a project to see what the bare minimum is to get it running. So we can step by step re-code everything we’ve deleted and by that understand the relation of it.
This is probably what you see right now:
Go ahead and delete (Move to Trash) both files AppDelegate.swift and ContentView.swift. Now go inside SceneDelegate.swift and add @UIApplicationMain at the top of the SceneDelegate class and let this class conform to UIApplicationDelegate — with that you are simply saying that this is the entry point of your application to start running your code. Finally, delete all the functions inside this class except of this one:
No fear — go and select for example the iPhone XR as a simulator and hit run!
It will launch your project and greet you with a working app! Yes it is a black screen hahah but what else should it show if we haven’t coded anything yet.
3. Understand the canvas (Simulator) next to your code
Go ahead and create a new Swift File:
Inside our new awesome file add the following code:
import SwiftUIstruct AwesomeView: View {
var body: some View {
Text("Hey! This is aawweesomee!")
}
}
We are defining a struct and when conforming to the protocol View we have one required implementation this protocol is asking for and that is the computed body property of type some View. This new Swift 5.1 keyword some means that the computed property can return anything when that something at least conforms to the View protocol. Which is true for Text. And with Swift 5.1 we also don’t have to add the return keyword anymore. The last line of a function or closure will be returned automagically.
This right here is a SwiftUI View. Our first SwiftUI View!
Let’s add some more code to trigger the fancy canvas that shows a live preview of every line of code that we are writing:
import SwiftUIstruct AwesomeView: View {
var body: some View {
Text("Hey! This is aawweesomee!")
}
}#if DEBUG
struct AwesomeView_Previews: PreviewProvider {
static var previews: some View {
AwesomeView()
}
}
#endif
I’ll explain in a second how this struct works — it’s nothing fancy I promise!
First, go on the top right corner click on that hamburger-like menu icon and click on the Editor and Canvas menu point:
Next click on Resume or on Try again depending on your canvas state:
This shows you a preview of all the Views that you return in the closure inside the AwesomeView_Previews struct. It’s only one View you are returning there. It’s an instance of our AwesomeView!
How does this struct work? How is this code connected to the canvas?
Xcode statically discovers types that conform to the PreviewProvider protocol in your app, and generates previews for each provider it discovers.
At the end you can call those structs whatever you want, having it named Whatever_Previews is just a convention provided by Apple to quickly see what this struct is for just conform to PreviewProvider to see the canvas.
Try editing your code on the left and see how the canvas reloads live 😍
4. Define your SwiftUI View as the first View on App launch
If you go and hit run to run the entire App you will still see a black screen. Let’s fix that. Conveniently the way how to define the first (root) View of your whole App is super similar to how you’d do it the old way back in the days. You know. Before SwiftUI. I remember those days as if it were last Monday.
So inside SceneDelegate.swift add the following code:
import UIKit
import SwiftUI@UIApplicationMain
class SceneDelegate: UIResponder, UIWindowSceneDelegate, UIApplicationDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { if let windowScene = scene as? UIWindowScene { // (1)
let window = UIWindow(windowScene: windowScene)
self.window = window // (2)
let vc = UIHostingController(rootView: AwesomeView())
window.rootViewController = vc // (3)
window.makeKeyAndVisible()
}
}
}
We will have to (1) instantiate a new UIWindow with the size of our phone screen that we get passed in here as a scene and assign it to the window property. We then (2) instantiate a UIHostingController which is a ViewController but it is capable of holding the new SwiftUI View. Here we pass in an instance of our AwesomeView that we want to have displayed as the first View. Finally, we will (3) make the window key and visible. Basically telling our app that this is our active window.
If you now run your App it will show your AwesomeView in the simulator 🥳
Question: How does Xcode know that the class SceneDelegate inside SceneDelegate.swift is the class to ask for the initial (root) View that your App should show on launch?
I’m glad you asked Watson! This is defined in the info.plist have a look:
The best way to understand how something works is to break and then fix it!
Let’s double click that $(PRODUCT_MODULE_NAME).SceneDelegate and rename it to $(PRODUCT_MODULE_NAME).martinlasek and run the app!
It will show you a black screen because the app launches but with no window let alone a View! Let’s go inside SceneDelegate.swift and rename the class to martinlasek as well — like so:
import UIKit
import SwiftUI@UIApplicationMain
class martinlasek: UIResponder, UIWindowSceneDelegate, UIApplicationDelegate { var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { window = UIWindow(frame: UIScreen.main.bounds)
let vc = UIHostingController(rootView: AwesomeView())
window?.rootViewController = vc
window?.makeKeyAndVisible()
}
}
Try running the App: it will work again! Because the class name matches!
That’s it! You made it! You successfully implemented the first SwiftUI App 🎉
5. Where to go from here
This article is to get you started — there is way more to come! You can follow me here on Medium as well as on Youtube, Instagram and Twitter to not miss out on all future Articles and Video Tutorials!