2

I'm developing an iOS 11 app using Swift 4 and using Firebase Cloud Messaging to send notifications to my users.

The process to set up the notifications was:

1) Join the Apple Developer program. Register an App ID for my app and generate a Key with the APNs Service, like you can see on the picture:

enter image description here

2) Add iOS App to my existing Firebase Project, and uploading the Apple Key generated on step one.

enter image description here

3) Prepare iOS app to receive notifications: Added GoogleServices-Info, installed required Firebase pods (Firebase Core and Messaging).

4) Enabled required capabilities. background Modes and Push Notifications.

enter image description here



enter image description here

5) Finally, I edited my appDelegate.swift allowing to receive notifications:

import UIKit
import CoreData
import KeychainAccess
import IQKeyboardManagerSwift
import Fabric
import Crashlytics
import Firebase
import FirebaseMessaging
import FirebaseInstanceID
import UserNotifications

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {


         // Override point for customization after application launch.

        //FIREBASE CONFIGURATION START
        FirebaseApp.configure()

        UNUserNotificationCenter.current().requestAuthorization( options: [.alert, .badge, .sound]) { (success, error) in
            if error == nil {
                print("Successfull authorization!")
            }
        }
        application.registerForRemoteNotifications()
        NotificationCenter.default.addObserver(self, selector: #selector(refreshToken(notification:)), name: NSNotification.Name.InstanceIDTokenRefresh, object: nil)

        UNUserNotificationCenter.current().delegate = self

        //FIREBASE CONFIGURATION END

        if let token = InstanceID.instanceID().token() {
            print("token: '\(token)'")
        }


        return true
    }


    func applicationDidEnterBackground(_ application: UIApplication) {
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
        Messaging.messaging().shouldEstablishDirectChannel = false
    }



    func applicationDidBecomeActive(_ application: UIApplication) {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
        FBHandler()
    }


    @objc func refreshToken(notification: NSNotification) {
        let refreshToken = InstanceID.instanceID().token()!
        print("*** \(refreshToken) ***")

        FBHandler()
    }

    func FBHandler() {
        Messaging.messaging().shouldEstablishDirectChannel = true
    }

    // MARK: - Handle Silent FCM notifications
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {

        showLocalNotification()
        print("Entire message \(userInfo)")

        let state : UIApplicationState = application.applicationState
        switch state {
        case UIApplicationState.active:
            print("If needed notify user about the message")
        default:
            print("Run code to download content")
        }


        completionHandler(UIBackgroundFetchResult.newData)
    }

    func showLocalNotification() {
        let notification = UNMutableNotificationContent()
        notification.title = "Nou contingut disponible: Benicolet"
        notification.body = "Restriccions cremes agrícoles"

        let notificationTrigger = 
        UNTimeIntervalNotificationTrigger(timeInterval: 3, repeats: false)

        let request = UNNotificationRequest(identifier: "\(Int(arc4random_uniform(999999)))", content: notification, trigger: notificationTrigger)

        UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
    }


// Make local notification to appear even when app is open
extension AppDelegate: UNUserNotificationCenterDelegate {

    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        print(response.notification.request.content.userInfo)
        completionHandler()
    }

    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void {
        completionHandler([.alert, .badge, .sound])
    }
}

My goal is to receive silent notifications, execute some logic, and then, show a Local Notification. This will happens each time I send a notification to my app, on the method didReceiveRemoteNotification.

At the moment I only need to be able to receive the silent notification and show up a local notification, using the method showLocalNotification.

The problem is that this method is only triggered when I send a notification while my iPhone is attached to Xcode (debugging). In this state, code works fine, but if I unplug the cable, notifications are not triggered anymore until I rerun the app using Xcode

I send my notifications using my own server, setting the Firebase topic, 'priority': 'High' and 'content_available':true

Simmilar unresolved questions:

didReceiveRemoteNotification:fetchCompletionHandler not called when not attached to xcode iOS 8.0.2

APNS: didReceiveRemoteNotification:fetchCompletionHandler did not run when app is in background (unless xcode is debugging)

didReceiveRemoteNotification:fetchCompletionHandler not being called in background with content-available flag (gets called when connected to XCode)

didReceiveRemoteNotification:fetchCompletionHandler: only called in background when connected to xcode

Isaac Bosca
  • 1,588
  • 1
  • 15
  • 34
  • 2
    1. curious, can you try connecting it to a power outlet rather than the macbook and see if it works? 2. Have you enabled `background apprefresh` for your app on the iPhone itself? see [this image](https://i.stack.imgur.com/AJ0cG.jpg) – mfaani May 17 '18 at 15:20
  • 3. in both (connected to Xcode and disconnected) scenarios are you keeping the app in foreground? Make sure you're comparing them to each other in a similar state. – mfaani May 17 '18 at 15:35
  • 1
    Thank you for your comments @Honey, really helpful! The problem was that I had disabled background refresh options. Please, feel free to post an answer with the image. I'll accept it. – Isaac Bosca May 18 '18 at 14:09
  • I'm not sure, but if you kept background App Refresh _disabled_ while the app was kept in foreground, I think you would have still received the notification. Try that. You can upvote my other answer :) – mfaani May 19 '18 at 00:08

0 Answers0