react-native-mapp-plugin 2.0.0-beta.1 → 2.0.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.
- package/CHANGELOG.md +20 -1
- package/MIGRATION_2.0.md +1 -1
- package/Mapp.js +15 -3
- package/README.md +14 -2
- package/android/src/main/java/com/reactlibrary/MappEngagementDispatcher.java +36 -4
- package/android/src/main/java/com/reactlibrary/NativeRNMappPluginModuleSpec.java +1 -1
- package/android/src/main/java/com/reactlibrary/RNMappPluginModule.java +32 -9
- package/android/src/main/jni/RNMappPlugin-generated.cpp +1 -1
- package/android/src/main/jni/react/renderer/components/RNMappPlugin/RNMappPluginJSI.h +2 -2
- package/ios/RNMappPluginModule.h +13 -1
- package/ios/RNMappPluginModule.mm +35 -3
- package/package.json +7 -2
- package/plugin/build/android.d.ts +1 -1
- package/plugin/build/android.js +57 -9
- package/plugin/build/index.d.ts +1 -1
- package/plugin/build/index.js +3 -1
- package/plugin/build/ios.d.ts +2 -0
- package/plugin/build/ios.js +161 -0
- package/specs/NativeRNMappPluginModule.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
|
-
## Version 2.0.
|
|
1
|
+
## Version 2.0.1 (2026/09/04)
|
|
2
|
+
|
|
3
|
+
***Bug Fixes***
|
|
4
|
+
|
|
5
|
+
- iOS: `Mapp.onInitCompletedListener()` now returns a `Promise<boolean>` instead of `null`.
|
|
6
|
+
- iOS: When the SDK is already initialized, `Mapp.onInitCompletedListener()` resolves immediately with `true`.
|
|
7
|
+
- iOS: When the SDK is still initializing, `Mapp.onInitCompletedListener()` waits for the existing `com.mapp.init` native event and removes its one-time listener after resolving.
|
|
8
|
+
- Android: Preserved the existing native `AppoxeeObserver` implementation and behavior of `Mapp.onInitCompletedListener()`.
|
|
9
|
+
- Android/iOS: Preserved the public `Mapp.onInitCompletedListener(): Promise<boolean>` API while providing consistent initialization-waiting behavior on both platforms.
|
|
10
|
+
|
|
11
|
+
***Tests***
|
|
12
|
+
|
|
13
|
+
- Added bridge coverage for both iOS readiness paths: already initialized and waiting for the native initialization event.
|
|
14
|
+
- Updated platform-dispatch coverage to verify that Android continues to use the native listener and iOS uses its existing initialization event.
|
|
15
|
+
|
|
16
|
+
## Version 2.0.0 (2026/09/03)
|
|
2
17
|
|
|
3
18
|
***Breaking Changes***
|
|
4
19
|
|
|
@@ -10,11 +25,15 @@
|
|
|
10
25
|
|
|
11
26
|
***Bug Fixes***
|
|
12
27
|
|
|
28
|
+
- `Mapp.engage(...)` is now awaitable so singleton-dependent calls can safely run after native initialization; engagement failures reject instead of being logged silently.
|
|
13
29
|
- Android: Failed Firebase token registration now rejects with `FCM_REGISTRATION_FAILED` instead of crashing while reading a failed task result.
|
|
14
30
|
- Android: All Mapp engage calls run on the main looper. Background Firebase callbacks wait for a bounded engage attempt and safely return failure after SDK errors, timeout, or interruption.
|
|
15
31
|
- Android/Expo: Mapp and custom push ownership now remove the SDK v7 Firebase service and remain idempotent when prebuild runs repeatedly or changes mode.
|
|
32
|
+
- Android/Expo: The config plugin now writes required permissions, the Mapp messaging service, and the push receiver through `withAndroidManifest`; Firebase configuration remains customer-owned through `expo.android.googleServicesFile`.
|
|
16
33
|
- Android: Exported Expo SDK 57 compatibility constraints stabilize WorkManager, Lifecycle, AndroidX Core, Play Services Location, and Kotlin stdlib for API 36/AGP 8.12/Kotlin 2.1.20 builds.
|
|
17
34
|
- Android: Coroutines remain aligned at 1.11.0 to preserve the Mapp 7.1.2 native in-app dismissal ABI.
|
|
35
|
+
- iOS/Expo: The config plugin now creates and embeds a standalone Notification Service Extension for Mapp rich-push media from `ios_apx_media`, with EAS app-extension metadata and no App Group or extra Pod.
|
|
36
|
+
- iOS/Expo: Verified that Expo SDK 57 CocoaPods autolinking discovers `RNMappPlugin.podspec` and processes `RNMappPlugin` under the New Architecture.
|
|
18
37
|
|
|
19
38
|
***Compatibility***
|
|
20
39
|
|
package/MIGRATION_2.0.md
CHANGED
|
@@ -31,7 +31,7 @@ Read [Breaking changes in 2.0.0](BREAKING_CHANGES.md) first to determine which c
|
|
|
31
31
|
| `Mapp.inAppMarkAsRead(templateId, eventId)` | Android no-op. | Android fetches the Mapp Engage 7.1.2 inbox message and updates it to `READ`. |
|
|
32
32
|
| `Mapp.inAppMarkAsUnRead(templateId, eventId)` | Android no-op. | Android fetches the inbox message and updates it to `UNREAD`. |
|
|
33
33
|
| `Mapp.inAppMarkAsDeleted(templateId, eventId)` | Android no-op. | Android fetches the inbox message and updates it to `DELETED`. |
|
|
34
|
-
| `Mapp.engage(...)` | iOS JavaScript called the private native `autoengage` and `engageInapp` methods separately. | All platforms use
|
|
34
|
+
| `Mapp.engage(...)` | iOS JavaScript called the private native `autoengage` and `engageInapp` methods separately, and Android engagement returned before its main-thread task completed. | All platforms use an awaitable native engagement entry point. Await it before calling singleton-dependent APIs. On iOS it still initializes both push and in-app, using `AppoxeeConfig.plist` as the credential source of truth. |
|
|
35
35
|
| iOS event listeners | Events emitted before a JavaScript listener was attached were dropped. | Up to 50 cold-start events are buffered and delivered after a listener attaches. Consumers should tolerate receiving an initial queued event. |
|
|
36
36
|
|
|
37
37
|
For the Android inbox methods, `eventId` remains accepted for source compatibility but Mapp Engage 7.1.2 identifies and fetches the message using `templateId`.
|
package/Mapp.js
CHANGED
|
@@ -127,7 +127,7 @@ export class Mapp {
|
|
|
127
127
|
server: string,
|
|
128
128
|
appID: string,
|
|
129
129
|
tenantID: string
|
|
130
|
-
) {
|
|
130
|
+
): Promise<boolean> {
|
|
131
131
|
return RNMappPluginModule.engage(
|
|
132
132
|
sdkKey,
|
|
133
133
|
googleProjectId,
|
|
@@ -163,10 +163,22 @@ export class Mapp {
|
|
|
163
163
|
* @return {Promise.<boolean>} A promise with the result.
|
|
164
164
|
*/
|
|
165
165
|
static onInitCompletedListener(): Promise<boolean> {
|
|
166
|
-
if (Platform.OS
|
|
166
|
+
if (Platform.OS === "android") {
|
|
167
167
|
return RNMappPluginModule.onInitCompletedListener();
|
|
168
168
|
}
|
|
169
|
-
|
|
169
|
+
|
|
170
|
+
return RNMappPluginModule.isReady().then((ready) => {
|
|
171
|
+
if (ready) {
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return new Promise((resolve) => {
|
|
176
|
+
const subscription = EventEmitter.addListener(IOS_INIT, () => {
|
|
177
|
+
subscription.remove();
|
|
178
|
+
resolve(true);
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
});
|
|
170
182
|
}
|
|
171
183
|
|
|
172
184
|
/**
|
package/README.md
CHANGED
|
@@ -59,6 +59,8 @@ Configure the installed package by name in `app.json` (values shown are examples
|
|
|
59
59
|
|
|
60
60
|
The Android package must match the Firebase Android application in `google-services.json`. For iOS, configure an APNs-enabled App ID, matching bundle identifier, and Apple/EAS signing credentials. Values embedded in app config and native resources are public application configuration; do not put service-account keys or signing secrets there.
|
|
61
61
|
|
|
62
|
+
The Firebase file is customer-owned configuration. Point Expo's built-in `expo.android.googleServicesFile` field at it; this plugin does not copy, generate, or modify `google-services.json`.
|
|
63
|
+
|
|
62
64
|
Generate and run development builds:
|
|
63
65
|
|
|
64
66
|
```bash
|
|
@@ -86,9 +88,11 @@ const subscription = events.addListener('com.mapp.deep_link_received', event =>
|
|
|
86
88
|
// Route the deep link.
|
|
87
89
|
});
|
|
88
90
|
|
|
89
|
-
Mapp.engage('ANDROID_SDK_KEY', 'FCM_PROJECT_ID', 'EMC', 'APP_ID', 'TENANT_ID');
|
|
91
|
+
await Mapp.engage('ANDROID_SDK_KEY', 'FCM_PROJECT_ID', 'EMC', 'APP_ID', 'TENANT_ID');
|
|
90
92
|
```
|
|
91
93
|
|
|
94
|
+
Always await `Mapp.engage(...)` before calling APIs that use the native Mapp singleton. The promise resolves after native engagement and bridge setup complete; use `Mapp.onInitCompletedListener()` or `Mapp.isReady()` when a feature specifically requires the SDK's later ready state.
|
|
95
|
+
|
|
92
96
|
### Android push ownership
|
|
93
97
|
|
|
94
98
|
`pushHandling: "mapp"` is the default. It requires `expo.android.googleServicesFile` and retains `com.reactlibrary.MessageService` as the sole normal-priority Mapp FCM callback owner. The config plugin removes the Mapp SDK v7 service (`com.appoxee.shared.MappMessagingService`) from the merged app manifest.
|
|
@@ -113,6 +117,12 @@ Use `pushHandling: "custom"` when another integration, such as a client-owned `F
|
|
|
113
117
|
|
|
114
118
|
Set `enableGeofencing` on each platform that needs it. Android then adds fine/background location permissions and `Mapp.requestGeofenceLocationPermission()` requests foreground permission before background permission. On iOS, also supply non-empty `locationWhenInUsePermission` and `locationAlwaysPermission` messages. Only request location access when your user-facing feature and store policy justify it.
|
|
115
119
|
|
|
120
|
+
### iOS rich push
|
|
121
|
+
|
|
122
|
+
The Expo config plugin creates a `MappNotificationService` Notification Service Extension with the bundle identifier `<expo.ios.bundleIdentifier>.mappnotificationservice`. The extension reads the public Mapp `ios_apx_media` payload key, downloads the media, and attaches it to the notification. It follows the app minimum deployment target and is set to iOS 15+, uses an App Group shared with the main app, and does not require an additional CocoaPod or React Native code.
|
|
123
|
+
|
|
124
|
+
The extension is also declared in Expo's experimental EAS app-extension metadata so EAS can prepare its signing credentials. Regenerate the iOS project after changing the application bundle identifier.
|
|
125
|
+
|
|
116
126
|
### Tested compatibility
|
|
117
127
|
|
|
118
128
|
| Component | Tested baseline |
|
|
@@ -143,6 +153,8 @@ cd ios && pod install
|
|
|
143
153
|
|
|
144
154
|
Modern React Native autolinking discovers the Android package and CocoaPod automatically. Do not run `react-native link`, edit `settings.gradle`, or add `compile project(...)`.
|
|
145
155
|
|
|
156
|
+
No `MainActivity` or `MainApplication` edit is required. The native module is registered by autolinking. In Expo projects, the config plugin applies Android permissions, the Mapp messaging service, and the push receiver during prebuild without modifying consumer Gradle files. React Native CLI projects receive the same declarations through the library manifest merge. On iOS, CocoaPods discovers `RNMappPlugin.podspec` automatically; do not add the pod manually.
|
|
157
|
+
|
|
146
158
|
For a manually maintained iOS native project, add Push Notifications, Remote Notifications background mode, and (only if needed) Location Updates, then include an `AppoxeeConfig.plist` in the application target. Expo clients should use the config plugin above instead.
|
|
147
159
|
|
|
148
160
|
Basic usage:
|
|
@@ -150,7 +162,7 @@ Basic usage:
|
|
|
150
162
|
```js
|
|
151
163
|
import { Mapp } from 'react-native-mapp-plugin';
|
|
152
164
|
|
|
153
|
-
Mapp.engage('SDK_KEY', 'FCM_PROJECT_ID', 'EMC', 'APP_ID', 'TENANT_ID');
|
|
165
|
+
await Mapp.engage('SDK_KEY', 'FCM_PROJECT_ID', 'EMC', 'APP_ID', 'TENANT_ID');
|
|
154
166
|
```
|
|
155
167
|
|
|
156
168
|
See the [Mapp integration documentation](https://mapp-wiki.atlassian.net/wiki/spaces/MIC/pages/1154875400/React+Native+Integration+for+Mapp+Cloud) for the full JavaScript API and native Mapp configuration values.
|
|
@@ -22,14 +22,40 @@ final class MappEngagementDispatcher {
|
|
|
22
22
|
|
|
23
23
|
private MappEngagementDispatcher() {}
|
|
24
24
|
|
|
25
|
+
interface EngagementCallback {
|
|
26
|
+
void onSuccess();
|
|
27
|
+
void onFailure(@NonNull Exception error);
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
static void engageAsync(
|
|
26
31
|
@NonNull Application application,
|
|
27
32
|
@Nullable AppoxeeOptions options,
|
|
28
33
|
@Nullable Runnable afterEngage
|
|
34
|
+
) {
|
|
35
|
+
engageAsync(application, options, afterEngage, null);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
static void engageAsync(
|
|
39
|
+
@NonNull Application application,
|
|
40
|
+
@Nullable AppoxeeOptions options,
|
|
41
|
+
@Nullable Runnable afterEngage,
|
|
42
|
+
@Nullable EngagementCallback callback
|
|
29
43
|
) {
|
|
30
44
|
Runnable operation = () -> {
|
|
31
|
-
|
|
32
|
-
|
|
45
|
+
try {
|
|
46
|
+
Appoxee.engage(application, options);
|
|
47
|
+
if (afterEngage != null) {
|
|
48
|
+
afterEngage.run();
|
|
49
|
+
}
|
|
50
|
+
} catch (Exception error) {
|
|
51
|
+
Log.e(TAG, "Mapp initialization failed", error);
|
|
52
|
+
if (callback != null) {
|
|
53
|
+
callback.onFailure(error);
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (callback != null) {
|
|
58
|
+
callback.onSuccess();
|
|
33
59
|
}
|
|
34
60
|
};
|
|
35
61
|
if (Looper.myLooper() == Looper.getMainLooper()) {
|
|
@@ -37,7 +63,13 @@ final class MappEngagementDispatcher {
|
|
|
37
63
|
return;
|
|
38
64
|
}
|
|
39
65
|
if (!new Handler(Looper.getMainLooper()).post(operation)) {
|
|
40
|
-
|
|
66
|
+
IllegalStateException error = new IllegalStateException(
|
|
67
|
+
"Unable to post Mapp initialization to the main looper"
|
|
68
|
+
);
|
|
69
|
+
Log.e(TAG, error.getMessage(), error);
|
|
70
|
+
if (callback != null) {
|
|
71
|
+
callback.onFailure(error);
|
|
72
|
+
}
|
|
41
73
|
}
|
|
42
74
|
}
|
|
43
75
|
|
|
@@ -94,7 +126,7 @@ final class MappEngagementDispatcher {
|
|
|
94
126
|
try {
|
|
95
127
|
Appoxee.engage(application, options);
|
|
96
128
|
return true;
|
|
97
|
-
} catch (
|
|
129
|
+
} catch (Exception error) {
|
|
98
130
|
Log.e(TAG, "Mapp initialization failed", error);
|
|
99
131
|
return false;
|
|
100
132
|
}
|
|
@@ -76,7 +76,7 @@ public abstract class NativeRNMappPluginModuleSpec extends ReactContextBaseJavaM
|
|
|
76
76
|
|
|
77
77
|
@ReactMethod
|
|
78
78
|
@DoNotStrip
|
|
79
|
-
public abstract void engage(String sdkKey, String googleProjectId, String server, String appID, String tenantID);
|
|
79
|
+
public abstract void engage(String sdkKey, String googleProjectId, String server, String appID, String tenantID, Promise promise);
|
|
80
80
|
|
|
81
81
|
@ReactMethod
|
|
82
82
|
@DoNotStrip
|
|
@@ -384,19 +384,42 @@ public class RNMappPluginModule extends NativeRNMappPluginModuleSpec implements
|
|
|
384
384
|
}
|
|
385
385
|
|
|
386
386
|
@ReactMethod
|
|
387
|
-
public void engage(String sdkKey, String googleProjectId, String server, String appID,
|
|
388
|
-
|
|
389
|
-
opt
|
|
387
|
+
public void engage(String sdkKey, String googleProjectId, String server, String appID,
|
|
388
|
+
String tenantID, Promise promise) {
|
|
389
|
+
final AppoxeeOptions opt;
|
|
390
|
+
try {
|
|
391
|
+
opt = createOptions(server, sdkKey, appID, tenantID);
|
|
392
|
+
opt.setNotificationMode(NotificationMode.BACKGROUND_AND_FOREGROUND);
|
|
393
|
+
} catch (RuntimeException error) {
|
|
394
|
+
promise.reject("MAPP_ENGAGE_INVALID_CONFIGURATION", error.getMessage(), error);
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
390
397
|
|
|
391
|
-
MappEngagementDispatcher.engageAsync(
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
398
|
+
MappEngagementDispatcher.engageAsync(
|
|
399
|
+
Objects.requireNonNull(application),
|
|
400
|
+
opt,
|
|
401
|
+
this::configureAfterEngage,
|
|
402
|
+
new MappEngagementDispatcher.EngagementCallback() {
|
|
403
|
+
@Override
|
|
404
|
+
public void onSuccess() {
|
|
405
|
+
promise.resolve(true);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
@Override
|
|
409
|
+
public void onFailure(@NonNull Exception error) {
|
|
410
|
+
promise.reject("MAPP_ENGAGE_FAILED", "Mapp initialization failed", error);
|
|
411
|
+
}
|
|
395
412
|
}
|
|
396
|
-
|
|
413
|
+
);
|
|
414
|
+
}
|
|
397
415
|
|
|
398
|
-
|
|
416
|
+
private void configureAfterEngage() {
|
|
417
|
+
Appoxee.instance().subscribe(new AppoxeeObserver() {
|
|
418
|
+
@Override
|
|
419
|
+
public void onReadyStatusChanged(boolean status, MappResult<DevicePayload> result) {
|
|
420
|
+
}
|
|
399
421
|
});
|
|
422
|
+
Appoxee.instance().setPushBroadcast(MyPushBroadcastReceiver.class);
|
|
400
423
|
}
|
|
401
424
|
|
|
402
425
|
@ReactMethod
|
|
@@ -64,7 +64,7 @@ static facebook::jsi::Value __hostFunction_NativeRNMappPluginModuleSpecJSI_engag
|
|
|
64
64
|
|
|
65
65
|
static facebook::jsi::Value __hostFunction_NativeRNMappPluginModuleSpecJSI_engage(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
66
66
|
static jmethodID cachedMethodId = nullptr;
|
|
67
|
-
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt,
|
|
67
|
+
return static_cast<JavaTurboModule &>(turboModule).invokeJavaMethod(rt, PromiseKind, "engage", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lcom/facebook/react/bridge/Promise;)V", args, count, cachedMethodId);
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
static facebook::jsi::Value __hostFunction_NativeRNMappPluginModuleSpecJSI_engageTestServer(facebook::jsi::Runtime& rt, TurboModule &turboModule, const facebook::jsi::Value* args, size_t count) {
|
|
@@ -154,12 +154,12 @@ private:
|
|
|
154
154
|
static_assert(
|
|
155
155
|
bridging::getParameterCount(&T::engage) == 6,
|
|
156
156
|
"Expected engage(...) to have 6 parameters");
|
|
157
|
-
bridging::callFromJs<
|
|
157
|
+
return bridging::callFromJs<jsi::Value>(rt, &T::engage, static_cast<NativeRNMappPluginModuleCxxSpec*>(&turboModule)->jsInvoker_, static_cast<T*>(&turboModule),
|
|
158
158
|
count <= 0 ? throw jsi::JSError(rt, "Expected argument in position 0 to be passed") : args[0].asString(rt),
|
|
159
159
|
count <= 1 ? throw jsi::JSError(rt, "Expected argument in position 1 to be passed") : args[1].asString(rt),
|
|
160
160
|
count <= 2 ? throw jsi::JSError(rt, "Expected argument in position 2 to be passed") : args[2].asString(rt),
|
|
161
161
|
count <= 3 ? throw jsi::JSError(rt, "Expected argument in position 3 to be passed") : args[3].asString(rt),
|
|
162
|
-
count <= 4 ? throw jsi::JSError(rt, "Expected argument in position 4 to be passed") : args[4].asString(rt));
|
|
162
|
+
count <= 4 ? throw jsi::JSError(rt, "Expected argument in position 4 to be passed") : args[4].asString(rt));
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
static jsi::Value __engageTestServer(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value* args, size_t count) {
|
package/ios/RNMappPluginModule.h
CHANGED
|
@@ -5,11 +5,23 @@
|
|
|
5
5
|
#import "AppoxeeLocationManager.h"
|
|
6
6
|
#import <UserNotifications/UNUserNotificationCenter.h>
|
|
7
7
|
#if RCT_NEW_ARCH_ENABLED
|
|
8
|
+
#import <ReactCommon/RCTTurboModule.h>
|
|
9
|
+
#if __has_include(<RNMappPluginSpec/RNMappPluginSpec.h>)
|
|
10
|
+
#import <RNMappPluginSpec/RNMappPluginSpec.h>
|
|
11
|
+
#define RNMAPP_HAS_TURBO_MODULE_SPEC 1
|
|
12
|
+
#elif __has_include(<RNMappPlugin/RNMappPlugin.h>)
|
|
8
13
|
#import <RNMappPlugin/RNMappPlugin.h>
|
|
14
|
+
#define RNMAPP_HAS_TURBO_MODULE_SPEC 1
|
|
15
|
+
#elif __has_include("RNMappPlugin.h")
|
|
16
|
+
#import "RNMappPlugin.h"
|
|
17
|
+
#define RNMAPP_HAS_TURBO_MODULE_SPEC 1
|
|
18
|
+
#else
|
|
19
|
+
#define RNMAPP_HAS_TURBO_MODULE_SPEC 0
|
|
20
|
+
#endif
|
|
9
21
|
#endif
|
|
10
22
|
|
|
11
23
|
@interface RNMappPluginModule : NSObject <RCTBridgeModule,AppoxeeInappDelegate, AppoxeeNotificationDelegate, AppoxeeLocationManagerDelegate
|
|
12
|
-
#if RCT_NEW_ARCH_ENABLED
|
|
24
|
+
#if RCT_NEW_ARCH_ENABLED && RNMAPP_HAS_TURBO_MODULE_SPEC
|
|
13
25
|
, NativeRNMappPluginModuleSpec
|
|
14
26
|
#endif
|
|
15
27
|
>
|
|
@@ -2,12 +2,24 @@
|
|
|
2
2
|
#import "RNMappEventEmmiter.h"
|
|
3
3
|
#if RCT_NEW_ARCH_ENABLED
|
|
4
4
|
#import <ReactCommon/RCTTurboModule.h>
|
|
5
|
+
#if __has_include(<RNMappPluginSpec/RNMappPluginSpec.h>)
|
|
6
|
+
#import <RNMappPluginSpec/RNMappPluginSpec.h>
|
|
7
|
+
#define RNMAPP_HAS_TURBO_MODULE_SPEC 1
|
|
8
|
+
#elif __has_include(<RNMappPlugin/RNMappPlugin.h>)
|
|
9
|
+
#import <RNMappPlugin/RNMappPlugin.h>
|
|
10
|
+
#define RNMAPP_HAS_TURBO_MODULE_SPEC 1
|
|
11
|
+
#elif __has_include("RNMappPlugin.h")
|
|
12
|
+
#import "RNMappPlugin.h"
|
|
13
|
+
#define RNMAPP_HAS_TURBO_MODULE_SPEC 1
|
|
14
|
+
#else
|
|
15
|
+
#define RNMAPP_HAS_TURBO_MODULE_SPEC 0
|
|
16
|
+
#endif
|
|
5
17
|
#endif
|
|
6
18
|
|
|
7
19
|
|
|
8
20
|
@implementation RNMappPluginModule
|
|
9
21
|
|
|
10
|
-
#if RCT_NEW_ARCH_ENABLED
|
|
22
|
+
#if RCT_NEW_ARCH_ENABLED && RNMAPP_HAS_TURBO_MODULE_SPEC
|
|
11
23
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
12
24
|
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
13
25
|
{
|
|
@@ -82,7 +94,7 @@ RCT_EXPORT_METHOD(getToken:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRej
|
|
|
82
94
|
RCT_EXPORT_METHOD(engage2) {}
|
|
83
95
|
|
|
84
96
|
RCT_EXPORT_METHOD(engageTestServer:(NSString *)cepUrl sdkKey:(NSString *)sdkKey googleProjectId:(NSString *)projectId server:(NSString *)server appID:(NSString *)appID tenantID:(NSString *)tenantID) {
|
|
85
|
-
[self
|
|
97
|
+
[self performEngage:sdkKey googleProjectId:projectId server:server appID:appID tenantID:tenantID];
|
|
86
98
|
}
|
|
87
99
|
|
|
88
100
|
RCT_EXPORT_METHOD(onInitCompletedListener:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
|
|
@@ -119,13 +131,33 @@ RCT_EXPORT_METHOD(removeAndroidListeners:(NSInteger)count) {}
|
|
|
119
131
|
|
|
120
132
|
#pragma mark Exported methods - Notifications
|
|
121
133
|
|
|
122
|
-
|
|
134
|
+
- (void)performEngage:(NSString *)sdkKey
|
|
135
|
+
googleProjectId:(NSString *)projectId
|
|
136
|
+
server:(NSString *)server
|
|
137
|
+
appID:(NSString *)appID
|
|
138
|
+
tenantID:(NSString *)tenantID {
|
|
123
139
|
SERVER serv = [self getServerKeyFor:server];
|
|
124
140
|
[[Appoxee shared] engageAndAutoIntegrateWithLaunchOptions:nil andDelegate:[RNMappEventEmmiter shared] with:serv];
|
|
125
141
|
[[Appoxee shared] addObserver: [RNMappEventEmmiter shared] forKeyPath:@"isReady" options:NSKeyValueObservingOptionNew context:nil];
|
|
126
142
|
[[AppoxeeInapp shared] engageWithDelegate:[RNMappEventEmmiter shared] with:[self getInappServerKeyFor:server]];
|
|
127
143
|
}
|
|
128
144
|
|
|
145
|
+
RCT_EXPORT_METHOD(engage:(NSString *)sdkKey
|
|
146
|
+
googleProjectId:(NSString *)projectId
|
|
147
|
+
server:(NSString *)server
|
|
148
|
+
appID:(NSString *)appID
|
|
149
|
+
tenantID:(NSString *)tenantID
|
|
150
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
151
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
|
152
|
+
@try {
|
|
153
|
+
[self performEngage:sdkKey googleProjectId:projectId server:server appID:appID tenantID:tenantID];
|
|
154
|
+
resolve(@YES);
|
|
155
|
+
} @catch (NSException *exception) {
|
|
156
|
+
NSString *message = exception.reason ?: @"Mapp initialization failed";
|
|
157
|
+
reject(@"MAPP_ENGAGE_FAILED", message, nil);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
129
161
|
RCT_EXPORT_METHOD(getAlias:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
|
|
130
162
|
[[Appoxee shared] getDeviceAliasWithCompletionHandler:^(NSError * _Nullable appoxeeError, id _Nullable data) {
|
|
131
163
|
if (appoxeeError == nil && data != nil) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-mapp-plugin",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Mapp SDK for React Native.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"files": [
|
|
@@ -99,7 +99,12 @@
|
|
|
99
99
|
],
|
|
100
100
|
"transform": {
|
|
101
101
|
"^.+\\.js$": "babel-jest",
|
|
102
|
-
"^.+\\.ts$": [
|
|
102
|
+
"^.+\\.ts$": [
|
|
103
|
+
"ts-jest",
|
|
104
|
+
{
|
|
105
|
+
"tsconfig": "plugin/tsconfig.json"
|
|
106
|
+
}
|
|
107
|
+
]
|
|
103
108
|
},
|
|
104
109
|
"moduleNameMapper": {
|
|
105
110
|
"^react-native$": "<rootDir>/__mocks__/react-native.js"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { ConfigPlugin } from '@expo/config-plugins';
|
|
2
2
|
import type { NormalizedMappExpoPluginProps } from './types';
|
|
3
|
-
export declare function
|
|
3
|
+
export declare function updateAndroidManifest(androidManifest: any, props: NormalizedMappExpoPluginProps['android']): any;
|
|
4
4
|
export declare const withMappEngageAndroid: ConfigPlugin<NormalizedMappExpoPluginProps['android']>;
|
package/plugin/build/android.js
CHANGED
|
@@ -1,28 +1,68 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.withMappEngageAndroid = void 0;
|
|
4
|
-
exports.
|
|
4
|
+
exports.updateAndroidManifest = updateAndroidManifest;
|
|
5
5
|
const config_plugins_1 = require("@expo/config-plugins");
|
|
6
6
|
const messageService = 'com.reactlibrary.MessageService';
|
|
7
|
+
const pushBroadcastReceiver = 'com.reactlibrary.MyPushBroadcastReceiver';
|
|
7
8
|
const sdkMessagingServices = [
|
|
8
9
|
'com.appoxee.shared.MappMessagingService',
|
|
9
10
|
'com.appoxee.push.fcm.MappMessagingService',
|
|
10
11
|
];
|
|
11
12
|
const managedMessagingServices = [messageService, ...sdkMessagingServices];
|
|
13
|
+
const receiveBootCompleted = 'android.permission.RECEIVE_BOOT_COMPLETED';
|
|
14
|
+
const internet = 'android.permission.INTERNET';
|
|
15
|
+
const postNotifications = 'android.permission.POST_NOTIFICATIONS';
|
|
12
16
|
const fineLocation = 'android.permission.ACCESS_FINE_LOCATION';
|
|
13
17
|
const backgroundLocation = 'android.permission.ACCESS_BACKGROUND_LOCATION';
|
|
18
|
+
const firebaseMessagingEvent = 'com.google.firebase.MESSAGING_EVENT';
|
|
14
19
|
function removeAll(items, predicate) {
|
|
15
20
|
return (items ?? []).filter(item => !predicate(item));
|
|
16
21
|
}
|
|
17
|
-
function
|
|
22
|
+
function addPermission(androidManifest, permission) {
|
|
23
|
+
const permissions = androidManifest.manifest['uses-permission'] ?? [];
|
|
24
|
+
if (!permissions.some((item) => item.$?.['android:name'] === permission)) {
|
|
25
|
+
permissions.push({ $: { 'android:name': permission } });
|
|
26
|
+
}
|
|
27
|
+
androidManifest.manifest['uses-permission'] = permissions;
|
|
28
|
+
}
|
|
29
|
+
function updateAndroidManifest(androidManifest, props) {
|
|
18
30
|
const manifest = androidManifest.manifest;
|
|
19
31
|
const application = config_plugins_1.AndroidConfig.Manifest.getMainApplicationOrThrow(androidManifest);
|
|
20
32
|
application.service = removeAll(application.service, service => {
|
|
21
|
-
return managedMessagingServices.includes(service.$?.['android:name'])
|
|
22
|
-
&& service.$?.['tools:node'] === 'remove';
|
|
33
|
+
return managedMessagingServices.includes(service.$?.['android:name']);
|
|
23
34
|
});
|
|
35
|
+
application.receiver = removeAll(application.receiver, receiver => (receiver.$?.['android:name'] === pushBroadcastReceiver));
|
|
24
36
|
manifest.$ = manifest.$ ?? {};
|
|
25
37
|
manifest.$['xmlns:tools'] = 'http://schemas.android.com/tools';
|
|
38
|
+
for (const permission of [receiveBootCompleted, internet, postNotifications]) {
|
|
39
|
+
addPermission(androidManifest, permission);
|
|
40
|
+
}
|
|
41
|
+
if (props.enableGeofencing) {
|
|
42
|
+
addPermission(androidManifest, fineLocation);
|
|
43
|
+
addPermission(androidManifest, backgroundLocation);
|
|
44
|
+
}
|
|
45
|
+
application.receiver.push({
|
|
46
|
+
$: {
|
|
47
|
+
'android:name': pushBroadcastReceiver,
|
|
48
|
+
'android:enabled': 'true',
|
|
49
|
+
'android:exported': 'false',
|
|
50
|
+
},
|
|
51
|
+
'intent-filter': [{
|
|
52
|
+
action: [
|
|
53
|
+
{ $: { 'android:name': 'com.appoxee.PUSH_OPENED' } },
|
|
54
|
+
{ $: { 'android:name': 'com.appoxee.PUSH_RECEIVED' } },
|
|
55
|
+
{ $: { 'android:name': 'com.appoxee.PUSH_DISMISSED' } },
|
|
56
|
+
{ $: { 'android:name': 'com.appoxee.BUTTON_CLICKED' } },
|
|
57
|
+
{ $: { 'android:name': 'android.intent.action.VIEW' } },
|
|
58
|
+
],
|
|
59
|
+
category: [
|
|
60
|
+
{ $: { 'android:name': '${applicationId}' } },
|
|
61
|
+
{ $: { 'android:name': 'android.intent.category.DEFAULT' } },
|
|
62
|
+
{ $: { 'android:name': 'android.intent.category.BROWSABLE' } },
|
|
63
|
+
],
|
|
64
|
+
}],
|
|
65
|
+
});
|
|
26
66
|
for (const serviceName of sdkMessagingServices) {
|
|
27
67
|
application.service.push({
|
|
28
68
|
$: {
|
|
@@ -31,7 +71,18 @@ function updatePushHandling(androidManifest, pushHandling) {
|
|
|
31
71
|
},
|
|
32
72
|
});
|
|
33
73
|
}
|
|
34
|
-
if (pushHandling === '
|
|
74
|
+
if (props.pushHandling === 'mapp') {
|
|
75
|
+
application.service.push({
|
|
76
|
+
$: {
|
|
77
|
+
'android:name': messageService,
|
|
78
|
+
'android:exported': 'false',
|
|
79
|
+
},
|
|
80
|
+
'intent-filter': [{
|
|
81
|
+
action: [{ $: { 'android:name': firebaseMessagingEvent } }],
|
|
82
|
+
}],
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
35
86
|
application.service.push({
|
|
36
87
|
$: {
|
|
37
88
|
'android:name': messageService,
|
|
@@ -42,11 +93,8 @@ function updatePushHandling(androidManifest, pushHandling) {
|
|
|
42
93
|
return androidManifest;
|
|
43
94
|
}
|
|
44
95
|
const withMappEngageAndroid = (config, props) => {
|
|
45
|
-
if (props.enableGeofencing) {
|
|
46
|
-
config = config_plugins_1.AndroidConfig.Permissions.withPermissions(config, [fineLocation, backgroundLocation]);
|
|
47
|
-
}
|
|
48
96
|
return (0, config_plugins_1.withAndroidManifest)(config, configWithManifest => {
|
|
49
|
-
configWithManifest.modResults =
|
|
97
|
+
configWithManifest.modResults = updateAndroidManifest(configWithManifest.modResults, props);
|
|
50
98
|
return configWithManifest;
|
|
51
99
|
});
|
|
52
100
|
};
|
package/plugin/build/index.d.ts
CHANGED
|
@@ -3,6 +3,6 @@ import type { MappExpoPluginProps } from './types';
|
|
|
3
3
|
export declare const withMappEngage: ConfigPlugin<MappExpoPluginProps>;
|
|
4
4
|
export type { MappExpoPluginProps } from './types';
|
|
5
5
|
export { withMappEngageAndroid } from './android';
|
|
6
|
-
export { withMappEngageIos, buildAppoxeeConfig, writeAppoxeeConfig } from './ios';
|
|
6
|
+
export { withMappEngageIos, buildAppoxeeConfig, writeAppoxeeConfig, buildNotificationServiceInfoPlist, writeNotificationServiceFiles, } from './ios';
|
|
7
7
|
export { validateAndNormalizeProps } from './validation';
|
|
8
8
|
export default withMappEngage;
|
package/plugin/build/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.validateAndNormalizeProps = exports.writeAppoxeeConfig = exports.buildAppoxeeConfig = exports.withMappEngageIos = exports.withMappEngageAndroid = exports.withMappEngage = void 0;
|
|
3
|
+
exports.validateAndNormalizeProps = exports.writeNotificationServiceFiles = exports.buildNotificationServiceInfoPlist = exports.writeAppoxeeConfig = exports.buildAppoxeeConfig = exports.withMappEngageIos = exports.withMappEngageAndroid = exports.withMappEngage = void 0;
|
|
4
4
|
const config_plugins_1 = require("@expo/config-plugins");
|
|
5
5
|
const android_1 = require("./android");
|
|
6
6
|
const ios_1 = require("./ios");
|
|
@@ -19,6 +19,8 @@ var ios_2 = require("./ios");
|
|
|
19
19
|
Object.defineProperty(exports, "withMappEngageIos", { enumerable: true, get: function () { return ios_2.withMappEngageIos; } });
|
|
20
20
|
Object.defineProperty(exports, "buildAppoxeeConfig", { enumerable: true, get: function () { return ios_2.buildAppoxeeConfig; } });
|
|
21
21
|
Object.defineProperty(exports, "writeAppoxeeConfig", { enumerable: true, get: function () { return ios_2.writeAppoxeeConfig; } });
|
|
22
|
+
Object.defineProperty(exports, "buildNotificationServiceInfoPlist", { enumerable: true, get: function () { return ios_2.buildNotificationServiceInfoPlist; } });
|
|
23
|
+
Object.defineProperty(exports, "writeNotificationServiceFiles", { enumerable: true, get: function () { return ios_2.writeNotificationServiceFiles; } });
|
|
22
24
|
var validation_2 = require("./validation");
|
|
23
25
|
Object.defineProperty(exports, "validateAndNormalizeProps", { enumerable: true, get: function () { return validation_2.validateAndNormalizeProps; } });
|
|
24
26
|
exports.default = exports.withMappEngage;
|
package/plugin/build/ios.d.ts
CHANGED
|
@@ -2,4 +2,6 @@ import { ConfigPlugin } from '@expo/config-plugins';
|
|
|
2
2
|
import type { NormalizedMappExpoPluginProps } from './types';
|
|
3
3
|
export declare function buildAppoxeeConfig(props: NormalizedMappExpoPluginProps['ios']): Record<string, unknown>;
|
|
4
4
|
export declare function writeAppoxeeConfig(platformProjectRoot: string, props: NormalizedMappExpoPluginProps['ios']): Promise<string>;
|
|
5
|
+
export declare function buildNotificationServiceInfoPlist(): Record<string, unknown>;
|
|
6
|
+
export declare function writeNotificationServiceFiles(platformProjectRoot: string, bundleIdentifier: string): Promise<string[]>;
|
|
5
7
|
export declare const withMappEngageIos: ConfigPlugin<NormalizedMappExpoPluginProps['ios']>;
|
package/plugin/build/ios.js
CHANGED
|
@@ -6,11 +6,84 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.withMappEngageIos = void 0;
|
|
7
7
|
exports.buildAppoxeeConfig = buildAppoxeeConfig;
|
|
8
8
|
exports.writeAppoxeeConfig = writeAppoxeeConfig;
|
|
9
|
+
exports.buildNotificationServiceInfoPlist = buildNotificationServiceInfoPlist;
|
|
10
|
+
exports.writeNotificationServiceFiles = writeNotificationServiceFiles;
|
|
9
11
|
const fs_1 = __importDefault(require("fs"));
|
|
10
12
|
const path_1 = __importDefault(require("path"));
|
|
11
13
|
const config_plugins_1 = require("@expo/config-plugins");
|
|
12
14
|
const plist_1 = __importDefault(require("@expo/plist"));
|
|
13
15
|
const plistName = 'AppoxeeConfig.plist';
|
|
16
|
+
const notificationServiceTargetName = 'MappNotificationService';
|
|
17
|
+
const notificationServiceInfoPlistName = 'Info.plist';
|
|
18
|
+
const notificationServiceEntitlementsName = 'MappNotificationService.entitlements';
|
|
19
|
+
const notificationServiceSourceName = 'NotificationService.swift';
|
|
20
|
+
const notificationServiceDeploymentTarget = '15.0';
|
|
21
|
+
const notificationServiceSource = `import UserNotifications
|
|
22
|
+
|
|
23
|
+
class NotificationService: UNNotificationServiceExtension {
|
|
24
|
+
|
|
25
|
+
var contentHandler: ((UNNotificationContent) -> Void)?
|
|
26
|
+
|
|
27
|
+
var bestAttemptContent: UNMutableNotificationContent?
|
|
28
|
+
|
|
29
|
+
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
|
|
30
|
+
|
|
31
|
+
self.contentHandler = contentHandler
|
|
32
|
+
|
|
33
|
+
bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
|
|
34
|
+
|
|
35
|
+
print("categori identifier: ", request.content)
|
|
36
|
+
|
|
37
|
+
UNUserNotificationCenter.current().getNotificationCategories { (categories) in
|
|
38
|
+
|
|
39
|
+
if let categoryIdentifier = self.bestAttemptContent?.categoryIdentifier, let lc = request.content.userInfo["aps"] {
|
|
40
|
+
|
|
41
|
+
self.bestAttemptContent?.categoryIdentifier = categoryIdentifier + "_" + ((lc as! NSDictionary)["lc"] as! String)
|
|
42
|
+
|
|
43
|
+
let categoryExistArray = categories.filter { (category) -> Bool in
|
|
44
|
+
category.identifier == self.bestAttemptContent?.categoryIdentifier
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if categoryExistArray.isEmpty {
|
|
48
|
+
self.bestAttemptContent?.categoryIdentifier = categoryIdentifier + "_en"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if let urlString = request.content.userInfo["ios_apx_media"], let fileUrl = URL(string: urlString as? String ?? "") {
|
|
53
|
+
|
|
54
|
+
URLSession.shared.downloadTask(with: fileUrl ) { (location, response, error) in
|
|
55
|
+
|
|
56
|
+
if let location = location {
|
|
57
|
+
let tmpDirectory = NSTemporaryDirectory()
|
|
58
|
+
let tmpFile = "file://".appending(tmpDirectory).appending(fileUrl.lastPathComponent)
|
|
59
|
+
let tmpUrl = URL(string: tmpFile)!
|
|
60
|
+
|
|
61
|
+
try! FileManager.default.moveItem(at: location, to: tmpUrl)
|
|
62
|
+
|
|
63
|
+
if let attachment = try? UNNotificationAttachment(identifier: "", url: tmpUrl) {
|
|
64
|
+
self.bestAttemptContent?.attachments = [attachment]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
print("categori identifier: ", self.bestAttemptContent?.categoryIdentifier ?? "no category identifier")
|
|
69
|
+
|
|
70
|
+
self.contentHandler!(self.bestAttemptContent!)
|
|
71
|
+
|
|
72
|
+
}.resume()
|
|
73
|
+
|
|
74
|
+
} else {
|
|
75
|
+
self.contentHandler!(self.bestAttemptContent!)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
override func serviceExtensionTimeWillExpire() {
|
|
81
|
+
if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
|
|
82
|
+
contentHandler(bestAttemptContent)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
`;
|
|
14
87
|
function buildAppoxeeConfig(props) {
|
|
15
88
|
return {
|
|
16
89
|
inapp: {
|
|
@@ -33,11 +106,96 @@ async function writeAppoxeeConfig(platformProjectRoot, props) {
|
|
|
33
106
|
await fs_1.default.promises.writeFile(outputPath, plist_1.default.build(buildAppoxeeConfig(props)));
|
|
34
107
|
return outputPath;
|
|
35
108
|
}
|
|
109
|
+
function buildNotificationServiceInfoPlist() {
|
|
110
|
+
return {
|
|
111
|
+
NSExtension: {
|
|
112
|
+
NSExtensionPointIdentifier: 'com.apple.usernotifications.service',
|
|
113
|
+
NSExtensionPrincipalClass: '$(PRODUCT_MODULE_NAME).NotificationService',
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
async function writeFileIfChanged(filePath, contents) {
|
|
118
|
+
const current = await fs_1.default.promises.readFile(filePath, 'utf8').catch(() => undefined);
|
|
119
|
+
if (current !== contents) {
|
|
120
|
+
await fs_1.default.promises.writeFile(filePath, contents);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async function writeNotificationServiceFiles(platformProjectRoot, bundleIdentifier) {
|
|
124
|
+
const extensionRoot = path_1.default.join(platformProjectRoot, notificationServiceTargetName);
|
|
125
|
+
const infoPlistPath = path_1.default.join(extensionRoot, notificationServiceInfoPlistName);
|
|
126
|
+
const entitlementsPath = path_1.default.join(extensionRoot, notificationServiceEntitlementsName);
|
|
127
|
+
const sourcePath = path_1.default.join(extensionRoot, notificationServiceSourceName);
|
|
128
|
+
await fs_1.default.promises.mkdir(extensionRoot, { recursive: true });
|
|
129
|
+
await writeFileIfChanged(infoPlistPath, plist_1.default.build(buildNotificationServiceInfoPlist()));
|
|
130
|
+
await writeFileIfChanged(entitlementsPath, plist_1.default.build(buildNotificationServiceEntitlements(bundleIdentifier)));
|
|
131
|
+
await writeFileIfChanged(sourcePath, notificationServiceSource);
|
|
132
|
+
return [infoPlistPath, entitlementsPath, sourcePath];
|
|
133
|
+
}
|
|
134
|
+
function getNotificationServiceBundleIdentifier(config) {
|
|
135
|
+
return `${config.ios.bundleIdentifier}.mappnotificationservice`;
|
|
136
|
+
}
|
|
137
|
+
function getAppGroupIdentifier(bundleIdentifier) {
|
|
138
|
+
return `group.${bundleIdentifier}`;
|
|
139
|
+
}
|
|
140
|
+
function buildNotificationServiceEntitlements(bundleIdentifier) {
|
|
141
|
+
return {
|
|
142
|
+
'com.apple.security.application-groups': [getAppGroupIdentifier(bundleIdentifier)],
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function addNotificationServiceToEasConfig(config, bundleIdentifier) {
|
|
146
|
+
config.extra = config.extra ?? {};
|
|
147
|
+
config.extra.eas = config.extra.eas ?? {};
|
|
148
|
+
config.extra.eas.build = config.extra.eas.build ?? {};
|
|
149
|
+
config.extra.eas.build.experimental = config.extra.eas.build.experimental ?? {};
|
|
150
|
+
config.extra.eas.build.experimental.ios = config.extra.eas.build.experimental.ios ?? {};
|
|
151
|
+
const ios = config.extra.eas.build.experimental.ios;
|
|
152
|
+
const appExtensions = Array.isArray(ios.appExtensions) ? ios.appExtensions : [];
|
|
153
|
+
ios.appExtensions = [
|
|
154
|
+
...appExtensions.filter((extension) => extension?.targetName !== notificationServiceTargetName),
|
|
155
|
+
{ targetName: notificationServiceTargetName, bundleIdentifier },
|
|
156
|
+
];
|
|
157
|
+
}
|
|
158
|
+
function addNotificationServiceTarget(project, bundleIdentifier) {
|
|
159
|
+
const existingTarget = config_plugins_1.IOSConfig.Target.getNativeTargets(project).find(([, target]) => (config_plugins_1.IOSConfig.XcodeUtils.unquote(target.name) === notificationServiceTargetName));
|
|
160
|
+
if (existingTarget) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
project.hash.project.objects.PBXContainerItemProxy =
|
|
164
|
+
project.hash.project.objects.PBXContainerItemProxy ?? {};
|
|
165
|
+
project.hash.project.objects.PBXTargetDependency =
|
|
166
|
+
project.hash.project.objects.PBXTargetDependency ?? {};
|
|
167
|
+
const target = project.addTarget(notificationServiceTargetName, 'app_extension', notificationServiceTargetName, bundleIdentifier);
|
|
168
|
+
project.addBuildPhase([], 'PBXSourcesBuildPhase', 'Sources', target.uuid);
|
|
169
|
+
project.addBuildPhase([], 'PBXFrameworksBuildPhase', 'Frameworks', target.uuid);
|
|
170
|
+
const groupKey = project.pbxCreateGroup(notificationServiceTargetName, notificationServiceTargetName);
|
|
171
|
+
const { firstProject } = project.getFirstProject();
|
|
172
|
+
project.getPBXGroupByKey(firstProject.mainGroup).children.push({
|
|
173
|
+
value: groupKey,
|
|
174
|
+
comment: notificationServiceTargetName,
|
|
175
|
+
});
|
|
176
|
+
project.addFile(notificationServiceInfoPlistName, groupKey);
|
|
177
|
+
project.addFile(notificationServiceEntitlementsName, groupKey);
|
|
178
|
+
project.addSourceFile(notificationServiceSourceName, { target: target.uuid }, groupKey);
|
|
179
|
+
project.addFramework('UserNotifications.framework', { target: target.uuid });
|
|
180
|
+
for (const [, buildConfiguration] of config_plugins_1.IOSConfig.XcodeUtils.getBuildConfigurationsForListId(project, target.pbxNativeTarget.buildConfigurationList)) {
|
|
181
|
+
const settings = buildConfiguration.buildSettings;
|
|
182
|
+
settings.CODE_SIGN_STYLE = 'Automatic';
|
|
183
|
+
settings.CODE_SIGN_ENTITLEMENTS = `"${notificationServiceTargetName}/${notificationServiceEntitlementsName}"`;
|
|
184
|
+
settings.GENERATE_INFOPLIST_FILE = 'NO';
|
|
185
|
+
settings.INFOPLIST_FILE = `"${notificationServiceTargetName}/${notificationServiceInfoPlistName}"`;
|
|
186
|
+
settings.IPHONEOS_DEPLOYMENT_TARGET = notificationServiceDeploymentTarget;
|
|
187
|
+
settings.PRODUCT_BUNDLE_IDENTIFIER = `"${bundleIdentifier}"`;
|
|
188
|
+
settings.SWIFT_VERSION = '5.0';
|
|
189
|
+
settings.TARGETED_DEVICE_FAMILY = '"1,2"';
|
|
190
|
+
}
|
|
191
|
+
}
|
|
36
192
|
function appendUnique(values, value) {
|
|
37
193
|
const result = Array.isArray(values) ? values.filter(item => typeof item === 'string') : [];
|
|
38
194
|
return result.includes(value) ? result : [...result, value];
|
|
39
195
|
}
|
|
40
196
|
const withMappEngageIos = (config, props) => {
|
|
197
|
+
const notificationServiceBundleIdentifier = getNotificationServiceBundleIdentifier(config);
|
|
198
|
+
addNotificationServiceToEasConfig(config, notificationServiceBundleIdentifier);
|
|
41
199
|
config = (0, config_plugins_1.withInfoPlist)(config, configWithPlist => {
|
|
42
200
|
configWithPlist.modResults.UIBackgroundModes = appendUnique(configWithPlist.modResults.UIBackgroundModes, 'remote-notification');
|
|
43
201
|
if (props.enableGeofencing) {
|
|
@@ -50,10 +208,12 @@ const withMappEngageIos = (config, props) => {
|
|
|
50
208
|
config = (0, config_plugins_1.withEntitlementsPlist)(config, configWithEntitlements => {
|
|
51
209
|
configWithEntitlements.modResults['aps-environment'] =
|
|
52
210
|
configWithEntitlements.modResults['aps-environment'] ?? 'development';
|
|
211
|
+
configWithEntitlements.modResults['com.apple.security.application-groups'] = appendUnique(configWithEntitlements.modResults['com.apple.security.application-groups'], getAppGroupIdentifier(config.ios.bundleIdentifier));
|
|
53
212
|
return configWithEntitlements;
|
|
54
213
|
});
|
|
55
214
|
config = (0, config_plugins_1.withDangerousMod)(config, ['ios', async (configWithFiles) => {
|
|
56
215
|
await writeAppoxeeConfig(configWithFiles.modRequest.platformProjectRoot, props);
|
|
216
|
+
await writeNotificationServiceFiles(configWithFiles.modRequest.platformProjectRoot, config.ios.bundleIdentifier);
|
|
57
217
|
return configWithFiles;
|
|
58
218
|
}]);
|
|
59
219
|
return (0, config_plugins_1.withXcodeProject)(config, configWithProject => {
|
|
@@ -71,6 +231,7 @@ const withMappEngageIos = (config, props) => {
|
|
|
71
231
|
targetUuid: target.uuid,
|
|
72
232
|
});
|
|
73
233
|
}
|
|
234
|
+
addNotificationServiceTarget(project, notificationServiceBundleIdentifier);
|
|
74
235
|
return configWithProject;
|
|
75
236
|
});
|
|
76
237
|
};
|
|
@@ -16,7 +16,7 @@ export interface Spec extends TurboModule {
|
|
|
16
16
|
getAlias(): Promise<string>;
|
|
17
17
|
/** @deprecated Use engage(...). */
|
|
18
18
|
engage2(): void;
|
|
19
|
-
engage(sdkKey: string, googleProjectId: string, server: string, appID: string, tenantID: string):
|
|
19
|
+
engage(sdkKey: string, googleProjectId: string, server: string, appID: string, tenantID: string): Promise<boolean>;
|
|
20
20
|
engageTestServer(cepURl: string, sdkKey: string, googleProjectId: string, server: string, appID: string, tenantID: string): void;
|
|
21
21
|
onInitCompletedListener(): Promise<boolean>;
|
|
22
22
|
isReady(): Promise<boolean>;
|