infobip-mobile-messaging-react-native-plugin 14.8.3 → 14.9.1

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.
@@ -66,7 +66,7 @@ repositories {
66
66
  }
67
67
 
68
68
  dependencies {
69
- def mmVersion = '14.14.2'
69
+ def mmVersion = '15.0.0'
70
70
  implementation "org.jetbrains.kotlin:kotlin-stdlib:2.1.20"
71
71
  compileOnly "com.facebook.react:react-android"
72
72
  implementation "androidx.annotation:annotation:1.9.1"
@@ -1,5 +1,4 @@
1
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
- xmlns:tools="http://schemas.android.com/tools">
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
3
2
 
4
3
  <!-- Mobile Messaging permissions -->
5
4
 
@@ -20,10 +19,7 @@
20
19
  <uses-permission android:name="android.permission.CAMERA" />
21
20
  <!-- Infobip rtc ui permissions -->
22
21
 
23
- <application
24
- tools:replace="android:usesCleartextTraffic"
25
- android:usesCleartextTraffic="true"
26
- >
22
+ <application>
27
23
  <!-- Mobile Messaging components -->
28
24
  <service
29
25
  android:name="org.infobip.mobile.messaging.platform.MobileMessagingJobService"
@@ -12,23 +12,17 @@ import android.content.Context;
12
12
  import android.content.SharedPreferences;
13
13
  import android.preference.PreferenceManager;
14
14
 
15
- import android.util.Log;
16
-
17
- import org.infobip.mobile.messaging.api.support.http.serialization.JsonSerializer;
18
- import org.infobip.mobile.messaging.dal.json.JSONArrayAdapter;
19
- import org.infobip.mobile.messaging.dal.json.JSONObjectAdapter;
20
15
  import org.json.JSONObject;
21
16
 
22
17
  import java.util.ArrayList;
23
- import java.util.Arrays;
24
- import java.util.HashSet;
18
+ import java.util.Iterator;
25
19
  import java.util.List;
26
- import java.util.Set;
27
20
 
28
21
  class CacheManager {
29
- private static final String EVENTS_KEY = Utils.TAG + ".cache.events";
22
+ private static final String LEGACY_EVENTS_KEY = Utils.TAG + ".cache.events";
30
23
  private static final Object cacheLock = new Object();
31
- private static final JsonSerializer serializer = new JsonSerializer(false, new JSONObjectAdapter(), new JSONArrayAdapter());
24
+ private static final List<Event> cachedEvents = new ArrayList<>();
25
+ private static final int MAX_CACHE_SIZE = 100;
32
26
 
33
27
  static class Event {
34
28
  String type;
@@ -45,102 +39,57 @@ class CacheManager {
45
39
  public String toString() {
46
40
  return type;
47
41
  }
48
-
49
- }
50
-
51
- static void saveEvent(Context context, String event, JSONObject object, String actionId, String actionInputText) {
52
- if (context == null) {
53
- RNMMLogger.e(Utils.TAG, "context is null, can't cache event " + event);
54
- return;
55
- }
56
- String serialized = serializer.serialize(new Event(event, object, actionId, actionInputText));
57
- saveStringsToSet(context, EVENTS_KEY, serialized);
58
42
  }
59
43
 
60
- static void saveEvent(Context context, String event, int unreadMessagesCounter) {
61
- if (context == null) {
62
- RNMMLogger.e(Utils.TAG, "context is null, can't cache event " + event);
63
- return;
64
- }
65
- //int `unreadMessagesCounter` isn't a JSONObject, so it'll go as a second argument
66
- String serialized = serializer.serialize(new Event(event, null, unreadMessagesCounter));
67
- saveStringsToSet(context, EVENTS_KEY, serialized);
68
- }
69
-
70
- static Event[] loadEvents(Context context, String eventType) {
71
- if (context == null) {
72
- RNMMLogger.e(Utils.TAG, "context is null, can't load cached events " + eventType);
73
- return new Event[0];
74
- }
75
- Set<String> serialized = getStringSet(context, EVENTS_KEY);
76
- if (serialized == null || serialized.isEmpty()) {
77
- return new Event[0];
78
- }
79
-
80
- List<Event> matched = new ArrayList<>();
81
- Set<String> unmatchedSerialized = new HashSet<>();
82
- for (String s : serialized) {
83
- Event e = serializer.deserialize(s, Event.class);
84
- if (eventType.equals(e.type)) {
85
- matched.add(e);
86
- } else {
87
- unmatchedSerialized.add(s);
44
+ static void saveEvent(String event, JSONObject object, String actionId, String actionInputText) {
45
+ synchronized (cacheLock) {
46
+ if (cachedEvents.size() >= MAX_CACHE_SIZE) {
47
+ Event removed = cachedEvents.remove(0);
48
+ RNMMLogger.w(Utils.TAG, "Cache full, dropping oldest event: " + removed.type);
88
49
  }
50
+ cachedEvents.add(new Event(event, object, actionId, actionInputText));
89
51
  }
90
-
91
- updateStringsSet(context, EVENTS_KEY, unmatchedSerialized);
92
-
93
- return matched.toArray(new Event[matched.size()]);
94
52
  }
95
53
 
96
- private static Set<String> getStringSet(Context context, String key) {
97
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
54
+ static void saveEvent(String event, int unreadMessagesCounter) {
98
55
  synchronized (cacheLock) {
99
- return new HashSet<>(sharedPreferences.getStringSet(key, new HashSet<String>()));
56
+ if (cachedEvents.size() >= MAX_CACHE_SIZE) {
57
+ Event removed = cachedEvents.remove(0);
58
+ RNMMLogger.w(Utils.TAG, "Cache full, dropping oldest event: " + removed.type);
59
+ }
60
+ cachedEvents.add(new Event(event, null, unreadMessagesCounter));
100
61
  }
101
62
  }
102
63
 
103
- private static Set<String> getAndRemoveStringSet(Context context, String key) {
104
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
105
- Set<String> set;
64
+ static Event[] loadEvents(String eventType) {
106
65
  synchronized (cacheLock) {
107
- set = sharedPreferences.getStringSet(key, new HashSet<String>());
108
- if (set.isEmpty()) {
109
- return new HashSet<String>();
66
+ List<Event> matched = new ArrayList<>();
67
+ Iterator<Event> iterator = cachedEvents.iterator();
68
+ while (iterator.hasNext()) {
69
+ Event e = iterator.next();
70
+ if (eventType.equals(e.type)) {
71
+ matched.add(e);
72
+ iterator.remove();
73
+ }
110
74
  }
111
- sharedPreferences
112
- .edit()
113
- .remove(key)
114
- .apply();
75
+ return matched.toArray(new Event[0]);
115
76
  }
116
- return set;
117
77
  }
118
78
 
119
- @SuppressWarnings("UnusedReturnValue")
120
- private static Set<String> saveStringsToSet(Context context, String key, String... strings) {
121
- return saveStringSet(context, key, new HashSet<String>(Arrays.asList(strings)));
122
- }
123
-
124
- private static Set<String> saveStringSet(Context context, String key, Set<String> newSet) {
125
- SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
79
+ static void clearCache() {
126
80
  synchronized (cacheLock) {
127
- Set<String> set = sharedPreferences.getStringSet(key, new HashSet<String>());
128
- newSet.addAll(set);
129
- sharedPreferences
130
- .edit()
131
- .putStringSet(key, newSet)
132
- .apply();
133
- return set;
81
+ cachedEvents.clear();
134
82
  }
135
83
  }
136
84
 
137
- private static void updateStringsSet(Context context, String key, Set<String> newSet) {
85
+ static void cleanupLegacyCache(Context context) {
86
+ if (context == null) {
87
+ return;
88
+ }
138
89
  SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
139
- synchronized (cacheLock) {
140
- sharedPreferences
141
- .edit()
142
- .putStringSet(key, newSet)
143
- .apply();
90
+ if (sharedPreferences.contains(LEGACY_EVENTS_KEY)) {
91
+ sharedPreferences.edit().remove(LEGACY_EVENTS_KEY).apply();
92
+ RNMMLogger.d(Utils.TAG, "Cleaned up legacy SharedPreferences event cache");
144
93
  }
145
94
  }
146
95
  }
@@ -392,18 +392,22 @@ class RNMMChatEventReceiver : ReactNativeBroadcastReceiver() {
392
392
  emitOrCache(context, RNMMChatService.EVENT_INAPPCHAT_UNREAD_MESSAGES_COUNT_UPDATED, unreadChatMessagesCounter)
393
393
  }
394
394
 
395
+ // Early-exit when plugin is not initialized avoids calling getReactContext() which throws
396
+ // UnsupportedOperationException on new architecture when app is killed. When reactContext is
397
+ // null (e.g. during lifecycle transitions), events are cached regardless of jsHasListeners
398
+ // since sending requires a valid ReactContext. This preserves the original behavior.
395
399
  private fun emitOrCache(context: Context?, eventType: String, unreadMessagesCounter: Int) {
400
+ if (!pluginInitialized) {
401
+ CacheManager.saveEvent(eventType, unreadMessagesCounter)
402
+ return
403
+ }
396
404
  val reactContext: ReactContext? = getReactContext(context)
397
- if (!pluginInitialized || reactContext == null) {
398
- CacheManager.saveEvent(context, eventType, unreadMessagesCounter)
399
- } else if (jsHasListeners && reactContext != null) {
405
+ if (reactContext == null) {
406
+ CacheManager.saveEvent(eventType, unreadMessagesCounter)
407
+ } else if (jsHasListeners) {
400
408
  ReactNativeEvent.send(eventType, reactContext, unreadMessagesCounter)
401
- } else if (reactContext != null) {
402
- CacheManager.saveEvent(reactContext, eventType, unreadMessagesCounter)
403
- } else if (context != null) {
404
- CacheManager.saveEvent(context, eventType, unreadMessagesCounter)
405
409
  } else {
406
- RNMMLogger.e(TAG, "Both reactContext and androidContext are null, can't emit or cache event " + eventType)
410
+ CacheManager.saveEvent(eventType, unreadMessagesCounter)
407
411
  }
408
412
  }
409
413
  }
@@ -27,11 +27,16 @@ abstract class ReactNativeBroadcastReceiver : BroadcastReceiver() {
27
27
  get() = ReactNativeMobileMessagingService.jsHasListeners
28
28
 
29
29
  protected fun getReactContext(context: Context?): ReactContext? {
30
- return ReactNativeMobileMessagingService.lastReactContext
31
- ?: (context?.applicationContext as? ReactApplication)
32
- ?.reactNativeHost
33
- ?.reactInstanceManager
34
- ?.currentReactContext
30
+ return try {
31
+ ReactNativeMobileMessagingService.lastReactContext
32
+ ?: (context?.applicationContext as? ReactApplication)
33
+ ?.reactNativeHost
34
+ ?.reactInstanceManager
35
+ ?.currentReactContext
36
+ } catch (e: Exception) {
37
+ RNMMLogger.w(Utils.TAG, "Failed to get ReactContext: ${e.message}")
38
+ null
39
+ }
35
40
  }
36
41
 
37
42
  }
@@ -417,6 +417,7 @@ class ReactNativeMobileMessagingService(
417
417
  ctx.userAttributes,
418
418
  ctx.forceDepersonalize,
419
419
  ctx.keepAsLead,
420
+ ctx.setDeviceAsPrimary,
420
421
  object : MobileMessaging.ResultListener<User>() {
421
422
  override fun onResult(result: Result<User, MobileMessagingError>) {
422
423
  if (result.isSuccess()) {
@@ -448,6 +449,7 @@ class ReactNativeMobileMessagingService(
448
449
  mobileMessaging.depersonalize(object : MobileMessaging.ResultListener<SuccessPending>() {
449
450
  override fun onResult(result: Result<SuccessPending, MobileMessagingError>) {
450
451
  if (result.isSuccess) {
452
+ mobileMessaging.setJwtSupplier { null }
451
453
  val state = when (result.data) {
452
454
  SuccessPending.Pending -> "pending"
453
455
  SuccessPending.Success -> "success"
@@ -510,10 +512,25 @@ class ReactNativeMobileMessagingService(
510
512
  successCallback.invoke(readableMap)
511
513
  }
512
514
 
515
+ fun cleanup(successCallback: Callback, errorCallback: Callback) {
516
+ RNMMLogger.d(Utils.TAG, "Cleanup...")
517
+ try {
518
+ mobileMessaging.setJwtSupplier { null }
519
+ mobileMessaging.cleanup()
520
+ successCallback.invoke()
521
+ } catch (e: Exception) {
522
+ errorCallback.invoke(Utils.callbackError(e.message, null))
523
+ }
524
+ }
525
+
513
526
  fun setUserDataJwt(jwt: String?, successCallback: Callback, errorCallback: Callback) {
514
527
  RNMMLogger.d(Utils.TAG, "SetUserDataJwt...")
515
528
  try {
516
- mobileMessaging.setJwtSupplier({ jwt })
529
+ if (jwt.isNullOrEmpty()) {
530
+ mobileMessaging.setJwtSupplier(null)
531
+ } else {
532
+ mobileMessaging.setJwtSupplier { jwt }
533
+ }
517
534
  successCallback.invoke()
518
535
  } catch (e: Exception) {
519
536
  errorCallback.invoke(Utils.callbackError(e.message, null))
@@ -951,7 +968,7 @@ class ReactNativeMobileMessagingService(
951
968
  fun addListener(eventName: String) {
952
969
  RNMMLogger.d(Utils.TAG, "addListener: $eventName")
953
970
  jsHasListeners = true
954
- val events = CacheManager.loadEvents(reactContext, eventName)
971
+ val events = CacheManager.loadEvents(eventName)
955
972
  for (event in events) {
956
973
  if (eventName == event.type) {
957
974
  ReactNativeEvent.send(event.type, reactContext, event.jsonObject, *event.objects)
@@ -1088,18 +1105,22 @@ class MessageEventReceiver : ReactNativeBroadcastReceiver() {
1088
1105
  emitOrCache(event, context, message, actionId, actionInputText)
1089
1106
  }
1090
1107
 
1108
+ // Early-exit when plugin is not initialized avoids calling getReactContext() which throws
1109
+ // UnsupportedOperationException on new architecture when app is killed. When reactContext is
1110
+ // null (e.g. during lifecycle transitions), events are cached regardless of jsHasListeners
1111
+ // since sending requires a valid ReactContext. This preserves the original behavior.
1091
1112
  private fun emitOrCache(eventType: String, context: Context?, message: JSONObject?, actionId: String?, actionInputText: String?) {
1113
+ if (!pluginInitialized) {
1114
+ CacheManager.saveEvent(eventType, message, actionId, actionInputText)
1115
+ return
1116
+ }
1092
1117
  val reactContext: ReactContext? = getReactContext(context)
1093
- if (!pluginInitialized || reactContext == null) {
1094
- CacheManager.saveEvent(context, eventType, message, actionId, actionInputText)
1095
- } else if (jsHasListeners && reactContext != null) {
1118
+ if (reactContext == null) {
1119
+ CacheManager.saveEvent(eventType, message, actionId, actionInputText)
1120
+ } else if (jsHasListeners) {
1096
1121
  ReactNativeEvent.send(eventType, reactContext, message, actionId, actionInputText)
1097
- } else if (reactContext != null) {
1098
- CacheManager.saveEvent(reactContext, eventType, message, actionId, actionInputText)
1099
- } else if (context != null) {
1100
- CacheManager.saveEvent(context, eventType, message, actionId, actionInputText)
1101
1122
  } else {
1102
- RNMMLogger.e(Utils.TAG, "Both reactContext and androidContext are null, can't emit or cache event " + eventType)
1123
+ CacheManager.saveEvent(eventType, message, actionId, actionInputText)
1103
1124
  }
1104
1125
  }
1105
1126
 
@@ -27,6 +27,7 @@ class MobileMessagingModule(reactContext: ReactApplicationContext) : NativeMobil
27
27
 
28
28
  init {
29
29
  reactApplicationContext.addLifecycleEventListener(this)
30
+ CacheManager.cleanupLegacyCache(reactApplicationContext)
30
31
  ReactNativeMobileMessagingService.pluginInitialized = true
31
32
  service.registerBroadcastReceiver()
32
33
  }
@@ -159,6 +160,12 @@ class MobileMessagingModule(reactContext: ReactApplicationContext) : NativeMobil
159
160
  service.showDialogForError(errorCode, successCallback, errorCallback)
160
161
  }
161
162
 
163
+ // Cleanup
164
+ override fun cleanup(successCallback: Callback, errorCallback: Callback) {
165
+ RNMMLogger.d(TAG, "Cleanup...")
166
+ service.cleanup(successCallback, errorCallback)
167
+ }
168
+
162
169
  // JWT
163
170
  override fun setUserDataJwt(jwt: String?, successCallback: Callback, errorCallback: Callback) {
164
171
  RNMMLogger.d(TAG, "SetUserDataJwt...")
@@ -199,6 +206,7 @@ class MobileMessagingModule(reactContext: ReactApplicationContext) : NativeMobil
199
206
  override fun onHostDestroy() {
200
207
  ReactNativeMobileMessagingService.pluginInitialized = false
201
208
  service.unregisterBroadcastReceiver()
209
+ CacheManager.clearCache()
202
210
  reactApplicationContext.removeLifecycleEventListener(this)
203
211
  ReactNativeMobileMessagingService.unregisterService(reactApplicationContext)
204
212
  }
@@ -28,6 +28,7 @@ class MobileMessagingModule(reactContext: ReactApplicationContext) : ReactContex
28
28
 
29
29
  init {
30
30
  reactApplicationContext.addLifecycleEventListener(this)
31
+ CacheManager.cleanupLegacyCache(reactApplicationContext)
31
32
  ReactNativeMobileMessagingService.pluginInitialized = true
32
33
  service.registerBroadcastReceiver()
33
34
  }
@@ -186,6 +187,13 @@ class MobileMessagingModule(reactContext: ReactApplicationContext) : ReactContex
186
187
  service.submitEventImmediately(eventData, successCallback, errorCallback)
187
188
  }
188
189
 
190
+ // Cleanup
191
+ @ReactMethod
192
+ fun cleanup(successCallback: Callback, errorCallback: Callback) {
193
+ RNMMLogger.d(TAG, "Cleanup...")
194
+ service.cleanup(successCallback, errorCallback)
195
+ }
196
+
189
197
  // JWT
190
198
  @ReactMethod
191
199
  fun setUserDataJwt(jwt: String?, successCallback: Callback, errorCallback: Callback) {
@@ -230,6 +238,7 @@ class MobileMessagingModule(reactContext: ReactApplicationContext) : ReactContex
230
238
 
231
239
  override fun onHostDestroy() {
232
240
  service.unregisterBroadcastReceiver()
241
+ CacheManager.clearCache()
233
242
  reactApplicationContext.removeLifecycleEventListener(this)
234
243
  ReactNativeMobileMessagingService.pluginInitialized = false
235
244
  ReactNativeMobileMessagingService.unregisterService(reactApplicationContext)
@@ -1,7 +1,7 @@
1
1
  require "json"
2
2
 
3
3
  package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
- mmVersion = "15.0.0"
4
+ mmVersion = "15.5.0"
5
5
 
6
6
  Pod::Spec.new do |s|
7
7
  s.name = "infobip-mobile-messaging-react-native-plugin"
@@ -20,7 +20,9 @@ Pod::Spec.new do |s|
20
20
  s.requires_arc = true
21
21
 
22
22
  s.dependency "React-Core"
23
- s.dependency "React-Core-prebuilt"
23
+ if ENV['RCT_USE_PREBUILT_RNCORE'] == '1'
24
+ s.dependency "React-Core-prebuilt"
25
+ end
24
26
 
25
27
  s.dependency "MobileMessaging/Core", mmVersion
26
28
  s.dependency "MobileMessaging/InAppChat", mmVersion
@@ -98,60 +98,109 @@ class ReactNativeMobileMessaging: RCTEventEmitter {
98
98
 
99
99
  @objc(init:onSuccess:onError:)
100
100
  func start(config: NSDictionary, onSuccess: @escaping RCTResponseSenderBlock, onError: @escaping RCTResponseSenderBlock) {
101
- guard let config = config as? [String : AnyObject], let configuration = RNMobileMessagingConfiguration(rawConfig: config) else {
101
+ guard var userConfigDict = config as? [String : AnyObject],
102
+ let applicationCode = userConfigDict.removeValue(forKey: RNMobileMessagingConfiguration.Keys.applicationCode) as? String,
103
+ let configuration = RNMobileMessagingConfiguration(rawConfig: userConfigDict)
104
+ else {
102
105
  onError([NSError(type: .InvalidArguments).reactNativeObject])
103
106
  return
104
107
  }
105
108
 
106
109
  let successCallback: RCTResponseSenderBlock = { [weak self] response in
107
- RNMobileMessagingConfiguration.saveConfigToDefaults(rawConfig: config)
110
+ RNMobileMessagingConfiguration.saveConfigToDefaults(rawConfig: userConfigDict)
108
111
  self?.isStarted = true
109
112
  onSuccess(response)
110
113
  }
111
-
114
+
112
115
  let cachedConfigDict = RNMobileMessagingConfiguration.getRawConfigFromDefaults()
113
- if let cachedConfigDict = cachedConfigDict, (config as NSDictionary) != (cachedConfigDict as NSDictionary)
114
- {
116
+ let keychainApplicationCode = MobileMessaging.getKeychainApplicationCode()
117
+ let shouldRestart = needsRestart(userConfigDict: userConfigDict, applicationCode: applicationCode)
118
+ let shouldStart = cachedConfigDict == nil || keychainApplicationCode == nil
119
+
120
+ if shouldRestart {
115
121
  stop {
116
- self.start(configuration: configuration, onSuccess: successCallback)
122
+ self.startWithApplicationCode(
123
+ configuration: configuration,
124
+ applicationCode: applicationCode,
125
+ onSuccess: successCallback,
126
+ onError: onError
127
+ )
117
128
  }
118
- } else if cachedConfigDict == nil {
119
- start(configuration: configuration, onSuccess: successCallback)
129
+ } else if shouldStart {
130
+ startWithApplicationCode(
131
+ configuration: configuration,
132
+ applicationCode: applicationCode,
133
+ onSuccess: successCallback,
134
+ onError: onError
135
+ )
120
136
  } else {
121
137
  successCallback(nil)
122
138
  }
123
139
  }
124
140
 
125
141
  private func performEarlyStartIfPossible() {
126
- if let cachedConfigDict = RNMobileMessagingConfiguration.getRawConfigFromDefaults(),
127
- let configuration = RNMobileMessagingConfiguration(rawConfig: cachedConfigDict),
128
- !self.isStarted,
129
- !isEarlyStartPerformed
142
+ let cachedConfigDict = RNMobileMessagingConfiguration.getRawConfigFromDefaults()
143
+ let configuration = cachedConfigDict.flatMap(RNMobileMessagingConfiguration.init)
144
+ let applicationCode = MobileMessaging.getKeychainApplicationCode()
145
+
146
+ guard let configuration = configuration,
147
+ let applicationCode = applicationCode,
148
+ !self.isStarted,
149
+ !isEarlyStartPerformed else
130
150
  {
131
- MMLogDebug("[RNMobileMessaging] Performing early start")
132
- isEarlyStartPerformed = true
133
- start(configuration: configuration) { response in }
151
+ return
152
+ }
153
+
154
+ isEarlyStartPerformed = true
155
+ startWithApplicationCode(configuration: configuration, applicationCode: applicationCode) { response in }
156
+ }
157
+
158
+ private func startWithApplicationCode(
159
+ configuration: RNMobileMessagingConfiguration,
160
+ applicationCode: String,
161
+ onSuccess: @escaping RCTResponseSenderBlock,
162
+ onError: RCTResponseSenderBlock? = nil
163
+ ) {
164
+ guard let mobileMessaging = createMobileMessagingInstance(
165
+ configuration: configuration,
166
+ applicationCode: applicationCode
167
+ ) else {
168
+ MMLogError("[RNMobileMessaging] Failed to initialize MobileMessaging instance, SDK can't start.")
169
+ onError?([NSError(type: .InitializationFailed).reactNativeObject])
170
+ return
134
171
  }
172
+
173
+ applyConfigurationAndStart(
174
+ mobileMessaging,
175
+ configuration: configuration,
176
+ onSuccess: onSuccess
177
+ )
135
178
  }
136
-
137
- private func start(configuration: RNMobileMessagingConfiguration, onSuccess: @escaping RCTResponseSenderBlock) {
179
+
180
+ private func createMobileMessagingInstance(configuration: RNMobileMessagingConfiguration, applicationCode: String) -> MobileMessaging? {
181
+ let backendBaseURL = configuration.backendBaseURL ?? MMConsts.APIValues.prodDynamicBaseURLString
182
+ return MobileMessaging.withApplicationCode(
183
+ applicationCode,
184
+ notificationType: configuration.notificationType,
185
+ backendBaseURL: backendBaseURL
186
+ )
187
+ }
188
+
189
+ private func applyConfigurationAndStart(_ mobileMessaging: MobileMessaging, configuration: RNMobileMessagingConfiguration, onSuccess: @escaping RCTResponseSenderBlock) {
138
190
  self.eventsManager?.startObserving()
139
191
  MobileMessaging.privacySettings.systemInfoSendingDisabled = configuration.privacySettings[RNMobileMessagingConfiguration.Keys.systemInfoSendingDisabled].unwrap(orDefault: false)
140
192
  MobileMessaging.privacySettings.carrierInfoSendingDisabled = configuration.privacySettings[RNMobileMessagingConfiguration.Keys.carrierInfoSendingDisabled].unwrap(orDefault: false)
141
193
  MobileMessaging.privacySettings.userDataPersistingDisabled = configuration.privacySettings[RNMobileMessagingConfiguration.Keys.userDataPersistingDisabled].unwrap(orDefault: false)
142
194
 
143
- var mobileMessaging = MobileMessaging.withApplicationCode(
144
- configuration.appCode,
145
- notificationType: configuration.notificationType,
146
- backendBaseURL: configuration.backendBaseURL ?? MMConsts.APIValues.prodDynamicBaseURLString)
195
+ var mobileMessaging = mobileMessaging
147
196
 
148
197
  if let storageAdapter = messageStorageAdapter, configuration.messageStorageEnabled {
149
- mobileMessaging = mobileMessaging?.withMessageStorage(storageAdapter)
198
+ mobileMessaging = mobileMessaging.withMessageStorage(storageAdapter)
150
199
  } else if configuration.defaultMessageStorage {
151
- mobileMessaging = mobileMessaging?.withDefaultMessageStorage()
200
+ mobileMessaging = mobileMessaging.withDefaultMessageStorage()
152
201
  }
153
202
  if let categories = configuration.categories {
154
- mobileMessaging = mobileMessaging?.withInteractiveNotificationCategories(Set(categories))
203
+ mobileMessaging = mobileMessaging.withInteractiveNotificationCategories(Set(categories))
155
204
  }
156
205
  MobileMessaging.userAgent.pluginVersion = "reactNative \(configuration.reactNativePluginVersion)"
157
206
  if configuration.logging {
@@ -159,22 +208,22 @@ class ReactNativeMobileMessaging: RCTEventEmitter {
159
208
  }
160
209
 
161
210
  if configuration.inAppChatEnabled {
162
- mobileMessaging = mobileMessaging?.withInAppChat()
211
+ mobileMessaging = mobileMessaging.withInAppChat()
163
212
  }
164
213
 
165
214
  if configuration.fullFeaturedInAppsEnabled {
166
- mobileMessaging = mobileMessaging?.withFullFeaturedInApps()
215
+ mobileMessaging = mobileMessaging.withFullFeaturedInApps()
167
216
  }
168
217
 
169
218
  if let webViewSettings = configuration.webViewSettings {
170
- mobileMessaging?.webViewSettings.configureWith(rawConfig: webViewSettings)
219
+ mobileMessaging.webViewSettings.configureWith(rawConfig: webViewSettings)
171
220
  }
172
221
 
173
222
  if let jwt = configuration.userDataJwt, !jwt.isEmpty {
174
- mobileMessaging = mobileMessaging?.withJwtSupplier(VariableJwtSupplier(jwt: jwt))
223
+ mobileMessaging = mobileMessaging.withJwtSupplier(VariableJwtSupplier(jwt: jwt))
175
224
  }
176
225
 
177
- mobileMessaging?.start({
226
+ mobileMessaging.start({
178
227
  onSuccess(nil)
179
228
  })
180
229
  }
@@ -183,6 +232,13 @@ class ReactNativeMobileMessaging: RCTEventEmitter {
183
232
  self.eventsManager?.stop(stopObservations: stopObservations)
184
233
  MobileMessaging.stop(false, completion: completion)
185
234
  }
235
+
236
+ private func needsRestart(userConfigDict: [String: AnyObject], applicationCode: String) -> Bool {
237
+ let configurationChanged = RNMobileMessagingConfiguration.didConfigurationChange(userConfigDict: userConfigDict)
238
+ let applicationCodeChanged = MobileMessaging.didApplicationCodeChange(applicationCode: applicationCode)
239
+
240
+ return configurationChanged || applicationCodeChanged
241
+ }
186
242
 
187
243
  /*User Profile Management*/
188
244
 
@@ -328,7 +384,8 @@ class ReactNativeMobileMessaging: RCTEventEmitter {
328
384
  let ua = uaDict == nil ? nil : MMUserAttributes(dictRepresentation: uaDict!)
329
385
  let keepAsLead = (context["keepAsLead"] as? Bool) ?? false
330
386
  let forceDepersonalize = context["forceDepersonalize"] as? Bool ?? false
331
- MobileMessaging.personalize(forceDepersonalize: forceDepersonalize, keepAsLead: keepAsLead, userIdentity: ui, userAttributes: ua) { (error) in
387
+ let setDeviceAsPrimary = (context["setDeviceAsPrimary"] as? Bool) ?? false
388
+ MobileMessaging.personalize(forceDepersonalize: forceDepersonalize, keepAsLead: keepAsLead, setDeviceAsPrimary: setDeviceAsPrimary, userIdentity: ui, userAttributes: ua) { (error) in
332
389
  if let error = error {
333
390
  onError([error.reactNativeObject])
334
391
  } else {
@@ -340,12 +397,15 @@ class ReactNativeMobileMessaging: RCTEventEmitter {
340
397
  @objc(depersonalize:onError:)
341
398
  func depersonalize(onSuccess: @escaping RCTResponseSenderBlock, onError: @escaping RCTResponseSenderBlock) {
342
399
  MobileMessaging.depersonalize(completion: { (status, error) in
343
- if (status == MMSuccessPending.pending) {
344
- onSuccess(["pending"])
345
- } else if let error = error {
400
+ if let error = error {
346
401
  onError([error.reactNativeObject])
347
402
  } else {
348
- onSuccess(["success"])
403
+ MobileMessaging.jwtSupplier = nil
404
+ if (status == MMSuccessPending.pending) {
405
+ onSuccess(["pending"])
406
+ } else {
407
+ onSuccess(["success"])
408
+ }
349
409
  }
350
410
  })
351
411
  }
@@ -472,9 +532,22 @@ class ReactNativeMobileMessaging: RCTEventEmitter {
472
532
  onError([NSError(type: .NotSupported).reactNativeObject])
473
533
  }
474
534
 
535
+ @objc(cleanup:onError:)
536
+ func cleanup(onSuccess: @escaping RCTResponseSenderBlock, onError: @escaping RCTResponseSenderBlock) {
537
+ MobileMessaging.jwtSupplier = nil
538
+ RNMobileMessagingConfiguration.saveConfigToDefaults(rawConfig: [:])
539
+ MobileMessaging.cleanUpAndStop(false, completion: {
540
+ onSuccess([])
541
+ })
542
+ }
543
+
475
544
  @objc(setUserDataJwt:onSuccess:onError:)
476
545
  func setUserDataJwt(jwt: String?, onSuccess: @escaping RCTResponseSenderBlock, onError: @escaping RCTResponseSenderBlock) {
477
- MobileMessaging.jwtSupplier = VariableJwtSupplier(jwt: jwt)
546
+ if let jwt = jwt, !jwt.isEmpty {
547
+ MobileMessaging.jwtSupplier = VariableJwtSupplier(jwt: jwt)
548
+ } else {
549
+ MobileMessaging.jwtSupplier = nil
550
+ }
478
551
  onSuccess(nil)
479
552
  }
480
553
 
@@ -54,6 +54,9 @@ RCT_EXTERN_METHOD(fetchInboxMessages:(NSString *)token externalUserId:(NSString
54
54
  RCT_EXTERN_METHOD(fetchInboxMessagesWithoutToken:(NSString *)externalUserId inboxFilterOptions:(NSDictionary *)inboxFilterOptions onSuccess:(RCTResponseSenderBlock)successCallback onError:(RCTResponseSenderBlock)errorCallback)
55
55
  RCT_EXTERN_METHOD(setInboxMessagesSeen:(NSString *)externalUserId messages:(NSArray *)messages onSuccess:(RCTResponseSenderBlock)successCallback onError:(RCTResponseSenderBlock)errorCallback)
56
56
 
57
+ /*Cleanup*/
58
+ RCT_EXTERN_METHOD(cleanup:(RCTResponseSenderBlock)onSuccess onError:(RCTResponseSenderBlock)onError)
59
+
57
60
  /*JWT*/
58
61
  RCT_EXTERN_METHOD(setUserDataJwt:(NSString *)jwt onSuccess:(RCTResponseSenderBlock)successCallback onError:(RCTResponseSenderBlock)errorCallback)
59
62
  @end
@@ -11,6 +11,7 @@ import MobileMessaging
11
11
 
12
12
  class RNMobileMessagingConfiguration {
13
13
  static let userDefaultsConfigKey = "com.mobile-messaging.reactNativePluginConfiguration"
14
+ static let ignoreKeysWhenComparing: [String] = [Keys.reactNativePluginVersion]
14
15
 
15
16
  struct Keys {
16
17
  static let privacySettings = "privacySettings"
@@ -34,7 +35,6 @@ class RNMobileMessagingConfiguration {
34
35
  static let backendBaseURL = "backendBaseURL"
35
36
  }
36
37
 
37
- let appCode: String
38
38
  let webRTCUI: [String: AnyObject]?
39
39
  let messageStorageEnabled: Bool
40
40
  let defaultMessageStorage: Bool
@@ -50,13 +50,11 @@ class RNMobileMessagingConfiguration {
50
50
  let backendBaseURL: String?
51
51
 
52
52
  init?(rawConfig: [String: AnyObject]) {
53
- guard let appCode = rawConfig[RNMobileMessagingConfiguration.Keys.applicationCode] as? String,
54
- let ios = rawConfig["ios"] as? [String: AnyObject] else
53
+ guard let ios = rawConfig["ios"] as? [String: AnyObject] else
55
54
  {
56
55
  return nil
57
56
  }
58
57
 
59
- self.appCode = appCode
60
58
  self.webRTCUI = rawConfig[RNMobileMessagingConfiguration.Keys.webRTCUI] as? [String: AnyObject]
61
59
  self.logging = rawConfig[RNMobileMessagingConfiguration.Keys.logging].unwrap(orDefault: false)
62
60
  self.defaultMessageStorage = rawConfig[RNMobileMessagingConfiguration.Keys.defaultMessageStorage].unwrap(orDefault: false)
@@ -70,7 +68,6 @@ class RNMobileMessagingConfiguration {
70
68
  ps[RNMobileMessagingConfiguration.Keys.userDataPersistingDisabled] = rawPrivacySettings[RNMobileMessagingConfiguration.Keys.userDataPersistingDisabled].unwrap(orDefault: false)
71
69
  ps[RNMobileMessagingConfiguration.Keys.carrierInfoSendingDisabled] = rawPrivacySettings[RNMobileMessagingConfiguration.Keys.carrierInfoSendingDisabled].unwrap(orDefault: false)
72
70
  ps[RNMobileMessagingConfiguration.Keys.systemInfoSendingDisabled] = rawPrivacySettings[RNMobileMessagingConfiguration.Keys.systemInfoSendingDisabled].unwrap(orDefault: false)
73
- ps[RNMobileMessagingConfiguration.Keys.applicationCodePersistingDisabled] = rawPrivacySettings[RNMobileMessagingConfiguration.Keys.applicationCodePersistingDisabled].unwrap(orDefault: false)
74
71
 
75
72
  privacySettings = ps
76
73
  } else {
@@ -108,17 +105,7 @@ class RNMobileMessagingConfiguration {
108
105
  }
109
106
 
110
107
  static func saveConfigToDefaults(rawConfig: [String: AnyObject]) {
111
- var serializableConfig = rawConfig.compactMapValues { value -> AnyObject? in
112
- if value is NSString || value is NSNumber || value is NSArray || value is NSDictionary || value is NSDate || value is NSData {
113
- return value
114
- }
115
- return nil
116
- }
117
- if serializableConfig[RNMobileMessagingConfiguration.Keys.messageStorage] != nil {
118
- // Temporal fix so the archiving doesn't fail (messageStorage doesn't contain codable objects).
119
- // Below we overwrite it with a dummy value so messageStorageEnabled keeps working.
120
- serializableConfig[RNMobileMessagingConfiguration.Keys.messageStorage] = NSString(string: "message_storage")
121
- }
108
+ let serializableConfig = serializedConfig(from: rawConfig)
122
109
 
123
110
  do {
124
111
  let data: Data = try NSKeyedArchiver.archivedData(withRootObject: serializableConfig, requiringSecureCoding: false)
@@ -135,10 +122,71 @@ class RNMobileMessagingConfiguration {
135
122
  }
136
123
 
137
124
  do {
138
- return try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? [String: AnyObject]
125
+ guard let rawConfig = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? [String: AnyObject] else {
126
+ return nil
127
+ }
128
+
129
+ let normalizedConfig = serializedConfig(from: rawConfig)
130
+ if (normalizedConfig as NSDictionary) != (rawConfig as NSDictionary) {
131
+ saveConfigToDefaults(rawConfig: normalizedConfig)
132
+ }
133
+
134
+ return normalizedConfig
139
135
  } catch {
140
136
  MMLogError("[MobileMessaging] Failed to unarchive config from UserDefaults: \(error)")
141
137
  return nil
142
138
  }
143
139
  }
140
+
141
+ static func didConfigurationChange(userConfigDict: [String: AnyObject]) -> Bool {
142
+ var serializedUserConfig = serializedConfig(from: userConfigDict)
143
+ guard let cachedConfigDict = getRawConfigFromDefaults() else {
144
+ return false
145
+ }
146
+ var serializedCachedConfig = serializedConfig(from: cachedConfigDict)
147
+
148
+ removeIgnoredKeys(from: &serializedUserConfig)
149
+ removeIgnoredKeys(from: &serializedCachedConfig)
150
+
151
+ return (serializedUserConfig as NSDictionary) != (serializedCachedConfig as NSDictionary)
152
+ }
153
+
154
+ private static func serializedConfig(from rawConfig: [String: AnyObject]) -> [String: AnyObject] {
155
+ var rawConfig = rawConfig
156
+ rawConfig.removeValue(forKey: RNMobileMessagingConfiguration.Keys.applicationCode)
157
+ rawConfig.removeValue(forKey: RNMobileMessagingConfiguration.Keys.userDataJwt)
158
+ sanitizePrivacySettings(in: &rawConfig)
159
+
160
+ var serializableConfig = rawConfig.compactMapValues { value -> AnyObject? in
161
+ if value is NSString || value is NSNumber || value is NSArray || value is NSDictionary || value is NSDate || value is NSData {
162
+ return value
163
+ }
164
+ return nil
165
+ }
166
+
167
+ if rawConfig[RNMobileMessagingConfiguration.Keys.messageStorage] != nil {
168
+ // Preserve the fact that custom message storage was configured without persisting the JS callbacks.
169
+ serializableConfig[RNMobileMessagingConfiguration.Keys.messageStorage] = NSString(string: "message_storage")
170
+ }
171
+
172
+ return serializableConfig
173
+ }
174
+
175
+ private static func removeIgnoredKeys(from config: inout [String: AnyObject]) {
176
+ ignoreKeysWhenComparing.forEach { config.removeValue(forKey: $0) }
177
+ }
178
+
179
+ private static func sanitizePrivacySettings(in rawConfig: inout [String: AnyObject]) {
180
+ guard var rawPrivacySettings = rawConfig[RNMobileMessagingConfiguration.Keys.privacySettings] as? [String: Any] else {
181
+ return
182
+ }
183
+
184
+ rawPrivacySettings.removeValue(forKey: RNMobileMessagingConfiguration.Keys.applicationCodePersistingDisabled)
185
+
186
+ if rawPrivacySettings.isEmpty {
187
+ rawConfig.removeValue(forKey: RNMobileMessagingConfiguration.Keys.privacySettings)
188
+ } else {
189
+ rawConfig[RNMobileMessagingConfiguration.Keys.privacySettings] = rawPrivacySettings as NSDictionary
190
+ }
191
+ }
144
192
  }
@@ -16,6 +16,7 @@ public enum RNMobileMessagingErrorType: Error {
16
16
  case DefaultStorageNotInitialized
17
17
  case NotSupported
18
18
  case NoData
19
+ case InitializationFailed
19
20
 
20
21
  fileprivate var errorCode: Int {
21
22
  switch self {
@@ -29,6 +30,8 @@ public enum RNMobileMessagingErrorType: Error {
29
30
  return 3
30
31
  case .NoData:
31
32
  return 4
33
+ case .InitializationFailed:
34
+ return 5
32
35
  }
33
36
 
34
37
  }
@@ -47,6 +50,8 @@ public enum RNMobileMessagingErrorType: Error {
47
50
  errorDescription = NSLocalizedString("Functionality is not supported.", comment: "")
48
51
  case .NoData:
49
52
  errorDescription = NSLocalizedString("No data retrieved.", comment: "")
53
+ case .InitializationFailed:
54
+ errorDescription = NSLocalizedString("Could not initialize MobileMessaging SDK.", comment: "")
50
55
  }
51
56
  return errorDescription
52
57
  }
@@ -39,6 +39,12 @@ extension MMInbox {
39
39
  var result = [String: Any]()
40
40
  result["countTotal"] = countTotal
41
41
  result["countUnread"] = countUnread
42
+ if let countTotalFiltered = countTotalFiltered {
43
+ result["countTotalFiltered"] = countTotalFiltered
44
+ }
45
+ if let countUnreadFiltered = countUnreadFiltered {
46
+ result["countUnreadFiltered"] = countUnreadFiltered
47
+ }
42
48
  result["messages"] = messages.map({ return $0.dictionaryRepresentation })
43
49
  return result
44
50
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "infobip-mobile-messaging-react-native-plugin",
3
3
  "title": "Infobip Mobile Messaging React Native Plugin",
4
- "version": "14.8.3",
4
+ "version": "14.9.1",
5
5
  "description": "Infobip Mobile Messaging React Native Plugin",
6
6
  "main": "./src/index.js",
7
7
  "scripts": {
package/src/index.d.ts CHANGED
@@ -64,7 +64,6 @@ declare namespace MobileMessagingReactNative {
64
64
  notificationSound?: string;
65
65
  };
66
66
  privacySettings?: {
67
- applicationCodePersistingDisabled?: boolean;
68
67
  userDataPersistingDisabled?: boolean;
69
68
  carrierInfoSendingDisabled?: boolean;
70
69
  systemInfoSendingDisabled?: boolean
@@ -130,13 +129,16 @@ declare namespace MobileMessagingReactNative {
130
129
  export interface MMInbox {
131
130
  countTotal: number;
132
131
  countUnread: number;
133
- messages?: [Message];
132
+ countTotalFiltered?: number;
133
+ countUnreadFiltered?: number;
134
+ messages?: Message[];
134
135
  }
135
136
 
136
137
  export interface MMInboxFilterOptions {
137
138
  fromDateTime?: string;
138
139
  toDateTime?: string;
139
140
  topic?: string;
141
+ topics?: string[];
140
142
  limit?: number;
141
143
  }
142
144
 
@@ -145,10 +147,12 @@ declare namespace MobileMessagingReactNative {
145
147
  userAttributes?: Record<string, string | number | boolean | object[]>;
146
148
  forceDepersonalize?: boolean;
147
149
  keepAsLead?: boolean;
150
+ setDeviceAsPrimary?: boolean;
148
151
  }
149
152
 
150
153
  export interface Message {
151
154
  messageId: string;
155
+ topic?: string;
152
156
  title?: string;
153
157
  body?: string;
154
158
  sound?: string;
@@ -720,6 +724,16 @@ declare namespace MobileMessagingReactNative {
720
724
  */
721
725
  setChatDomain(domain?: string): void;
722
726
 
727
+ /**
728
+ * Cleans up the SDK, removing all data and stopping all services.
729
+ * After cleanup, you should call init() again with a new configuration to restart the SDK.
730
+ * JWT supplier is also cleared during cleanup.
731
+ *
732
+ * @param onSuccess will be called on success
733
+ * @param onError will be called on error
734
+ */
735
+ cleanup(onSuccess: () => void, onError: (error: MobileMessagingError) => void): void;
736
+
723
737
  /**
724
738
  * Set JSON Web Token for user data operations and personalization.
725
739
  * @param jwt JWT token in predefined format
package/src/index.js CHANGED
@@ -166,7 +166,6 @@ class MobileMessaging {
166
166
  * notificationSound: <String>
167
167
  * }
168
168
  * privacySettings: {
169
- * applicationCodePersistingDisabled: <Boolean>,
170
169
  * userDataPersistingDisabled: <Boolean>,
171
170
  * carrierInfoSendingDisabled: <Boolean>,
172
171
  * systemInfoSendingDisabled: <Boolean>
@@ -285,7 +284,14 @@ class MobileMessaging {
285
284
  * @name fetchInboxMessages
286
285
  * @param token access token (JWT in a strictly predefined format) required for current user to have access to the Inbox messages
287
286
  * @param externalUserId External User ID is meant to be an ID of a user in an external (non-Infobip) service
288
- * @param filterOptions filtering options applied to messages list in response. Nullable, will return default number of messages
287
+ * @param filterOptions filtering options applied to messages list in response.
288
+ * {
289
+ * fromDateTime: <String; filter messages received after this datetime in ISO8601 format with timezone, e.g. "2024-03-11T12:00:00+01:00">,
290
+ * toDateTime: <String; filter messages received before this datetime in ISO8601 format with timezone, e.g. "2024-03-20T12:00:00+01:00">,
291
+ * topic: <String; filter messages by a single topic. Mutually exclusive with 'topics'>,
292
+ * topics: <Array<String>; filter messages by multiple topics, e.g. ["topic1", "topic2"]. Mutually exclusive with 'topic'>,
293
+ * limit: <Number; maximum number of messages to return. Default is 20 for standard/single-topic fetches. When using 'topics', no default limit is applied unless explicitly provided, and the limit applies to the total returned messages, not per topic>,
294
+ * }
289
295
  * @param onSuccess will be called on success
290
296
  * @param {Function} onError will be called on error
291
297
  */
@@ -298,7 +304,14 @@ class MobileMessaging {
298
304
  *
299
305
  * @name fetchInboxMessagesWithoutToken
300
306
  * @param externalUserId External User ID is meant to be an ID of a user in an external (non-Infobip) service
301
- * @param filterOptions filtering options applied to messages list in response. Nullable, will return default number of messages
307
+ * @param filterOptions filtering options applied to messages list in response.
308
+ * {
309
+ * fromDateTime: <String; filter messages received after this datetime in ISO8601 format with timezone, e.g. "2024-03-11T12:00:00+01:00">,
310
+ * toDateTime: <String; filter messages received before this datetime in ISO8601 format with timezone, e.g. "2024-03-20T12:00:00+01:00">,
311
+ * topic: <String; filter messages by a single topic. Mutually exclusive with 'topics'>,
312
+ * topics: <Array<String>; filter messages by multiple topics, e.g. ["topic1", "topic2"]. Mutually exclusive with 'topic'>,
313
+ * limit: <Number; maximum number of messages to return. Default is 20 for standard/single-topic fetches. When using 'topics', no default limit is applied unless explicitly provided, and the limit applies to the total returned messages, not per topic>,
314
+ * }
302
315
  * @param onSuccess will be called on success
303
316
  * @param {Function} onError will be called on error
304
317
  */
@@ -873,6 +886,19 @@ class MobileMessaging {
873
886
  RNMMChat.setChatDomain(domain);
874
887
  }
875
888
 
889
+ /**
890
+ * Cleans up the SDK, removing all data and stopping all services.
891
+ * After cleanup, you should call init() again with a new configuration to restart the SDK.
892
+ * JWT supplier is also cleared during cleanup.
893
+ *
894
+ * @name cleanup
895
+ * @param {Function} onSuccess will be called on success
896
+ * @param {Function} onError will be called on error
897
+ */
898
+ cleanup(onSuccess = function() {}, onError = function() {}) {
899
+ ReactNativeMobileMessaging.cleanup(onSuccess, onError);
900
+ }
901
+
876
902
  /**
877
903
  * Set JSON Web Token for user data operations and personalization.
878
904
  * @param {String} jwt JWT token in predefined format
@@ -52,6 +52,9 @@ export interface Spec extends TurboModule {
52
52
  submitEvent(eventData: Object, onError: (error: Object) => void): void;
53
53
  submitEventImmediately(eventData: Object, onSuccess: () => void, onError: (error: Object) => void): void;
54
54
 
55
+ // Cleanup
56
+ cleanup(onSuccess: () => void, onError: (error: Object) => void): void;
57
+
55
58
  // JWT
56
59
  setUserDataJwt(jwt: string, onSuccess: () => void, onError: (error: Object) => void): void;
57
60