Change the iOS app lifecycle from UIKit to SwiftUI

Newer versions of iOS and macOS have been released, and the features offered by SwiftUI have also expanded. As a result, it is 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 application’s structure 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.

Create a new ContentView.swift file and populate it with 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 render a UIKit view controller as a SwiftUI view, UIViewControllerRepresentable can be utilized. We want to display a ViewController, so we implement a view that displays ViewController and is inherited from the 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. Let’s name this SampleApp. Create a new file SampleApp.swift and add the following code.

// SampleApp.swift
import SwiftUI

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

Migration of entry points

Migrate the app’s entry point 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.

(1) Open the target’s settings and display the “Info” tab.

(2) Remove “Main storyboard file base name” and “Principal class” (if available).

Remove "Main storyboard file base name"
Remove “Main storyboard file base name”

(3) Delete “Scene Configuration” in “Application Scene Manifest”.

Remove "Scene Configuration"
Remove “Scene Configuration”

Instantiate the AppDelegate

Some processes can only be implemented in AppDelegate, so we must ensure that AppDelegate is properly used in the SwiftUI lifecycle. 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()
        }
    }
}

SwiftUI instantiates the AppDelegate class. You can obtain instances through EnvironmentObject. To do so, the AppDelegate class must adopt to the ObservableObject protocol. For example, 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

Run a test object application. If successful, the ViewController scene in the Main.storyboard will be displayed.

The "ViewController" scene is displayed.
The “ViewController” scene is displayed.

Authored Books

Let's share this post !

Author of this article

Akira Hayashi (林 晃)のアバター Akira Hayashi (林 晃) Representative(代表), Software Engineer(ソフトウェアエンジニア)

アールケー開発代表。Appleプラットフォーム向けの開発を専門としているソフトウェアエンジニア。ソフトウェアの受託開発、技術書執筆、技術指導・セミナー講師。note, Medium, LinkedIn
-
Representative of RK Kaihatsu. Software Engineer Specializing in Development for the Apple Platform. Specializing in contract software development, technical writing, and serving as a tech workshop lecturer. note, Medium, LinkedIn

TOC