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

[DOC NEEDED] - How to implement a custom notifications delegate on iOS? #1631

Closed
2 of 7 tasks
rpanadero opened this issue Jun 4, 2019 · 11 comments · Fixed by #1805
Closed
2 of 7 tasks

[DOC NEEDED] - How to implement a custom notifications delegate on iOS? #1631

rpanadero opened this issue Jun 4, 2019 · 11 comments · Fixed by #1805

Comments

@rpanadero
Copy link

rpanadero commented Jun 4, 2019

Description of the problem:

First of all, I'm sorry if this shouldn't be reported as bug report, but I need help with this matter as soon as possible.

The problem that I'm facing is related to having a custom notifications delegate on iOS. I have been having a look at Capacitor core and I have seen that there is a delegate set for UNUserNotificationCenter. However, this delegate implementation doesn't fit me because my app business requirements need another one. How could I override this implementation? If I try to register another delegate in my application AppDelegate, which is versioned, it doesn't work because Capacitor core registers its delegate later.

On the other hand, I does be able to do what I want on Android by registering a custom FirebaseMessagingService in AndroidManifest.xml. In addition, I can even call Capacitor Push Notifications plugin to avoid breaking its implementation.

public class MyAppFirebaseMessagingService extends FirebaseMessagingService {

    @Override
    public void onNewToken(String newToken) {
        super.onNewToken(newToken);
        PushNotifications.onNewToken(newToken);
    }

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);
        PushNotifications.sendRemoteMessage(remoteMessage);
        // I can do whatever I want
    }

}
<service android:name="com.demo.myapp.services.MyAppFirebaseMessagingService" android:stopWithTask="false">
    <intent-filter android:priority="9999">
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

Affected platform

  • Android
  • iOS
  • electron
  • web

OS of the development machine

  • Windows
  • macOS
  • linux

Other information:

Capacitor version: 1.0

node version: 11.4.0

npm version: 6.5.0

CocoaPods version: 1.7.1

@rpanadero
Copy link
Author

rpanadero commented Jun 4, 2019

By the way, I have found out another issue related to the iOS notifications. The application is not notified by any Capacitor push notification callback when a notification is received while the application is in background. In addition, if the notification is "content-available" the application is not notified either in background or foreground.

In order to support this iOS feature it should be necessary to implement this method of AppDelegate:

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler NS_AVAILABLE_IOS(7_0);

@gubbigubbi
Copy link

Hey @rpanadero,

I have found this issue too. My iOS app is not receiving FCM push notifications. It does when I send the test message to the entire app, but not individual messages, which are working on Android.

How do we implement your code you mentioned above?

@philet
Copy link

philet commented Jun 21, 2019

However, this delegate implementation doesn't fit me because my app business requirements need another one. How could I override this implementation? If I try to register another delegate in my application AppDelegate, which is versioned, it doesn't work because Capacitor core registers its delegate later.

This is an issue for me too. Any plan on supporting custom UNUserNotificationCenterDelegate implementations anytime soon?

Thanks!

@jcesarmobile
Copy link
Member

how are you doing the custom UNUserNotificationCenterDelegate? with a plugin? custom code? could you provide a sample?
What do you need to do that the Capacitor one can't?

If you do it in the load method of a custom plugin it should be called after the CAPUNUserNotificationCenterDelegate one and use yours.

@philet
Copy link

philet commented Jul 2, 2019

I'm working on a plugin for the Salesforce MarketingCloudSDK (https://salesforce-marketingcloud.github.io/MarketingCloudSDK-iOS/get-started/apple.html).

As per the instructions of this SDK (and also the Apple iOS documentation*), the UNUserNotificationCenterDelegate must be set in "application(_:didFinishLaunchingWithOptions:)". The problem is, that CAPUNUserNotificationCenterDelegate sets itself as the delegate after this. So there is no way for me to set my custom delegate at the right point without it being "overwritten" by Capacitor afterwards.

* See the "Important" box here: https://developer.apple.com/documentation/usernotifications/unusernotificationcenterdelegate

@rpanadero
Copy link
Author

rpanadero commented Jul 5, 2019

Hi @jcesarmobile,

I tried it with custom code and it happens what @philet says. Capacitor registers his delegate after mine it is set, so we could say that yours have more priority.

My problem was the same as @philet describes above. My notifications SDK required that my application called a specific method on willPresentNotification of UNUserNotificationCenterDelegate, just to notify the SDK of the push reception.

Finally, as I could do that easily, I decided to just use the Capacitor Core for push notifications handling and notify the notifications SDK from hybrid side by calling a plugin method. Maybe, it is not the best approach, but I guess that it is the easiest way to avoid having the same problem we had on Cordova when we had multiple notifications plugins which overrided the same delegate methods by doing swizzling. That experience on Cordova was awful because I was always frightened of breaking something.

If you let me suggest something, I think that another possible approach could be to implement the UNUserNotificationCenterDelegate in AppDelegate and emit an event when the method is fired. Something similar to what you are doing with didRegisterForRemoteNotificationsWithDeviceToken.

https://github.com/ionic-team/capacitor/blob/master/ios-template/App/App/AppDelegate.swift#L64

Like that, you could catch this event in CAPUNUserNotificationCenterDelegate, propagate the events to the respective methods and notify the web side by the Capacitor bridge. In addition, this would allow developers to put its custom code into AppDelegate methods accepting responsibility.

On the other hand, I remember you that I also realized that you are not implementing in AppDelegate:

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler NS_AVAILABLE_IOS(7_0);

So, if I receive a silent push notification my application is not notified and I cannot handle it. And the same happens, if the push notification is received in background although it is a non-silent one.

Feel free to tell me anything about my suggestions.

Thanks in advance

@jcesarmobile
Copy link
Member

I've sent a PR to not override the UNUserNotificationCenterDelegate if present, so people can use custom delegates if they want.

I don't like the idea of moving the whole UNUserNotificationCenterDelegate to the AppDelegate, we would like to keep the AppDelegate as clean as possible, the other methods that we've put there was because they are AppDelegate methods, but UNUserNotificationCenterDelegate can be any class. But we have to find a way of making easier to use a custom UNUserNotificationCenterDelegate but at the same time make it easy to propagate the data to the Push/Notification plugins if the users want to. Will think more about it and implement it in a future PR.

I didn't have any problem receiving regular push when the app is in background or closed, but silent pushes are not supported at the moment, we will add the - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler in a future release.

@jcesarmobile
Copy link
Member

Just did some testing and by doing this with a custom UNUserNotificationCenterDelegate in AppDelegate the Capacitor one keeps working as expected.

func userNotificationCenter(_ center: UNUserNotificationCenter,
                              didReceive response: UNNotificationResponse,
                              withCompletionHandler completionHandler: @escaping (() -> Void)) {
    // Do your custom handling here
    let capVC = self.window?.rootViewController as? CAPBridgeViewController
    capVC?.bridge?.notificationsDelegate.userNotificationCenter(center, didReceive: response, withCompletionHandler: completionHandler)
  }
    
  func userNotificationCenter(_ center: UNUserNotificationCenter,
                              willPresent notification: UNNotification,
                              withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    // Do your custom handling here
    let capVC = self.window?.rootViewController as? CAPBridgeViewController
    capVC?.bridge?.notificationsDelegate.userNotificationCenter(center, willPresent: notification, withCompletionHandler: completionHandler)
  }

From custom plugins is even easier as they already have the bridge object and can skip the let capVC = self.window?.rootViewController as? CAPBridgeViewController part.

@rpanadero
Copy link
Author

Thank you very much @jcesarmobile

@yacut
Copy link

yacut commented Jun 13, 2021

@jcesarmobile does your solution work for Capacitor v3?

@ionitron-bot
Copy link

ionitron-bot bot commented Nov 11, 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 11, 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

Successfully merging a pull request may close this issue.

5 participants