Push Notifications in Qt for iOS and Android
Note: So far, I’ve only gotten as far as building with Firebase and getting the Device Tokens on Android and iOS. But, getting to that point is (knock on wood) the largest portion of the work. I’m sure I’ll hit some more snags, but it was a journey to get this far, so I’ll put down pen to medium…
Things you need to know: Some Objective-C, a little more C++. A little QT knowledge, and some basic build and system knowledge, as well as typing things on the command line. And, you’ll also need Xcode installed and the Android SDK.
I’ll also cover building the apps on the command line as well, which should help if you like using your own editor instead of Qt Creator.
What’s behind Push Notifications?
Push notifications on iOS use APNS (Apple Push Notification Service) and FCM (Firebase Cloud Messaging), formerly known as GCM (Google Could Messaging). Technically, GCM is another service, but it’s deprecated. There’s also Windows (Windows Notification Service), but, for the amount of work, I’ll just stick with Android and iOS is enough for me. Please let me know if you have covered that topic, and I’ll link to you.
It’s definitely worth noting that Firebase can send push notifications to iOS devices. But, it’s just doing the same thing on the server that you would do yourself, so you have to provide your APNS credentials to Firebase. And, you can add the Firebase libraries to your iOS app to handle the client side of this. That’s a valid way to do it, but that’s not how I’m doing it here.
Server side Preparation
Notifications have a few moving pieces. So, instead of just a mobile app, or even a mobile app that’s a REST client to a server, you actually have to communicate with your “vendor”, which is Apple and Google. I won’t go over the specifics, but you basically need to do the same things in both cases:
- Register as a developer
- Get a developer signing key or token. Both services let you use one key for all your apps
- Register your unique application ID.
Generally, your application service will send a message to a device or devices by some unique device ID that FCM or APNS gives to the mobile app running on the user’s device. Your mobile app has to share that device ID with your application server so that the application server can tell FCM or APNS where to send a notification. Getting that ID is platform specific and what this article covers. Getting the ID to the application server is just your run of the mill client/server work.
You need to create a project, and an app in that project, then choose “Add Firebase to your Android App”, and choose the name, using the reverse domain system that you find in Java classes or iOS projects.
Once you’ve done that, you need to go to the Project Settings and get the google-services.json for your app and download it. We’ll put that to use once our application is ready to be configured.
Download the Firebase C++ API from that link and install it somewhere.
Go to the developer portal and go to the Certificates, Identifiers & Profiles
You have two options: Go through the big hassle, for each app, of creating a certificate signing request, uploading, generating the certificate, downloading it, exporting the key and … or… just create an APNs Auth Key that works for all apps. I’m doing the latter. And then create a unique identifier for your app. Unless you’re a masochist, keep it the same as the Firebase one. Be sure to check “Push Notifications” in your App Services.
Creating Your Qt Application
Choose “Qt Widgets” or one of the “Qt Quick” applications. For the point of this article, the debate between Widgets and Quick is irrelevant. I’ve done both, and I like both. And, I also kind of hate both. But, not important right now. For the platforms, You obviously need to choose Android and iOS. Push notifications don’t work on the simulator (on iOS, anyway), so you only have to choose the “real” platforms for this example. I just named my app PushApp.
Configure the Project
To receive notifications, you’ll need to have a unique name for your app. This is the bundle identifier in iOS found in the Info.plist, and the package name for Android in the AndroidManifest.xml (and other places). You can generate everything, and Qt (through qmake) will guess at these, but will most likely not be what you want. So, you can either build your project, and go to the build directory to get the generated files, or run qmake in another directory to just get the generated files. Both just run qmake on your project file, and some of Qt’s application specific tools, so let’s run qmake by hand, just so we know what’s actually going on. If you want to skip all this and use Creator, go ahead. You can just get your files from the build-PushApp-Android_for_… and build-PushApp-iOS…
Qt Tools: qmake, androiddeployqt
First for iOS, because it’s quite a bit simpler. In fact, you don’t even have to generate anything. But, I’ll do it, just because this section would be almost empty:
In your PushApp.pro:
It’s the same idea, but a bit more complicated.
At this point, we need to change the default Qt app name to our real one that matches the name that we created in Firebase in AndroidManifest.xml. And, there’s a bunch of stuff to do in the other files too.
Now, for the tough part. Adding the firebase API so that your code can access it and so your app has access to the libraries and jar files.
First, create a project.properties, and append to gradle.properties:
Then, for the complicated part: Edit the build.gradle. Add the properties in here, but don’t remove the ones that already exist.
Then, update your PushApp.pro with the android information:
Phew. Now for the fun part. Our app actually doesn’t do anything on the GUI, so just know, it’ll just be a blank screen. All the relevant information will be on the console. We’ll create a class to handle notification information for either FCM or APNs. There will be some #ifdef’s for the android part. If I was doing this for real, I might create a base interface class and subclasses for Android and iOS. But, we’ll just keep it as simple as we can right now.
Conceptually, we will be getting messages from origin. That origin will send messages to FCM or APNS. In order for that origin to send us messages, we need to give them our unique device ID. In both cases, the “system” will tell us our device ID through an overridden virtual function, which is FBListener in Android, or APNSApplicationDelegate in iOS. We’ll create a NotificationHandler as the common part. It will instantiate the FBListener for Android. For iOS, it’s a little more behind the scenes and we don’t have to worry about it.
This is just a singleton class that will be called by iOS and, if we’re compiling for Android, will instantiate the firebase listener class and initialized and register with the Firebase Server.
Create an FBListener class.
This class will initialize the Java environment, load the Java classes, and the Java/C++ interface. Since this only compiles for Android, move those files to an android stanza in the PushApp.pro:
If you go ahead and run this, you’ll see the app spew out a ton of messages, but at, or near, the end, you’ll see the Token qDebug() message print out.
The mechanics of getting your device token and receiving notifications are almost identical. In a “pure” iOS app, written in Swift or Objective-C, you would override or implement a few methods in your UIApplicationDelegate. Well, it’s the same thing in this case, although you’re restricted to Objective-C. Actually, Objective-C++, which is really the same thing. It compiles differently, which is the important thing, because that’s how you connect it with Qt. The Objective-C++ can call our NotificationHandler singleton to get the information into Qt.
The implementation should be self-evident. In Objective-C++, we want to add some methods to the UIApplicationDelegate, only, in this case, Qt is already subclassing it, so we want to add some methods to their subclass. This is just adding a category.
If you’ve ever done this before, this is old hat. Of course, we also need to override some other methods when we receive that actually notifications. This is only getting the device ID back into the Qt world.
After you create this file, you have to add it to the ios section of the PushApp.pro:
Then, run it. Your app should start, and it will ask you if it can send you push notifications. Say yes, and you’ll see the output from NotificationHandler::RegisterToken().
Well, that’s it. It’s not easy and it certainly wasn’t easy figuring all this stuff out, especially the android/gradle stuff. But, I still think it’s worth it to have a cross platform app. If you want to take a look at the (now dated) source code, I’ve put it onto github under PushApp.