cordova-plugin-reteno 2.2.0 → 2.2.2

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.
Files changed (38) hide show
  1. package/README.md +2 -4
  2. package/docs/Cordova/Action Buttons/README.md +62 -0
  3. package/docs/Cordova/AppInbox/README.md +55 -0
  4. package/docs/Cordova/AppLifeCycleEvents/README.md +80 -0
  5. package/docs/Cordova/Ecommerce/README.md +144 -0
  6. package/docs/Cordova/InAppMessages/README.md +72 -0
  7. package/docs/Cordova/Push notification/README.md +129 -0
  8. package/docs/Cordova/Recommendations/README.md +62 -0
  9. package/docs/Cordova/SetupGuide/Android.md +135 -0
  10. package/docs/Cordova/SetupGuide/IOS.md +325 -0
  11. package/docs/Cordova/SetupGuide/README.md +91 -0
  12. package/docs/Cordova/Tracking user behaviour/README.md +52 -0
  13. package/docs/Cordova/Tracking user information/README.md +112 -0
  14. package/docs/Ionic/Action Buttons/README.md +66 -0
  15. package/docs/Ionic/AppInbox/README.md +61 -0
  16. package/docs/Ionic/AppLifeCycleEvents/README.md +77 -0
  17. package/docs/Ionic/Ecommerce/README.md +139 -0
  18. package/docs/Ionic/InAppMessages/README.md +65 -0
  19. package/docs/Ionic/Push notification/README.md +133 -0
  20. package/docs/Ionic/Recommendations/README.md +46 -0
  21. package/docs/Ionic/SetupGuide/Android.md +173 -0
  22. package/docs/Ionic/SetupGuide/IOS.md +354 -0
  23. package/docs/Ionic/SetupGuide/README.md +167 -0
  24. package/docs/Ionic/Tracking user behaviour/README.md +31 -0
  25. package/docs/Ionic/Tracking user information/README.md +98 -0
  26. package/package.json +1 -1
  27. package/plugin.xml +20 -2
  28. package/scripts/ios-podfile-use-modular-headers.js +109 -0
  29. package/scripts/validate-install.js +37 -9
  30. package/src/android/app/build.gradle +1 -1
  31. package/src/android/app/src/main/java/com/reteno/plugin/RetenoPlugin.java +29 -9
  32. package/src/android/reteno.gradle +19 -9
  33. package/src/ios/RetenoPlugin.swift +37 -5
  34. package/src/ios/RetenoPluginBootstrap.m +117 -0
  35. package/types/index.ts +1 -1
  36. package/docs/android.md +0 -264
  37. package/docs/api.md +0 -734
  38. package/docs/ios.md +0 -495
@@ -0,0 +1,117 @@
1
+ #import <Foundation/Foundation.h>
2
+ #import <UIKit/UIKit.h>
3
+ #import <objc/runtime.h>
4
+
5
+ static void RetenoStartDelayedInitializationIfNeeded(void)
6
+ {
7
+ Class pluginClass = NSClassFromString(@"RetenoPlugin");
8
+ SEL selector = NSSelectorFromString(@"delayedStartIfNeeded");
9
+ if (pluginClass == Nil || ![pluginClass respondsToSelector:selector]) {
10
+ return;
11
+ }
12
+
13
+ IMP implementation = [pluginClass methodForSelector:selector];
14
+ if (implementation != NULL) {
15
+ void (*invoke)(id, SEL) = (void (*)(id, SEL))implementation;
16
+ invoke(pluginClass, selector);
17
+ }
18
+ }
19
+
20
+ static const void *RetenoLaunchHookInstalledKey = &RetenoLaunchHookInstalledKey;
21
+
22
+ static void RetenoInstallAppDelegateLaunchHook(Class delegateClass)
23
+ {
24
+ if ([objc_getAssociatedObject(delegateClass, RetenoLaunchHookInstalledKey) boolValue]) {
25
+ return;
26
+ }
27
+ objc_setAssociatedObject(
28
+ delegateClass,
29
+ RetenoLaunchHookInstalledKey,
30
+ @YES,
31
+ OBJC_ASSOCIATION_RETAIN_NONATOMIC
32
+ );
33
+
34
+ SEL launchSelector = @selector(application:didFinishLaunchingWithOptions:);
35
+ Method inheritedOrOwnMethod = class_getInstanceMethod(delegateClass, launchSelector);
36
+ IMP originalImplementation = inheritedOrOwnMethod != NULL
37
+ ? method_getImplementation(inheritedOrOwnMethod)
38
+ : NULL;
39
+ const char *typeEncoding = inheritedOrOwnMethod != NULL
40
+ ? method_getTypeEncoding(inheritedOrOwnMethod)
41
+ : "c@:@@";
42
+
43
+ // Give the concrete AppDelegate its own method before replacing the IMP. If
44
+ // the implementation is inherited (Cordova), changing the inherited Method
45
+ // would otherwise affect every subclass of that base class.
46
+ if (inheritedOrOwnMethod != NULL) {
47
+ class_addMethod(delegateClass, launchSelector, originalImplementation, typeEncoding);
48
+ }
49
+
50
+ IMP launchHook = imp_implementationWithBlock(^BOOL(
51
+ id appDelegate,
52
+ UIApplication *application,
53
+ NSDictionary<UIApplicationLaunchOptionsKey, id> *launchOptions
54
+ ) {
55
+ // UIApplication is ready here, but the notification response that
56
+ // cold-launched the app has not yet been delivered.
57
+ RetenoStartDelayedInitializationIfNeeded();
58
+
59
+ if (originalImplementation == NULL) {
60
+ return YES;
61
+ }
62
+
63
+ BOOL (*invokeOriginal)(id, SEL, UIApplication *, NSDictionary *) =
64
+ (BOOL (*)(id, SEL, UIApplication *, NSDictionary *))originalImplementation;
65
+ return invokeOriginal(appDelegate, launchSelector, application, launchOptions);
66
+ });
67
+
68
+ Method concreteMethod = class_getInstanceMethod(delegateClass, launchSelector);
69
+ if (concreteMethod != NULL) {
70
+ method_setImplementation(concreteMethod, launchHook);
71
+ } else {
72
+ class_addMethod(delegateClass, launchSelector, launchHook, typeEncoding);
73
+ }
74
+ }
75
+
76
+ /**
77
+ Hooks the concrete AppDelegate as soon as UIKit installs it, then starts
78
+ Reteno's delayed initialization from didFinishLaunchingWithOptions. Cordova and
79
+ Capacitor would otherwise instantiate RetenoPlugin only on the first JS call.
80
+ */
81
+ @interface UIApplication (RetenoPluginBootstrap)
82
+ - (void)reteno_plugin_setDelegate:(nullable id<UIApplicationDelegate>)delegate;
83
+ @end
84
+
85
+ @implementation UIApplication (RetenoPluginBootstrap)
86
+
87
+ + (void)load
88
+ {
89
+ // CordovaPlugins can also be linked into notification extensions. Reteno's
90
+ // application-level delayed initialization must only run in the main app.
91
+ NSString *bundleExtension = [[[[NSBundle mainBundle] bundlePath] pathExtension] lowercaseString];
92
+ if ([bundleExtension isEqualToString:@"appex"]) {
93
+ return;
94
+ }
95
+
96
+ static dispatch_once_t onceToken;
97
+ dispatch_once(&onceToken, ^{
98
+ Method originalMethod = class_getInstanceMethod(self, @selector(setDelegate:));
99
+ Method replacementMethod = class_getInstanceMethod(self, @selector(reteno_plugin_setDelegate:));
100
+ if (originalMethod != NULL && replacementMethod != NULL) {
101
+ method_exchangeImplementations(originalMethod, replacementMethod);
102
+ }
103
+ });
104
+ }
105
+
106
+ - (void)reteno_plugin_setDelegate:(nullable id<UIApplicationDelegate>)delegate
107
+ {
108
+ // Implementations are exchanged in +load, so this invokes UIKit's original
109
+ // setDelegate: implementation.
110
+ [self reteno_plugin_setDelegate:delegate];
111
+
112
+ if (delegate != nil) {
113
+ RetenoInstallAppDelegateLaunchHook([delegate class]);
114
+ }
115
+ }
116
+
117
+ @end
package/types/index.ts CHANGED
@@ -97,7 +97,7 @@ export type LifecycleTrackingOptions =
97
97
  | string;
98
98
 
99
99
  export type RetenoInitializeOptions = {
100
- /** SDK access key. If omitted, the plugin reads SDK_ACCESS_KEY from config.xml / capacitor.config. */
100
+ /** SDK access key. If omitted, the plugin reads RETENO_ACCESS_KEY (or legacy SDK_ACCESS_KEY) from config.xml / capacitor.config. */
101
101
  accessKey?: string;
102
102
  pauseInAppMessages?: boolean;
103
103
  pausePushInAppMessages?: boolean;
package/docs/android.md DELETED
@@ -1,264 +0,0 @@
1
- ## Installation
2
-
3
- > Requirement: `cordova-android >= 12.0.0`. Older Cordova Android platforms will refuse to install the plugin.
4
-
5
- 1. Run next command from root of your project:
6
-
7
- ```sh
8
- cordova plugin add cordova-plugin-reteno --variable SDK_ACCESS_KEY=YOUR_KEY
9
- ```
10
-
11
- ### Configure via config.xml (Cordova)
12
-
13
- If you prefer to keep the access key in your Cordova app config (instead of passing `--variable` during installation), configure it as **plugin variables** in `config.xml`:
14
-
15
- ```xml
16
- <widget ...>
17
- <plugin name="cordova-plugin-reteno" spec="cordova-plugin-reteno">
18
- <!-- Reteno SDK access key (required) -->
19
- <variable name="SDK_ACCESS_KEY" value="YOUR_KEY" />
20
- </plugin>
21
- </widget>
22
- ```
23
-
24
- Notes:
25
-
26
- - Precedence for `SDK_ACCESS_KEY` is plugin variables (from install command or `config.xml`) → AndroidManifest meta-data injected by the plugin.
27
- - Changing plugin variables often requires reinstalling the plugin or re-adding the platform:
28
- - `cordova plugin rm cordova-plugin-reteno && cordova plugin add ...`
29
- - or `cordova platform rm android && cordova platform add android`
30
-
31
- 2. Reteno dependency is added automatically by the plugin via its Gradle hook.
32
-
33
- 3. Android 13+ (and `targetSdkVersion >= 33`) requires notification runtime permission. The plugin injects the manifest permission (`POST_NOTIFICATIONS`), but you still must request it at runtime in your app.
34
-
35
- You can request it via the plugin API:
36
-
37
- ```js
38
- RetenoPlugin.requestNotificationPermission()
39
- .then((grantedInt) => {
40
- // grantedInt: 1 (granted) or 0 (declined)
41
- })
42
- .catch((err) => {
43
- console.error(err);
44
- });
45
- ```
46
-
47
- 4. SDK initialization
48
-
49
- Initialize Reteno once during app startup:
50
-
51
- ```js
52
- RetenoPlugin.init()
53
- .then(() => {
54
- // initialized
55
- })
56
- .catch((err) => {
57
- console.error(err);
58
- });
59
- ```
60
-
61
- For Android SDK `2.9.6+`, you can also configure session duration and split session lifecycle flags during init:
62
-
63
- ```js
64
- RetenoPlugin.init({
65
- // Optional: session reset duration
66
- sessionDurationSeconds: 30 * 60,
67
- lifecycleTrackingOptions: {
68
- appLifecycleEnabled: true,
69
- // ApplicationForgrounded/ApplicationBackgrounded events (disabled by default)
70
- foregroundLifecycleEnabled: false,
71
- pushSubscriptionEnabled: true,
72
- // SessionStart/SessionEnd can now be managed separately
73
- sessionStartEventsEnabled: true,
74
- sessionEndEventsEnabled: false,
75
- },
76
- });
77
- ```
78
-
79
- If you need advanced Reteno configuration (custom `RetenoConfig`, custom device id provider, etc), you can initialize Reteno in your custom `Application` as described here:
80
- [link](https://docs.reteno.com/reference/android-sdk-setup#step-4-initialize-the-reteno-sdk).
81
-
82
- 5. Set up Firebase for Cloud Messaging (create Firebase project, add `google-services.json`, etc):
83
- [link](https://docs.reteno.com/reference/setting-up-your-firebase-application-for-firebase-cloud-messaging).
84
-
85
- ## Push notification listeners
86
-
87
- The plugin uses the listener-based API (`EventListener` / `Procedure`, Android SDK `2.9.6`) for all push events.
88
-
89
- - `setOnRetenoPushDismissedListener(listener)` — called when a push notification is dismissed (swiped away).
90
- - `setOnRetenoCustomPushReceivedListener(listener)` — called when a custom push notification is received.
91
-
92
- Each method accepts a listener that receives a payload object. Use the corresponding `remove...` method to unsubscribe:
93
-
94
- ```js
95
- const onDismissed = (payload) => {
96
- console.log('Push dismissed', payload);
97
- };
98
-
99
- const onCustomReceived = (payload) => {
100
- console.log('Custom push received', payload);
101
- };
102
-
103
- RetenoPlugin.setOnRetenoPushDismissedListener(onDismissed);
104
- RetenoPlugin.setOnRetenoCustomPushReceivedListener(onCustomReceived);
105
-
106
- // later:
107
- RetenoPlugin.removeOnRetenoPushDismissedListener(onDismissed);
108
- RetenoPlugin.removeOnRetenoCustomPushReceivedListener(onCustomReceived);
109
- ```
110
-
111
- ## Notification grouping
112
-
113
- Android notifications can be assigned to a group using either a value from the push payload or a constant group ID. The configuration is stored natively and restored when the Android process starts, so it does not depend on the Cordova WebView being active.
114
-
115
- ```js
116
- // Group notifications by the `chatId` field in the push payload.
117
- await RetenoPlugin.setNotificationGroupingRule({ payloadKey: 'chatId' });
118
-
119
- // Alternatively, assign all Reteno notifications to one group.
120
- await RetenoPlugin.setNotificationGroupingRule({ groupId: 'messages' });
121
-
122
- // Disable grouping.
123
- await RetenoPlugin.setNotificationGroupingRule(null);
124
- ```
125
-
126
- This configures the group key on each notification. If you want Android to display a summary row for the group, the application must create its own notification with `setGroupSummary(true)` using the same group ID.
127
-
128
- ### Without Firebasex (Cordova)
129
-
130
- If you **do not need** `cordova-plugin-firebasex` features, you can still use Reteno Push on Android.
131
- For FCM to work, your Cordova Android project must have:
132
-
133
- 1. `google-services.json` (from Firebase Console)
134
-
135
- - Download it from Firebase Console → Project settings → Your apps (Android) → `google-services.json`.
136
- - Put it into your Cordova project in one of these locations (the plugin will copy it into `platforms/android/app/` automatically):
137
- - `<projectRoot>/google-services.json`
138
- - `<projectRoot>/resources/android/google-services.json`
139
- - `<projectRoot>/resources/google-services.json`
140
-
141
- 2. Google Services Gradle plugin applied (`com.google.gms.google-services`)
142
-
143
- The plugin automatically applies Google Services Gradle plugin during `after_platform_add` and `after_prepare`.
144
-
145
- How to verify:
146
-
147
- - Check that `platforms/android/app/google-services.json` exists.
148
- - Check `platforms/android/app/build.gradle` contains `com.google.gms.google-services`.
149
- - If your Gradle build logs contain a message like “google-services.json not found, google-services plugin not applied. Push Notifications won't work”, then the plugin is NOT applied and push won’t work.
150
-
151
- ### Ionic/Capacitor note
152
-
153
- For Capacitor apps, see the dedicated [Capacitor setup (Android)](#capacitor-setup-android) section below.
154
-
155
- ### Warning: Reteno + Firebasex (Android)
156
-
157
- If you use `cordova-plugin-firebasex` in the same app, be aware it can register its own `FirebaseMessagingService`.
158
- On Android, only one service may receive `com.google.firebase.MESSAGING_EVENT`, so Firebasex can prevent Reteno from receiving `onNewToken` / `onMessageReceived` callbacks.
159
-
160
- Recommendation: don’t install Firebasex “just for push”. Install it only if you need its other Firebase features.
161
-
162
- ### Custom FCM service (Android)
163
-
164
- In most Cordova apps you **do not need** a custom `FirebaseMessagingService`.
165
- Only add one if you have your **own native push handling** (or another SDK that requires a custom FCM service).
166
-
167
- If you do add a custom service, follow Reteno guidance: extend `RetenoFirebaseMessagingService` (not `FirebaseMessagingService`) and call `super` methods.
168
-
169
- See Reteno docs:
170
-
171
- - https://docs.reteno.com/reference/android-sdk-setup
172
- - https://docs.reteno.com/reference/android-push-handling
173
-
174
- ## Capacitor setup (Android)
175
-
176
- Capacitor uses Cordova plugins through a compatibility layer, but **does not execute Cordova plugin hooks**. This means several automatic steps that work in Cordova must be done manually.
177
-
178
- ### 1. Install the plugin
179
-
180
- ```sh
181
- npm install cordova-plugin-reteno
182
- npx cap sync android
183
- ```
184
-
185
- ### 2. Configure `SDK_ACCESS_KEY`
186
-
187
- In `capacitor.config.ts`, pass the access key through `cordova.preferences`:
188
-
189
- ```ts
190
- import { CapacitorConfig } from '@capacitor/cli';
191
-
192
- const config: CapacitorConfig = {
193
- // ...
194
- cordova: {
195
- preferences: {
196
- SDK_ACCESS_KEY: 'YOUR_KEY',
197
- },
198
- },
199
- };
200
-
201
- export default config;
202
- ```
203
-
204
- The plugin's `reteno.gradle` script automatically reads this value from `capacitor.config.json` (generated by Capacitor CLI) and patches the Android manifest at build time. No manual manifest editing is required.
205
-
206
- You can also pass the key explicitly in JS:
207
-
208
- ```js
209
- await RetenoPlugin.init({ accessKey: 'YOUR_KEY' });
210
- ```
211
-
212
- ### 3. Set up `google-services.json`
213
-
214
- The Cordova hook that auto-copies `google-services.json` does **not** run in Capacitor.
215
-
216
- Do this manually:
217
-
218
- 1. Download `google-services.json` from [Firebase Console](https://console.firebase.google.com/).
219
- 2. Copy it to `android/app/google-services.json`.
220
- 3. Run `npx cap sync android`.
221
-
222
- ### 4. Google Services Gradle plugin
223
-
224
- In Capacitor projects, the Google Services Gradle plugin is typically already applied if you use any Firebase dependency. Verify that `android/app/build.gradle` contains:
225
-
226
- ```gradle
227
- apply plugin: 'com.google.gms.google-services'
228
- ```
229
-
230
- If not, add it manually or install `@capacitor-firebase/messaging` (or similar) which applies it automatically.
231
-
232
- ### 5. Initialize the SDK
233
-
234
- ```js
235
- await RetenoPlugin.init();
236
- ```
237
-
238
- ### 6. Request notification permission
239
-
240
- Same as Cordova — call at the appropriate point in your UX:
241
-
242
- ```js
243
- const granted = await RetenoPlugin.requestNotificationPermission();
244
- ```
245
-
246
- ### Deeplinks in push payloads
247
-
248
- Reteno push notifications on Android can include deeplinks.
249
-
250
- In a native Android app, you can usually route those links with your own `Intent` handling, Android App Links, or custom URL schemes.
251
-
252
- For Cordova/Ionic/Capacitor apps, deeplink routing usually requires a separate app-level solution. This plugin does not provide a full deeplink router by itself, so if you need custom navigation from push links, use a dedicated deeplink integration, for example:
253
-
254
- - Branch.io
255
- - a custom URL scheme plugin
256
- - an Android App Links / Universal Links plugin
257
-
258
- Typical setup for hybrid apps:
259
-
260
- 1. Configure your app to support the required link type (custom scheme or Android App Links).
261
- 2. Install and configure your deeplink provider/router (for example, Branch.io) in the app project.
262
- 3. Route the tapped push link into that provider/router so the app opens the correct webview route or native screen.
263
-
264
- In practice, Reteno can deliver the link in the push payload, but opening the correct destination inside a hybrid app is still handled by your deeplink integration layer.