2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

SwiftAdvent Calendar 2021

Day 16

FlutterでシステムのライフサイクルをSwiftで

Last updated at Posted at 2021-12-16

はじめに

ここ最近はFlutterをやっていて、業務でSwiftでプログラミングはほとんどやっていないですが、

やっぱりif-letとか恋しくなります。(アンラップの処理がDartめんどいというか自作しないといけてない)

そんなこんなですが、ネイティブ側とやりとりすることもあり、Flutterで書いてたSwiftの部分を備忘録的に載せていきます。

AppDelegateでアプリのライフサイクルを通知

Flutterをやっていると言ってもまだ4ヶ月くらい(Flutter開発を4ヶ月ほどやっての振り返り)なのでわからないことだらけです。

Flutterでもライフサイクルの概念はあり、全く同じではないのですがモバイル開発をしていればそんなに問題はないのですが、画面のライフサイクルというよりアプリ全体のシステムのライフサイクルで苦戦しました。

アプリ復帰時、アプリをバックグラウンドに移行したときにハンドリングできるライフサイクルメソッドは提供されていますが、僕たちのやり方が良くないのかその画面のStateが生きているときしかやってくれないような動きでした。
なので、下タブありのアプリだったんですが、Aという画面で実装してもBという画面で実装していなければ、Bの画面でバックグラウンドに移行すると処理に入ってくれませんでした。

なので、常に生きているというかAppDelegateのようなアプリを管理する画面を下に設定して、システムのライフサイクルはネイティブのを利用しました。

EventChannelを利用

main.dartがAppDelegateのようなものでそこでやるのが普通なのかなと思うのですが、root.dartという画面でハンドリングしました。

やり方としてはEvent Channelというものを利用してアプリ側からapplicationDidBecomeActiveなどでFlutter側に処理に入ったことを通知してあげる感じです。


import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    var eventSink: FlutterEventSink?
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?

    ) -> Bool {

        guard let controller: FlutterViewController = window?.rootViewController as? FlutterViewController else {
            GeneratedPluginRegistrant.register(with: self)
            return super.application(application, didFinishLaunchingWithOptions: launchOptions)
        }

        let methodChannel = FlutterMethodChannel(name: "jp.co.hoge/nativeStorage",
                                                 binaryMessenger: controller.binaryMessenger)

        let eventChannel = FlutterEventChannel(name: "jp.co.hoge/nativeLifeCycle", binaryMessenger: controller.binaryMessenger)

        eventChannel.setStreamHandler(self)

        methodChannel.setMethodCallHandler({
            (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in

            if call.method == "getUserLocal" {
                self.getUserLocal(result: result)
                return
            } else if call.method == "getUsers" {
                self.getUsers(result: result)
                return
            } else {
                result(FlutterMethodNotImplemented)
                return
            }
        })

        GeneratedPluginRegistrant.register(with: self)
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }

    // アプリ起動時と復帰時に呼ばれる(didFinishLaunchingWithOptions, applicationWillEnterForegroundの後続処理)
    override func applicationDidBecomeActive(_ application: UIApplication) {
        self.clearNotificationBadges(application)
    }

    override func applicationWillEnterForeground(_ application: UIApplication) {
        // flutter側にアプリ復帰を通知
        self.eventSink?("onResume")
    }

    override func applicationDidEnterBackground(_ application: UIApplication) {
        // flutter側にアプリ復帰を通知
        self.eventSink?("onPause")
    }

    private func getUsers(result: FlutterResult) {

        guard let users = UserDefaults.standard.string(forKey: "users") else {
            result("")
            return
        }
        result(users)
    }


    private func getUserLocal(result: FlutterResult) {
        guard let userLocal = UserDefaults.standard.string(forKey: "userLocal") else {
            result("")
            return
        }

        result(userLocal)
    }

    // 通知バッジ削除
    private func clearNotificationBadges(_ application: UIApplication) {
        application.applicationIconBadgeNumber = 0
        UNUserNotificationCenter.current().removeAllDeliveredNotifications()
    }
}

extension AppDelegate: FlutterStreamHandler {

    func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
        // iOSでchannel設定成功であることを通知
        // Flutter側で設定成功通知を受け取っての処理は行っていない
        self.eventSink = events
        self.eventSink?("iOS setStreamHandler Success")
        return nil

    }

    func onCancel(withArguments arguments: Any?) -> FlutterError? {
        return nil
    }
}

Method Channelとかとも混ざってしまっていますが、こんな感じです。
具体的な説明は省きますが、AppDelgateにFlutterStreamHandlerを継承してEventSinkというのを使うと通知ができます。

ここでは

override func applicationWillEnterForeground(_ application: UIApplication) {
        // flutter側にアプリ復帰を通知
        self.eventSink?("onResume")
    }

のようにStringでFlutter側でライフサイクルの種類を通知しています。
めんどくさがらずにenumとかに定義してやりとりしたほうが良いと思いますが、、
AndroidやFlutterのonResumeのタイミングとも厳密には違いますが、applicationWillEnterForeground
で送っています。

最後に

最近個人でも触れてないですが、なんとかSwiftの記事を書こうとしたものの無情にも時間はいつの間にかすぎていき結局ギリギリで今考えつくものを投稿する形になってしまいました。。

SwiftっちゃSwiftだけど!みたいなご指摘あると思うのですが優しい目で見てくださると嬉しいです(白目)

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?