A maintained fork of com.williamrijksen.onesignal, the OneSignal push notification module for Titanium. This fork updates both platforms so the module builds and runs on current toolchains.
The module id stays com.williamrijksen.onesignal, so you can drop this into
an existing app without touching your JavaScript.
The original module still works, but its releases stopped in 2020 and the build has aged out from under it.
On iOS the problem is fatal. The last published iOS build is a fat static
library holding armv7 arm64 i386 x86_64, which predates Apple Silicon. There
is no arm64 simulator slice, so on any modern Mac the build stops here:
The app is using native modules that do not support arm64 simulators
and you are on an arm64 device: com.williamrijksen.onesignal
You cannot run your app in the simulator at all. The dead armv7 and i386
slices also draw App Store rejections.
On Android the module works but ships an old OneSignal SDK, and its manifest
sets a package attribute that current versions of the Android Gradle Plugin
reject outright.
iOS now builds against OneSignal iOS SDK 3.12.10, which ships XCFrameworks
containing an ios-arm64_x86_64-simulator slice. Several OneSignal calls were
removed between 2.x and 3.x, so the module code was ported:
| 2.x | 3.x |
|---|---|
initWithLaunchOptions:appId:handleNotificationReceived:handleNotificationAction:settings: |
setAppId: then initWithLaunchOptions: |
| notification received block | setNotificationWillShowInForegroundHandler: |
| notification opened block | setNotificationOpenedHandler: |
inFocusDisplayType = OSNotificationDisplayTypeNone |
complete the foreground handler with nil |
OSNotificationPayload |
folded into OSNotification |
OSNotificationPayload parseWithApns: |
not needed, 3.x reports cold start opens itself |
getPermissionSubscriptionState |
getDeviceState |
setSubscription: |
disablePush:, which is inverted |
setExternalUserId:withCompletion: |
setExternalUserId:withSuccess:withFailure: |
The event dictionary keys your JavaScript already reads, such as
notificationID and additionalData, are unchanged.
Android carries the OneSignal 4.x work from
m1ga's fork, which
resolves OneSignal Android SDK 4.8.x, plus a fix for the manifest package
attribute that modern Gradle refuses to build.
Download the zip for your platform from
Releases and
unzip it in your project root. Each zip contains a modules/ directory, so it
lands where Titanium expects.
unzip com.williamrijksen.onesignal-iphone-3.1.0.zip -d /path/to/your/app
unzip com.williamrijksen.onesignal-android-3.1.0.zip -d /path/to/your/appThen declare it in tiapp.xml:
<modules>
<module platform="iphone" version="3.1.0">com.williamrijksen.onesignal</module>
<module platform="android" version="3.1.0">com.williamrijksen.onesignal</module>
</modules>
<property name="OneSignal_AppID" type="string">your-onesignal-app-id</property>Android also reads the app id from a manifest entry:
<meta-data android:name="onesignal_app_id" android:value="your-onesignal-app-id" />Titanium SDK 13.0.0.GA or later. iOS builds need Xcode 15 or later.
const onesignal = require('com.williamrijksen.onesignal');
onesignal.sendTag({ key: 'facility', value: '42' });
onesignal.getTags(function (e) {
if (e.success) {
Ti.API.info(JSON.stringify(e.results));
}
});
onesignal.addEventListener('notificationOpened', function (e) {
Ti.API.info('opened: ' + e.title + ' ' + JSON.stringify(e.additionalData));
});Ask for permission when it makes sense in your flow, rather than at launch:
onesignal.promptForPushNotificationsWithUserResponse(function (e) {
Ti.API.info('accepted: ' + e.accepted);
});On Android 13 and later you also need the runtime notification permission, which the module does not request for you:
if (OS_ANDROID && Ti.Platform.Android.API_LEVEL >= 33
&& !Ti.Android.hasPermission('android.permission.POST_NOTIFICATIONS')) {
Ti.Android.requestPermissions(['android.permission.POST_NOTIFICATIONS']);
}Without that call, push is silently blocked on modern Android phones.
Other methods: deleteTag, setExternalUserId, removeExternalUserId,
setSubscription, getPermissionSubscriptionState, retrieveSubscribed,
retrievePlayerId, retrieveToken, postNotification, setLogLevel.
Verified by building and running, not only by compiling.
- iOS 26.5 simulator, Xcode 26.5, Titanium SDK 13.4.0.GA on an Apple Silicon Mac. The app launches, the module initialises, and the OneSignal SDK reaches its API with the configured app id.
- Android 14 emulator, Titanium SDK 13.4.0.GA. The module loads, its methods
are present, and
sendTagruns without throwing.
The 2019 Xcode project will not build on a current SDK, so generate a fresh module scaffold and copy these sources into it:
ti create --type module --platforms ios --name onesignal \
--id com.williamrijksen.onesignal --workspace-dir /tmp/modgen
cp ios/Classes/* /tmp/modgen/onesignal/ios/Classes/
cp ios/manifest ios/titanium.xcconfig /tmp/modgen/onesignal/ios/Keep the guid 67065763-fd5e-4069-a877-6c7fd328f877 so existing apps still
resolve the module. Download the four OneSignal 3.12.10 XCFrameworks into
platform/, then build:
cd /tmp/modgen/onesignal/ios && ti build -p ios --build-onlyTwo things will trip you up. Xcode resolves <OneSignal/OneSignal.h> only from
a concrete XCFramework slice, so titanium.xcconfig sets framework search paths
per SDK, including the Mac Catalyst slice that Titanium 13.4 also builds. And
the generated Xcode project compiles only the sources it was generated with, so
an added .m file is skipped without warning and its class turns up undefined
at link time. That is why the helper class lives inside the module's main file.
cd android && ti build -p android --build-onlyTitanium's ndk-build toolchain fails on NDK r27 and later, so pin r25 or r26.
The original module is by William Rijksen, with Android SDK 4.x work by Michael Gangolf. This repository is a GitHub fork of their work and keeps their history.
The upstream project never declared a license. Its LICENSE file is still the
Titanium template placeholder, which means the original code carries no grant
of rights and GitHub reports it as unlicensed. We have not added a license of
our own, because we cannot license someone else's code. Treat this fork the
same way you would treat upstream, and take it up with the original author if
you need certainty for commercial use.
Changes in this fork are offered freely back to the upstream project.