Подтвердить что ты не робот

Фоновые уведомления FCM, не работающие в iOS

У меня проблема с уведомлением FCM на iOS.

Я получаю уведомления с успехом, когда мое приложение находится на переднем плане (обратный вызов didReceiveRemoteNotification в appdelegate запущен), но я не получаю уведомления, когда приложение находится в фоновом режиме (я ничего не вижу в уведомлении лоток iOS).

Итак, я думаю, что проблема заключается в формате сообщения, отправленного FCM. Json, отправленный моим сервером в FCM, находится в следующем формате:

{  
   "data":{  
      "title":"mytitle",
      "body":"mybody",
      "url":"myurl"
   },
   "notification":{  
      "title":"mytitle",
      "body":"mybody"
   },
   "to":"/topics/topic"
}

Как вы можете видеть, в моем json есть два блока: один блок уведомлений (для получения уведомлений в фоновом режиме) и один блок данных (для получения уведомлений на переднем плане).

Я не могу понять, почему уведомления в фоновом режиме не получены. Мои сомнения касаются порядка блоков (проблема, если я поставил блок данных перед блоком "уведомление"?).

EDIT: Подробнее о проблеме.

Это мой appdelegate.swift:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate
{
    var window: UIWindow?


    // Application started
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool
    {
        let pushNotificationSettings: UIUserNotificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
        application.registerUserNotificationSettings(pushNotificationSettings)
        application.registerForRemoteNotifications()

        FIRApp.configure()

        NSNotificationCenter.defaultCenter().addObserver(self, selector: "tokenRefreshNotification:", name: kFIRInstanceIDTokenRefreshNotification, object: nil)

        return true
    }




    // Handle refresh notification token
    func tokenRefreshNotification(notification: NSNotification) {
        let refreshedToken = FIRInstanceID.instanceID().token()
        print("InstanceID token: \(refreshedToken)")

        // Connect to FCM since connection may have failed when attempted before having a token.
        if (refreshedToken != nil)
        {
            connectToFcm()

            FIRMessaging.messaging().subscribeToTopic("/topics/topic")
        }

    }


    // Connect to FCM
    func connectToFcm() {
        FIRMessaging.messaging().connectWithCompletion { (error) in
            if (error != nil) {
                print("Unable to connect with FCM. \(error)")
            } else {
                print("Connected to FCM.")
            }
        }
    }


    // Handle notification when the application is in foreground
    func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {
            // If you are receiving a notification message while your app is in the background,
            // this callback will not be fired till the user taps on the notification launching the application.
            // TODO: Handle data of notification

            // Print message ID.
            print("Message ID: \(userInfo["gcm.message_id"])")

            // Print full message.
            print("%@", userInfo)
    }


    // Application will enter in background
    func applicationWillResignActive(application: UIApplication)
    {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    }



    // Application entered in background
    func applicationDidEnterBackground(application: UIApplication)
    {
        FIRMessaging.messaging().disconnect()
        print("Disconnected from FCM.")
    }



    // Application will enter in foreground
    func applicationWillEnterForeground(application: UIApplication)
    {
        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
    }



    // Application entered in foreground
    func applicationDidBecomeActive(application: UIApplication)
    {
        connectToFcm()

        application.applicationIconBadgeNumber = 0;
    }



    // Application will terminate
    func applicationWillTerminate(application: UIApplication)
    {
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    }


}

Единственный способ получить сообщения на переднем плане - отключить метод swizzling, установив FirebaseAppDelegateProxyEnabled в NO в моем info.plist.

В этом случае в документации FCM говорится, что мне нужно реализовать в моем appdelegate.swift два метода:

 - FIRMessaging.messaging().appDidReceiveMessage(userInfo)  in didReceiveRemoteNotification callback
 - FIRInstanceID.instanceID().setAPNSToken(deviceToken, type: FIRInstanceIDAPNSTokenType.Sandbox) in didRegisterForRemoteNotificationsWithDeviceToken callback

Но если я реализую эти функции, сообщения перестают появляться, даже когда приложение находится на переднем плане.

Я знаю, это очень странно.

ИЗМЕНИТЬ 2:

Когда приложение находится в фоновом режиме, уведомление не получено, но когда я открываю приложение, одно и то же уведомление принимается немедленно (метод doneReceiveRemoteNotification запущен).

4b9b3361

Ответ 1

Предполагая, что вы все правильно настроили, установка priority сообщения от normal до high должна немедленно появиться. Это связано с тем, как iOS связывает уведомления и обрабатывает их. Вы можете прочитать о Приоритете уведомлений FCM здесь. Обратите внимание, что вы не должны использовать high в производстве, если для этого нет хорошего случая, так как он имеет заряд батареи.

Вот ссылка из Документов Apple

Приоритет уведомления. Укажите одно из следующих значений:

10 - Немедленно отправьте push-сообщение. Уведомления с этим приоритетом должен запускать оповещение, звук или значок на целевом устройстве. Это ошибка для использования этого приоритета для push-уведомления, которое содержит только доступный для содержимого ключ.

5 - отправьте push-сообщение в то время, которое учитывает мощность соображения для устройства. Уведомления с таким приоритетом могут группироваться и поставляться в пакетах. Они дросселируются, а в некоторых случаи не доставляются. Если вы опускаете этот заголовок, сервер APNs устанавливает приоритет 10.

Ответ 2

Вам нужно установить для свойства content_available значение true следующим образом:

{  
   "data":{  
      "title":"mytitle",
      "body":"mybody",
      "url":"myurl"
   },
   "notification":{  
      "title":"mytitle",
      "body":"mybody",
      "content_available": true
   },
   "to":"/topics/topic"
}

В этом разделе содержится синий блокнот, в котором говорится следующее: https://firebase.google.com/docs/cloud-messaging/concept-options#notifications

Ответ 3

Возможно, вам потребуется добавить право на push-уведомление. Сделайте это, перейдя к вашим целевым настройкам, затем нажав "Возможности" и включив "Push Notifications".

Целевые возможности

Ответ 4

Приоритет и content_available (как упоминалось в других ответах) являются ключевыми элементами, чтобы убедиться, что вы получаете уведомления. Тесты показали интересные результаты, поэтому я решил поделиться ими здесь.

Результаты тестирования: Swift 3, Xcode 8, iOS 10

Приоритет = "высокий" = > "немедленный" (при явных задержках сети) прием сообщения.

Приоритет = "нормальный" = > различные результаты (обычно быстрые, хотя, очевидно, медленнее, чем "высокие" )

content_available = true в уведомлениях (без сообщения с полезной нагрузкой)

  • Foreground = данные, полученные как ожидалось
  • Фон = данные получены как ожидалось (при открытии приложения)

content_available = true на верхнем уровне (без сообщения с полезной нагрузкой)

  • Foreground = данные, полученные как ожидалось
  • Фон = данные получены как ожидалось (при открытии приложения)

content_available = true в уведомлениях (с сообщением {title/body})

  • Foreground = полученные данные TWICE
  • Фон = данные получены TWICE (при открытии приложения)

content_available = true на верхнем уровне (с сообщением полезной нагрузки)

  • Foreground = полученные данные TWICE
  • Фон = данные получены TWICE (при открытии приложения)

Выводы:

  • В то время как приоритет является возможной причиной не получения сообщений, наиболее важным фактором является то, что у вас должно быть либо "content_available", либо сообщение полезной нагрузки.
  • content_available ДОЛЖЕН использоваться для данных только для данных (без него ни одно сообщение не отправляется).
  • content_available НЕ ДОЛЖНО использоваться в полезных файлах, содержащих сообщения, поскольку это приводит к отправке двойных сообщений из FCM.
  • Не найдено различий в использовании content_available на верхнем уровне или в уведомлениях.

EDIT: Дополнительные результаты тестирования: - если у вас есть msg title, вы ДОЛЖНЫ иметь тело msg, или вы не получите предупреждение.

Нечетная часть этого состоит в том, что вы получите вибрацию, значок и звук, но окно предупреждения не появится, если у вас нет тела, а также название.

Ответ 5

-For FCM, когда приложение находится в фоновом режиме или на переднем плане, и запускается программа OS <10 (_: didReceiveRemoteNotification :).

-When - приложение на переднем плане и OS => 10 userNotificationCenter: willPresentNotification: withCompletionHandler: будет запущен метод.

-When отправка сообщения данных без компонента уведомления: будет срабатывать приложение (_: didReceiveRemoteNotification :).

-When отправляет сообщение с компонентом уведомления: userNotificationCenter: willPresentNotification: withCompletionHandler: будет запущен метод.

Ответ 6

когда вы используете прямые сообщения канала FCM, вы не можете получать уведомления в фоновом режиме

это абзац из документа Firebase:

При включенном прямом канале бэкэнд FCM использует надежную очередь сообщений для отслеживания ожидающих сообщений, когда приложение находится в фоновом режиме или закрыто. Когда приложение перейдет на передний план и соединение будет восстановлено, канал автоматически отправит ожидающие сообщения клиенту, пока он не получит подтверждение от клиента.

Ответ 7

У меня была эта проблема с набором свойств content_available. Решение заключалось в том, чтобы удалить и переустановить приложение с устройства iOS.