How to create notifications?

Hello,

We want to implement push notifications for both ios and android (this is a libgdx project by the way)

So far, we only know how to create a local notification in android native code. Need help for iOS, is there any framework for this?

1 Like

I’m successfully using Firebase for this.
Assuming you are using CocoaPods, add Firebase framework to your Podfile, create a moe binding for the framework and follow the Firebase integration guide. The guide have examples in both ObjC and Swift, they are easy to translate in Java.

1 Like

Thank you for helping. I will check it out. Now I do not know how to create a notification in iOS and no knowledge of objc and swift. Is there a way to handle it without native code? Moe supports it?

Yes, you can do everything with MOE, there is no need to write swift or objc code in your app. You have to follow the documentation for swift/objc on Firebase website and then rewrite the code in java, but it’s not a complex task. Personally I was able to do all the tutorial in less then 1 hour the first time and I had zero knowledge of swift/objc.
The thing that took me the most time was to find the value of the constant NSFoundationVersionNumber_iOS_9_x_Max that is required at some point in the program. Either it’s not defined in MOE or I was not able to found it. By the way, the value is 1299, I ended up hardcoding it like this:

if (NSFoundationVersionNumber() <= 1299/*NSFoundationVersionNumber_iOS_9_x_Max*/) {//iOS <= 9

I hope this help you.

Thank you very much!

Hi Marco,

I have implemented Firebase and most of the things are working as expected. Since i could not do the firebase initialization in AppDelegate i ended up having it in some ViewController, which is causing some issues as described here

i was wondering if you can share the Firebase code that you have rewritten in Java so i test it by integrating it into MOE AppDelegate.

Thank you in Advance!.

Hi Sachin,

here is the code I’m using, I’ve removed other parts of my app.

I hope this will help you.

import apple.NSObject;
import apple.foundation.NSDictionary;
import apple.foundation.NSString;
import apple.uikit.UIAlertView;
import apple.uikit.UIApplication;
import apple.uikit.UIColor;
import apple.uikit.UIImage;
import apple.uikit.UIImageView;
import apple.uikit.UImyViewController;
import apple.uikit.UIScreen;
import apple.uikit.UIUserNotificationSettings;
import apple.uikit.UIWindow;
import apple.uikit.c.UIKit;
import apple.uikit.enums.UIBackgroundFetchResult;
import apple.uikit.enums.UIRectEdge;
import apple.uikit.enums.UIUserNotificationType;
import apple.uikit.protocol.UIApplicationDelegate;
import apple.usernotifications.UNNotification;
import apple.usernotifications.UNNotificationResponse;
import apple.usernotifications.UNUserNotificationCenter;
import apple.usernotifications.enums.UNAuthorizationOptions;
import apple.usernotifications.enums.UNNotificationPresentationOptions;
import apple.usernotifications.protocol.UNUserNotificationCenterDelegate;

import org.moe.binding.firebasecore.FIRApp;
import org.moe.binding.firebasemessaging.FIRMessaging;
import org.moe.binding.firebasemessaging.FIRMessagingRemoteMessage;
import org.moe.binding.firebasemessaging.protocol.FIRMessagingDelegate;
import org.moe.natj.general.Pointer;
import org.moe.natj.general.ann.RegisterOnStartup;
import org.moe.natj.objc.ann.ObjCBlock;
import org.moe.natj.objc.ann.Selector;

import java.util.Locale;

import static apple.foundation.c.Foundation.NSFoundationVersionNumber;
import static apple.foundation.c.Foundation.NSLocalizedString;

@RegisterOnStartup
public class Main extends NSObject implements UIApplicationDelegate, FIRMessagingDelegate{

    public static void main(String[] args) {
        UIKit.UIApplicationMain(0, null, null, Main.class.getName());
    }

    @Selector("alloc")
    public static native Main alloc();

    protected Main(Pointer peer) {
        super(peer);
    }

    private UIWindow window;

    private void handleNotification(NSDictionary<?, ?> userInfo) {
        NSDictionary<?, ?> aps = (NSDictionary<?, ?>)userInfo.get("aps");
        if(aps!=null){
            NSDictionary<?, ?> alert = (NSDictionary<?, ?>)aps.get(NSString.stringWithString("alert"));
            if(alert!=null){
                //show an alert with the notification body
            }
        }
    }

    @Override
    public void applicationDidReceiveRemoteNotification(UIApplication application, NSDictionary<?, ?> userInfo) {
        handleNotification(userInfo);
    }

    @Override
    public void applicationDidReceiveRemoteNotificationFetchCompletionHandler(UIApplication application, NSDictionary<?, ?> userInfo, @ObjCBlock(name = "call_applicationDidReceiveRemoteNotificationFetchCompletionHandler") Block_applicationDidReceiveRemoteNotificationFetchCompletionHandler completionHandler) {
        handleNotification(userInfo);
        completionHandler.call_applicationDidReceiveRemoteNotificationFetchCompletionHandler(UIBackgroundFetchResult.NewData);
    }


    //FIRMessagingDelegate
    @Override
    @Selector("messaging:didRefreshRegistrationToken:")
    public void messagingDidRefreshRegistrationToken(FIRMessaging messaging, String fcmToken) {
        //save the token if you need it
    }

    @Override
    @Selector("application:didFinishLaunchingWithOptions:")
    public boolean applicationDidFinishLaunchingWithOptions(UIApplication application, NSDictionary launchOptions) {
        FIRApp.configure();

        //FIREBASE MESSAGING
        FIRMessaging.messaging().setShouldEstablishDirectChannel(true);
        FIRMessaging.messaging().setDelegate(this);

        if (NSFoundationVersionNumber() <= 1299/*NSFoundationVersionNumber_iOS_9_x_Max*/) {//iOS <= 9
            UIUserNotificationSettings settings = UIUserNotificationSettings.settingsForTypesCategories(
                    (UIUserNotificationType.Sound | UIUserNotificationType.Alert | UIUserNotificationType.Badge),
                    null
            );
            UIApplication.sharedApplication().registerUserNotificationSettings(settings);
        } else {//iOS >= 10
            UNUserNotificationCenter.currentNotificationCenter().setDelegate(new UNUserNotificationCenterDelegate() {
                @Override
                public void userNotificationCenterDidReceiveNotificationResponseWithCompletionHandler(UNUserNotificationCenter center, UNNotificationResponse response, @ObjCBlock(name = "call_userNotificationCenterDidReceiveNotificationResponseWithCompletionHandler") Block_userNotificationCenterDidReceiveNotificationResponseWithCompletionHandler completionHandler) {
                    NSDictionary userInfo = response.notification().request().content().userInfo();
                    handleNotification(userInfo);

                    completionHandler.call_userNotificationCenterDidReceiveNotificationResponseWithCompletionHandler();
                }

                @Override
                public void userNotificationCenterWillPresentNotificationWithCompletionHandler(UNUserNotificationCenter center, UNNotification notification, @ObjCBlock(name = "call_userNotificationCenterWillPresentNotificationWithCompletionHandler") Block_userNotificationCenterWillPresentNotificationWithCompletionHandler completionHandler) {
                    NSDictionary userInfo = notification.request().content().userInfo();
                    handleNotification(userInfo);

                    completionHandler.call_userNotificationCenterWillPresentNotificationWithCompletionHandler(0/*UNNotificationPresentationOptions.None*/);
                }
            });
            UNUserNotificationCenter.currentNotificationCenter().requestAuthorizationWithOptionsCompletionHandler(
                    (UNAuthorizationOptions.Alert | UNAuthorizationOptions.Sound | UNAuthorizationOptions.Badge), (granted, error) -> { }
            );
        }

        UIApplication.sharedApplication().registerForRemoteNotifications();
        //END FIREBASE MESSAGING

        MyViewController myViewController = MyViewController.alloc().init();
        window = UIWindow.alloc().initWithFrame(UIScreen.mainScreen().bounds());
        window.setRootViewController(myViewController);
        window.makeKeyAndVisible();

        return true;
    }

    @Override
    public void setWindow(UIWindow value) {
        window = value;
    }

    @Override
    public UIWindow window() {
        return window;
    }
}
1 Like

Hi Marco,

Thank you so much for that!
is it possible for you to share source of firebasemessaging related packages that you have referred from your code.

import org.moe.binding.firebasemessaging.FIRMessaging;
import org.moe.binding.firebasemessaging.FIRMessagingRemoteMessage;
import org.moe.binding.firebasemessaging.protocol.FIRMessagingDelegate;

I tried the code from here: https://www.programcreek.com/java-api-examples/?code=TomGrill/gdx-firebase/gdx-firebase-master/ios-moe/src/com/google/firebasemessaging/FIRMessaging.java#
this did not work as expected.

Thanks!

Regards,
Sachin

Ah, sorry about that.
It’s been more than an year since I wrote that code and I forgot that I did the library binding myself.
I think at the time there wasn’t a binding available, but I can’t remember.
It should work if you use that binding and replace the imports from “org.moe.binding.firebasemessaging.” to “com.google.firebasemessaging.”.
If it doesn’t please share the error you have and I’ll try to help.
I don’t have problems sharing my binding if you want but it refers to an older version of firebase, not the current one.

I got the basic code working, there were some references that your version of the code was using that were not available in the version of the binding that i used. Below are those broken reference.

//&#32;FIRMessagingDelegate
&#32;@Override
&#32;@Selector("messaging:didRefreshRegistrationToken:")

error: method does not override or implement a method from a supertype

    FIRMessaging.messaging().setShouldEstablishDirectChannel(true);
    FIRMessaging.messaging().setDelegate(this);

error: cannot find symbol method setShouldEstablishDirectChannel(boolean)
error: cannot find symbol method setDelegate(Main)

I was able to comment out these code and still get Firebase ID and test notification. method call in the code was triggered as well. what concerned me was the what happens if firebase ID refreshed or any unknown issues that might arise because of missing code. Or maybe these are removed from the latest Firebase.

Ok, this is strange.
I see both methods are implemented in the binding you have linked.

The method call to setShouldEnstablishDirectChannel is probably not needed in your case.
From the documentation: https://firebase.google.com/docs/reference/ios/firebasemessaging/api/reference/Classes/FIRMessaging#shouldestablishdirectchannel
" When set to YES , Firebase Messaging will automatically establish a socket-based, direct channel to the FCM server. Enable this only if you are sending upstream messages or receiving non-APNS, data-only messages in foregrounded apps. Default is NO ."

The call to setDelegate is more important.

Have you implemented FIRMessagingDelegate in your Main class? From the error I see I think not.

Can you share your code?

I have implemented FIRMessagingDelegate in my main. Following is the full code.

import apple.NSObject;
import apple.foundation.NSDictionary;
import apple.uikit.UIApplication;
import apple.uikit.UIWindow;
import apple.uikit.c.UIKit;
import apple.uikit.protocol.UIApplicationDelegate;


import org.moe.natj.general.Pointer;
import org.moe.natj.general.ann.RegisterOnStartup;
import org.moe.natj.objc.ann.Selector;

import apple.NSObject;
import apple.foundation.NSDictionary;
import apple.foundation.NSString;
import apple.uikit.UIAlertView;
import apple.uikit.UIApplication;
import apple.uikit.UIColor;
import apple.uikit.UIImage;
import apple.uikit.UIImageView;
//import apple.uikit.UImyViewController;
import apple.uikit.UIScreen;
import apple.uikit.UIUserNotificationSettings;
import apple.uikit.UIWindow;
import apple.uikit.c.UIKit;
import apple.uikit.enums.UIBackgroundFetchResult;
import apple.uikit.enums.UIRectEdge;
import apple.uikit.enums.UIUserNotificationType;
import apple.uikit.protocol.UIApplicationDelegate;
import apple.usernotifications.UNNotification;
import apple.usernotifications.UNNotificationResponse;
import apple.usernotifications.UNUserNotificationCenter;
import apple.usernotifications.enums.UNAuthorizationOptions;
import apple.usernotifications.enums.UNNotificationPresentationOptions;
import apple.usernotifications.protocol.UNUserNotificationCenterDelegate;

import org.moe.*;
//binding.firebasecore.FIRApp;
import binding.firebasecore.FIRApp;
import binding.firebaseinstanceid.FIRInstanceID;
import binding.firebaseinstanceid.c.FirebaseInstanceID;
import binding.firebasemessaging.FIRMessaging;
import binding.firebasemessaging.FIRMessagingRemoteMessage;
import binding.firebasemessaging.protocol.FIRMessagingDelegate;
//import org.moe.binding.firebasemessaging.FIRMessagingRemoteMessage;
//import org.moe.binding.firebasemessaging.protocol.FIRMessagingDelegate;
import org.moe.natj.general.Pointer;
import org.moe.natj.general.ann.RegisterOnStartup;
import org.moe.natj.objc.ann.ObjCBlock;
import org.moe.natj.objc.ann.Selector;

import java.util.Locale;

import java.util.Locale;

import static apple.foundation.c.Foundation.NSFoundationVersionNumber;
import static apple.foundation.c.Foundation.NSLocalizedString;

@RegisterOnStartup
public class Main extends NSObject implements UIApplicationDelegate, FIRMessagingDelegate {

    public static void main(String[] args) {
        UIKit.UIApplicationMain(0, null, null, Main.class.getName());

    }

    @Selector("alloc")
    public static native Main alloc();


    protected Main(Pointer peer) {
        super(peer);
    }

    private UIWindow window;



    private void handleNotification(NSDictionary<?, ?> userInfo) {
        NSDictionary<?, ?> aps = (NSDictionary<?, ?>)userInfo.get("aps");
        if(aps!=null){
            NSDictionary<?, ?> alert = (NSDictionary<?, ?>)aps.get(NSString.stringWithString("alert"));
            if(alert!=null){
                //show an alert with the notification body
            }
        }
    }

    @Override
    public void applicationDidReceiveRemoteNotification(UIApplication application, NSDictionary<?, ?> userInfo) {
        System.out.println("#B Main.applicationDidReceiveRemoteNotification");
        //handleNotification(userInfo);
    }

    @Override
    public void applicationDidReceiveRemoteNotificationFetchCompletionHandler(UIApplication application, NSDictionary<?, ?> userInfo, @ObjCBlock(name = "call_applicationDidReceiveRemoteNotificationFetchCompletionHandler") Block_applicationDidReceiveRemoteNotificationFetchCompletionHandler completionHandler) {
        System.out.println("#B Main.applicationDidReceiveRemoteNotificationFetchCompletionHandler");
       // handleNotification(userInfo);
        completionHandler.call_applicationDidReceiveRemoteNotificationFetchCompletionHandler(UIBackgroundFetchResult.NewData);
    }


   // FIRMessagingDelegate
  //  @Override
    @Selector("messaging:didRefreshRegistrationToken:")
    public void messagingDidRefreshRegistrationToken(FIRMessaging messaging, String fcmToken) {
        //save the token if you need it
    }

    @Override
    @Selector("application:didFinishLaunchingWithOptions:")
    public boolean applicationDidFinishLaunchingWithOptions(UIApplication application, NSDictionary launchOptions) {
        System.out.println("#B Main.applicationDidFinishLaunchingWithOptions: Start()");
        FIRApp.configure();

        //FIREBASE MESSAGING TODO see if these affect code in anyway
//        FIRMessaging.messaging().setShouldEstablishDirectChannel(true);
//        FIRMessaging.messaging().setDelegate(this);

        if (NSFoundationVersionNumber() <= 1299/*NSFoundationVersionNumber_iOS_9_x_Max*/) {//iOS <= 9
            UIUserNotificationSettings settings = UIUserNotificationSettings.settingsForTypesCategories(
                    (UIUserNotificationType.Sound | UIUserNotificationType.Alert | UIUserNotificationType.Badge),
                    null
            );
            UIApplication.sharedApplication().registerUserNotificationSettings(settings);
        } else {//iOS >= 10
            UNUserNotificationCenter.currentNotificationCenter().setDelegate(new UNUserNotificationCenterDelegate() {
                @Override
                public void userNotificationCenterDidReceiveNotificationResponseWithCompletionHandler(UNUserNotificationCenter center, UNNotificationResponse response, @ObjCBlock(name = "call_userNotificationCenterDidReceiveNotificationResponseWithCompletionHandler") Block_userNotificationCenterDidReceiveNotificationResponseWithCompletionHandler completionHandler) {
                    NSDictionary userInfo = response.notification().request().content().userInfo();
                    System.out.println("#B Main.userNotificationCenterDidReceiveNotificationResponseWithCompletionHandler");
                    //handleNotification(userInfo);

                    completionHandler.call_userNotificationCenterDidReceiveNotificationResponseWithCompletionHandler();
                }

                @Override
                public void userNotificationCenterWillPresentNotificationWithCompletionHandler(UNUserNotificationCenter center, UNNotification notification, @ObjCBlock(name = "call_userNotificationCenterWillPresentNotificationWithCompletionHandler") Block_userNotificationCenterWillPresentNotificationWithCompletionHandler completionHandler) {
                    NSDictionary userInfo = notification.request().content().userInfo();
                    System.out.println("#B Main.userNotificationCenterWillPresentNotificationWithCompletionHandler");
                    //handleNotification(userInfo);

                    completionHandler.call_userNotificationCenterWillPresentNotificationWithCompletionHandler(0/*UNNotificationPresentationOptions.None*/);
                }
            });
            UNUserNotificationCenter.currentNotificationCenter().requestAuthorizationWithOptionsCompletionHandler(
                    (UNAuthorizationOptions.Alert | UNAuthorizationOptions.Sound | UNAuthorizationOptions.Badge), (granted, error) -> { }
            );
        }

        UIApplication.sharedApplication().registerForRemoteNotifications();
        System.out.println("#B Main.applicationDidFinishLaunchingWithOptions: INSTANCE ID: "+FIRInstanceID.instanceID().token());
        //END FIREBASE MESSAGING

//        LoginPageViewController myViewController = LoginPageViewController.alloc().init();
//        window = UIWindow.alloc().initWithFrame(UIScreen.mainScreen().bounds());
//        window.setRootViewController(myViewController);
//        window.makeKeyAndVisible();

        return true;
    }

    @Override
    public void setWindow(UIWindow value) {
        window = value;
    }

    @Override
    public UIWindow window() {
        return window;
    }

    @Override
    public void applicationReceivedRemoteMessage(FIRMessagingRemoteMessage remoteMessage) {
        System.out.println("#B Main.applicationReceivedRemoteMessage");
    }
}

This is the FIRMessagingDelegate code from the link i shared before

import org.moe.natj.general.ann.Runtime;
import org.moe.natj.general.ann.Generated;
import org.moe.natj.general.ann.Library;
import org.moe.natj.objc.ObjCRuntime;
import org.moe.natj.objc.ann.ObjCProtocolName;
import org.moe.natj.objc.ann.Selector;

import binding.firebasemessaging.FIRMessagingRemoteMessage;

@Generated
@Library("FirebaseMessaging")
@Runtime(ObjCRuntime.class)
@ObjCProtocolName("FIRMessagingDelegate")
public interface FIRMessagingDelegate {
    @Generated
    @Selector("applicationReceivedRemoteMessage:")
    void applicationReceivedRemoteMessage(
            FIRMessagingRemoteMessage remoteMessage);
}
1 Like

You are right i dont need setShouldEnstablishDirectChannel. At this point i was able to get FCM data message notification in my code so can i assume Message Delegate set is non-mandatory?
The only other issue then is how to handle if FCM token refresh occurs without fixing messaging:didRefreshRegistrationToken method override issue.

This is the code of my FIRMessagingDelegate, try it.
The call to setDelegate is important to get the registration token, I’m not sure why you get that error it the interface is implemented.

@Generated
@Library("FirebaseMessaging")
@Runtime(ObjCRuntime.class)
@ObjCProtocolName("FIRMessagingDelegate")
public interface FIRMessagingDelegate {
    @Generated
    @IsOptional
    @Deprecated
    @Selector("applicationReceivedRemoteMessage:")
    default void applicationReceivedRemoteMessage(FIRMessagingRemoteMessage remoteMessage) {
        throw new java.lang.UnsupportedOperationException();
    }

    @Generated
    @IsOptional
    @Selector("messaging:didReceiveMessage:")
    default void messagingDidReceiveMessage(FIRMessaging messaging, FIRMessagingRemoteMessage remoteMessage) {
        throw new java.lang.UnsupportedOperationException();
    }

    @Generated
    @Selector("messaging:didRefreshRegistrationToken:")
    void messagingDidRefreshRegistrationToken(FIRMessaging messaging, String fcmToken);
}
1 Like

Updating FIRMessaging with your code fixed the Override problem.

Regards to setDelegate, referring to my main class, was able to get the FCM ID after FCM initialization here. Below line printed a working FCM code and i was able to sent message to my IOS device.

        System.out.println("#B Main.applicationDidFinishLaunchingWithOptions: INSTANCE ID: "+FIRInstanceID.instanceID().token());

Not sure what other adverse affect missing FIRMessaging.messaging().setDelegate(this); would have though.

You’ll miss the call to applicationReceivedRemoteMessage for iOS < 10, but that’s probably ok given that we are at iOS 12 now and most users have updated.

While i see
@Generated @Selector("setRemoteMessageDelegate:") and
public void setRemoteMessageDelegate( @Mapped(ObjCObjectMapper.class) FIRMessagingDelegate value)
I did not see setDelegate in the FIRMessaging.java
Can you share your setDelegate implementation in FIRMessaging.java. that might fix the problem.
And as you mentioned, in extreme case if i cant fix it, its should still be not a big deal considering only 5% of the devices are on <= ios 9

setDelegate is the method above setRemoteMessageDelegate in the link you posted, https://www.programcreek.com/java-api-examples/?code=TomGrill/gdx-firebase/gdx-firebase-master/ios-moe/src/com/google/firebasemessaging/FIRMessaging.java#

But now that I look at it, the code you are using must be different, because the implementation of FIRMessagingDelegate I see on that link match mine and not the one you posted.

Maybe you are using a different code?

somehow i imported older version of that code from TomGrill git account. thank you for pointing it out. i have fixed it now. Thank you for help on this matter throughout . :smiley:

Glad I was helpful :slight_smile: Have a nice day!