アプリがプッシュ通知から起動/起動されたかどうかを知ることは可能ですか?
起動イベントはここでキャッチできます。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
if (launchOptions != nil) {
// Launched from Push notification
NSDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
}
}
ただし、アプリがバックグラウンドにあるときにプッシュ通知から開かれたことを検出するにはどうすればよいですか?
このコードを参照してください:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
if ( application.applicationState == UIApplicationStateInactive || application.applicationState == UIApplicationStateBackground )
{
//opened from a Push notification when the app was on background
}
}
と同じ
-(void)application:(UIApplication *)application didReceiveLocalNotification (UILocalNotification *)notification
遅くても便利かもしれない
アプリが実行されていないとき
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
と呼ばれる..
プッシュ通知を確認する必要がある場合
NSDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
if (notification) {
NSLog(@"app recieved notification from remote%@",notification);
[self application:application didReceiveRemoteNotification:notification];
} else {
NSLog(@"app did not recieve notification");
}
問題は、アプリの起動後にビューを正しく更新することでした。混乱を招くライフサイクルメソッドの複雑なシーケンスがここにあります。
ライフサイクルメソッド
IOS 10のテストでは、さまざまな場合のライフサイクルメソッドの次のシーケンスが明らかになりました。
DELEGATE METHODS CALLED WHEN OPENING APP
Opening app when system killed or user killed
didFinishLaunchingWithOptions
applicationDidBecomeActive
Opening app when backgrounded
applicationWillEnterForeground
applicationDidBecomeActive
DELEGATE METHODS WHEN OPENING Push
Opening Push when system killed
[receiving Push causes didFinishLaunchingWithOptions (with options) and didReceiveRemoteNotification:background]
applicationWillEnterForeground
didReceiveRemoteNotification:inactive
applicationDidBecomeActive
Opening Push when user killed
didFinishLaunchingWithOptions (with options)
didReceiveRemoteNotification:inactive [only completionHandler version]
applicationDidBecomeActive
Opening Push when backgrounded
[receiving Push causes didReceiveRemoteNotification:background]
applicationWillEnterForeground
didReceiveRemoteNotification:inactive
applicationDidBecomeActive
問題
わかりました、それで今私達はする必要があります:
厄介な点は、アプリケーションが実際にアクティブになったときにビューを更新する必要があることです。これは、すべての場合で同じライフサイクルメソッドです。
ソリューションのスケッチ
ソリューションの主なコンポーネントは次のとおりです。
notificationUserInfo
インスタンス変数をAppDelegateに保存します。applicationWillEnterForeground
とdidFinishLaunchingWithOptions
の両方にnotificationUserInfo = nil
を設定します。notificationUserInfo = userInfo
にdidReceiveRemoteNotification:inactive
を設定しますapplicationDidBecomeActive
からは常にカスタムメソッドopenViewFromNotification
を呼び出し、self.notificationUserInfo
を渡します。 self.notificationUserInfo
がnilの場合は早く戻り、そうでない場合はself.notificationUserInfo
で見つかった通知状態に基づいてビューを開きます。説明
プッシュから開く場合、didFinishLaunchingWithOptions
またはapplicationWillEnterForeground
は常にdidReceiveRemoteNotification:inactive
の直前に呼び出されるため、これらのメソッドで最初にnotificationUserInfoをリセットして、古い状態がないようにします。次に、didReceiveRemoteNotification:inactive
が呼び出されると、プッシュから開いていることがわかるので、self.notificationUserInfo
を設定し、applicationDidBecomeActive
によってピックアップされて、ユーザーを正しいビューに転送します。
最後の1つのケースとして、ユーザーがアプリスイッチャー内でアプリを開いた場合(つまり、アプリがフォアグラウンドにあるときにホームボタンをダブルタップした場合)、プッシュ通知を受信します。この場合はdidReceiveRemoteNotification:inactive
のみが呼び出され、WillEnterForegroundもdidFinishLaunchingも呼び出されないため、そのケースを処理するには特別な状態が必要です。
お役に立てれば。
これは使い古された投稿です...しかし、実際の解決策がまだ問題に欠けています(さまざまなコメントで指摘されているように)。
元の質問は、アプリがプッシュ通知から起動/開いたであったことを検出することです。egユーザーが通知をタップします。実際、このケースをカバーする回答はありません。
理由は、通知が到着したときの呼び出しフロー、application:didReceiveRemoteNotification...
で確認できます。
通知が受信されたときに呼び出されますAND通知がユーザーによってタップされたときに再び。このため、ユーザーがUIApplicationState
をタップしたかどうかだけではわかりません。
さらに、application:didFinishLaunchingWithOptions...
がiOS 9+(おそらく8)での起動後に再度呼び出されるため、application:didReceiveRemoteNotification...
でアプリの「コールドスタート」の状況を処理する必要がなくなりました。
では、ユーザータップが一連のイベントを開始したかどうかをどのように確認できますか?私の解決策は、アプリがバックグラウンドまたはコールドスタートから抜け出す時間をマークし、application:didReceiveRemoteNotification...
でその時間をチェックすることです。 0.1秒未満の場合、タップが起動をトリガーしたことを確認できます。
Swift 2.x
class AppDelegate: UIResponder, UIApplicationDelegate {
var wakeTime : NSDate = NSDate() // when did our application wake up most recently?
func applicationWillEnterForeground(application: UIApplication) {
// time stamp the entering of foreground so we can tell how we got here
wakeTime = NSDate()
}
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
// ensure the userInfo dictionary has the data you expect
if let type = userInfo["type"] as? String where type == "status" {
// IF the wakeTime is less than 1/10 of a second, then we got here by tapping a notification
if application.applicationState != UIApplicationState.Background && NSDate().timeIntervalSinceDate(wakeTime) < 0.1 {
// User Tap on notification Started the App
}
else {
// DO stuff here if you ONLY want it to happen when the Push arrives
}
completionHandler(.NewData)
}
else {
completionHandler(.NoData)
}
}
}
Swift
class AppDelegate: UIResponder, UIApplicationDelegate {
var wakeTime : Date = Date() // when did our application wake up most recently?
func applicationWillEnterForeground(_ application: UIApplication) {
// time stamp the entering of foreground so we can tell how we got here
wakeTime = Date()
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// ensure the userInfo dictionary has the data you expect
if let type = userInfo["type"] as? String, type == "status" {
// IF the wakeTime is less than 1/10 of a second, then we got here by tapping a notification
if application.applicationState != UIApplicationState.background && Date().timeIntervalSince(wakeTime) < 0.1 {
// User Tap on notification Started the App
}
else {
// DO stuff here if you ONLY want it to happen when the Push arrives
}
completionHandler(.newData)
}
else {
completionHandler(.noData)
}
}
}
私はiOS 9以降で両方のケース(バックグラウンドのアプリ、アプリが実行されていない)でこれをテストしました。 0.1秒もかなり控えめで、実際の値は〜0.002秒なので、0.01でも問題ありません。
「実行されていない」状態のSwift 2.0(ローカルおよびリモート通知)
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Handle notification
if (launchOptions != nil) {
// For local Notification
if let localNotificationInfo = launchOptions?[UIApplicationLaunchOptionsLocalNotificationKey] as? UILocalNotification {
if let something = localNotificationInfo.userInfo!["yourKey"] as? String {
self.window!.rootViewController = UINavigationController(rootViewController: YourController(yourMember: something))
}
} else
// For remote Notification
if let remoteNotification = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] as! [NSObject : AnyObject]? {
if let something = remoteNotification["yourKey"] as? String {
self.window!.rootViewController = UINavigationController(rootViewController: YourController(yourMember: something))
}
}
}
return true
}
application:didReceiveRemoteNotification:
で、アプリがフォアグラウンドまたはバックグラウンドにあるときに通知を受け取ったかどうかを確認します。
バックグラウンドで受信した場合は、通知からアプリを起動します。
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) {
NSLog(@"Notification received by running app");
} else {
NSLog(@"App opened from Notification");
}
}
アプリが終了し、ユーザーがプッシュ通知をタップしたとき
public func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
if launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] != nil {
print("from Push")
}
}
アプリがバックグラウンドにあり、ユーザーがプッシュ通知をタップした場合
ユーザーがシステムに表示されたアラートからアプリを開いた場合、アプリがフォアグラウンドに入ろうとしているときにシステムがこのメソッドを再度呼び出しますユーザーインターフェイスを更新し、通知に関する情報を表示できます。
public func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
if application.applicationState == .Inactive {
print("from Push")
}
}
アプリによっては、aps
内のcontent-available
を使用してサイレントプッシュを送信することもできるため、これにも注意してください:) https://stackoverflow.com/a/33778990/1418457
Swiftの場合:
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
PFPush.handlePush(userInfo)
if application.applicationState == UIApplicationState.Inactive || application.applicationState == UIApplicationState.Background {
//opened from a Push notification when the app was on background
}
}
はい、このメソッドでappDelegateで検出できます:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
/* your Code*/
}
ローカル通知の場合:
- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification
{
/* your Code*/
}
これをXamarinユーザーに投稿します。
アプリがプッシュ通知を介して起動されたかどうかを検出する鍵は、AppDelegate.FinishedLaunching(UIApplication app, NSDictionary options)
メソッドと渡されるオプション辞書です。
オプション通知は、ローカル通知の場合、UIApplication.LaunchOptionsLocalNotificationKey
というキーを持ちます。
リモート通知の場合、UIApplication.LaunchOptionsRemoteNotificationKey
になります。
キーがLaunchOptionsLocalNotificationKey
の場合、オブジェクトはUILocalNotification
型です。その後、通知を見て、どの特定の通知であるかを判断できます。
プロのヒント:UILocalNotification
には識別子がありません。UNNotificationRequest
には識別子があります。 requestIdを含むUserInfoにディクショナリキーを配置して、UILocalNotification
をテストするときに、特定のロジックをベースにする特定のrequestIdを使用できるようにします。
IOS 10以降のデバイスでも、UNUserNotificationCenter
のAddNotificationRequest
とUNMutableNotificationContent
を使用して位置通知を作成するとき、アプリが実行されていないとき(強制終了)、通知センターで通知をタップすることで起動されること、辞書がまだあることがわかりましたUILocalNotificaiton
オブジェクトが含まれます。
これは、通知ベースの起動を確認するコードがiOS8およびiOS 10以降のデバイスで機能することを意味します
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
{
_logger.InfoFormat("FinishedLaunching");
if(options != null)
{
if (options.ContainsKey(UIApplication.LaunchOptionsLocalNotificationKey))
{
//was started by tapping a local notification when app wasn't previously running.
//works if using UNUserNotificationCenter.Current.AddNotificationRequest OR UIApplication.SharedApplication.PresentLocalNotificationNow);
var localNotification = options[UIApplication.LaunchOptionsLocalNotificationKey] as UILocalNotification;
//I would recommended a key such as this :
var requestId = localNotification.UserInfo["RequestId"].ToString();
}
}
return true;
}
誰かがSwift 3の答えを望んでいる場合
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
switch application.applicationState {
case .active:
//app is currently active, can update badges count here
break
case .inactive:
//app is transitioning from background to foreground (user taps notification), do what you need when user taps here
break
case .background:
//app is in background, if content-available key of your notification is set to 1, poll to your backend to retrieve data and update your interface here
break
default:
break
}
}
私が自分で使用するために作成した状態図から始めて、より正確に視覚化し、他のすべての状態を検討します: https://docs.google.com/spreadsheets/d/e/ 2PACX-1vSdKOgo_F1TZwGJBAED4C_7cml0bEATqeL3P9UKpBwASlT6ZkU3iLdZnOZoevkMzOeng7gs31IFhD-L/pubhtml?gid = 0&single = true
このチャートを使用すると、考えられるほぼすべてのユースケースで機能する堅牢な通知処理システムを開発するために実際に必要なものを確認できます。
完全なソリューション↓
注:同様の回答がEricの回答に対するコメントで提案されていますが、状態シートは、アプリで行ったように、考えられるすべてのシナリオを見つけるのに役立ちます。
以下の完全なコードを見つけて、特定のケースが処理されない場合は以下にコメントしてください:
AppDelegate
class AppDelegate: UIResponder, UIApplicationDelegate {
private var willResignActiveCalled = false
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
NotificationUtils.shared.notification = nil
return true
}
func applicationWillResignActive(_ application: UIApplication) {
willResignActiveCalled = true
}
func applicationDidEnterBackground(_ application: UIApplication) {
willResignActiveCalled = false
}
func applicationWillEnterForeground(_ application: UIApplication) {
NotificationUtils.shared.notification = nil
}
func applicationDidBecomeActive(_ application: UIApplication) {
willResignActiveCalled = false
NotificationUtils.shared.performActionOnNotification()
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
if !willResignActiveCalled { // Check if app is in inactive by app switcher, control center, or notification center
NotificationUtils.shared.handleNotification(userInfo: userInfo)
}
}
}
NotificationUtils:これは、アプリケーションのさまざまな部分にナビゲートするためのすべてのコードを記述し、データベース(CoreData/Realm)を処理し、通知を受け取ったときに実行する必要のある他のすべてを行うことができます。
class NotificationUtils {
static let shared = NotificationUtils()
private init() {}
var notification : [AnyHashable: Any]?
func handleNotification(userInfo : [AnyHashable: Any]){
if UIApplication.shared.applicationState == UIApplicationState.active {
self.notification = userInfo //Save Payload
//Show inApp Alert/Banner/Action etc
// perform immediate action on notification
}
else if UIApplication.shared.applicationState == UIApplicationState.inactive{
self.notification = userInfo
}
else if UIApplication.shared.applicationState == UIApplicationState.background{
//Process notification in background,
// Update badges, save some data received from notification payload in Databases (CoreData/Realm)
}
}
func performActionOnNotification(){
// Do all the stuffs like navigating to ViewControllers, updating Badges etc
defer {
notification = nil
}
}
}
唯一の信頼できる方法があり、それはiOS 10 +に対してのみ機能します:
UNUserNotificationCenter
を使用してUNUserNotificationCenterDelegate
メソッドを実装します。
- (void) userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler {
//Here you can get your original Push if you need to
NSDictionary* pusDict = response.notification.request.content.userInfo;
if ([response.actionIdentifier isEqualToString: UNNotificationDefaultActionIdentifier]) {
//User tapped the notification
} else if ([response.actionIdentifier isEqualToString: UNNotificationDismissActionIdentifier]) {
//User dismissed the notification
} else if ([response.actionIdentifier isEqualToString: MYCustomActionId]) {
//User chose my custom defined action
}
...
}
のドキュメントから直接
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo:nil
アプリが実行中で、リモート通知を受信した場合、アプリはこのメソッドを呼び出して通知を処理します。
このメソッドの実装では、通知を使用して適切な一連のアクションを実行する必要があります。
そして少し後で
プッシュ通知の到着時にアプリが実行されていない場合、メソッドはアプリを起動し、起動オプション辞書に適切な情報を提供します。
アプリは、そのプッシュ通知を処理するためにこのメソッドを呼び出しません。
代わりに、
application:willFinishLaunchingWithOptions:
または
application:didFinishLaunchingWithOptions:
メソッドは、プッシュ通知ペイロードデータを取得し、適切に応答する必要があります。
Swift
の場合
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]){
++notificationNumber
application.applicationIconBadgeNumber = notificationNumber;
if let aps = userInfo["aps"] as? NSDictionary {
var message = aps["alert"]
println("my messages : \(message)")
}
}
// shanegao's code in Swift 2.0
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject])
{
if ( application.applicationState == UIApplicationState.Inactive || application.applicationState == UIApplicationState.Background ){
print("opened from a Push notification when the app was on background")
}else{
print("opened from a Push notification when the app was on foreground")
}
}
次を使用できます。
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
リモートプッシュ通知を処理します。
こちらをご覧ください documentation
アプリがshanegaoとしてバックグラウンドにある場合、使用できます
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
if ( application.applicationState == UIApplicationStateInactive || application.applicationState == UIApplicationStateBackground )
{
//opened from a Push notification when the app was on background
}
}
ただし、アプリケーションを起動し、アプリケーションを閉じてアプリケーションをデバッグする場合は、スキームの編集に移動し、左側のメニューで実行を選択してから起動します実行される実行可能ファイルの待機を選択し、プッシュ通知をクリックするとアプリケーションが起動します
スキームの編集>実行>実行可能ファイルの起動を待つ
Xcode 10 Swift 4.2
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) {
let state : UIApplicationState = application.applicationState
if (state == .Inactive || state == .Background) {
// coming from background
} else {
// App is running in foreground
}
}
この質問の問題は、アプリを「開く」ことが明確に定義されていないことです。アプリは、非実行状態からコールドローンチされるか、非アクティブ状態から再アクティブ化されます(たとえば、別のアプリから元の状態に切り替えるなど)。これらの可能な状態をすべて区別するための私のソリューションは次のとおりです。
typedef NS_ENUM(NSInteger, MXAppState) {
MXAppStateActive = 0,
MXAppStateReactivated = 1,
MXAppStateLaunched = 2
};
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// ... your custom launch stuff
[[MXDefaults instance] setDateOfLastLaunch:[NSDate date]];
// ... more custom launch stuff
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
// Through a lot of trial and error (by showing alerts), I can confirm that on iOS 10
// this method is only called when the app has been launched from a Push notification
// or when the app is already in the Active state. When you receive a Push
// and then launch the app from the icon or apps view, this method is _not_ called.
// So with 99% confidence, it means this method is called in one of the 3 mutually exclusive cases
// 1) we are active in the foreground, no action was taken by the user
// 2) we were 'launched' from an inactive state (so we may already be in the main section) by a tap
// on a Push notification
// 3) we were truly launched from a not running state by a tap on a Push notification
// Beware that cases (2) and (3) may both show UIApplicationStateInactive and cant be easily distinguished.
// We check the last launch date to distinguish (2) and (3).
MXAppState appState = [self mxAppStateFromApplicationState:[application applicationState]];
//... your app's logic
}
- (MXAppState)mxAppStateFromApplicationState:(UIApplicationState)state {
if (state == UIApplicationStateActive) {
return MXAppStateActive;
} else {
NSDate* lastLaunchDate = [[MXDefaults instance] dateOfLastLaunch];
if (lastLaunchDate && [[NSDate date] timeIntervalSinceDate:lastLaunchDate] < 0.5f) {
return MXAppStateLaunched;
} else {
return MXAppStateReactivated;
}
}
return MXAppStateActive;
}
そして、MXDefaults
はNSUserDefaults
の単なる小さなラッパーです。
func application(_ application: UIApplication, didReceiveRemoteNotification data: [AnyHashable : Any]) {
print("Push notification received: \(data)")
if let info = data["aps"] as? Dictionary<String, AnyObject> {
let alertMsg = info["alert"] as! String
print(alertMsg)
switch application.applicationState {
case .active:
print("do stuff in case App is active")
case .background:
print("do stuff in case App is in background")
// navigateToChatDetailViewControler(pushdata: data)
case .inactive:
print("do stuff in case App is inactive")
// navigateToChatDetailViewControler(pushdata: data)
}
}
}
まだ試していませんが、通知を自分に送信できますか? http://nshipster.com/nsnotification-and-nsnotificationcenter/