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.
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).
(3) Delete “Scene Configuration” in “Application Scene Manifest”.
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.