Change iOS app lifecycle from UIKit to SwiftUI

As the versions of iOS and macOS have increased, SwiftUI features have also increased, and I think it is gradually becoming possible to use SwiftUI for the parts that can be created with SwiftUI, and use it in conjunction with UIKit.

This article describes how to change the application lifecycle from UIKit to SwiftUI.

TOC

Implementing Views

The structure of the application before migration is that the Main.storyboard file is loaded and the view of the ViewController class is displayed. We will implement a SwiftUI view that displays this view.

Add ContentView.swift and enter the following code.

import SwiftUI

struct ContentView: UIViewControllerRepresentable {
    typealias UIViewControllerType = ViewController

    func makeUIViewController(context: Context) -> ViewController {
        
        // Load Main.storyboard
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        
        // Instantiate ViewController
        guard let viewController = storyboard.instantiateInitialViewController() as? ViewController else {
            fatalError("Couldn't instanciate a ViewController class.")
        }
        
        return viewController
    }
    
    func updateUIViewController(_ uiViewController: ViewController, context: Context) {
        
    }
    
}

To display a UIKit view controller as a SwiftUI view, you can use UIViewControllerRepresentable. We want to display a ViewController, so we implement a view which displays ViewController and inherited from UIViewControllerRepresentable class.

In the work that follows, Main.storyboard is changed so that it is not loaded, so it is manually loaded in the ContentView.makeUIViewController() method.

Add SwiftUI’s App adopted type

Add a SwiftUI App adopted type. Here we have decided to name it SampleApp, so add SampleApp.swift and enter the following code.

// SampleApp.swift
import SwiftUI

@main
struct SampleApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Migration of entry points

Migrate the entry point of the app from AppDelegate (the adopted class of UIApplicationDelegate) to App of SwiftUI.

First, remove @main from AppDelegate. In this article, it is commented out so that you can see what it looked like before the change, but you can delete it.

// AppDelegate.swift
import UIKit

// Delete it
//@main
class AppDelegate: UIResponder, UIApplicationDelegate {
// ... omission
}

Edit the Info.plist

Edit Info.plist to change Main.storyboard so that it is not automatically loaded when the app starts. Do the following.

STEP
Open the target’s settings and display the “Info” tab.
STEP
Remove “Main storyboard file base name” and “Principal class” (if available).
Remove "Main storyboard file base name"
Remove “Main storyboard file base name”
STEP
Delete “Scene Configuration” in “Application Scene Manifest”.
Remove "Scene Configuration"
Remove “Scene Configuration”

Instantiate the AppDelegate

There are some processes that can only be implemented in AppDelegate, so we need to ensure that AppDelegate is properly used in the SwiftUI lifecycle as well. To do so, use the UIApplicationDelegateAdaptor and add the following code to SampleApp.swift.

// SampleApp.swift
import SwiftUI

@main
struct SampleApp: App {
    @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

Instantiation of the AppDelegate class is done by SwiftUI. Instances can be obtained via EnvironmentObject. To do so, the AppDelegate class must adopt to the ObservableObject protocol. Add code to AppDelegate.swift as follows.

// AppDelegate.swift

import UIKit

class AppDelegate: UIResponder, UIApplicationDelegate, ObservableObject {
// ... omission
}

Change scene processing

Since “Scene Configuration” in Info.plist has been deleted, change the processing of the corresponding part of AppDelegate. Delete the following two methods. If you need them for the convenience of your application, check the behavior and leave them as they are.

  • application(_:configurationForConnecting:options:)
  • application(_:didDiscardSceneSessions:)

Running Test

Test the application. If successful, the ViewController scene in the Main.storyboard will be displayed.

The "ViewController" scene is displayed.
The “ViewController” scene is displayed
Let's share this post !

Author of this article

Akira Hayashiのアバター Akira Hayashi Representative, Software Engineer

I am an application developer loves programming. This blog is a tech blog, its articles are learning notes. In my work, I mainly focus on desktop and mobile application development, but I also write technical books and teach seminars. The websites of my work and books are here -> RK Kaihatsu.

TOC