Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug: Capacitor Register for push but use OneSignal for receiving #2441

Closed
REPTILEHAUS opened this issue Feb 14, 2020 · 4 comments
Closed

bug: Capacitor Register for push but use OneSignal for receiving #2441

REPTILEHAUS opened this issue Feb 14, 2020 · 4 comments

Comments

@REPTILEHAUS
Copy link

REPTILEHAUS commented Feb 14, 2020

Im trying to register for push using capacitors native functionality, I then use One Signal for generating the token using the apn token, everything works fine but when sending a notification it comes but the following code in app.component.ts never runs

      PushNotifications.addListener('pushNotificationActionPerformed', (notification: PushNotificationActionPerformed) => {
        alert(JSON.stringify(notification))
      });  

This is my modified AppDelegate with logic for adding one signal as outlined here https://documentation.onesignal.com/docs/ios-sdk-setup Is there some kind of compatability issue with Capacitor Push PushNotificationActionPerformed and receiving a notification from OneSignal ? Must I use some way of receiving using OneSignals API now ?

import UIKit
import Capacitor
import OneSignal

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, OSPermissionObserver, OSSubscriptionObserver  {

  var window: UIWindow?

  func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    let onesignalInitSettings = [kOSSettingsKeyAutoPrompt: false]
    // Replace 'YOUR_APP_ID' with your OneSignal App ID.
    OneSignal.initWithLaunchOptions(
      launchOptions,
      appId: "xxxxxx",
      handleNotificationAction: nil,
      settings: onesignalInitSettings
    )

    OneSignal.inFocusDisplayType = OSNotificationDisplayType.notification;
    
    // Add your AppDelegate as an obsserver
    OneSignal.add(self as OSPermissionObserver)
    
    OneSignal.add(self as OSSubscriptionObserver)
    //END OneSignal initializataion code
    
    return true
  }

  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 invalidate graphics rendering callbacks. Games should use this method to pause the game.
  }

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

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

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

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

  func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    // Called when the app was launched with a url. Feel free to add additional processing here,
    // but if you want the App API to support tracking app url opens, make sure to keep this call
    return CAPBridge.handleOpenUrl(url, options)
  }
  
  func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    // Called when the app was launched with an activity, including Universal Links.
    // Feel free to add additional processing here, but if you want the App API to support
    // tracking app url opens, make sure to keep this call
    return CAPBridge.handleContinueActivity(userActivity, restorationHandler)
  }

  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesBegan(touches, with: event)

    let statusBarRect = UIApplication.shared.statusBarFrame
    guard let touchPoint = event?.allTouches?.first?.location(in: self.window) else { return }

    if statusBarRect.contains(touchPoint) {
      NotificationCenter.default.post(CAPBridge.statusBarTappedNotification)
    }
  }

  #if USE_PUSH

  func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    let status: OSPermissionSubscriptionState = OneSignal.getPermissionSubscriptionState()
    let hasPrompted = status.permissionStatus.hasPrompted
    print("hasPrompted = \(hasPrompted)")
    let userStatus = status.permissionStatus.status
    print("userStatus = \(userStatus)")
    let isSubscribed = status.subscriptionStatus.subscribed
    print("isSubscribed = \(isSubscribed)")
    let userSubscriptionSetting = status.subscriptionStatus.userSubscriptionSetting
    print("userSubscriptionSetting = \(userSubscriptionSetting)")
    let userID = status.subscriptionStatus.userId
    NotificationCenter.default.post(name: Notification.Name(CAPNotifications.DidRegisterForRemoteNotificationsWithDeviceToken.name()), object: userID)
  }

  func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
    NotificationCenter.default.post(name: Notification.Name(CAPNotifications.DidFailToRegisterForRemoteNotificationsWithError.name()), object: error)
  }

#endif

    
    
    // Add this new method
    func onOSPermissionChanged(_ stateChanges: OSPermissionStateChanges!) {
        if stateChanges.from.status == OSNotificationPermission.notDetermined {
            if stateChanges.to.status == OSNotificationPermission.authorized {
                print("Thanks for accepting notifications!")
                
          let status: OSPermissionSubscriptionState = OneSignal.getPermissionSubscriptionState()
          let hasPrompted = status.permissionStatus.hasPrompted
          print("hasPrompted = \(hasPrompted)")
          let userStatus = status.permissionStatus.status
          print("userStatus = \(userStatus)")
          let isSubscribed = status.subscriptionStatus.subscribed
          print("isSubscribed = \(isSubscribed)")
          let userSubscriptionSetting = status.subscriptionStatus.userSubscriptionSetting
          print("userSubscriptionSetting = \(userSubscriptionSetting)")
          let userID = status.subscriptionStatus.userId
          NotificationCenter.default.post(name: Notification.Name(CAPNotifications.DidRegisterForRemoteNotificationsWithDeviceToken.name()), object: userID)
          print("Subscribed for OneSignal push notifications!")
            } else if stateChanges.to.status == OSNotificationPermission.denied {
                print("Notifications not accepted. You can turn them on later under your iOS settings.")
                 NotificationCenter.default.post(name: Notification.Name(CAPNotifications.DidFailToRegisterForRemoteNotificationsWithError.name()), object: "Notifications not accepted. You can turn them on later under your iOS settings.")
            }
        }
        print( String("PermissionStateChanges: \n\(stateChanges)") )
    }
    
     func onOSSubscriptionChanged(_ stateChanges: OSSubscriptionStateChanges!) {
         if !stateChanges.from.subscribed && stateChanges.to.subscribed {
           
          let status: OSPermissionSubscriptionState = OneSignal.getPermissionSubscriptionState()

          let hasPrompted = status.permissionStatus.hasPrompted
          print("hasPrompted = \(hasPrompted)")
          let userStatus = status.permissionStatus.status
          print("userStatus = \(userStatus)")

          let isSubscribed = status.subscriptionStatus.subscribed
          print("isSubscribed = \(isSubscribed)")
          let userSubscriptionSetting = status.subscriptionStatus.userSubscriptionSetting
          print("userSubscriptionSetting = \(userSubscriptionSetting)")
          let userID = status.subscriptionStatus.userId
          NotificationCenter.default.post(name: Notification.Name(CAPNotifications.DidRegisterForRemoteNotificationsWithDeviceToken.name()), object: userID)
         }
     }
}
@jcesarmobile
Copy link
Member

OneSignal probably uses its own UNUserNotificationCenterDelegate that prevents the Capacitor one from working.

Check their docs and see if you can make it not use their delegate or report it to them, there is nothing we can do about it.

@jkasten2
Copy link

@jcesarmobile OneSignal does also setup it's own UNUserNotificationCenterDelegate, however it uses swizzling so any delegate setup before or after OneSignal's will still be called.
This can be found in OneSignal's UNUserNotificationCenter+OneSignal.m file:
https://github.com/OneSignal/OneSignal-iOS-SDK/blob/064c46b9da438c672535befbdabd744495a08c68/iOS_SDK/OneSignalSDK/Source/UNUserNotificationCenter%2BOneSignal.m#L193-L195

However Capacitor won't setup it's own delegate if it detects one is already set found here.

public override init(){
super.init()
let center = UNUserNotificationCenter.current()
if center.delegate == nil {
center.delegate = self
}
}

@jcesarmobile For OneSignal compatibility you could simply remove your if center.delegate == nil check, however I see it was internally added to fix another issue as noted by this PR #1805 to resolve issue #1631.
Some other options to resolve this would be to;

  1. Sizzle methods like OneSignal does to handle both cases;
    • setDelegate on UNUserNotificationCenter
    • userNotificationCenter:willPresentNotification:withCompletionHandler: on UNUserNotificationCenterDelegate
    • userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler: on UNUserNotificationCenterDelegate
    • This down side to this one is it's complexity. However feel free to use the OneSignal code as a reference.
  2. Add a flag setting somewhere that is read if center.delegate == nil should be checked or not.
    • This would give control the app developer depending on the other notification SDK they use.
    • Maybe a .plist setting?
    • A rare but possible downside is that if their is a 3rd notification SDK involved that doesn't swizzle then either this delegate or the 3rd delegate would not fire. (2/3 or 3/3 SDKs would have to swizzle for all 3 to work together)
  3. Detect if center.delegate.class equals OneSignalHelper and if so set Capacitor's delegate. Since OneSignal swizzles setDelegate it will be able to intercept the set call, OneSignal will then make sure your delegate is still called.
    • Down side of this is it is fragile, if OneSignal changes this internal class name compatibility will be broken again.
    • Anther down side that it won't cover other SDKs that also swizzle like OneSignal does.
  4. Something else I did not think of.

My top 3 recommendations are in order of what will provide the best possible compatibility and stability with other plugins.

@REPTILEHAUS
Copy link
Author

@jcesarmobile Id love to get One Signal integrated into my app asap, just wondering if this is something I should pursue with a private fork ? Im far from being a Swift expert so any effort will take time from me as opposed to you guys - any tips from your side most welcome

@ionitron-bot
Copy link

ionitron-bot bot commented Nov 12, 2022

Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of Capacitor, please create a new issue and ensure the template is fully filled out.

@ionitron-bot ionitron-bot bot locked and limited conversation to collaborators Nov 12, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants