[SwiftUI] SwiftUI から AppDelegate にアクセスする方法

SwiftUI2021

UIApplicationDelegateAdaptor を使って作成した delegate class を environmentObject としてアクセスする方法を説明します。

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Monterery beta 3
  • Xcode 13 beta3
  • iOS 15 beta

AppDelegate

UIApplicationDelegateAdaptor を使って、ScenePhase では対応できないイベント対応を、自前の UIApplicateionDelegate に準拠したクラスで対応しているケースがまだまだ多いと思います。

特定のイベントに対応するだけであれば、問題ありませんが、ケースによっては、AppDelegate にデータを保持させているケースがあり、SwiftUI の View とのデータ共有が複雑になりがちで、相互にアクセスすることが必要なケースもあります。

UIApplication.shared.delegate へのアクセス

UIKit であれば、UIApplication.shared.delegate でアクセスできます。

ですが、SwiftUI で @UIApplicationDelegateAdaptor を使用して設定した AppDelegate は、UIApplication.shared.delegate には、セットされていません。

ですので、UIApplication.shared.delegate.... とアクセスしても、期待するデータを取得することはできません。

SwiftUI の ビューから AppDelegate へアクセスする方法

AppDelegate を ObesrvableObject に準拠させる

NSObject, UIApplicationDelegate に準拠させていると思いますが、追加で、ObservableObject にも準拠させます。

ObservableObject は、対象が class であることを要求するだけですので、準拠させるだけで それ以上のコードを必要としません。

必要に応じて、@Published

データの変更を外部に通知したいのであれば、プロパティ定義に @Published を付与します。

アクセスするビューで environmentObject 定義する

アクセスしたいビューで、以下のように定義することで、アクセスが可能となります。


struct ContentView: View {
    @EnvironmentObject var appDelegate: MyAppDelegate   // アクセスしたいビューで @EnvironmentObject 宣言

    var body: some View {
        VStack {
            Text("Value from AppDelegate \(appDelegate.appDelegateData)")  // アクセス(1)
                .padding()
            Button(action: {
                appDelegate.appDelegateData += 1                            // アクセス(2)
            }, label: {
                Text("appDelegateData ++")
            })
            .padding()
        }
        .padding()
    }
}

@main
struct AppDelegateAsEnvironmentApp: App {
    @UIApplicationDelegateAdaptor(MyAppDelegate.self) var appDelegate
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

class MyAppDelegate: NSObject, UIApplicationDelegate, ObservableObject {
    @Published var appDelegateData: Int = 19
}

変数に @Published を付与して宣言しているので、変数が変更されると画面が更新されます。

MEMO
Apple のドキュメントをよ〜〜〜〜く読むと見つかります。

実は、UIApplicationDelegateAdaptor には、同じ引数をとる init が2つ定義されています。渡されるクラスが、Observable に準拠しているかどうかで振る舞いが変わるようになっています。

NSObject と UIApplicationDelegate のみに準拠しているクラスを使用する init は、こちら

NSObject, UIApplicationDelegate, ObservableObject に準拠しているクラスを使用する init は、こちら

2つめの init のドキュメントに、 note として、今回説明したことが説明されています。

UIKit と合わせて説明していますが、macOS での NSApplicationDelegateAdaptor でも同様の説明がされています。

まとめ:SwiftUI から AppDelegate にアクセスする方法

SwiftUI から AppDelegate にアクセスする方法
  • UIApplicationDelegateAdaptor を使って、AppDelegate 使用を設定する
  • AppDelegate に使用するクラスを ObservableObject に準拠させる
  • 使用したい SwiftUI の View で、@EnvironmentObject として定義してアクセスする

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です