react-native-notify-sphere 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,37 +1,471 @@
1
1
  # react-native-notify-sphere
2
2
 
3
- test
3
+ A React Native push notification SDK built on Firebase Cloud Messaging (FCM) and Notifee. Handles delivery tracking, press tracking, user segmentation via tags, and rich notifications — with full Android and iOS support.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ 1. [Features](#features)
10
+ 2. [Prerequisites](#prerequisites)
11
+ 3. [Installation](#installation)
12
+ 4. [Android Setup](#android-setup)
13
+ 5. [iOS Setup](#ios-setup)
14
+ 6. [Usage](#usage)
15
+ 7. [API Reference](#api-reference)
16
+ 8. [FCM Payload Format](#fcm-payload-format)
17
+ 9. [Troubleshooting](#troubleshooting)
18
+ 10. [Version History](#version-history)
19
+
20
+ ---
21
+
22
+ ## Features
23
+
24
+ - ✅ Foreground, background, and terminated-state notification handling
25
+ - ✅ Delivery confirmation API — fires in all three app states
26
+ - ✅ Press / click tracking API
27
+ - ✅ Smart re-registration — only calls the API when FCM token or user details change
28
+ - ✅ Automatic FCM token refresh handling
29
+ - ✅ User segmentation via custom tags
30
+ - ✅ Rich notifications — images, custom sounds, action buttons
31
+ - ✅ Bearer token authentication on all API calls
32
+ - ✅ Full TypeScript support
33
+ - ✅ Android & iOS
34
+
35
+ ---
36
+
37
+ ## Prerequisites
38
+
39
+ - React Native `0.70+`
40
+ - A Firebase project with Cloud Messaging enabled
41
+ - A **NotifySphere account** with an App ID and API Token
42
+
43
+ ---
4
44
 
5
45
  ## Installation
6
46
 
47
+ ### Step 1 — Install the package and peer dependencies
7
48
 
8
- ```sh
49
+ ```bash
9
50
  npm install react-native-notify-sphere
51
+
52
+ # Peer dependencies (native — must be installed in your app)
53
+ npm install @react-native-firebase/app
54
+ npm install @react-native-firebase/messaging
55
+ npm install @notifee/react-native
56
+ npm install @react-native-async-storage/async-storage
57
+ ```
58
+
59
+ ### Step 2 — iOS: install pods
60
+
61
+ ```bash
62
+ cd ios && pod install && cd ..
63
+ ```
64
+
65
+ ---
66
+
67
+ ## Android Setup
68
+
69
+ ### 1. Add `google-services.json`
70
+
71
+ Download from the Firebase Console and place it at `android/app/google-services.json`.
72
+
73
+ ### 2. Configure `android/build.gradle`
74
+
75
+ ```gradle
76
+ buildscript {
77
+ dependencies {
78
+ classpath 'com.google.gms:google-services:4.4.0'
79
+ }
80
+ }
81
+ ```
82
+
83
+ ### 3. Configure `android/app/build.gradle`
84
+
85
+ ```gradle
86
+ apply plugin: "com.android.application"
87
+ apply plugin: "com.facebook.react"
88
+
89
+ android {
90
+ packagingOptions {
91
+ pickFirst 'lib/x86/libc++_shared.so'
92
+ pickFirst 'lib/x86_64/libc++_shared.so'
93
+ pickFirst 'lib/armeabi-v7a/libc++_shared.so'
94
+ pickFirst 'lib/arm64-v8a/libc++_shared.so'
95
+ }
96
+ }
97
+
98
+ // Must be the last line in the file
99
+ apply plugin: 'com.google.gms.google-services'
100
+ ```
101
+
102
+ ### 4. Update `AndroidManifest.xml`
103
+
104
+ ```xml
105
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
106
+ <uses-permission android:name="android.permission.INTERNET" />
107
+ <uses-permission android:name="android.permission.VIBRATE" />
108
+
109
+ <application ...>
110
+ <!-- Default notification icon -->
111
+ <meta-data
112
+ android:name="com.google.firebase.messaging.default_notification_icon"
113
+ android:resource="@drawable/ic_stat_onesignal_default"
114
+ tools:replace="android:resource" />
115
+
116
+ <!-- Default notification channel -->
117
+ <meta-data
118
+ android:name="com.google.firebase.messaging.default_notification_channel_id"
119
+ android:value="channel_default"
120
+ tools:replace="android:value" />
121
+ </application>
122
+ ```
123
+
124
+ ### 5. Add notification icon
125
+
126
+ Place your notification icon at:
127
+ ```
128
+ android/app/src/main/res/drawable/ic_stat_onesignal_default.png
129
+ ```
130
+
131
+ ---
132
+
133
+ ## iOS Setup
134
+
135
+ ### 1. Add `GoogleService-Info.plist`
136
+
137
+ Download from the Firebase Console, open Xcode, and drag the file into your project root. Make sure "Copy items if needed" is checked and it is added to all targets.
138
+
139
+ ### 2. Enable capabilities in Xcode
140
+
141
+ Go to **Target → Signing & Capabilities → + Capability** and add:
142
+ - `Push Notifications`
143
+ - `Background Modes` → check **Remote notifications**
144
+
145
+ ### 3. Update `AppDelegate.mm`
146
+
147
+ ```objc
148
+ #import <Firebase.h>
149
+ #import <UserNotifications/UserNotifications.h>
150
+
151
+ - (BOOL)application:(UIApplication *)application
152
+ didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
153
+ {
154
+ if ([FIRApp defaultApp] == nil) {
155
+ [FIRApp configure];
156
+ }
157
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
158
+ center.delegate = self;
159
+ return YES;
160
+ }
161
+
162
+ - (void)userNotificationCenter:(UNUserNotificationCenter *)center
163
+ willPresentNotification:(UNNotification *)notification
164
+ withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
165
+ {
166
+ completionHandler(UNNotificationPresentationOptionSound |
167
+ UNNotificationPresentationOptionAlert |
168
+ UNNotificationPresentationOptionBadge);
169
+ }
10
170
  ```
11
171
 
172
+ ---
12
173
 
13
174
  ## Usage
14
175
 
176
+ You need two things from your NotifySphere dashboard before you start:
177
+ - **App ID** — identifies your application
178
+ - **API Token** — authenticates all requests to the NotifySphere API
179
+
180
+ ### `index.js` — register the background handler
181
+
182
+ This **must** be called before `AppRegistry.registerComponent()` so the handler is in place when the app is woken from a terminated state to handle a background notification.
15
183
 
16
184
  ```js
17
- import { multiply } from 'react-native-notify-sphere';
185
+ import { AppRegistry } from 'react-native';
186
+ import AsyncStorage from '@react-native-async-storage/async-storage';
187
+ import App from './src/App';
188
+ import { name as appName } from './app.json';
189
+ import NotifySphere from 'react-native-notify-sphere';
190
+ import { API_TOKEN } from './src/App';
191
+
192
+ const STORAGE_KEY = '@notifysphere_subscription_id';
193
+
194
+ // Load the persisted subscription_id so delivery receipts include it
195
+ // even when the app starts from a terminated state.
196
+ AsyncStorage.getItem(STORAGE_KEY).then((subscriptionId) => {
197
+ NotifySphere.setBackgroundHandler({
198
+ apiKey: API_TOKEN,
199
+ subscriptionId: subscriptionId ?? undefined,
200
+ });
201
+ });
202
+
203
+ AppRegistry.registerComponent(appName, () => App);
204
+ ```
205
+
206
+ ### `App.tsx` — initialize and listen
207
+
208
+ ```tsx
209
+ import { useEffect } from 'react';
210
+ import { Platform } from 'react-native';
211
+ import NotifySphere from 'react-native-notify-sphere';
212
+
213
+ const APP_ID = 'your-notifysphere-app-id';
214
+ export const API_TOKEN = 'your-api-token'; // Get this from your NotifySphere dashboard
215
+ const PUSH_TYPE = Platform.OS === 'android' ? 'AndroidPush' : 'IOSPush';
216
+
217
+ export default function App() {
218
+ useEffect(() => {
219
+ const init = async () => {
220
+ // Safe to call on every app open — only hits the registration API
221
+ // when the FCM token or user details have changed.
222
+ const subscriptionId = await NotifySphere.initialize({
223
+ applicationUserId: 123, // your internal user ID
224
+ type: PUSH_TYPE,
225
+ appId: APP_ID,
226
+ apiKey: API_TOKEN,
227
+ name: 'John Doe',
228
+ email: 'john@example.com',
229
+ phone: '9876543210', // string — preserves leading zeros
230
+ lat: '26.9124',
231
+ long: '75.7873',
232
+ city: 'Jaipur',
233
+ state: 'Rajasthan',
234
+ tags: {
235
+ userType: 'customer',
236
+ premium: 'true',
237
+ },
238
+ debug: __DEV__, // enable verbose logs in development
239
+ });
240
+
241
+ console.log('NotifySphere subscriptionId:', subscriptionId);
242
+
243
+ // Optionally update tags without re-registering the device
244
+ await NotifySphere.updateTags({
245
+ applicationUserId: 123,
246
+ type: PUSH_TYPE,
247
+ tags: { lastLogin: new Date().toISOString() },
248
+ });
249
+ };
250
+
251
+ init();
252
+
253
+ // Listen for all notification events
254
+ NotifySphere.onNotification((notification, type) => {
255
+ console.log('Notification event:', type, notification);
256
+
257
+ if (type === 'press' || type === 'opened' || type === 'initial') {
258
+ // User tapped the notification — navigate, show modal, etc.
259
+ }
18
260
 
19
- // ...
261
+ if (type === 'received') {
262
+ // Notification arrived while the app was in the foreground
263
+ }
264
+ });
20
265
 
21
- const result = multiply(3, 7);
266
+ // Clean up listeners on unmount / logout
267
+ return () => NotifySphere.destroy();
268
+ }, []);
269
+ }
22
270
  ```
23
271
 
272
+ > **Security note:** Never commit your `API_TOKEN` to source control. In production, read it from a secure config file or environment variable (e.g. via `react-native-config`).
273
+
274
+ ---
275
+
276
+ ## API Reference
277
+
278
+ ### `NotifySphere.initialize(config)`
279
+
280
+ Initialises the SDK, registers the device with the NotifySphere server, and sets up notification listeners. Safe to call on every app open — the registration API is only called when the FCM token or user details have changed since the last run.
281
+
282
+ **Returns:** `Promise<string | undefined>` — the `subscription_id` assigned by the server.
283
+
284
+ | Field | Type | Required | Description |
285
+ |---|---|---|---|
286
+ | `applicationUserId` | `number` | ✅ | Your internal user ID for this device |
287
+ | `type` | `string` | ✅ | `'AndroidPush'` or `'IOSPush'` |
288
+ | `appId` | `string` | ✅ | Your NotifySphere App ID |
289
+ | `apiKey` | `string` | ✅ | Bearer token from your NotifySphere dashboard |
290
+ | `name` | `string` | | User display name |
291
+ | `email` | `string` | | User email address |
292
+ | `phone` | `string` | | User phone number (string to preserve leading zeros) |
293
+ | `lat` | `string` | | User latitude |
294
+ | `long` | `string` | | User longitude |
295
+ | `city` | `string` | | User city |
296
+ | `state` | `string` | | User state / region |
297
+ | `tags` | `Record<string, string>` | | Custom key-value segmentation tags |
298
+ | `baseUrl` | `string` | | Override the API base URL (defaults to hosted service) |
299
+ | `trackingUrl` | `string` | | Override the press-tracking endpoint |
300
+ | `deliveryUrl` | `string` | | Override the delivery-confirmation endpoint |
301
+ | `debug` | `boolean` | | Enable verbose `[NotifySphere]` console logs (default: `false`) |
302
+
303
+ ---
304
+
305
+ ### `NotifySphere.setBackgroundHandler(config?)`
306
+
307
+ Registers Firebase and Notifee background/terminated-state handlers. **Must be called in `index.js`** before `AppRegistry.registerComponent()`.
308
+
309
+ When the app is **terminated**, `initialize()` never runs, so any config you passed there is not available. Pass `apiKey` and `subscriptionId` here directly so delivery receipts still work.
310
+
311
+ | Field | Type | Description |
312
+ |---|---|---|
313
+ | `apiKey` | `string` | Bearer token — required for authenticated delivery receipts in terminated state |
314
+ | `subscriptionId` | `string` | Persisted subscription ID — include it so receipts are linked to the correct device |
315
+ | `deliveryUrl` | `string` | Override the delivery URL for terminated state (optional) |
316
+
317
+ ---
318
+
319
+ ### `NotifySphere.onNotification(callback)`
320
+
321
+ Register a callback to receive all notification events.
322
+
323
+ ```ts
324
+ NotifySphere.onNotification((notification, type) => {
325
+ // notification: { title, body, data, image, sound }
326
+ // type: 'received' | 'press' | 'opened' | 'initial'
327
+ });
328
+ ```
329
+
330
+ | Event type | When it fires |
331
+ |---|---|
332
+ | `received` | Notification arrived while the app was in the foreground |
333
+ | `press` | A Notifee background notification was tapped |
334
+ | `opened` | App brought from background via notification tap |
335
+ | `initial` | App launched from a quit state via notification tap |
336
+
337
+ ---
338
+
339
+ ### `NotifySphere.updateTags(params)`
340
+
341
+ Updates user tags without re-registering the device. `appId` is optional — falls back to the value passed in `initialize()`.
342
+
343
+ ```ts
344
+ await NotifySphere.updateTags({
345
+ applicationUserId: 123,
346
+ type: 'AndroidPush',
347
+ tags: { premium: 'true', age: '33' },
348
+ });
349
+ ```
350
+
351
+ | Field | Type | Required | Description |
352
+ |---|---|---|---|
353
+ | `applicationUserId` | `number` | ✅ | Your internal user ID |
354
+ | `type` | `string` | ✅ | `'AndroidPush'` or `'IOSPush'` |
355
+ | `tags` | `Record<string, string>` | ✅ | Tags to update |
356
+ | `appId` | `string` | | Defaults to the `appId` passed in `initialize()` |
357
+
358
+ ---
359
+
360
+ ### `NotifySphere.destroy()`
361
+
362
+ Unsubscribes all active listeners and resets internal state. Call this on logout or when you no longer need notifications.
363
+
364
+ ```ts
365
+ NotifySphere.destroy();
366
+ ```
367
+
368
+ ---
369
+
370
+ ### `NotifySphere.checkApplicationPermission()`
371
+
372
+ Requests notification permission from the OS and returns `true` if granted. Called automatically by `initialize()` — you only need to call this directly if you want to check permission status before initialising.
373
+
374
+ ---
375
+
376
+ ## FCM Payload Format
377
+
378
+ NotifySphere expects **data-only** FCM messages (no `notification` field). This is required for delivery confirmation to work in the terminated state.
379
+
380
+ ```json
381
+ {
382
+ "to": "<device-fcm-token>",
383
+ "data": {
384
+ "title": "Hello!",
385
+ "body": "You have a new message.",
386
+ "notification_id": "uuid-here",
387
+ "sound": "default",
388
+ "imageUrl": "https://example.com/image.jpg",
389
+ "smallIcon": "ic_stat_onesignal_default",
390
+ "redirect_action": "home"
391
+ }
392
+ }
393
+ ```
394
+
395
+ > **Why data-only?** If a `notification` field is present in the FCM payload, the OS handles display silently — your JavaScript never runs and delivery receipts cannot fire in the terminated state.
396
+
397
+ ### Supported `data` fields
398
+
399
+ | Field | Description |
400
+ |---|---|
401
+ | `title` | Notification title |
402
+ | `body` | Notification body text |
403
+ | `notification_id` | ID used for delivery and press tracking |
404
+ | `sound` | Sound file name without extension, or `'default'` |
405
+ | `imageUrl` | URL for a big-picture style notification image |
406
+ | `smallIcon` | Android small icon resource name (falls back to `ic_stat_onesignal_default`) |
407
+ | `actionButtons` | JSON string of Notifee action button definitions |
408
+
409
+ ---
410
+
411
+ ## Troubleshooting
412
+
413
+ ### API calls returning 401 Unauthorized
414
+
415
+ Make sure you are passing `apiKey` in both `initialize()` and `setBackgroundHandler()`. The API Token can be found in your NotifySphere dashboard under your app settings.
416
+
417
+ ### Notifications not displaying on Android
418
+
419
+ Check that `ic_stat_onesignal_default.png` exists at `android/app/src/main/res/drawable/`. Android silently drops notifications with a missing or invalid small icon.
420
+
421
+ ### Delivery receipt not firing in terminated state
422
+
423
+ Make sure you are sending a **data-only** FCM message (no `notification` field in the payload). Also confirm that `setBackgroundHandler()` is called in `index.js` before `AppRegistry.registerComponent()`, and that you are passing both `apiKey` and `subscriptionId` to it.
424
+
425
+ ### `async-storage` build error
426
+
427
+ Use `@react-native-async-storage/async-storage@^1.23.1`. Version 2+ requires a custom Maven repository that is not configured by default in most React Native projects.
428
+
429
+ ### iOS notifications not working
430
+
431
+ Push notifications do not work on the iOS Simulator. Test on a physical device with a push-enabled provisioning profile.
432
+
433
+ ### Debug logging
434
+
435
+ Pass `debug: true` (or `debug: __DEV__`) to `initialize()` to see verbose `[NotifySphere]` logs in the Metro console:
436
+
437
+ ```ts
438
+ await NotifySphere.initialize({ ..., debug: __DEV__ });
439
+ ```
440
+
441
+ ---
442
+
443
+ ## Version History
444
+
445
+ ### v1.2.0 (March 2026)
446
+
447
+ - **Bearer token authentication** — all API endpoints now require an `apiKey` (Bearer token); added to `initialize()`, `registerDevice`, and `setBackgroundHandler()`
448
+ - **Updated API URL structure** — endpoints now follow `client/{appId}/...` path pattern
449
+ - **Fixed `registerDevice` URL** — corrected a template literal bug that was constructing an invalid URL
450
+ - **Fixed `updateTags` URL** — removed incorrect `/apps/` path segment
451
+ - **Dynamic tracking / delivery URLs** — `trackingUrl` and `deliveryUrl` are now built with `appId` at runtime rather than using static defaults
24
452
 
25
- ## Contributing
453
+ ### v1.1.0 (January 2026)
26
454
 
27
- - [Development workflow](CONTRIBUTING.md#development-workflow)
28
- - [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
29
- - [Code of conduct](CODE_OF_CONDUCT.md)
455
+ - **Delivery confirmation API** — fires in foreground, background, and terminated state
456
+ - **Smart re-registration** registration API only called when FCM token or user details change
457
+ - **Automatic token refresh** — `onTokenRefresh` watcher re-registers without requiring another `initialize()` call
458
+ - **`destroy()` method** — clean listener unsubscription on logout
459
+ - **Configurable endpoints** — `baseUrl`, `trackingUrl`, `deliveryUrl` in `initialize()`
460
+ - **`phone` changed to `string`** — prevents corruption of numbers with leading zeros
461
+ - **`appId` stored internally** — `updateTags` no longer requires passing `appId` again
462
+ - **`debug` flag** — all `console.log` calls gated behind `debug: true`
463
+ - **Removed unused dependencies** — `react-native-device-info`, `react-native-localize`
30
464
 
31
- ## License
465
+ ### v1.0.1 (October 2025)
32
466
 
33
- MIT
467
+ - Initial release
34
468
 
35
469
  ---
36
470
 
37
- Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
471
+ **Platform Support:** Android ✅ | iOS ✅
@@ -1,2 +1,9 @@
1
- <manifest xmlns:android="http://schemas.android.com/apk/res/android">
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+ xmlns:tools="http://schemas.android.com/tools">
3
+ <application>
4
+ <meta-data
5
+ android:name="com.google.firebase.messaging.default_notification_channel_id"
6
+ android:value="channel_default"
7
+ tools:replace="android:value" />
8
+ </application>
2
9
  </manifest>
@@ -11,12 +11,6 @@ class NotifySphereModule(reactContext: ReactApplicationContext) :
11
11
  return NAME
12
12
  }
13
13
 
14
- // Example method
15
- // See https://reactnative.dev/docs/native-modules-android
16
- override fun multiply(a: Double, b: Double): Double {
17
- return a * b
18
- }
19
-
20
14
  companion object {
21
15
  const val NAME = "NotifySphere"
22
16
  }
@@ -3,12 +3,6 @@
3
3
  @implementation NotifySphere
4
4
  RCT_EXPORT_MODULE()
5
5
 
6
- - (NSNumber *)multiply:(double)a b:(double)b {
7
- NSNumber *result = @(a * b);
8
-
9
- return result;
10
- }
11
-
12
6
  - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
13
7
  (const facebook::react::ObjCTurboModule::InitParams &)params
14
8
  {
@@ -1,5 +1,16 @@
1
1
  "use strict";
2
2
 
3
+ /**
4
+ * TurboModule spec for NotifySphere.
5
+ *
6
+ * This file is intentionally minimal — all notification logic is implemented
7
+ * in JavaScript (src/index.tsx) via @react-native-firebase/messaging and
8
+ * @notifee/react-native. No native bridge methods are required.
9
+ *
10
+ * The module registration below is kept for New Architecture compatibility;
11
+ * it allows the codegen to produce the required C++ glue code without
12
+ * exposing any unnecessary native surface.
13
+ */
3
14
  import { TurboModuleRegistry } from 'react-native';
4
15
  export default TurboModuleRegistry.getEnforcing('NotifySphere');
5
16
  //# sourceMappingURL=NativeNotifySphere.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeNotifySphere.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AAMpE,eAAeA,mBAAmB,CAACC,YAAY,CAAO,cAAc,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeNotifySphere.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASA,mBAAmB,QAA0B,cAAc;AAIpE,eAAeA,mBAAmB,CAACC,YAAY,CAAO,cAAc,CAAC","ignoreList":[]}