Pushwoosh/pushwoosh-ios-sdk

Local Notifications OR Remote Push Notifications not working

Closed this issue · 14 comments

I integrated Pushwoosh SDK and remote notifications were working. To make local notifications work again, they appeared, but didn't trigger the delegate methods, I used the solution provided in #65 . Local notifications are working now, but remote push notifications from Pushwoosh don't get delivered anymore.

Ok, I tried the way I found in the docs https://docs.pushwoosh.com/platform-docs/pushwoosh-sdk/ios-push-notifications/customizing-ios-sdk
Pushwoosh.sharedInstance()?.delegate = self
Pushwoosh.sharedInstance()?.notificationCenterDelegateProxy.add(self)

Local notifications are working, but remote notifications from Pushwoosh are not appearing on the device.

wfhm commented

@bb-git Hey,

Could you please share your entire Pushwoosh-related code so we could check it on our side?

Here is the related code. registerForPushNotifications() is called during the setup.
Local notifications work and the delegate gets triggered. But pushwoosh remote notifications don't even show.

private var localNotificationDelegate = LocalNotificationDelegate()

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
...
       Pushwoosh.sharedInstance()?.delegate = self
        Pushwoosh.sharedInstance()?.notificationCenterDelegateProxy.add(localNotificationDelegate)
        Pushwoosh.sharedInstance()?.language = 'en'
...
}
extension AppDelegate:PWMessagingDelegate{
    //handle token received from APNS
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        DebugLog.print("Did register with device token \(deviceToken)")
        Pushwoosh.sharedInstance()?.handlePushRegistration(deviceToken)
        
    }
    
    //handle token receiving error
    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        Pushwoosh.sharedInstance()?.handlePushRegistrationFailure(error);
    }
    
    //this is for iOS < 10 and for silent push notifications
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        DebugLog.print("silent push received")
        Pushwoosh.sharedInstance()?.handlePushReceived(userInfo)
        completionHandler(.noData)
    }
    
    //this event is fired when the push gets received
    func pushwoosh(_ pushwoosh: Pushwoosh!, onMessageReceived message: PWMessage!) {
        DebugLog.print("onMessageReceived: ", message.payload.description)
    }
    
    //this event is fired when user taps the notification
    func pushwoosh(_ pushwoosh: Pushwoosh!, onMessageOpened message: PWMessage!) {
        DebugLog.print("onMessageOpened: ", message.payload.description)
    }
    
}
class LocalNotificationDelegate:NSObject, UNUserNotificationCenterDelegate{
    // This function will be called when the app receive notification
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        DebugLog.print("notification show")
        if (!PWMessage.isPushwooshMessage(notification.request.content.userInfo)) {
            // handle your notification
            completionHandler(UNNotificationPresentationOptions.alert)
        }
    }
    
    // This function will be called right after user tap on the notification
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        DebugLog.print("notification clicked")
        if (!PWMessage.isPushwooshMessage(response.notification.request.content.userInfo)) {
            // handle your notification
            ...
            completionHandler()
        }
    }
}
wfhm commented

@bb-git please let me know if the sample below works for you:

//  AppDelegate.swift

import UIKit
import Pushwoosh

@main
class AppDelegate: UIResponder, UIApplicationDelegate, PWMessagingDelegate, UNUserNotificationCenterDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        Pushwoosh.sharedInstance()?.delegate = self;
        let localNotificationDelegate = LocalNotificationDelegate();
        Pushwoosh.sharedInstance()?.notificationCenterDelegateProxy.add(localNotificationDelegate);
        Pushwoosh.sharedInstance()?.registerForPushNotifications();
        // Override point for customization after application launch.
        
        scheduleNotification(notificationType: "test")
        
        return true
    }

    // MARK: UISceneSession Lifecycle
    
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
            Pushwoosh.sharedInstance()?.handlePushRegistration(deviceToken)
        }
        
        //handle token receiving error
        func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
            Pushwoosh.sharedInstance()?.handlePushRegistrationFailure(error);
        }
        
        //this is for iOS < 10 and for silent push notifications
        func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
            Pushwoosh.sharedInstance()?.handlePushReceived(userInfo)
            completionHandler(.noData)
        }

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }
    
    //this event is fired when the push gets received
        func pushwoosh(_ pushwoosh: Pushwoosh!, onMessageReceived message: PWMessage!) {
            print("onMessageReceived: ", message.payload.description)
        }
        
        //this event is fired when user taps the notification
        func pushwoosh(_ pushwoosh: Pushwoosh!, onMessageOpened message: PWMessage!) {
            print("onMessageOpened: ", message.payload.description)
        }

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }
    
    func scheduleNotification(notificationType: String) {
        let content = UNMutableNotificationContent()
        content.title = notificationType
        content.body = "Local notification body"
        content.sound = UNNotificationSound.default
        content.badge = 1
        
        let identifier = "Local notification"
        let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
        
        let localNotificationRequest = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)
        UNUserNotificationCenter.current().add(localNotificationRequest, withCompletionHandler: nil)
    }
}


class LocalNotificationDelegate:NSObject, UNUserNotificationCenterDelegate{
    // This function will be called when the app receive notification
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        print("notification show")
        if (!PWMessage.isPushwooshMessage(notification.request.content.userInfo)) {
            // handle your notification
            completionHandler(UNNotificationPresentationOptions.alert)
        }
    }
    
    // This function will be called right after user tap on the notification
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        print("notification clicked")
        if (!PWMessage.isPushwooshMessage(response.notification.request.content.userInfo)) {
            // handle your notification
            completionHandler()
        }
    }
}

ok, I'm testing it now, will keep you updated.

It seems to be working. The problem probably is, that the journeys from journey builder do not start from the beginning, when reinstalling the app. How can you reset the journey progress, to receive the first message after reinstall?

wfhm commented

@bb-git,

The journey is created for unique user IDs, so if, after reinstalling the app, a User ID remains the same, a user has to complete the journey. May I ask you to create a support ticket at https://www.pushwoosh.com/contact-us regarding this issue and describe your use-case in a bit more detail so we could find the best solution for it?

As the SDK ist handling the User ID, it must be created and stored somehow. So how to reset it?

wfhm commented

@bb-git,

By default, a user ID is equal to a Pushwoosh HWID which is generated automatically on the first application launch. HWIDs are randomly generated if there are no other applications with Pushwoosh SDK installed on a device, otherwise it will use a HWID stored in shared preferences. So, if you have any other apps of the same vendor with Pushwoosh SDK installed, you will have the same HWID for a device after reinstalling your app, and the User ID will remain the same. To set a new User ID, you can use the -(void)setUserId:(NSString *)userId method:

https://github.com/Pushwoosh/pushwoosh-ios-sdk/blob/master/Documentation/Pushwoosh.md#--voidsetuseridnsstring-userid

I've recently integrated @capacitor/local-notifications in an Ionic project that already uses Pushwoosh (pushwoosh-cordova-plugin). But it seems that the two listeners in the Capacitor plugin aren't triggering on iOS. Tested on simulator and device.

https://capacitorjs.com/docs/apis/local-notifications#addlistenerlocalnotificationreceived-

Is anyone aware of such a conflict between the two plugins/libraries?

@zarko-tg hi!
There should be no conflict. Could you build a project with local notifications and listeners only and let us know if it works? If the issue occurs when you add Pushwoosh SDK, please share your project with us, you may send it to our support at help@pushwoosh.com.

@enginseer-dev Thanks, will do ASAP. It's been somewhat time-consuming to figure it out but I'll try to build scenarios / sample project(s).

@enginseer-dev Hi, somewhat late do to other obligations but here's a demo project.
Unpack the zip file, then: npm i && ionic cap run ios

Note that the problem is equally present on a real iOS device, not just on the simulator.
Also note that the goal with this is not to prove that Pushwoosh is wired up and functioning correctly but to show that the listeners for the LocationNotifications don't work when the Pushwoosh code runs.

At the same time, this doesn't seem to be an issue on Android.

local-test.zip

wfhm commented

@zarko-tg hey,

It looks like both @capacitor/local-notifications and pushwoosh-cordova-plugin plugins try to use methods of UNUserNotificationCenterDelegate, and the Capacitor plugin doesn't expect other plugins to swizzle these methods (like our plugin does). So the swizzled versions of these methods do not trigger callbacks of the local-notifications plugin at all.

With Ionic Capacitor you should still have access to the native iOS project, so theoretically you can manually update your AppDelegate to let both plugins access UNUserNotificationCenterDelegate methods, however it might depend on the local-notifications implementation of these methods in their plugin.

May I ask you create a separate issue for this topic in our Cordova plugin repo?