This guide discusses how to implement local and push notifications.
Solar2D supports two types of notifications: local and push. The purpose of both notification types is to notify the user about something — a message or an upcoming appointment, for example — when the application isn't running in the foreground. The essential difference between local notifications and push notifications is simple:
Local notifications are scheduled by an app locally and are delivered by the same device.
Push notifications are sent by a remote server (its provider) which sends these notifications to devices on which the app is installed.
Notifications are only supported on iOS and Android, not on macOS desktop, Windows desktop, Apple TV, or Android TV.
To use push notifications, remember the following points:
The app has to support a registration process and it must support incoming messages.
You must configure your app and devices so that Apple and/or Google recognizes them.
If using iOS/Apple, your app cannot send push requests directly to their servers.
We recommend that you use a third-party service to send push requests to Apple or Google, who in turn sends them onward to devices. That service can be a company like OneSignal, PushWoosh, or Airship that specializes in push notifications.
How an app reacts to notifications depends on its state:
Not running — the app has exited by design, user decision, reboot, etc. In this state, if the user interacts with the notification, the operating system will launch the app. Information about the notification event will be passed to the app using "launch arguments". The app should watch for these and react accordingly, for example, show a "snooze" button for an alarm or open a native.newWebView() if it has a URL. Note, however, that if the user starts the app by tapping directly on its icon, the notification will not be presented.
Backgrounded — the app is running but it's not the active app. In this state, if the user interacts with the notification by swiping on the unlock screen while the notification is showing or tapping on it in the notification center, the operating system will bring the app to the foreground and make it active. At this time, the app will receive the notification event but not receive launch arguments.
Foregrounded — the app is active and the user is currently interacting with it. In this state, the app will receive the notification event.
To use the Notifications plugin, add an entry into the plugins
table of build.settings
. When added, the build server will integrate the plugin during the build phase.
settings = { plugins = { ["plugin.notifications.v2"] = { publisherId = "com.coronalabs" }, }, }
Inside this build.settings
file, also include the minSdkVersion
of 16 or higher. For more info about setting minimum SDK version, see this
settings = { android = { minSdkVersion = "16", }, }
Then, within the code module which uses notifications functions, simply require()
the library as follows:
local notifications = require( "plugin.notifications.v2" )
For Android, all new apps, or existing apps being updated with push notifications for the first time, must use Firebase Cloud Messaging (FCM), supported by the current Notifications plugin (above). If you have a legacy app already configured for Google Cloud Messaging (GCM), it will continue to work indefinitely, but you should continue using the legacy plugin instead of the current notifications plugin.
For iOS, you can either use the Apple Push Notification Service (APNS) or Firebase Cloud Messaging (FCM) for push notifications (see below).
Much of the iOS configuration for push notifications is done within the Apple Developer portal. In addition, you must specify what types of notifications your app will use within the notification
→ iphone
→ types
config.lua
:
application = { notification = { iphone = { types = { "badge", "sound", "alert" }, }, }, }
If you want to use Google Firebase Cloud Messaging (FCM) for push notifications, follow these additional steps:
Copy GoogleService-Info.plist
, provided in the Firebase console, to your project's root directory alongside main.lua
.
Add the following entries to the iphone
→ plist
build.settings
:
settings = { iphone = { plist = { UIBackgroundModes = { "remote-notification" }, FirebaseAppDelegateProxyEnabled = false, }, }, }
Android also requires some additional configuration for push notifications:
Copy google-services.json
, provided in the Firebase console, to your project's root directory alongside main.lua
.
Add an additional useGoogleServicesJson
entry into the android
table of build.settings
. When added, the build server will read the settings from the JSON file and integrate them into your app during the build phase.
settings = { android = { useGoogleServicesJson = true, }, }
You can set custom notification icons in a project by adding the following files to the root of the project directory, just like custom application icons. Please see Google's official documentation for further details.
File | Size (w×h) |
---|---|
IconNotificationDefault-ldpi.png |
18 × 18 |
IconNotificationDefault-mdpi.png |
24 × 24 |
IconNotificationDefault-hdpi.png |
36 × 36 |
IconNotificationDefault-xhdpi.png |
48 × 48 |
IconNotificationDefault-xxhdpi.png |
72 × 72 |
IconNotificationDefault-xxxhdpi.png |
96 × 96 |
To schedule a local notification, use the notifications.scheduleNotification() function and specify the time of the future event in one of two formats:
Number of seconds from the call time when the notification should be delivered.
Time specified in Coordinated Universal Time (UTC). This should be a time table as returned by os.date("!*t")
. Note that a common pitfall is to forget the exclamation point and pass "*t"
instead "!*t"
—
You should also pass an options
table containing any of the following parameters:
alert
(string) — The notification message to be displayed to the user. If the application is not currently running, a system alert will display this message.
badge
(number) — This option is only supported on iOS and it indicates the badge number to be displayed on the application icon when the scheduled notification triggers. This replaces the last badge number that was applied. Set to 0
to omit the badge number.
sound
(string) — Name of the sound file in the system.ResourceDirectory to be played when the scheduled notification triggers. This sound is only played if the app is not currently in the foreground. On iOS, there are limitations on the kinds of sound that can be played (consult Apple's documentation for more details).
custom
(table) — A table that will be delivered with the notification event. This allows you to pass custom information with the notification.
To cancel a local notification before it triggers, use the notifications.cancelNotification() function and pass the ID returned by notifications.scheduleNotification().
In accordance with the Solar2D event/listener model, if your app is running in either the foreground or background and a notification arrives, you'll receive a notification event. It's your responsibility to initialize the system and set up a listener function to handle these events. Please review the following framework and then read the detailed subsections below.
local function notificationListener( event ) if ( event.type == "remote" ) then -- Handle the push notification elseif ( event.type == "local" ) then -- Handle the local notification end end Runtime:addEventListener( "notification", notificationListener )
A notification event returns a table of information which you can use to manage the specific notification. This table includes the following:
"active"
or "inactive"
."notification"
."alarm.caf"
."local"
for local notifications or "remote"
for push notifications.When the operating system, as the result of an incoming notification, starts your app from an inactive (not running) state, the app receives the data as part of launchArgs
instead of triggering a notification event. You can use your notificationListener
function to process the launch arguments.
local launchArgs = ... if ( launchArgs and launchArgs.notification ) then notificationListener( launchArgs.notification ) end
This launchArgs
processing must be in main.lua
since it's the code which receives the data.
Notification badges on iOS are easily recognized by a small circle/number overlaying the app icon. If a notification passes a badge
value, a badge equal to that value will appear over the app icon.
It's your responsibility to read and set the badge number depending on its previous value. This is accomplished with the native.getProperty() and native.setProperty() functions. In almost every case, when the user opens/handles the notification, you should decrement the badge number by 1 as follows:
-- Decrement the badge number by 1 local function notificationListener( event ) if ( event.type == "local" ) then -- Handle the local notification local badgeNum = native.getProperty( "applicationIconBadgeNumber" ) if ( badgeNum > 0 ) then badgeNum = badgeNum - 1 native.setProperty( "applicationIconBadgeNumber", badgeNum ) end elseif ( event.type == "remote" ) then -- Handle the push notification if ( event.badge and event.badge > 0 ) then native.setProperty( "applicationIconBadgeNumber", event.badge - 1 ) end end end
Alternatively, you can clear the badge entirely:
native.setProperty( "applicationIconBadgeNumber", 0 )
By default, the device will play a sound when a notification comes in. You can specify a custom sound file in your project to play instead of the default sound. For instance, if you have the file notification.wav
inside your app bundle, you can specify the string "notification.wav"
as part of the push bundle (if the sending service supports it) and the operating system will play that custom sound instead of the default. In this case, the event.sound
entry in the event listener will equal this string.
On iOS, when you're ready to ask the user for permission to allow push notifications, you should call notifications.registerForPushNotifications(). This will show the popup which asks the user if they want to enable push notifications. It's recommended that you only ask for this permission when you need it and not at the startup of your app.
If you're using Google Firebase Cloud Messaging (FCM) for push notifications, you should pass an additional parameter to notifications.registerForPushNotifications(). This parameter should be a table containing the useFCM
key with a value of true
:
notifications.registerForPushNotifications( { useFCM=true } )
Calling notifications.registerForPushNotifications() will trigger a remote registration event to your notification listener function. To accommodate this, add an additional condition:
local function notificationListener( event ) if ( event.type == "remote" ) then -- Handle the push notification elseif ( event.type == "remoteRegistration" ) then -- Code to register your device with the service elseif ( event.type == "local" ) then -- Handle the local notification end end