Hey everyone! How’s it going? The idea of this article is to provide a basic guide on how to create a project using the ViewCode approach.
If you’re not familiar with what ViewCode is, I’ll explain a bit below.
What problem does it solve?
The storyboard pattern, used to create interfaces in Xcode, is very cool and intuitive. However, when working in a team, this approach can cause a lot of headaches.
Basically, screens created with storyboard have an xml file that stores everything we insert or edit. Since these changes are not made programmatically, they can lead to many conflicts when working in a team.
There are other ways to avoid this problem, and one of them is using ViewCode. This approach involves creating the screen elements programmatically, via code, without relying on Interface Builder and storyboards.
What about SwiftUI?
SwiftUI is one of the alternatives to solve this problem. It brings a declarative language for creating screens and components, with a very interesting Live Preview. If you’ve used Jetpack Compose or Flutter before, you’ll notice some similarities.
However, SwiftUI is only available starting from iOS 13. Depending on your app’s target audience, this could be a limitation, as devices like the iPhone 6, 6 Plus, and earlier models won’t have access. You can check the minimum and maximum supported versions for Apple devices here.
Let’s get to work
01. Creating the project
To start, let’s open Xcode and create a new project. In this example, I selected the iOS/App option.
Enter the project name and ensure the options are configured as follows:
Inferface: Storyboard
Language: Swift
Choose where to save the project and that’s it! Now we can start coding.
During the creation of this article, I used Xcode version 15.3 (15E204a), and the latest available iOS version is 17.4.
Our project will look like this once Xcode opens it:
If you unchecked the “Include Tests” option, the Tests and UITests folders will not be present. But don’t worry, we won’t be using them in this article, and they can be created later if needed.
With the project open, we see that it has two .storyboard
files: LaunchScreen.storyboard
and Main.storyboard
. The LaunchScreen
is the screen displayed while the operating system loads resources for our app to function correctly. The Main
is the main screen of our app.
The file that interests us at this point is Main.storyboard
.
02. Initial changes
Delete the Main.storyboard
file.
Click on “Move to trash”.
Now, let’s access the Target Properties settings of our application.
With the settings open, delete the following lines:
“Main storyboard file base name”
e
"Application Scene Manisfest" > "Scene Configuration" > "Window Application Session Role" > "Item 0" > "Storyboard Name"
After making these changes, let’s access the SceneDelegate to configure the entry point of our application since we removed the storyboard references.
We’ll make a few small changes to this file.
- Remove some comments and create our
windowScene
variable. It will be used to configure which controller/screen will be rendered.
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
}
- Configure the controller that will be displayed when the app starts::
let safeWindow = UIWindow(windowScene: windowScene)
safeWindow.frame = UIScreen.main.bounds
safeWindow.rootViewController = ViewController()
safeWindow.makeKeyAndVisible()
window = safeWindow
At this stage, we create our window based on the scene provided by the guard let windowScene
. Then, we configure the screen to occupy the entire available space and define the controller that will be used. Finally, we assign our safeWindow
to the window variable.
This ViewController
is the one created during the initial project setup
- Running the application
Now, by selecting one of the emulators and running the application (either by clicking the play button at the top or using the CMD + R command), we should see the following behavior:
What’s happening is:
- The
LaunchScreen.storyboard
file is displayed. - The view of our controller is rendered. And since it has nothing in it, the screen appears blank.
If we access our ViewController
and set a background color for the view, we’ll already see the change when running the project again:
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
}
Now we’re all set to start building our application programmatically.
Creating elements with ViewCode
Creating screen elements is done entirely programmatically. But how do we do that?
Example:
private lazy var label: UILabel = {
let label = UILabel()
label.text = "Hello World"
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
We can observe the following:
- We use the
private
keyword to ensure that no one outside our class can access this information. -
lazy
is used so the variable only enters memory when it’s utilized. - The
{}()
approach is a function that self-executes and returns the variable’s type element. -
translatesAutoresizingMaskIntoConstraints = false
to prevent automatically generated constraint configurations from conflicting with the ones we manually configure.
It’s important to note that every element created programmatically needs the translatesAutoresizingMaskIntoConstraints = false
configuration. Otherwise, conflicts may occur, and the elements might not behave as expected.
Now, we need to add this element to the screen.
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(label)
}
We change the background of the view attached to our controller to white and add our label inside it.
To position our element:
NSLayoutConstraint.activate([
label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
label.centerYAnchor.constraint(equalTo: view.centerYAnchor)
])
Then run the application, and voilà:
Conclusion
As mentioned at the beginning, this is a basic guide for creating projects using ViewCode. There are several improvements we can make, such as creating separate view files—after all, it’s not ideal to create these elements directly in our controller. But I wanted to keep this article concise and to the point. In the next one, we’ll explore element creation on the screen in more depth.
That’s it for today, folks. Thanks, and see you next time!
Let me know if you need any further adjustments!