Native Support

divbloxPHP allows you to export your progressive web app to a native project using React Native. To export to a native project you need the following:

  • Your web app needs to be deployed on a server with an ssl certificate
  • You need to configure your environment for native export:
    Native Export Config
  • Follow the steps in the "Export to Native" section on the divbloxPHP setup page to configure React Native and export your environment of choice

You can export a native app project for any of your configured environments. The export creates the following:

  • A React Native project, from the divbloxPHP template, that acts as a wrapper for your web app
  • The project already has supporting functions to communicate with your app environment's web server
  • The project has a boot screen pre-installed (You can edit this for your app)
  • The project has divbloxPHP icons pre-installed (You can edit these for your app)
  • The project checks for internet connectivity before trying to load your web environment and shows a helpful error message when it cannot connect
  • The project automatically registers a device with a permanent authentication token as a native device to ensure ease of use in terms of user authentication (Meaning, once a user logs in they will stay logged in by default)
  • A workflow skeleton for initialization of push notifications and other native specific requirements is already in place

Push Notifications

info

divbloxPHP push notifications implement Firebase Cloud Messaging. API integration to register and send push notifications is already present in every Divblox project

Support for push notifications on the native device is provided for by an API that allows for the registration of devices and their push registration tokens. The developer needs to implement the native library of their choice to handle push notifications on the device.

Server-side

In order to be able to send push notifications we need to have devices registered with push registration tokens. This data is stored in the entity "PushRegistration". A PushRegistration is device-specific, but divbloxPHP will link the PushRegistration to the relevant Account if an authentication takes place.

  • To register a device's push registration token we will use this api operation: /api/global_functions/updatePushRegistration.
  • You can read more about how this operation works by visiting its docs page: /api/global_functions/updatePushRegistration/docs

Push notifications can be sent from the server using the built-in divbloxPHP Project Functions. In this example we want to send a push notification to all users with the role "Administrator":

$PushRegistrationArray = PushRegistration::QueryArray(
dxQ::Equal(
dxQN::PushRegistration()->AccountObject->UserRoleObject->Role, "Administrator"
)
);
$TokenArray = [];
foreach ($PushRegistrationArray as $PushRegistrationObj) {
$TokenArray[] = $PushRegistrationObj->RegistrationId;
}
ProjectFunctions::deliverBatchedPushPayload(
$TokenArray,
"Admin message title",
"Here is a message only for admins");

Device-side

Divblox does not implement the device-side functionality to deal with push notifications by default, because:

  • There are many different flavours for frontend implementation (search npm registry for "react native push notifications"). The developer can choose the implementation of their choice
  • Setting up push notifications with Firebase Cloud Messaging (FCM) involves quite a few configuration steps for both Android and iOS. If the resulting configuration files are present in the React Native project, but not properly set up, the build process will fail

However, please see below a working example of how to implement this functionality in a freshly exported Divblox project:

Setting up the prerequisites

  1. Open your browser and go to Google Firebase Console. Then log in using your Google account

  2. From that page, click the "+" add project button to create a Google Firebase project

  3. Follow the steps to create your project and then add both an Android and an iOS app. When creating your Android and iOS apps, ensure that the "Android package name" and "iOS bundle ID" matches the "Widget Id" as configured in your environment

  4. You do not need to do the steps involving adding Firebase SDK & verification of installation for now

  5. After all the setup is complete, you need to have downloaded the following 2 files for Android and iOS:

    • google-services.json (Android)
    • GoogleService-info.plist (iOS)
  6. On the Firebase Project Overview page, ensure that your newly created project is selected and click on either the iOS or Android app to go to the project settings page

  7. Click on Cloud Messaging. You want to now copy the Server Key into your Divblox project:

    • In /divblox/config/framework/config.php update the value for "FIREBASE_SERVER_KEY_STR" to match your Server Key
  8. For iOS you will need to get an APNs Authentication Key. You can do this by following the steps provided by Apple at: https://developer.apple.com/account/resources/authkeys/list. Once you have the APNs key, upload it to your iOS app in Firebase

Configuring React Native

  1. Open /native/[Your Environment Name]/ios/[Your Environment Name].xcworkspace using Xcode. Ensure that the Bundle Identifier matches the "Widget Id" as configured in your environment.
  2. Edit /native/[Your Environment Name]/android/app/src/main/java/com/[Your Environment Name]/MainActivity.java, ensure the first line package [xxx] is package [Your Widget Id]
  3. Edit /native/[Your Environment Name]/android/app/src/main/java/com/[Your Environment Name]/MainApplication.java, ensure the first line package [xxx] is package [Your Widget Id]
  4. Edit the value for "package" to match your Widget Id in /native/[Your Environment Name]/android/app/src/main/AndroidManifest.xml and add the following after INTERNET permissions:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />
  1. In the same file, add the Firebase MESSAGING_EVENT before the closing of the <application> tag:
<application ...>
//...
<service android:name="io.invertase.firebase.messaging.RNFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
  1. Edit the value for "applicationId" to match your Widget Id in /native/[Your Environment Name]/android/app/build.gradle:
android {
//...
defaultConfig {
applicationId "[Your Widget Id]"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0"
}
//...
}
  1. Edit the value for "package" to match your Widget Id in /native/[Your Environment Name]/android/app/BUCK
android_build_config(
name = "build_config",
package = "[Your Widget Id]",
)
android_resource(
name = "res",
package = "[Your Widget Id]",
res = "src/main/res",
)
  1. Run this command from the android folder to clean Gradle:
./gradlew clean
  1. Install and configure React Native Firebase
npm install --save react-native-firebase
  • Copy the previously downloaded google-services.json to the /native/android/app/ folder
  • Edit /native/android/build.gradle and add this classpath dependency for Google Services:
dependencies {
classpath("com.android.tools.build:gradle:3.4.1")
classpath 'com.google.gms:google-services:4.2.0'
}
  • Edit /native/android/app/build.gradle and add this line to the bottom of the file:
apply plugin: "com.google.gms.google-services"
  • Also add these lines for the Firebase implementation to the dependencies in the same file:
dependencies {
//....
implementation "com.google.android.gms:play-services-base:16.1.0"
implementation "com.google.firebase:firebase-core:17.0.1"
implementation "com.google.firebase:firebase-messaging:19.0.1"
implementation 'me.leolin:ShortcutBadger:1.1.21@aar'
//....
}
  • Edit /native/[Your Environment Name]/android/app/src/main/java/com/[Your Environment Name]/MainApplication.java and add these imports for RNFirebaseMessagingPackage and RNFirebaseNotificationsPackage
import io.invertase.firebase.messaging.RNFirebaseMessagingPackage;
import io.invertase.firebase.notifications.RNFirebaseNotificationsPackage;
  • In the same file, add those packages to the list of packages:
@Override
protected List<ReactPackage> getPackages() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
packages.add(new RNFirebaseMessagingPackage());
packages.add(new RNFirebaseNotificationsPackage());
return packages;
}
  • At this point, the build for our Android app might fail. We solve this by Enabling Multidex. Open the /native//android/app/build.gradle file. Under dependencies we need to add the module, and then enable it within our defaultConfig:
android {
defaultConfig {
// ...
multiDexEnabled true
}
// ...
}
dependencies {
implementation 'com.android.support:multidex:1.0.3'
}
  • Open /native/[Your Environment Name]/ios/[Your Environment Name].xcworkspace using Xcode and add the previously downloaded GoogleService-Info.plist to the XCode project name by right clicking and selecting "Add Files to [Your Environment Name]"
  • In Xcode, enable the remote notifications by clicking on the project name in the left pane then clicking the Capabilities tab. Add Push Notifications.
  • In Xcode, edit the Pods/podfile and add these lines:
pod 'Firebase/Core'
pod 'Firebase/Messaging'
  • Also add the Pod path for RNFirebase to the app under "# Pods for [Your Environment Name]":
pod 'RNFirebase', :path => '../node_modules/react-native-firebase/ios'
  • Run this command from the terminal inside the ios folder:
pod update
  • Edit /native/[Your Environment Name]/ios/[Your Environment Name]/AppDelegate.m and add the imports for Firebase, React Native Firebase Notifications, and Messaging:
#import <Firebase.h>
#import "RNFirebaseNotifications.h"
#import "RNFirebaseMessaging.h"
  • At the beginning of the didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method add these lines to initialize Firebase and RNFirebaseNotifications:
[FIRApp configure];
[RNFirebaseNotifications configure];
  • Add a new method to receive local RNFirebaseNotifications:
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
[[RNFirebaseNotifications instance] didReceiveLocalNotification:notification];
}
  • Add a new method to receive remote RNFirebaseNotifications:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo
fetchCompletionHandler:(nonnull void (^)(UIBackgroundFetchResult))completionHandler{
[[RNFirebaseNotifications instance] didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
  • Add a new method to register with Firebase and receive the FCM token:
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
[[RNFirebaseMessaging instance] didRegisterUserNotificationSettings:notificationSettings];
}

Handling Push Notifications in App

You can add the following code to the file divblox_react_native.js in the root of your native project:

import firebase from 'react-native-firebase';
import {Alert} from "react-native";
  • Edit the function registerPushNotifications() to handle the registration correctly:
async registerPushNotifications(success_callback,failed_callback) {
// You can safely remove this line
//console.log("TODO: Put your code that asks for push notification permissions here. Once a successful Push" +
// " registration ID is received, send it to the server with");
// Add these 2 lines:
this.checkPermission();
this.messageListener();
}
  • Then simply add the following functions to the same file inside the Divblox class:
checkPermission = async () => {
const enabled = await firebase.messaging().hasPermission();
if (enabled) {
this.getFcmToken();
} else {
this.requestPermission();
}
};
getFcmToken = async () => {
const fcmToken = await firebase.messaging().getToken();
if (fcmToken) {
this.createPushRegistration(
fcmToken,
function (data) {
//console.log("Registered with app: "+data);
},
function (data) {
//console.log("NOT Registered with app: "+data);
}
);
} else {
//this.showAlert('Failed', 'No token received');
}
};
requestPermission = async () => {
try {
await firebase.messaging().requestPermission();
this.getFcmToken();
// User has authorised
} catch (error) {
// User has rejected permissions
}
};
messageListener = async () => {
this.notificationListener = firebase
.notifications()
.onNotification((notification) => {
const { title, body } = notification;
this.showAlert(title, body);
});
this.notificationOpenedListener = firebase
.notifications()
.onNotificationOpened((notificationOpen) => {
const { title, body } = notificationOpen.notification;
//this.showAlert(title, body);
});
const notificationOpen = await firebase
.notifications()
.getInitialNotification();
if (notificationOpen) {
const { title, body } = notificationOpen.notification;
//this.showAlert(title, body);
}
this.messageListener = firebase.messaging().onMessage((message) => {
//console.log(JSON.stringify(message));
});
};
showAlert = (title, message) => {
Alert.alert(
title,
message,
[{ text: "OK", onPress: () => console.log("OK Pressed") }],
{ cancelable: false }
);
};