web-dev-qa-db-ja.com

SWIFTでプッシュ通知を受信した後にどのView Controllerがロードされるかを制御する

プッシュ通知を受け取ってスワイプして開くと、VC欲しい。

だから私の質問は、どのようにVC欲しいですか?アプリが開いている場合は、VCをdidReceiveRemoteNotificationしかし、アプリが開いていない場合、またはバックグラウンドモードの場合はどうすればよいですか?

また、2つの異なるプッシュ通知があるため、2つの異なるVCの1つを移動する必要があります。プッシュ通知の違いを区別するにはどうすればよいですか?

ありがとう。

29
Henry Brown

Swift 4.2に更新

それが言われたように、applicationDidLaunchWithOptionsでリモート通知に登録したい:

 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    let pushSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
    UIApplication.shared.registerUserNotificationSettings(pushSettings)
    UIApplication.shared.registerForRemoteNotifications()
}

LockScreen/Backgroundから戻ったときにどのviewControllerにいるかを知る方法はありません。私がやっていることは、appDelegateから通知を送信することです。 remoteNotificationを受信すると、appDelegateのdidReceiveRemoteNotificationが呼び出されます。

 func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
    let notif = JSON(userInfo) // SwiftyJSON required 

通知に含まれるものに応じて、まずnilでないことを確認してから、この通知をキャッチするviewControllerによってキャッチされる通知を呼び出す必要があります。このように見えるかもしれませんが、例としてそれを取るだけです:

if notif["callback"]["type"] != nil{
    NotificationCenter.default.post(name: Notification.Name(rawValue: "myNotif"), object: nil)
    // This is where you read your JSON to know what kind of notification you received, for example :    

}

たとえば、メッセージ通知を受信し、トークンの有効期限が切れたためにログインしていない場合、通知は監視されないためView Controllerでキャッチされません。

次に、View Controllerで通知をキャッチする部分について説明します。 viewWillAppearで:

 override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    NotificationCenter.default.addObserver(self, selector: #selector(self.catchIt), name: NSNotification.Name(rawValue: "myNotif"), object: nil)
}

このオブザーバーを追加したので、このコントローラーで通知が呼び出されるたびに、関数catchItも呼び出されます。特定のアクションを実装するすべてのView Controllerに実装する必要があります。

func catchIt(_ userInfo: Notification){

    let prefs: UserDefaults = UserDefaults.standard
    prefs.removeObject(forKey: "startUpNotif")

    if userInfo.userInfo?["userInfo"] != nil{
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let vc: RedirectAppInactiveVC = storyboard.instantiateViewController(withIdentifier: "RedirectAppInactiveVC") as! RedirectAppInactiveVC
        self.navigationController?.pushViewController(vc, animated: true)
    } else {
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let vc: RedirectAppActiveVC = storyboard.instantiateViewController(withIdentifier: "RedirectAppActiveVC") as! RedirectAppActiveVC
        self.navigationController?.pushViewController(vc, animated: true)
    }
}

View Controllerを終了するときに通知の登録を解除することを忘れないでください。さもなければ、まだスタックにあるviewControllerが通知をキャッチして実行します)。したがって、viewWillDisappearでサブスクライブ解除することをお勧めします。

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillAppear(animated)
    NotificationCenter.default.removeObserver(self)
}

このようにして、必要なviewControllerをロードします。現在、すべてのケースを処理していません。アプリケーションをまだ開いていない場合はどうなりますか。明らかに、UIViewControllerはロードされておらず、いずれも通知をキャッチできません。 appDelegateのdidFinishLaunchingWithOptions:で通知を受け取ったかどうかを知りたい。私がやることは:

let prefs: UserDefaults = UserDefaults.standard
if let remoteNotification = launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] as? NSDictionary {
    prefs.set(remoteNotification as! [AnyHashable: Any], forKey: "startUpNotif")
    prefs.synchronize()
}

これで、リモート通知を使用してアプリケーションが開始されたことを示すプリファレンスを設定しました。アプリケーションで最初にロードする必要があるコントローラーでは、viewDidAppearで以下を実行することをお勧めします。

override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    let prefs: UserDefaults = UserDefaults.standard
    if prefs.value(forKey: "startUpNotif") != nil {
        let userInfo: [AnyHashable: Any] = ["inactive": "inactive"]
        NotificationCenter.default.post(name: Notification.Name(rawValue: "myNotif"), object: nil, userInfo: userInfo as [AnyHashable: Any])
    }
}

それが役に立てば幸い。また、ローカル通知で説明するためにGitHubリポジトリを作成しました: Local Notifications Observer Pattern (リモート通知と同様)。ルートビューコントローラーを使用して同様のロジックを実装できます Local Notifications Root Pattern .

これらの例は、単純に実装する方法を説明するためのものです。より大きなプロジェクトでは、内部的に同様のメカニズムを利用するコーディネーターなどのより複雑なアーキテクチャになります。

46
Swift Rabbit

@NickCatibの答えに加えて、アプリの実行中に通知を受信したかどうかを確認するには、その場合、フォアグラウンドまたはバックグラウンドでAppDelegateでこのメソッドを使用する必要があります。

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {


// You can determine your application state by
if UIApplication.sharedApplication().applicationState == UIApplicationState.Active {

// Do something you want when the app is active

} else {

// Do something else when your app is in the background


}
}
7
Fred Faust

アプリケーションを実行すると、applicationDidLaunchWithOptionsが呼び出されます。

UIApplication.sharedApplication().registerUserNotificationSettings ( UIUserNotificationSettings(forTypes: (UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound), categories: nil))



if( launchOptions != nil){
    var notificationDict: AnyObject? = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey]
    if(notificationDict != nil){
        handleNotification(notificationDict! as! [NSObject : AnyObject])
    }

}

ここには、基本的に通知からデータを抽出し、その情報を使用して対応するコントローラーを表示するカスタム関数であるhandleNotificationがあります。

以下にその例を示します。

let notificationType = userInfo["aps"]!["alert"]!!["some-key-I-Need"]! as! String
var storyboard = UIStoryboard(name: "Main", bundle: nil)
let mainViewController = storyboard.instantiateInitialViewController() as! MyViewController
self.window?.rootViewController  = mainViewController
5
Miknash

上記の答えはすべて非常に役立つことがわかりました。それでも、アプリが非アクティブ化されたとき、最も投票された人は私のために働きませんでした。後で@NickCatibと@thefredelementからの回答を組み合わせて実装しようとすると、storyboard.instantiateInitialViewController()の実行時にエラーが生成されました-「 'UINavigationController'型の値をキャストできませんでした」。 rootViewControllerとしてNavControllerを使用したスト​​ーリーボードファイルがあるため、これが発生することがわかりました。この問題を解決するために、私は新しいNavigation Controllerを作成しましたが、それは問題を解決しました。アプリの正しいナビゲーションを失い、ビューに戻るボタンさえ表示されませんでした。私の問題に対する答えは、@ NickCatibと@thefredelementの答えを使用することでしたが、識別子を使用してビューをインスタンス化し、次に示すように、rootViewControllerをUINavigationControllerとして使用してプッシュしました。

let rootViewController = self.window?.rootViewController as! UINavigationController
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let mvc = storyboard.instantiateViewControllerWithIdentifier("MyViewController") as! 
             MyViewController
rootViewController.pushViewController(mvc, animated: true)

これはうまく機能し、アプリの正しいナビゲーションプロパティを失うことはありませんでした。

2
Pablo

以前の回答の補足情報

状態に応じて、ロジックを変更できます。メソッド内でdidReceiveRemoteNotification

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {}

このような切り替えを行うことができます

let state = UIApplication.sharedApplication().applicationState switch state { case UIApplicationState.Active: case UIApplicationState.Inactive: case UIApplicationState.Background: }

アプリの現在の状態に基づいて、必要なアクションを実行します。

0
Jesus Rodriguez

Swift Rabbitの答えが最高です。

アプリが閉じられてアクティブ状態になったときに、まだ見つからないことを追加します。

DidFinishedLaunchingWithOptions内のAppDelegateに追加できます。

if let notification = launchOptions?[.remoteNotification] as? [AnyHashable : Any] {

            notificationsUserInfo = notification as [AnyHashable : Any]
            serveNotifications = true

        }

通知の値を使用してグローバル変数またはuserdefaultを作成し、フラグを使用してアプリの他の部分に通知があることを通知できます。

MainViewControllerが表示されたら、通知を処理するアクションを実行できます。

override func viewDidAppear(_ animated: Bool) {
        if serveNotifications {
      notificationManager.sharedInstance.processNotification(userInfo: notificationsUserInfo)

            serveNotifications = false

        }

    }

世話をする。

0
Jorge Cardenas