iOSアプリのライフサイクルをUIKitからSwiftUIに変更する

iOSやmacOSのバージョンが上がり、SwiftUIの機能も増えてきました。SwiftUIで作れる部分はSwiftUIを使い、UIKitと併用するということも徐々に出てきているのではないでしょうか?

この記事では、アプリのライフサイクルをUIKitからSwiftUIに変更する方法について解説します。

目次

ビューを実装する

移行前のアプリは、Main.storyboardファイルがロードされて、ViewControllerクラスのビューが表示されるという構造になっています。このビューを表示する、SwiftUIのビューを実装します。

ContentView.swiftを追加し、次のコードを入力します。

import SwiftUI

struct ContentView: UIViewControllerRepresentable {
    typealias UIViewControllerType = ViewController

    func makeUIViewController(context: Context) -> ViewController {
        
        // Main.storyboardをロードする
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        
        // ViewControllerをインスタンス化
        guard let viewController = storyboard.instantiateInitialViewController() as? ViewController else {
            fatalError("Couldn't instanciate a ViewController class.")
        }
        
        return viewController
    }
    
    func updateUIViewController(_ uiViewController: ViewController, context: Context) {
        
    }
    
}

UIKitのビューコントローラをSwiftUIのビューとして表示するには、UIViewControllerRepresentableを使います。ViewControllerを表示したいので、ViewControllerを表示するUIViewControllerRepresentableを継承したビューを実装しています。

この後の作業で、Main.storyboardはロードされないように変更するので、ContentView.makeUIViewController()メソッドで手動でロードしています。

SwiftUIのAppの適合タイプの追加

SwiftUIのAppの適合タイプを追加します。ここではSampleAppという名前にすることにしたので、SampleApp.swiftを追加し、次のようなコードを入力します。

// SampleApp.swift
import SwiftUI

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

エントリーポイントの移行

アプリのエントリーポイントを、AppDelegate (UIApplicationDelegateの適合クラス)からSwiftUIのAppに移行します。

まず、AppDelegate@mainを削除します。この記事では変更前の状態が分かるようにコメントアウトにしていますが、削除でOKです。

// AppDelegate.swift
import UIKit

// 削除する
//@main
class AppDelegate: UIResponder, UIApplicationDelegate {
// ... 省略
}

Info.plistの編集

Info.plistを編集して、アプリ起動時にMain.storyboardを自動的に読み込まないように変更します。次のように操作します。

(1) ターゲットの設定を開き、「Info」タブを表示します。

(2) 「Main storyboard file base name」と「Principal class」(もし有ったら)を削除します。

「Main storyboard file base name」を削除する
「Main storyboard file base name」を削除する

(3) 「Application Scene Manifest」内の「Scene Configuration」を削除します。

「Scene Configuration」を削除する
「Scene Configuration」を削除する

AppDelegateのインスタンス化

AppDelegateでしか実装できない処理もあるので、AppDelegateが適切にSwiftUIのライフサイクルでも使われるようにします。それには、UIApplicationDelegateAdaptorを使用します。SampleApp.swiftに次のようにコードを追加します。

// SampleApp.swift
import SwiftUI

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

AppDelegateクラスのインスタンス化はSwiftUIによって行われます。インスタンスはEnvironmentObject経由で取得できます。そのためには、AppDelegateクラスがObservableObjectプロトコルに適合する必要があります。次のように、AppDelegate.swiftにコードを追加します。

// AppDelegate.swift

import UIKit

class AppDelegate: UIResponder, UIApplicationDelegate, ObservableObject {
// ... 省略
}

シーンの処理を変更

Info.plistの「Scene Configuration」を削除したので、AppDelegateの対応する部分の処理を変更します。次の2つのメソッドを削除します。アプリの都合で必要という場合には、動作を確認して残してください。

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

動作テスト

動作テストを行います。成功すると、Main.storyboard内のViewControllerのシーンが表示されます。

「ViewController」のシーンが表示される
「ViewController」のシーンが表示される

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

アプリ開発が好きなアプリ開発者。このブログは学習メモを記事にしたテックブログです。仕事ではアプリ開発をメインに、技術書の執筆やセミナーの講師などもしています。業務や著書のサイトはこちらです→ アールケー開発

目次