ตรวจสอบว่าแอปเปิดตัว / เปิดจากการแจ้งเตือนแบบพุช


172

เป็นไปได้ไหมที่จะรู้ว่าแอปเปิดตัว / เปิดจากการแจ้งเตือนแบบพุชหรือไม่?

ฉันเดาว่าจะมีกิจกรรมเปิดตัวที่นี่:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    if (launchOptions != nil) {
         // Launched from push notification
         NSDictionary *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];

    }
}

อย่างไรก็ตามฉันจะตรวจจับได้อย่างไรว่าเปิดขึ้นมาจากการแจ้งเตือนแบบพุชเมื่อแอปอยู่ในพื้นหลัง


6
นี่เป็นโพสต์เก่า แต่มีประโยชน์มาก น่าเสียดายที่คำตอบยอดนิยมไม่ได้แก้ปัญหาจริง (ตามที่ระบุความคิดเห็น) โปรดลองทำเครื่องหมายคำตอบใหม่เป็น 'ยอมรับ' เนื่องจากคำตอบปัจจุบันยังไม่สมบูรณ์
MobileVet

1
คำถามนี้มีการดู 100k + ครั้ง แต่คำตอบที่เลือกไม่ถูกต้องหรือครบถ้วน สำหรับผู้เข้าชมให้พิจารณาเรียงลำดับตามใช้งานแทนโดยโหวตเพื่อค้นหาวิธีแก้ไขที่ทันสมัย
Albert Renshaw

คำตอบ:


187

ดูรหัสนี้:

- (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

19
@ManuelM นี่เป็นคำตอบที่ดีที่แสดงวิธีการตรวจสอบเมื่อมีการนำแอพในพื้นหลังมาที่เบื้องหน้าจากการแจ้งเตือนแบบพุช สำหรับเมื่อแอปไม่ทำงานคุณต้องมีคำตอบของ M.Othman ด้านล่าง
OpenUserX03

6
ฉันได้รับสายเข้าสู่แอปพลิเคชัน: didReceiveRemoteNotification: หลังจากแตะการแจ้งเตือนโดยไม่คำนึงว่าแอปอยู่ในพื้นหลังหรือไม่ทำงานเลยคำตอบนี้เหมาะกับความต้องการของฉันอย่างสมบูรณ์ ทดสอบบน iOS 7 และ 8
Newtz

16
เช่นเดียวกับคนอื่น ๆ ที่ชี้ให้เห็นสิ่งนี้ไม่พบ "เปิด / เปิดจากการแจ้งเตือนแบบพุช" สิ่งนี้ถูกเรียกเมื่อได้รับการแจ้งเตือนไม่ใช่เมื่อเปิด ดังนั้นหากคุณได้รับการแจ้งเตือนใน bg แต่แตะไอคอนแอปเพื่อเปิดแอปรหัสที่คุณมีที่นี่จะยังคงทำงานอยู่และคุณอาจเปิดหน้าเว็บที่ผู้ใช้ไม่ต้องการเปิด
Bao Lei

4
@ManuelM วิธีนี้จะไม่บอกว่าแอพนั้นเปิดผ่านศูนย์การแจ้งเตือนหรือไอคอนของแอปหากมีโหมดพื้นหลัง - ตรวจสอบการแจ้งเตือนระยะไกล มันทำเมื่อมันไม่ถูกตรวจสอบ ฉันได้ทำเอกสารความแตกต่างในโพสต์นี้: stackoverflow.com/questions/32061897/…
Bao Lei

2
ยืนยันว่าใช้งานได้กับ Google Cloud Messaging
CularBytes

127

สาย แต่อาจมีประโยชน์

เมื่อแอพไม่ทำงาน

- (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");
}

2
โปรดทราบว่าในตัวอย่างข้างต้นการแจ้งเตือนไม่ควรประกาศเป็น (UILocalNotification *) แต่ (NSDictionary *)
cosmix

1
วิธีนี้คุณสามารถดูว่ามีการแจ้งเตือนใด ๆ สำหรับแอพหรือไม่ในขณะที่ไม่ได้ใช้ คำถามคือวิธีการตรวจสอบว่าเปิดแอปจากการแจ้งเตือน ในกรณีนี้ didReceiveRemoteNotification จะถูกเรียกแม้ว่าแอปจะไม่ทำงานเลย - ฉันชอบคำตอบของคุณเพราะมันค่อนข้างสำคัญสำหรับหลาย ๆ กรณี แต่ไม่ใช่คำตอบที่ถูกต้องสำหรับคำถาม
Axel Zehden

คำตอบของคุณและคำตอบนี้ทั้งคู่ทำสิ่งเดียวกันหรือไม่?
ฮันนี่

38

ปัญหาที่เรามีคือการอัปเดตมุมมองอย่างถูกต้องหลังจากเปิดตัวแอป มีลำดับขั้นตอนที่ซับซ้อนของวิธีวงจรชีวิตที่นี่ซึ่งทำให้เกิดความสับสน

วิธีการวงจรชีวิต

การทดสอบสำหรับ 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

ปัญหา

ตกลงดังนั้นตอนนี้เราต้อง:

  1. ตรวจสอบว่าผู้ใช้เปิดแอปจากการพุชหรือไม่
  2. อัพเดตมุมมองตามสถานะการพุช
  3. ล้างสถานะเพื่อให้การเปิดในภายหลังไม่ทำให้ผู้ใช้กลับสู่ตำแหน่งเดิม

บิตที่ยุ่งยากคือการอัปเดตมุมมองจะเกิดขึ้นเมื่อแอปพลิเคชันกลายเป็นใช้งานจริงซึ่งเป็นวิธีวงจรชีวิตเดียวกันในทุกกรณี

ร่างของการแก้ปัญหาของเรา

นี่คือส่วนประกอบหลักของโซลูชันของเรา:

  1. เก็บnotificationUserInfoตัวแปรอินสแตนซ์บน AppDelegate
  2. ตั้งnotificationUserInfo = nilทั้งในและapplicationWillEnterForegrounddidFinishLaunchingWithOptions
  3. ตั้งnotificationUserInfo = userInfoอยู่ที่didReceiveRemoteNotification:inactive
  4. จากapplicationDidBecomeActiveมักจะเรียกวิธีการที่กำหนดเองและผ่านopenViewFromNotification self.notificationUserInfoถ้าเป็นศูนย์จากนั้นกลับมาในช่วงต้นมิฉะนั้นเปิดมุมมองบนพื้นฐานของรัฐการแจ้งเตือนที่พบในself.notificationUserInfoself.notificationUserInfo

คำอธิบาย

เมื่อเปิดจากการพุชdidFinishLaunchingWithOptionsหรือapplicationWillEnterForegroundถูกเรียกทันทีก่อนdidReceiveRemoteNotification:inactiveดังนั้นเราจึงรีเซ็ตการแจ้งเตือน UserInfo เป็นครั้งแรกในวิธีการเหล่านี้ดังนั้นจึงไม่มีสถานะเก่า จากนั้นหากdidReceiveRemoteNotification:inactiveถูกเรียกว่าเรารู้ว่าเรากำลังเปิดจากการพุชดังนั้นเราจึงตั้งค่าself.notificationUserInfoซึ่งจะถูกเลือกโดยapplicationDidBecomeActiveเพื่อส่งต่อผู้ใช้ไปยังมุมมองที่ถูกต้อง

มีกรณีสุดท้ายหนึ่งกรณีที่ผู้ใช้เปิดแอปไว้ภายในตัวสลับแอป (เช่นโดยการแตะสองครั้งที่ปุ่มโฮมขณะที่แอพอยู่ในเบื้องหน้า) จากนั้นรับการแจ้งเตือนแบบพุช ในกรณีนี้didReceiveRemoteNotification:inactiveมีการเรียกเท่านั้นและจะไม่มีการเรียกใช้ WillEnterForeground และFinishLaunchingดังนั้นคุณต้องมีสถานะพิเศษเพื่อจัดการกับกรณีดังกล่าว

หวังว่านี่จะช่วยได้


ในที่สุดสิ่งที่ใช้งานได้ขอบคุณ! ฉันต้องการสร้างการตั้งค่าสถานะ "appResuming" และเปิดหน้าจอในreceiveวิธีการเมื่อสถานะแอปทำงานอยู่หรือแอปเริ่มทำงานต่อ ซึ่งอาจนำไปสู่ปัญหาเกี่ยวกับการเปลี่ยน VC เมื่อแอปยังคงไม่ทำงาน โซลูชันของคุณดูดีมากจนกระทั่ง Apple เปลี่ยนวงจรชีวิตอีกครั้ง
shelll

สิ่งที่เกี่ยวกับ iOS 9 มีการเรียกวิธีการวงจรชีวิตในลักษณะเดียวกันและการสั่งซื้อ? ฉันไม่มีอุปกรณ์ iOS 9 อยู่แล้วดังนั้นฉันจึงไม่สามารถทดสอบได้อย่างถูกต้อง
shelll

2
มีสองกรณีขอบเพิ่มเติมยกเว้นตัวสลับแอป 1) เมื่อศูนย์การแจ้งเตือนถูกดึงจากด้านบนและวางซ้อนแอพ 2) เมื่อแผง iOS ของ wifi / BT / ฯลฯ ถูกดึงจากด้านล่างและซ้อนทับแอพ ในทุกกรณีที่สามเพียงแค่เรียกว่าแล้วapplicationWillResignActive applicationDidBecomeActiveดังนั้นหลังจากที่applicationWillResignActiveเรียกว่าไม่ได้บันทึกการแจ้งเตือนที่ได้รับจนกว่าจะมีapplicationDidEnterBackgroundหรือapplicationDidBecomeActiveจะเรียกว่า
shelll

ขอบคุณที่เพิ่มเคสเหล่านี้ @shelll มันซับซ้อนมากขึ้นเสมอ! ฉันไม่แน่ใจเกี่ยวกับ iOS9 ฉันว่ามันคงปลอดภัยที่จะคิดว่ามันเหมือนกัน แต่ใครจะรู้
Eric Conner

แค่หัวขึ้น ฉันทดสอบ iOS 11 Beta 9 วันนี้และพบว่าในกรณีที่คุณมีแอพของคุณอยู่เบื้องหน้าล็อคโทรศัพท์แล้วเลือกการแจ้งเตือนแบบพุชจากหน้าจอล็อค สิ่งที่เราเห็นใน iOS 10 ที่เรียกแอปพลิเคชัน WillEnterForeground และจากนั้นได้รับRemoteNotification: ไม่ได้ใช้งาน - ดังนั้นนี่คือเคสขอบที่ยังไม่ครอบคลุม ในความเห็นของฉันนี่เป็นข้อผิดพลาดในรหัส iOS แต่เมื่อพิจารณาว่าการเปิดตัว iOS 11 นั้นใกล้เคียงแค่ไหนมันเป็นสิ่งที่ต้องระวัง
Roy

24

นี่คือโพสต์ที่สวมใส่ได้ดี ... แต่ก็ยังขาดวิธีการแก้ปัญหาที่แท้จริง(ตามที่ได้กล่าวไว้ในความคิดเห็นต่างๆ)

คำถามเดิมเกี่ยวกับการตรวจสอบเมื่อเปิดตัวแอพ / เปิดจากการแจ้งเตือนแบบพุชเช่นผู้ใช้แตะที่การแจ้งเตือน ไม่มีคำตอบใดที่ครอบคลุมกรณีนี้

เหตุผลสามารถเห็นได้ในโฟลว์การโทรเมื่อการแจ้งเตือนมาถึง application:didReceiveRemoteNotification...

ถูกเรียกเมื่อได้รับการแจ้งเตือนและอีกครั้งเมื่อผู้ใช้แตะที่การแจ้งเตือน ด้วยเหตุนี้คุณไม่สามารถบอกได้โดยดูUIApplicationStateจากผู้ใช้ที่แตะมัน

นอกจากนี้คุณไม่จำเป็นต้องจัดการกับสถานการณ์ 'แอปเริ่มต้นใหม่' ของแอปอีกต่อไปapplication:didFinishLaunchingWithOptions...ซึ่งapplication:didReceiveRemoteNotification...จะถูกเรียกอีกครั้งหลังจากเปิดตัวใน iOS 9+ (อาจเป็น 8 เช่นกัน)

ดังนั้นคุณจะบอกได้อย่างไรว่าผู้ใช้การแตะเริ่มต้นห่วงโซ่เหตุการณ์หรือไม่ วิธีแก้ปัญหาของฉันคือการทำเครื่องหมายเวลาที่แอปเริ่มออกมาจากพื้นหลังหรือเริ่มเย็นจากนั้นตรวจสอบเวลาapplication:didReceiveRemoteNotification...นั้น หากน้อยกว่า 0.1 วินาทีคุณสามารถมั่นใจได้ว่าการแตะเริ่มการทำงาน

สวิฟท์ 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)
    }
  }
}

สวิฟท์ 3

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 ก็ดีเช่นกัน


1
สิ่งนี้ดูเหมือนจะเป็นวิธีแก้ปัญหาการทำงานเพียงอย่างเดียวที่แตกต่างระหว่างการแตะที่การแจ้งเตือนจริง ๆ และเปิดแถบสถานะบนแอพ
liviucmg

4
นี่เป็นโซลูชั่นเดียวที่ทำงานได้จาก StackOverflow ทั้งหมด สิ่งเดียวที่ฉันต้องการเพิ่มคือเมื่อคุณรองรับ iOS 10 ขึ้นไปคุณสามารถใช้UNNotificationCenterAPI ได้โดยเฉพาะวิธี UNNotificationCenterDelegate userNotificationCenter(UNUserNotificationCenter, didReceive: UNNotificationResponse, withCompletionHandler: @escaping () -> Void) วิธีการเรียก API ของ func เหล่านั้นก็ต่อเมื่อผู้ใช้แตะที่การแจ้งเตือนจริง
DenHeadless

มันจะมองหา swift 3 ได้อย่างไร
Jochen Österreicher

การแก้ปัญหาไม่ทำงานในขณะที่แอปอยู่ในสถานะไม่ใช้งาน (ผู้ใช้เลื่อนลงศูนย์การแจ้งเตือนหรือการเลื่อนขึ้นศูนย์ควบคุม) และได้รับการแจ้งเตือน เมื่อผู้ใช้แตะที่การแจ้งเตือนแอปจะไม่รับapplicationWillEnterForeground สายดังนั้นวิธีการแก้ปัญหาล้มเหลวในการตรวจจับการแตะ
DevGansta

@DevGansta เมื่อคุณเพิ่มชั้นเรียนของคุณเป็นUNUserNotificationCenter.current().delegateในapplication:didFinishLaunchingWithOptionsการตรวจสอบจะโทรuserNotificationCenter(didReceive response)หลังจากแตะในกรณีที่คุณอธิบาย
Dorian Roy

22

เมื่อแอปถูกยกเลิกและผู้ใช้แตะที่การแจ้งเตือนแบบพุช

public func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
   if launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] != nil {
      print("from push")
    }
}

เมื่อแอปอยู่ในพื้นหลังและผู้ใช้แตะที่การแจ้งเตือนแบบพุช

หากผู้ใช้เปิดแอปของคุณจากการแจ้งเตือนที่แสดงโดยระบบระบบอาจเรียกวิธีนี้อีกครั้งเมื่อแอพของคุณกำลังจะเข้าสู่ส่วนหน้าเพื่อให้คุณสามารถอัปเดตส่วนต่อประสานผู้ใช้และแสดงข้อมูลที่เกี่ยวข้องกับการแจ้งเตือน

public func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
  if application.applicationState == .inactive {
    print("from push")
  }
}

ขึ้นอยู่กับแอพของคุณมันยังสามารถส่งข้อความแบบไม่มีเสียงcontent-availableภายในapsได้ดังนั้นโปรดทราบสิ่งนี้เช่นกัน :) ดูhttps://stackoverflow.com/a/33778990/1418457


2
เฉพาะคำตอบที่ไม่รู้สึกว่าแฮ็คสกปรกและถูกต้อง สิ่งที่ฉันขาดหายไปคือหากแอปอยู่ในพื้นหลังและผู้ใช้เปิดด้วยตนเองจะตรวจสอบได้อย่างไร ในขณะที่ยังคงสามารถตรวจสอบการกดเริ่มเย็นและกดจากพื้นหลัง
Jochen Österreicher

1
@ JochenÖsterreicherสวัสดีฉันสรุปไว้ที่นี่โปรดตรวจสอบmedium.com/@onmyway133/…
onmyway133

19

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
}

15

ในapplication:didReceiveRemoteNotification:การตรวจสอบว่าคุณได้รับการแจ้งเตือนเมื่อ app ของคุณอยู่ในเบื้องหน้าหรือพื้นหลัง

หากได้รับมาในพื้นหลังให้เปิดแอปจากการแจ้งเตือน

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
    if ([UIApplication sharedApplication].applicationState == UIApplicationStateActive) {
        NSLog(@"Notification received by running app");
    } else {
        NSLog(@"App opened from Notification");
    }
}

3
โปรดทราบว่า "แอปที่เปิดจากการแจ้งเตือน" จะเป็นค่าบวกหากการแจ้งเตือนถูกส่งไปในขณะที่ผู้ใช้อยู่บนหน้าจออื่น (ตัวอย่างเช่นหากพวกเขาดึงแถบสถานะลงแล้วได้รับการแจ้งเตือนจากแอปของคุณ)
Kevin Cooper

4
@ Kevin อย่างแน่นอน มันทำให้คุณสงสัยว่าทำไม Apple ถึงได้ฝึกงานเพื่อออกแบบกระบวนการจัดการการแจ้งเตือน ...
Andreas

เราจะตรวจสอบได้อย่างไรถ้าเราแตะที่การแจ้งเตือนที่ได้รับในสถานะใช้งาน
Mayank Jain

13

สำหรับรวดเร็ว:

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 in the background

    }

}

4

ใช่คุณสามารถตรวจจับด้วยวิธีนี้ในแอตัวแทน :

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
      /* your Code*/
}

สำหรับการแจ้งเตือนในท้องถิ่น:

- (void)application:(UIApplication *)application
didReceiveLocalNotification:(UILocalNotification *)notification
{
         /* your Code*/
}

1
วิธีนี้จะไม่ถูกเรียกหากแอพไม่ได้ทำงานอยู่
นั่น

ปัญหาของฉันไม่ได้จัดการการแจ้งเตือน แต่ควรรู้ว่าเปิดขึ้นเมื่อคุณคลิกที่แบนเนอร์ (เมื่อแอปอยู่บนพื้นหลัง)
joao

3

ถ้าใครต้องการคำตอบอย่างรวดเร็ว 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
    }
}

แต่จะรู้ได้อย่างไรว่าแอปพลิเคชั่นเปิดอยู่หรือไม่โดยแตะที่การแจ้งเตือนแบบพุชเมื่อแอปถูกยกเลิก
user3804063

1
เมื่อมีคนแตะที่กดแอพจะเปิดไม่ว่าจะถูกยกเลิกหรือไม่ และ. inactive case is calling
Hamid Shahsavari

1
ฉันจำเป็นต้องตรวจสอบว่าแอปเปิดอยู่หรือไม่โดยการกดปุ่มพุชและต้องการไปยังเนื้อหาที่เกี่ยวข้องฉันเห็นว่าอินสตาแกรมทำเช่นนั้น
user3804063

การแจ้งเตือนในท้องถิ่นเป็นอย่างไร
Amir Shabani

3

โพสต์สิ่งนี้สำหรับผู้ใช้ Xamarin

กุญแจสำคัญในการตรวจสอบว่ามีการเปิดตัวแอปผ่านการแจ้งเตือนแบบพุชAppDelegate.FinishedLaunching(UIApplication app, NSDictionary options)หรือไม่และเป็นพจนานุกรมตัวเลือกที่ผ่านเข้ามา

UIApplication.LaunchOptionsLocalNotificationKeyตัวเลือกพจนานุกรมจะมีคีย์นี้ในนั้นถ้ามันมีการแจ้งเตือนในท้องถิ่น:

UIApplication.LaunchOptionsRemoteNotificationKeyถ้ามันมีการแจ้งเตือนระยะไกลก็จะเป็น

เมื่อที่สำคัญคือวัตถุที่เป็นประเภทLaunchOptionsLocalNotificationKey UILocalNotificationจากนั้นคุณสามารถดูการแจ้งเตือนและกำหนดการแจ้งเตือนเฉพาะ

เคล็ดลับ Pro: UILocalNotificationไม่มีตัวระบุในนั้นเช่นเดียวกับที่UNNotificationRequestทำ วางคีย์พจนานุกรมใน UserInfo ที่มี requestId เพื่อที่จะทดสอบเมื่อUILocalNotificationนั้นคุณจะมี requestId ที่เฉพาะเจาะจงที่พร้อมใช้งานเพื่อวางตรรกะบางอย่าง

ผมพบว่าแม้บน iOS 10+ อุปกรณ์ที่เมื่อมีการสร้างการแจ้งเตือนที่ตั้งโดยใช้UNUserNotificationCenter's 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;
}

2

ส่งตรงจากเอกสารสำหรับ

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo:nil

หากแอพทำงานและได้รับการแจ้งเตือนระยะไกลแอพจะเรียกวิธีการนี้เพื่อประมวลผลการแจ้งเตือน

การใช้วิธีนี้ของคุณควรใช้การแจ้งเตือนเพื่อดำเนินการตามความเหมาะสม

และอีกเล็กน้อยในภายหลัง

หากแอปไม่ทำงานเมื่อมีการแจ้งเตือนแบบพุชมาวิธีการเปิดใช้แอพและให้ข้อมูลที่เหมาะสมในพจนานุกรมตัวเลือกการเปิดตัว

แอพไม่ได้เรียกวิธีนี้ในการจัดการการแจ้งเตือนแบบพุชนั้น

แต่คุณใช้งานของ

application:willFinishLaunchingWithOptions:

หรือ

application:didFinishLaunchingWithOptions:

วิธีการต้องได้รับข้อมูลน้ำหนักบรรทุกการแจ้งเตือนแบบพุชและตอบสนองอย่างเหมาะสม


2

ฉันจะเริ่มต้นด้วยแผนภูมิสถานะที่ฉันสร้างขึ้นเพื่อการใช้งานของฉันเองเพื่อให้เห็นภาพได้อย่างแม่นยำมากขึ้นและเพื่อพิจารณาสถานะอื่น ๆ ทั้งหมด: https://docs.google.com/spreadsheets/d/e/2PACX-1vSdKOgo_F1TZwGJBAED4C_7cml0bEATqeL3P9UKI ? GID = 0 & เดียว = true

การใช้แผนภูมินี้เราสามารถเห็นสิ่งที่จำเป็นจริง ๆ เพื่อพัฒนาระบบการจัดการการแจ้งเตือนที่มีประสิทธิภาพซึ่งทำงานได้ในเกือบทุกกรณีที่เป็นไปได้

โซลูชันที่สมบูรณ์↓

  • payload การแจ้งเตือนของร้านค้าในdidReceiveRemoteNotification
  • ล้างการแจ้งเตือนที่เก็บไว้ในแอปพลิเคชันWillEnterForegroundและทำFinishLaunchingWithOptions
  • ในการจัดการกรณีที่ศูนย์ควบคุม / ศูนย์การแจ้งเตือนดึงคุณสามารถใช้การตั้งค่าสถานะwillResignActiveCalledและตั้งค่าเป็นเท็จเริ่มแรกตั้งค่านี้เป็นจริงในเมธอดapplicationWillResignActive
  • ในวิธีdidReceiveRemoteNotificationให้บันทึกการแจ้งเตือน (userInfo) เฉพาะเมื่อ willResignActiveCalled เป็นเท็จ
  • รีเซ็ตwillResignActiveCalled เท็จในapplicationDidEnterBackgroundและapplicationDidBecomeActiveวิธี

หมายเหตุ: แนะนำคำตอบที่คล้ายกันในความคิดเห็นเกี่ยวกับคำตอบของ 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
    }
  }
}

ใส่ความคิดเห็นนี้ดีกว่าเนื่องจากนี่ไม่ใช่คำตอบ
แมดดี้

@Maddy ขอบคุณสำหรับคำแนะนำอัพเดทคำตอบพร้อมรายละเอียดทั้งหมด
chetan anand

1
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)
        }
    }
}

1

มีวิธีที่เชื่อถือได้เพียงวิธีเดียวและใช้งานได้กับ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
    }
    ...
}

0

คุณสามารถใช้ได้:

-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo

เพื่อจัดการการแจ้งเตือนแบบพุชระยะไกล

ตรวจสอบเอกสารที่นี่



0
     // 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")
            }
    }

แต่จะเกิดอะไรขึ้นถ้าปิดแอป (ยกเลิก) เช่นเดียวกับ Twitter หรือ Instagram มันจะตรวจจับได้และถ้าแอปนั้นปิดลงก็จะนำคุณไปยังโพสต์ใหม่หรือรูปภาพหรือโปรไฟล์ของคุณ ฯลฯ
Tarvo Mäesepp

0

ปัญหาของคำถามนี้คือ "การเปิด" แอปไม่ได้กำหนดไว้อย่างดี แอปนั้นเปิดตัวแบบเย็นจากสถานะไม่ทำงานหรือถูกเปิดใช้งานใหม่จากสถานะไม่ใช้งาน (เช่นจากการสลับกลับไปเป็นแอปอื่นจากแอปอื่น) นี่คือทางออกของฉันในการแยกแยะสถานะที่เป็นไปได้ทั้งหมด:

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น้อย


0

สำหรับ 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)")

    }
}

0

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
    }
}

0

สำหรับ iOS 10+ คุณสามารถใช้วิธีนี้ในการรู้ว่าเมื่อไรที่การแจ้งเตือนของคุณถูกคลิกโดยไม่คำนึงถึงสถานะของแอพ

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {

    //Notification clicked
    completionHandler()
}

0

คำตอบของ M.Othman นั้นถูกต้องสำหรับแอพที่ไม่มีผู้แทนฉาก สำหรับแอพScene Delegateสิ่งนี้ใช้ได้กับฉันในiOS 13

นี่คือรหัสสำหรับสิ่งที่ควรเขียนในจะเชื่อมต่อซีน

if connectionOptions.notificationResponse == nil { 
//Not opened from push notification
} else {
  //Opened from push notification
}

รหัสสำหรับตัวแทนแอปเพื่อสนับสนุนเวอร์ชันก่อนหน้านี้ไม่ได้ FinishLaunchingWithOptions

let notification = launchOptions?[UIApplication.LaunchOptionsKey.remoteNotification]
        if (notification != nil) {

            //Launched from push notification
        } else {

            //Launch from other source
        }

-1

สำหรับผู้ใช้ Swift:

หากคุณต้องการเปิดหน้าอื่นเมื่อเปิดจากการกดหรืออะไรทำนองนั้นคุณต้องตรวจสอบdidFinishLaunchingWithOptionsดังนี้:

let directVc: directVC! = directVC(nibName:"directVC", bundle: nil)
let pushVc: pushVC! = pushVC(nibName:"pushVC", bundle: nil)

if let remoteNotification = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] as? NSDictionary {
     self.navigationController = UINavigationController(rootViewController: pushVc!)
} else {
     self.navigationController = UINavigationController(rootViewController: directVc!)
}
self.window!.rootViewController = self.navigationController

ผู้เข้าร่วมไม่มีสมาชิก navigationController
Pablo Cegarra

1
สร้างตัวควบคุมการนำทางในไฟล์ AppDelegate.h กำลังใช้งานอยู่และใช้งานได้!
AAA

-1

ใน SWIFT:

ฉันกำลังเรียกใช้การแจ้งเตือนแบบพุช (พร้อมการดึงข้อมูลพื้นหลัง) เมื่อแอพของฉันอยู่ในพื้นหลังและฉันได้รับการแจ้งเตือนแบบพุชฉันพบว่า didReceiveRemote การแจ้งเตือนใน appDelegate จะถูกเรียกสองครั้ง หนึ่งครั้งเมื่อได้รับการแจ้งเตือนและอีกครั้งเมื่อผู้ใช้คลิกที่การแจ้งเตือน

ในการตรวจสอบว่ามีการคลิกการแจ้งเตือนการแจ้งเตือนหรือไม่เพียงตรวจสอบว่า applicationState มูลค่าดิบ == 1 ภายใน didReceiveRemoteNotificationNotification ใน appDelegate

func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject: AnyObject]) {
    // If not from alert click applicationState(1)
    if (application.applicationState.rawValue != 1) {
        // Run your code here
    }
}

ฉันหวังว่านี่จะช่วยได้.


-1

เมื่อแอปอยู่ในพื้นหลังเป็น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
    }
}

แต่ถ้าคุณต้องการที่จะเปิดตัวแอพลิเคชันและเมื่อตรวจสอบที่ถูกปิดและคุณต้องการที่จะแก้ปัญหาการสมัครของคุณคุณสามารถไปแก้ไขโครงการและในเมนูด้านซ้ายเลือกRunแล้วในการเปิดตัวเลือกรอปฏิบัติการที่จะเปิดตัวและจากนั้นคุณเปิดตัวแอพลิเคชันเมื่อคุณ คลิกที่การแจ้งเตือนผลักดัน

แก้ไข Scheme> Run> รอให้เรียกใช้งานเรียกทำงานได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.