You're reading this because you're looking for an equivalent for UIKIt's viewDidLoad() method. Right? I have good news and I have bad news. The bad news is that SwiftUI doesn't define an equivalent for UIKit's viewDidLoad() method. The good news is that I may be able to clear a few things up for you about SwiftUI.

Understanding the Problem

The UIKit and SwiftUI frameworks are very different in some ways. They're both frameworks for building the user interface of your application. The key difference is that UIKit takes an imperative approach whereas SwiftUI takes a declarative approach. What does that mean?

If you opt for UIKit to build your application's user interface, then you explicitly create objects, UIView instances, to build the user interface. That isn't true if you choose for SwiftUI. A SwiftUI view merely describes what the user interface of your application should look like. SwiftUI uses that description to create the user interface of your application.

This fundamental difference has several side effects. A UIView instance has a clearly defined lifecycle. Your application can hook into that lifecycle through a collection of methods, such as UIView's layoutSubviews() and UIViewController's viewDidLoad(), viewWillAppear(_:), and viewWillDisappear(_:).

You may have noticed that a SwiftUI application doesn't have view controllers so you don't have access to methods such as viewDidLoad(). That's besides the point, though. What's important to understand is that, unlike a UIView instance, a SwiftUI view doesn't have a clearly defined lifecycle. As I mentioned earlier, SwiftUI uses the views you create to build the user interface, creating and destroying views when needed.

On Appear and On Disappear

While there is no equivalent for UIKit's viewDidLoad() method, there are a handful of modifiers you can use to be notified of several key events. The onAppear and onDisappear modifiers can be used to be notified when the view appears and disappears respectively. Let's take a look at an example.

import SwiftUI

struct RootView: View {
    var body: some View {
        TabView {
            FirstView()
                .tabItem {
                    Label("First", systemImage: "sun.max")
                }
            SecondView()
                .tabItem {
                    Label("Second", systemImage: "moon")
                }
        }
    }
}

We define a view with two tabs. The first tab displays FirstView. The second tab displays SecondView. Let's look at the implementation of FirstView.

struct FirstView: View {
    var body: some View {
        Text("First View")
    }
}

View Did Load Equivalent for SwiftUI Views

The FirstView struct displays a string. That's it. Let's add the onAppear and onDisappear modifiers. Both modifiers accept an action or a closure. The action of the onAppear modifier is executed before the view appears. The action of the onDisappear modifier is executed before the view disappears.

struct FirstView: View {
    var body: some View {
        Text("First View")
            .onAppear {
                print("On Appear")
            }
            .onDisappear {
                print("On Disappear")
            }
    }
}

Let's run the application. As expected, the action we passed to the onAppear modifier is executed. The action we passed to the onDisappear modifier is executed if we navigate to the second tab. When we navigate back to the first tab, the action of the onAppear modifier is executed. This behavior may remind you of UIViewController's viewWillAppear(_:) and viewWillDisappear(_:) methods.

Avoid the Initializer

You may have read that you can move the code you used to add to the viewDidLoad() method to the view's initializer. I strongly recommend to avoid that solution for several reasons. First, the initializer of a view is invoked before the view is rendered. Remember that SwiftUI decides when it creates a view and when it renders it. The initializer is therefore not a viable alternative for the viewDidLoad() method. Second, a view should be lightweight and cheap to create. It isn't a good idea to perform significant work in the initializer of a view.

Consider a Different Architecture

A view should be dumb and lightweight. This is true if you use UIKit to build your application's user interface, but it is even more true if you adopt SwiftUI. A user interface can consist of dozens or hundreds of views. You need to make sure those views are lightweight to ensure your application is snappy and performant.

This means you may want to consider architecting your application differently, moving code that isn't related to the user interface out of the view. The Model-View-ViewModel pattern is the architecture I use for every SwiftUI project. The view model is responsible for driving the view. It carefully hides the business logic from the view, keeping it dumb, focused, and lightweight. Take a look at Mastering MVVM with SwiftUI if you want to learn more about MVVM and SwiftUI.