@bubblsdk/react-native-sdk 4.1.7 → 5.0.0-alpha.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.
- package/BubblReactNativeSdk.podspec +29 -9
- package/CHANGELOG.md +52 -1
- package/LICENSE +202 -1
- package/README.md +172 -83
- package/android/build.gradle +33 -30
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/tech/bubbl/reactnative/BubblJson.kt +135 -0
- package/android/src/main/java/tech/bubbl/reactnative/BubblSdkModule.kt +277 -0
- package/android/src/main/java/tech/bubbl/reactnative/BubblSdkPackage.kt +26 -0
- package/app.plugin.js +2 -0
- package/ios/BubblPush.swift +44 -0
- package/ios/BubblSdk.h +7 -0
- package/ios/BubblSdk.mm +252 -0
- package/ios/BubblSdkImpl.swift +335 -0
- package/ios-extension/BubblReactNativeNotificationService.podspec +44 -0
- package/ios-extension/Sources/BubblCore/API/ApiResponse.swift +90 -0
- package/ios-extension/Sources/BubblCore/API/CredentialStore.swift +116 -0
- package/ios-extension/Sources/BubblCore/API/DeviceApiClient.swift +239 -0
- package/ios-extension/Sources/BubblCore/API/ErrorActions.swift +68 -0
- package/ios-extension/Sources/BubblCore/API/Http.swift +39 -0
- package/ios-extension/Sources/BubblCore/API/ServerClock.swift +69 -0
- package/ios-extension/Sources/BubblCore/BubblVersion.swift +5 -0
- package/ios-extension/Sources/BubblCore/Device/ConfigSync.swift +216 -0
- package/ios-extension/Sources/BubblCore/Device/DeviceSync.swift +160 -0
- package/ios-extension/Sources/BubblCore/Device/ProvisioningProfile.swift +24 -0
- package/ios-extension/Sources/BubblCore/Engine/EngineCore.swift +523 -0
- package/ios-extension/Sources/BubblCore/Engine/Geofencing.swift +170 -0
- package/ios-extension/Sources/BubblCore/Engine/Permissions.swift +90 -0
- package/ios-extension/Sources/BubblCore/Engine/Privacy.swift +64 -0
- package/ios-extension/Sources/BubblCore/Engine/PrivacyControls.swift +184 -0
- package/ios-extension/Sources/BubblCore/Engine/Pushes.swift +61 -0
- package/ios-extension/Sources/BubblCore/Engine/Sandbox.swift +149 -0
- package/ios-extension/Sources/BubblCore/Events/EventQueue.swift +207 -0
- package/ios-extension/Sources/BubblCore/Events/EventStore.swift +159 -0
- package/ios-extension/Sources/BubblCore/Geofence/Geofence.swift +160 -0
- package/ios-extension/Sources/BubblCore/Geofence/GeofenceEngine.swift +488 -0
- package/ios-extension/Sources/BubblCore/Geofence/GeofenceState.swift +103 -0
- package/ios-extension/Sources/BubblCore/Geofence/RegionBudget.swift +62 -0
- package/ios-extension/Sources/BubblCore/Notification/BubblNotification.swift +137 -0
- package/ios-extension/Sources/BubblCore/Notification/NotificationEvents.swift +160 -0
- package/ios-extension/Sources/BubblCore/Notification/Push.swift +134 -0
- package/ios-extension/Sources/BubblCore/Notification/SurveyForm.swift +185 -0
- package/ios-extension/Sources/BubblCore/RequestSigner.swift +56 -0
- package/ios-extension/Sources/BubblCore/Support/AsyncMutex.swift +39 -0
- package/ios-extension/Sources/BubblCore/Support/BubblLog.swift +52 -0
- package/ios-extension/Sources/BubblCore/Support/JSON.swift +31 -0
- package/ios-extension/Sources/BubblCore/Support/JSONValue.swift +94 -0
- package/ios-extension/Sources/BubblCore/Support/Locking.swift +11 -0
- package/ios-extension/Sources/BubblCore/Support/ValueStore.swift +109 -0
- package/ios-extension/Sources/BubblNotificationService/BubblNotificationService.swift +124 -0
- package/ios-sdk/BubblCore/API/ApiResponse.swift +90 -0
- package/ios-sdk/BubblCore/API/CredentialStore.swift +116 -0
- package/ios-sdk/BubblCore/API/DeviceApiClient.swift +239 -0
- package/ios-sdk/BubblCore/API/ErrorActions.swift +68 -0
- package/ios-sdk/BubblCore/API/Http.swift +39 -0
- package/ios-sdk/BubblCore/API/ServerClock.swift +69 -0
- package/ios-sdk/BubblCore/BubblVersion.swift +5 -0
- package/ios-sdk/BubblCore/Device/ConfigSync.swift +216 -0
- package/ios-sdk/BubblCore/Device/DeviceSync.swift +160 -0
- package/ios-sdk/BubblCore/Device/ProvisioningProfile.swift +24 -0
- package/ios-sdk/BubblCore/Engine/EngineCore.swift +523 -0
- package/ios-sdk/BubblCore/Engine/Geofencing.swift +170 -0
- package/ios-sdk/BubblCore/Engine/Permissions.swift +90 -0
- package/ios-sdk/BubblCore/Engine/Privacy.swift +64 -0
- package/ios-sdk/BubblCore/Engine/PrivacyControls.swift +184 -0
- package/ios-sdk/BubblCore/Engine/Pushes.swift +61 -0
- package/ios-sdk/BubblCore/Engine/Sandbox.swift +149 -0
- package/ios-sdk/BubblCore/Events/EventQueue.swift +207 -0
- package/ios-sdk/BubblCore/Events/EventStore.swift +159 -0
- package/ios-sdk/BubblCore/Geofence/Geofence.swift +160 -0
- package/ios-sdk/BubblCore/Geofence/GeofenceEngine.swift +488 -0
- package/ios-sdk/BubblCore/Geofence/GeofenceState.swift +103 -0
- package/ios-sdk/BubblCore/Geofence/RegionBudget.swift +62 -0
- package/ios-sdk/BubblCore/Notification/BubblNotification.swift +137 -0
- package/ios-sdk/BubblCore/Notification/NotificationEvents.swift +160 -0
- package/ios-sdk/BubblCore/Notification/Push.swift +134 -0
- package/ios-sdk/BubblCore/Notification/SurveyForm.swift +185 -0
- package/ios-sdk/BubblCore/RequestSigner.swift +56 -0
- package/ios-sdk/BubblCore/Support/AsyncMutex.swift +39 -0
- package/ios-sdk/BubblCore/Support/BubblLog.swift +52 -0
- package/ios-sdk/BubblCore/Support/JSON.swift +31 -0
- package/ios-sdk/BubblCore/Support/JSONValue.swift +94 -0
- package/ios-sdk/BubblCore/Support/Locking.swift +11 -0
- package/ios-sdk/BubblCore/Support/ValueStore.swift +109 -0
- package/ios-sdk/BubblLaunch/BubblLaunch.m +45 -0
- package/ios-sdk/BubblLaunch/include/BubblLaunch.h +5 -0
- package/ios-sdk/BubblSDK/Bubbl.swift +308 -0
- package/ios-sdk/BubblSDK/BubblBackend.swift +118 -0
- package/ios-sdk/BubblSDK/BubblCredential.swift +30 -0
- package/ios-sdk/BubblSDK/BubblMessage.swift +151 -0
- package/ios-sdk/BubblSDK/BubblOptions.swift +39 -0
- package/ios-sdk/BubblSDK/BubblPermissions.swift +79 -0
- package/ios-sdk/BubblSDK/BubblTestDeviceResult.swift +10 -0
- package/ios-sdk/BubblSDK/Engine/BackgroundRefresh.swift +92 -0
- package/ios-sdk/BubblSDK/Engine/EngineHost+Geofences.swift +177 -0
- package/ios-sdk/BubblSDK/Engine/EngineHost+Privacy.swift +65 -0
- package/ios-sdk/BubblSDK/Engine/EngineHost+Push.swift +154 -0
- package/ios-sdk/BubblSDK/Engine/EngineHost.swift +356 -0
- package/ios-sdk/BubblSDK/Engine/SupportedBubbl.swift +320 -0
- package/ios-sdk/BubblSDK/Engine/SystemLog.swift +27 -0
- package/ios-sdk/BubblSDK/Engine/WorkRunner.swift +107 -0
- package/ios-sdk/BubblSDK/Notification/Look.swift +78 -0
- package/ios-sdk/BubblSDK/Notification/NotificationMedia.swift +329 -0
- package/ios-sdk/BubblSDK/Notification/NotificationModel.swift +172 -0
- package/ios-sdk/BubblSDK/Notification/NotificationScreen.swift +428 -0
- package/ios-sdk/BubblSDK/Notification/ScreenPresenter.swift +148 -0
- package/ios-sdk/BubblSDK/Permissions/PermissionFlow.swift +169 -0
- package/ios-sdk/BubblSDK/Permissions/PrivacyView.swift +110 -0
- package/ios-sdk/BubblSDK/Platform/AppleDevicePlatform.swift +95 -0
- package/ios-sdk/BubblSDK/Platform/KeychainCredentialStore.swift +114 -0
- package/ios-sdk/BubblSDK/Platform/LocationService.swift +418 -0
- package/ios-sdk/BubblSDK/Platform/PushIntegration.swift +288 -0
- package/ios-sdk/BubblSDK/Platform/URLSessionHttpClient.swift +69 -0
- package/ios-sdk/BubblSDK/PrivacyInfo.xcprivacy +76 -0
- package/jest/index.js +75 -0
- package/lib/commonjs/BubblModule.js +14 -0
- package/lib/commonjs/BubblModule.js.map +1 -0
- package/lib/commonjs/BubblModule.web.js +13 -0
- package/lib/commonjs/BubblModule.web.js.map +1 -0
- package/lib/commonjs/NativeBubblSdk.js +20 -0
- package/lib/commonjs/NativeBubblSdk.js.map +1 -0
- package/lib/commonjs/index.js +336 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/types.js +2 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/module/BubblModule.js +5 -0
- package/lib/module/BubblModule.js.map +1 -0
- package/lib/module/BubblModule.web.js +9 -0
- package/lib/module/BubblModule.web.js.map +1 -0
- package/lib/module/NativeBubblSdk.js +18 -0
- package/lib/module/NativeBubblSdk.js.map +1 -0
- package/lib/module/index.js +331 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/commonjs/package.json +1 -0
- package/lib/typescript/commonjs/src/BubblModule.d.ts +3 -0
- package/lib/typescript/commonjs/src/BubblModule.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/BubblModule.web.d.ts +8 -0
- package/lib/typescript/commonjs/src/BubblModule.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/NativeBubblSdk.d.ts +59 -0
- package/lib/typescript/commonjs/src/NativeBubblSdk.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/index.d.ts +111 -0
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/types.d.ts +192 -0
- package/lib/typescript/commonjs/src/types.d.ts.map +1 -0
- package/lib/typescript/module/package.json +1 -0
- package/lib/typescript/module/src/BubblModule.d.ts +3 -0
- package/lib/typescript/module/src/BubblModule.d.ts.map +1 -0
- package/lib/typescript/module/src/BubblModule.web.d.ts +8 -0
- package/lib/typescript/module/src/BubblModule.web.d.ts.map +1 -0
- package/lib/typescript/module/src/NativeBubblSdk.d.ts +59 -0
- package/lib/typescript/module/src/NativeBubblSdk.d.ts.map +1 -0
- package/lib/typescript/module/src/index.d.ts +111 -0
- package/lib/typescript/module/src/index.d.ts.map +1 -0
- package/lib/typescript/module/src/types.d.ts +192 -0
- package/lib/typescript/module/src/types.d.ts.map +1 -0
- package/package.json +105 -24
- package/plugin/build/imageNotifications.d.ts +35 -0
- package/plugin/build/imageNotifications.js +198 -0
- package/plugin/build/index.d.ts +84 -0
- package/plugin/build/index.js +176 -0
- package/src/BubblModule.ts +2 -0
- package/src/BubblModule.web.ts +9 -0
- package/src/NativeBubblSdk.ts +67 -0
- package/src/index.ts +338 -282
- package/src/types.ts +182 -0
- package/android/src/main/kotlin/tech/bubbl/reactnative/BubblSdkModule.kt +0 -680
- package/android/src/main/kotlin/tech/bubbl/reactnative/BubblSdkNotificationIntents.kt +0 -32
- package/android/src/main/kotlin/tech/bubbl/reactnative/BubblSdkPackage.kt +0 -14
- package/ios/BubblSdk.swift +0 -802
- package/ios/BubblSdkBridge.m +0 -86
- package/react-native.config.js +0 -11
- package/src/specs/NativeBubblSdk.ts +0 -54
package/package.json
CHANGED
|
@@ -1,48 +1,129 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bubblsdk/react-native-sdk",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "React Native
|
|
5
|
-
"main": "
|
|
6
|
-
"module": "
|
|
7
|
-
"types": "src/index.ts",
|
|
8
|
-
"react-native": "src/index.ts",
|
|
9
|
-
"source": "src/index.ts",
|
|
3
|
+
"version": "5.0.0-alpha.2",
|
|
4
|
+
"description": "Bubbl for React Native: geofenced and scheduled notifications, surveys and analytics, over the native Bubbl SDKs.",
|
|
5
|
+
"main": "./lib/commonjs/index.js",
|
|
6
|
+
"module": "./lib/module/index.js",
|
|
7
|
+
"types": "./lib/typescript/commonjs/src/index.d.ts",
|
|
8
|
+
"react-native": "./src/index.ts",
|
|
9
|
+
"source": "./src/index.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"source": "./src/index.ts",
|
|
13
|
+
"import": {
|
|
14
|
+
"types": "./lib/typescript/module/src/index.d.ts",
|
|
15
|
+
"default": "./lib/module/index.js"
|
|
16
|
+
},
|
|
17
|
+
"require": {
|
|
18
|
+
"types": "./lib/typescript/commonjs/src/index.d.ts",
|
|
19
|
+
"default": "./lib/commonjs/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"./app.plugin.js": "./app.plugin.js",
|
|
23
|
+
"./jest": "./jest/index.js",
|
|
24
|
+
"./jest": "./jest/index.js",
|
|
25
|
+
"./package.json": "./package.json"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"src",
|
|
29
|
+
"lib",
|
|
30
|
+
"android/build.gradle",
|
|
31
|
+
"android/src",
|
|
32
|
+
"ios",
|
|
33
|
+
"ios-sdk",
|
|
34
|
+
"ios-extension",
|
|
35
|
+
"BubblReactNativeSdk.podspec",
|
|
36
|
+
"plugin/build",
|
|
37
|
+
"app.plugin.js",
|
|
38
|
+
"jest",
|
|
39
|
+
"README.md",
|
|
40
|
+
"CHANGELOG.md",
|
|
41
|
+
"LICENSE",
|
|
42
|
+
"!**/__tests__",
|
|
43
|
+
"!**/.*"
|
|
44
|
+
],
|
|
10
45
|
"repository": {
|
|
11
46
|
"type": "git",
|
|
12
47
|
"url": "git+https://github.com/bubbl-platform/renewed-sdk.git",
|
|
13
48
|
"directory": "react-native"
|
|
14
49
|
},
|
|
15
|
-
"license": "
|
|
50
|
+
"license": "Apache-2.0",
|
|
16
51
|
"publishConfig": {
|
|
17
52
|
"access": "public"
|
|
18
53
|
},
|
|
19
54
|
"scripts": {
|
|
20
|
-
"build": "
|
|
21
|
-
"test": "
|
|
22
|
-
"typecheck": "
|
|
55
|
+
"build": "bob build && tsc -p plugin",
|
|
56
|
+
"test": "jest",
|
|
57
|
+
"typecheck": "tsc --noEmit && tsc -p plugin --noEmit",
|
|
58
|
+
"prepack": "npm run sync:ios && npm run build",
|
|
59
|
+
"sync:ios": "node scripts/sync-ios-sources.mjs"
|
|
23
60
|
},
|
|
24
|
-
"files": [
|
|
25
|
-
"android/build.gradle",
|
|
26
|
-
"android/src",
|
|
27
|
-
"ios/BubblSdk.swift",
|
|
28
|
-
"ios/BubblSdkBridge.m",
|
|
29
|
-
"src",
|
|
30
|
-
"BubblReactNativeSdk.podspec",
|
|
31
|
-
"react-native.config.js",
|
|
32
|
-
"README.md",
|
|
33
|
-
"CHANGELOG.md",
|
|
34
|
-
"LICENSE"
|
|
35
|
-
],
|
|
36
61
|
"peerDependencies": {
|
|
62
|
+
"@expo/config-plugins": ">=9",
|
|
37
63
|
"react": "*",
|
|
38
64
|
"react-native": ">=0.76"
|
|
39
65
|
},
|
|
66
|
+
"peerDependenciesMeta": {
|
|
67
|
+
"@expo/config-plugins": {
|
|
68
|
+
"optional": true
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"devDependencies": {
|
|
72
|
+
"@expo/config-plugins": "^57.0.9",
|
|
73
|
+
"@react-native/babel-preset": "0.87.1",
|
|
74
|
+
"@react-native/jest-preset": "^0.87.1",
|
|
75
|
+
"@types/jest": "^30.0.0",
|
|
76
|
+
"@types/node": "^22.20.4",
|
|
77
|
+
"@types/react": "^19.1.0",
|
|
78
|
+
"jest": "^30.0.0",
|
|
79
|
+
"react": "19.2.3",
|
|
80
|
+
"react-native": "0.87.1",
|
|
81
|
+
"react-native-builder-bob": "^0.43.1",
|
|
82
|
+
"typescript": "^5.9.0"
|
|
83
|
+
},
|
|
84
|
+
"react-native-builder-bob": {
|
|
85
|
+
"source": "src",
|
|
86
|
+
"output": "lib",
|
|
87
|
+
"targets": [
|
|
88
|
+
[
|
|
89
|
+
"commonjs",
|
|
90
|
+
{
|
|
91
|
+
"esm": true
|
|
92
|
+
}
|
|
93
|
+
],
|
|
94
|
+
[
|
|
95
|
+
"module",
|
|
96
|
+
{
|
|
97
|
+
"esm": true
|
|
98
|
+
}
|
|
99
|
+
],
|
|
100
|
+
[
|
|
101
|
+
"typescript",
|
|
102
|
+
{
|
|
103
|
+
"project": "tsconfig.build.json"
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
]
|
|
107
|
+
},
|
|
40
108
|
"codegenConfig": {
|
|
41
109
|
"name": "BubblSdkSpec",
|
|
42
110
|
"type": "modules",
|
|
43
|
-
"jsSrcsDir": "src
|
|
111
|
+
"jsSrcsDir": "src",
|
|
44
112
|
"android": {
|
|
45
113
|
"javaPackageName": "tech.bubbl.reactnative"
|
|
114
|
+
},
|
|
115
|
+
"ios": {
|
|
116
|
+
"modulesProvider": {
|
|
117
|
+
"BubblSdk": "BubblSdk"
|
|
118
|
+
}
|
|
46
119
|
}
|
|
120
|
+
},
|
|
121
|
+
"jest": {
|
|
122
|
+
"preset": "@react-native/jest-preset",
|
|
123
|
+
"modulePathIgnorePatterns": [
|
|
124
|
+
"<rootDir>/lib/",
|
|
125
|
+
"<rootDir>/example/",
|
|
126
|
+
"<rootDir>/example-expo/"
|
|
127
|
+
]
|
|
47
128
|
}
|
|
48
129
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type ConfigPlugin, type ExportedConfig, type XcodeProject } from '@expo/config-plugins';
|
|
2
|
+
/**
|
|
3
|
+
* ios.imageNotifications: pictures on Bubbl's pushes. Adds a Notification Service Extension target
|
|
4
|
+
* whose whole code is `class NotificationService: BubblNotificationService {}` (the iOS SDK's, from
|
|
5
|
+
* this package's ios-extension pod), its Podfile target, and EAS Build's entry for it (so EAS signs
|
|
6
|
+
* it). The same as the README's steps for a React Native CLI app, done at prebuild.
|
|
7
|
+
*/
|
|
8
|
+
/** The extension's target (and folder) name: not BubblNotificationService, the module it imports. */
|
|
9
|
+
export declare const EXTENSION_TARGET = "BubblNotificationExtension";
|
|
10
|
+
/**
|
|
11
|
+
* The extension's files, named after it: the xcode package reuses an existing file reference with the
|
|
12
|
+
* same path, so a common name (NotificationService.swift, Info.plist) could pick up another target's.
|
|
13
|
+
*/
|
|
14
|
+
export declare const SWIFT_FILE = "BubblNotificationExtension.swift";
|
|
15
|
+
export declare const PLIST_FILE = "BubblNotificationExtension-Info.plist";
|
|
16
|
+
/** The extension's Info.plist, with the app's version and build number (App Store requires them to match). */
|
|
17
|
+
export declare function extensionInfoPlist(version: string, buildNumber: string): string;
|
|
18
|
+
/** The Podfile with the extension's target, which gets Bubbl's service pod and nothing else. */
|
|
19
|
+
export declare function podfileWithExtension(podfile: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* extra.eas.build.experimental.ios.appExtensions with the extension, which EAS Build needs to
|
|
22
|
+
* provision and sign it (anything else there is kept).
|
|
23
|
+
*/
|
|
24
|
+
export declare function withEasAppExtension(config: ExportedConfig, bundleIdentifier: string): ExportedConfig;
|
|
25
|
+
/**
|
|
26
|
+
* The Xcode project with the extension target: its files in a group, its Sources, Frameworks
|
|
27
|
+
* and Resources phases, built before the app (a target dependency) and embedded in it (right
|
|
28
|
+
* after the app's Resources, ahead of the script phases, which Xcode otherwise reports as a
|
|
29
|
+
* build cycle). Nothing changes when the target is already there (a second prebuild).
|
|
30
|
+
*/
|
|
31
|
+
export declare function addNotificationExtension(project: XcodeProject, options: {
|
|
32
|
+
bundleIdentifier: string;
|
|
33
|
+
developmentTeam?: string;
|
|
34
|
+
}): XcodeProject;
|
|
35
|
+
export declare const withImageNotifications: ConfigPlugin;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withImageNotifications = exports.PLIST_FILE = exports.SWIFT_FILE = exports.EXTENSION_TARGET = void 0;
|
|
4
|
+
exports.extensionInfoPlist = extensionInfoPlist;
|
|
5
|
+
exports.podfileWithExtension = podfileWithExtension;
|
|
6
|
+
exports.withEasAppExtension = withEasAppExtension;
|
|
7
|
+
exports.addNotificationExtension = addNotificationExtension;
|
|
8
|
+
const config_plugins_1 = require("@expo/config-plugins");
|
|
9
|
+
const node_fs_1 = require("node:fs");
|
|
10
|
+
const node_path_1 = require("node:path");
|
|
11
|
+
/**
|
|
12
|
+
* ios.imageNotifications: pictures on Bubbl's pushes. Adds a Notification Service Extension target
|
|
13
|
+
* whose whole code is `class NotificationService: BubblNotificationService {}` (the iOS SDK's, from
|
|
14
|
+
* this package's ios-extension pod), its Podfile target, and EAS Build's entry for it (so EAS signs
|
|
15
|
+
* it). The same as the README's steps for a React Native CLI app, done at prebuild.
|
|
16
|
+
*/
|
|
17
|
+
/** The extension's target (and folder) name: not BubblNotificationService, the module it imports. */
|
|
18
|
+
exports.EXTENSION_TARGET = 'BubblNotificationExtension';
|
|
19
|
+
/**
|
|
20
|
+
* The extension's deployment target when the app's can't be read: React Native's floor, which its
|
|
21
|
+
* pod (built for React Native's floor too) needs. It shows pictures on iOS 17 and up.
|
|
22
|
+
*/
|
|
23
|
+
const DEPLOYMENT_TARGET = '15.1';
|
|
24
|
+
/**
|
|
25
|
+
* The extension's files, named after it: the xcode package reuses an existing file reference with the
|
|
26
|
+
* same path, so a common name (NotificationService.swift, Info.plist) could pick up another target's.
|
|
27
|
+
*/
|
|
28
|
+
exports.SWIFT_FILE = `${exports.EXTENSION_TARGET}.swift`;
|
|
29
|
+
exports.PLIST_FILE = `${exports.EXTENSION_TARGET}-Info.plist`;
|
|
30
|
+
const SERVICE_SWIFT = `// Written by @bubblsdk/react-native-sdk (ios.imageNotifications): pictures on Bubbl's pushes.
|
|
31
|
+
import BubblNotificationService
|
|
32
|
+
|
|
33
|
+
class NotificationService: BubblNotificationService {}
|
|
34
|
+
`;
|
|
35
|
+
/** The extension's Info.plist, with the app's version and build number (App Store requires them to match). */
|
|
36
|
+
function extensionInfoPlist(version, buildNumber) {
|
|
37
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
38
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
39
|
+
<plist version="1.0">
|
|
40
|
+
<dict>
|
|
41
|
+
<key>CFBundleDevelopmentRegion</key>
|
|
42
|
+
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
|
43
|
+
<key>CFBundleDisplayName</key>
|
|
44
|
+
<string>${exports.EXTENSION_TARGET}</string>
|
|
45
|
+
<key>CFBundleExecutable</key>
|
|
46
|
+
<string>$(EXECUTABLE_NAME)</string>
|
|
47
|
+
<key>CFBundleIdentifier</key>
|
|
48
|
+
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
|
49
|
+
<key>CFBundleInfoDictionaryVersion</key>
|
|
50
|
+
<string>6.0</string>
|
|
51
|
+
<key>CFBundleName</key>
|
|
52
|
+
<string>$(PRODUCT_NAME)</string>
|
|
53
|
+
<key>CFBundlePackageType</key>
|
|
54
|
+
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
|
55
|
+
<key>CFBundleShortVersionString</key>
|
|
56
|
+
<string>${escapeXml(version)}</string>
|
|
57
|
+
<key>CFBundleVersion</key>
|
|
58
|
+
<string>${escapeXml(buildNumber)}</string>
|
|
59
|
+
<key>NSExtension</key>
|
|
60
|
+
<dict>
|
|
61
|
+
<key>NSExtensionPointIdentifier</key>
|
|
62
|
+
<string>com.apple.usernotifications.service</string>
|
|
63
|
+
<key>NSExtensionPrincipalClass</key>
|
|
64
|
+
<string>$(PRODUCT_MODULE_NAME).NotificationService</string>
|
|
65
|
+
</dict>
|
|
66
|
+
</dict>
|
|
67
|
+
</plist>
|
|
68
|
+
`;
|
|
69
|
+
}
|
|
70
|
+
function escapeXml(text) {
|
|
71
|
+
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
72
|
+
}
|
|
73
|
+
const PODFILE_MARK = '# @bubblsdk/react-native-sdk (ios.imageNotifications)';
|
|
74
|
+
/** The Podfile with the extension's target, which gets Bubbl's service pod and nothing else. */
|
|
75
|
+
function podfileWithExtension(podfile) {
|
|
76
|
+
if (podfile.includes(PODFILE_MARK)) {
|
|
77
|
+
return podfile;
|
|
78
|
+
}
|
|
79
|
+
return `${podfile.trimEnd()}
|
|
80
|
+
|
|
81
|
+
${PODFILE_MARK}: pictures on Bubbl's pushes.
|
|
82
|
+
target '${exports.EXTENSION_TARGET}' do
|
|
83
|
+
pod 'BubblReactNativeNotificationService', :path => File.join(File.dirname(\`node --print "require.resolve('@bubblsdk/react-native-sdk/package.json')"\`.strip), 'ios-extension')
|
|
84
|
+
end
|
|
85
|
+
`;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* extra.eas.build.experimental.ios.appExtensions with the extension, which EAS Build needs to
|
|
89
|
+
* provision and sign it (anything else there is kept).
|
|
90
|
+
*/
|
|
91
|
+
function withEasAppExtension(config, bundleIdentifier) {
|
|
92
|
+
const extra = (config.extra ?? {});
|
|
93
|
+
const eas = extra.eas ?? {};
|
|
94
|
+
const build = eas.build ?? {};
|
|
95
|
+
const experimental = build.experimental ?? {};
|
|
96
|
+
const ios = experimental.ios ?? {};
|
|
97
|
+
const others = (ios.appExtensions ?? []).filter((ext) => ext.targetName !== exports.EXTENSION_TARGET);
|
|
98
|
+
const extension = { targetName: exports.EXTENSION_TARGET, bundleIdentifier, entitlements: {} };
|
|
99
|
+
config.extra = {
|
|
100
|
+
...extra,
|
|
101
|
+
eas: { ...eas, build: { ...build, experimental: { ...experimental, ios: { ...ios, appExtensions: [...others, extension] } } } },
|
|
102
|
+
};
|
|
103
|
+
return config;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* The Xcode project with the extension target: its files in a group, its Sources, Frameworks
|
|
107
|
+
* and Resources phases, built before the app (a target dependency) and embedded in it (right
|
|
108
|
+
* after the app's Resources, ahead of the script phases, which Xcode otherwise reports as a
|
|
109
|
+
* build cycle). Nothing changes when the target is already there (a second prebuild).
|
|
110
|
+
*/
|
|
111
|
+
function addNotificationExtension(project, options) {
|
|
112
|
+
// Its name is quoted once written out ("BubblNotificationExtension"), and bare before.
|
|
113
|
+
if (project.pbxTargetByName(exports.EXTENSION_TARGET) || project.pbxTargetByName(`"${exports.EXTENSION_TARGET}"`)) {
|
|
114
|
+
return project;
|
|
115
|
+
}
|
|
116
|
+
const objects = project.hash.project.objects;
|
|
117
|
+
// addTarget makes the app depend on the extension, but only when both sections exist.
|
|
118
|
+
objects.PBXTargetDependency = objects.PBXTargetDependency ?? {};
|
|
119
|
+
objects.PBXContainerItemProxy = objects.PBXContainerItemProxy ?? {};
|
|
120
|
+
const app = project.getFirstTarget();
|
|
121
|
+
const group = project.addPbxGroup([exports.SWIFT_FILE, exports.PLIST_FILE], exports.EXTENSION_TARGET, exports.EXTENSION_TARGET);
|
|
122
|
+
const mainGroup = project.getFirstProject().firstProject.mainGroup;
|
|
123
|
+
project.addToPbxGroup(group.uuid, mainGroup);
|
|
124
|
+
const target = project.addTarget(exports.EXTENSION_TARGET, 'app_extension', exports.EXTENSION_TARGET, `${options.bundleIdentifier}.${exports.EXTENSION_TARGET}`);
|
|
125
|
+
project.addBuildPhase([exports.SWIFT_FILE], 'PBXSourcesBuildPhase', 'Sources', target.uuid);
|
|
126
|
+
project.addBuildPhase([], 'PBXFrameworksBuildPhase', 'Frameworks', target.uuid);
|
|
127
|
+
project.addBuildPhase([], 'PBXResourcesBuildPhase', 'Resources', target.uuid);
|
|
128
|
+
embedAfterResources(project, app.uuid);
|
|
129
|
+
const configurations = project.pbxXCBuildConfigurationSection();
|
|
130
|
+
for (const [key, configuration] of Object.entries(configurations)) {
|
|
131
|
+
if (key.endsWith('_comment') || configuration.buildSettings?.PRODUCT_NAME !== `"${exports.EXTENSION_TARGET}"`) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
Object.assign(configuration.buildSettings, {
|
|
135
|
+
INFOPLIST_FILE: `${exports.EXTENSION_TARGET}/${exports.PLIST_FILE}`,
|
|
136
|
+
// The app's, as Xcode gives a new extension.
|
|
137
|
+
IPHONEOS_DEPLOYMENT_TARGET: appSetting(project, app.firstTarget, 'IPHONEOS_DEPLOYMENT_TARGET') ?? DEPLOYMENT_TARGET,
|
|
138
|
+
SWIFT_VERSION: '5.0',
|
|
139
|
+
// The app's (Expo's own mod sets every target's to the app's on later prebuilds anyway).
|
|
140
|
+
TARGETED_DEVICE_FAMILY: appSetting(project, app.firstTarget, 'TARGETED_DEVICE_FAMILY') ?? '"1,2"',
|
|
141
|
+
CLANG_ENABLE_MODULES: 'YES',
|
|
142
|
+
CODE_SIGN_STYLE: 'Automatic',
|
|
143
|
+
...(options.developmentTeam ? { DEVELOPMENT_TEAM: options.developmentTeam } : {}),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return project;
|
|
147
|
+
}
|
|
148
|
+
/** A build setting of the app target (its first configuration that has it). */
|
|
149
|
+
function appSetting(project, app, name) {
|
|
150
|
+
const lists = project.pbxXCConfigurationList();
|
|
151
|
+
const configurations = project.pbxXCBuildConfigurationSection();
|
|
152
|
+
return lists[app.buildConfigurationList]?.buildConfigurations
|
|
153
|
+
?.map((entry) => configurations[entry.value]?.buildSettings?.[name])
|
|
154
|
+
.find((value) => value !== undefined);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Moves the "Copy Files" phase the xcode package just appended to the app (the last one: the app
|
|
158
|
+
* may have its own) to just after the app's Resources.
|
|
159
|
+
*/
|
|
160
|
+
function embedAfterResources(project, appUuid) {
|
|
161
|
+
const phases = project.pbxNativeTargetSection()[appUuid].buildPhases;
|
|
162
|
+
const embed = phases.map((phase) => phase.comment).lastIndexOf('Copy Files');
|
|
163
|
+
if (embed < 0 || !phases.some((phase) => phase.comment === 'Resources')) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const [phase] = phases.splice(embed, 1);
|
|
167
|
+
phases.splice(phases.findIndex((p) => p.comment === 'Resources') + 1, 0, phase);
|
|
168
|
+
}
|
|
169
|
+
const withImageNotifications = (config) => {
|
|
170
|
+
const bundleIdentifier = config.ios?.bundleIdentifier;
|
|
171
|
+
if (!bundleIdentifier) {
|
|
172
|
+
throw new Error('@bubblsdk/react-native-sdk: ios.imageNotifications needs ios.bundleIdentifier in the app config ' +
|
|
173
|
+
'(the extension\'s is it plus .BubblNotificationExtension).');
|
|
174
|
+
}
|
|
175
|
+
config = withEasAppExtension(config, `${bundleIdentifier}.${exports.EXTENSION_TARGET}`);
|
|
176
|
+
config = (0, config_plugins_1.withDangerousMod)(config, [
|
|
177
|
+
'ios',
|
|
178
|
+
(config) => {
|
|
179
|
+
const dir = (0, node_path_1.join)(config.modRequest.platformProjectRoot, exports.EXTENSION_TARGET);
|
|
180
|
+
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
181
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, exports.SWIFT_FILE), SERVICE_SWIFT);
|
|
182
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, exports.PLIST_FILE), extensionInfoPlist(config.version ?? '1.0.0', config.ios?.buildNumber ?? '1'));
|
|
183
|
+
return config;
|
|
184
|
+
},
|
|
185
|
+
]);
|
|
186
|
+
config = (0, config_plugins_1.withXcodeProject)(config, (config) => {
|
|
187
|
+
config.modResults = addNotificationExtension(config.modResults, {
|
|
188
|
+
bundleIdentifier,
|
|
189
|
+
developmentTeam: config.ios?.appleTeamId,
|
|
190
|
+
});
|
|
191
|
+
return config;
|
|
192
|
+
});
|
|
193
|
+
return (0, config_plugins_1.withPodfile)(config, (config) => {
|
|
194
|
+
config.modResults.contents = podfileWithExtension(config.modResults.contents);
|
|
195
|
+
return config;
|
|
196
|
+
});
|
|
197
|
+
};
|
|
198
|
+
exports.withImageNotifications = withImageNotifications;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { AndroidConfig, type ConfigPlugin, type XcodeProject } from '@expo/config-plugins';
|
|
2
|
+
/**
|
|
3
|
+
* The Expo config plugin: what `npx expo prebuild` needs to add to the native projects for
|
|
4
|
+
* Bubbl, from one entry in app.json:
|
|
5
|
+
*
|
|
6
|
+
* "plugins": [["@bubblsdk/react-native-sdk", { "android": { "cleartextHosts": ["10.0.2.2"] } }]]
|
|
7
|
+
*
|
|
8
|
+
* Android: the Bubbl SDK's manifest already brings its permissions, receivers and screens; this
|
|
9
|
+
* only raises the app's minSdkVersion to the SDK's, and (for development against a local API)
|
|
10
|
+
* allows plain http to the hosts listed.
|
|
11
|
+
*
|
|
12
|
+
* iOS: the location usage strings (the app's own, or Bubbl's defaults), the push entitlement
|
|
13
|
+
* (aps-environment: Bubbl sends to APNs directly), iOS 17 as the deployment target, and (for
|
|
14
|
+
* development) local networking over http; optionally (ios.imageNotifications) the extension that
|
|
15
|
+
* shows the pictures on Bubbl's pushes.
|
|
16
|
+
*/
|
|
17
|
+
export type BubblPluginProps = {
|
|
18
|
+
ios?: {
|
|
19
|
+
/** NSLocationWhenInUseUsageDescription, when the app doesn't set its own. */
|
|
20
|
+
locationWhenInUse?: string;
|
|
21
|
+
/** NSLocationAlwaysAndWhenInUseUsageDescription, when the app doesn't set its own. */
|
|
22
|
+
locationAlways?: string;
|
|
23
|
+
/** aps-environment: "development" (the default; App Store builds are re-signed for production) or "production". */
|
|
24
|
+
apsEnvironment?: 'development' | 'production';
|
|
25
|
+
/** NSAllowsLocalNetworking, for development against a local Bubbl API over http. Leave out of release builds. */
|
|
26
|
+
allowLocalNetworking?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Pictures on Bubbl's pushes: adds a Notification Service Extension target
|
|
29
|
+
* (BubblNotificationExtension, bundle id: the app's + .BubblNotificationExtension), its Podfile
|
|
30
|
+
* target and its EAS Build entry. Needs ios.bundleIdentifier.
|
|
31
|
+
*/
|
|
32
|
+
imageNotifications?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Background app refresh: iOS wakes the app now and then and Bubbl sends what's queued. Adds
|
|
35
|
+
* "tech.bubbl.sdk.refresh" to BGTaskSchedulerPermittedIdentifiers and "fetch" to
|
|
36
|
+
* UIBackgroundModes (the app's own entries are kept).
|
|
37
|
+
*/
|
|
38
|
+
backgroundRefresh?: boolean;
|
|
39
|
+
};
|
|
40
|
+
android?: {
|
|
41
|
+
/**
|
|
42
|
+
* Hosts the app may reach over plain http, for development against a local Bubbl API (e.g.
|
|
43
|
+
* "10.0.2.2", the emulator's host). Leave out of release builds: Bubbl only allows http to
|
|
44
|
+
* localhost, 10.0.2.2 and *.test anyway.
|
|
45
|
+
*/
|
|
46
|
+
cleartextHosts?: string[];
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* The Bubbl Android SDK's minSdkVersion: Android 6 and up install it (Bubbl runs on 8.1, API 27,
|
|
51
|
+
* and up, and does nothing below). Expo's own default is higher, so this rarely changes anything.
|
|
52
|
+
*/
|
|
53
|
+
export declare const ANDROID_MIN_SDK = 23;
|
|
54
|
+
type PropertiesItem = AndroidConfig.Properties.PropertiesItem;
|
|
55
|
+
/**
|
|
56
|
+
* gradle.properties with android.minSdkVersion (which Expo's Android template reads) at least
|
|
57
|
+
* the SDK's: raised when an app set it lower; left alone when higher or unset (Expo's default,
|
|
58
|
+
* higher than the SDK's, applies then).
|
|
59
|
+
*/
|
|
60
|
+
export declare function raiseMinSdk(items: PropertiesItem[]): PropertiesItem[];
|
|
61
|
+
export declare function networkSecurityConfig(hosts: string[]): string;
|
|
62
|
+
/**
|
|
63
|
+
* The Bubbl iOS SDK's deployment target: iOS 13 and up install it (Bubbl runs on 17 and up and does
|
|
64
|
+
* nothing below). React Native's and Expo's own floors are higher, so this rarely changes anything.
|
|
65
|
+
*/
|
|
66
|
+
export declare const IOS_DEPLOYMENT_TARGET = "13.0";
|
|
67
|
+
type Plist = Record<string, unknown>;
|
|
68
|
+
/** Info.plist with Bubbl's keys: the app's own usage strings win over Bubbl's defaults. */
|
|
69
|
+
export declare function bubblInfoPlist(plist: Plist, ios?: BubblPluginProps['ios']): Plist;
|
|
70
|
+
/** The Bubbl iOS SDK's background refresh task. */
|
|
71
|
+
export declare const BACKGROUND_REFRESH_TASK = "tech.bubbl.sdk.refresh";
|
|
72
|
+
/**
|
|
73
|
+
* The deployment target, at least the SDK's: a lower one is raised, a higher one kept, and none
|
|
74
|
+
* stays none (Expo's and React Native's own default, higher than the SDK's, applies then).
|
|
75
|
+
*/
|
|
76
|
+
export declare function raiseDeploymentTarget(current: string | undefined): string | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* The app target's and the project's IPHONEOS_DEPLOYMENT_TARGET at least the SDK's where they're
|
|
79
|
+
* set (a higher one is kept). Podfile.properties.json has the pods'; this is the app's own. Other
|
|
80
|
+
* targets (an app's widget, say) are left as they are.
|
|
81
|
+
*/
|
|
82
|
+
export declare function raiseAppDeploymentTarget(project: XcodeProject): XcodeProject;
|
|
83
|
+
declare const _default: ConfigPlugin<void | BubblPluginProps>;
|
|
84
|
+
export default _default;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BACKGROUND_REFRESH_TASK = exports.IOS_DEPLOYMENT_TARGET = exports.ANDROID_MIN_SDK = void 0;
|
|
4
|
+
exports.raiseMinSdk = raiseMinSdk;
|
|
5
|
+
exports.networkSecurityConfig = networkSecurityConfig;
|
|
6
|
+
exports.bubblInfoPlist = bubblInfoPlist;
|
|
7
|
+
exports.raiseDeploymentTarget = raiseDeploymentTarget;
|
|
8
|
+
exports.raiseAppDeploymentTarget = raiseAppDeploymentTarget;
|
|
9
|
+
const config_plugins_1 = require("@expo/config-plugins");
|
|
10
|
+
const node_fs_1 = require("node:fs");
|
|
11
|
+
const node_path_1 = require("node:path");
|
|
12
|
+
const imageNotifications_1 = require("./imageNotifications");
|
|
13
|
+
/**
|
|
14
|
+
* The Bubbl Android SDK's minSdkVersion: Android 6 and up install it (Bubbl runs on 8.1, API 27,
|
|
15
|
+
* and up, and does nothing below). Expo's own default is higher, so this rarely changes anything.
|
|
16
|
+
*/
|
|
17
|
+
exports.ANDROID_MIN_SDK = 23;
|
|
18
|
+
const NETWORK_CONFIG = 'bubbl_network_security_config';
|
|
19
|
+
/**
|
|
20
|
+
* gradle.properties with android.minSdkVersion (which Expo's Android template reads) at least
|
|
21
|
+
* the SDK's: raised when an app set it lower; left alone when higher or unset (Expo's default,
|
|
22
|
+
* higher than the SDK's, applies then).
|
|
23
|
+
*/
|
|
24
|
+
function raiseMinSdk(items) {
|
|
25
|
+
const key = 'android.minSdkVersion';
|
|
26
|
+
const existing = items.find((item) => item.type === 'property' && item.key === key);
|
|
27
|
+
if (!existing || existing.type !== 'property') {
|
|
28
|
+
return items;
|
|
29
|
+
}
|
|
30
|
+
if (Number.parseInt(existing.value, 10) >= exports.ANDROID_MIN_SDK) {
|
|
31
|
+
return items;
|
|
32
|
+
}
|
|
33
|
+
return items.map((item) => (item === existing ? { ...existing, value: String(exports.ANDROID_MIN_SDK) } : item));
|
|
34
|
+
}
|
|
35
|
+
const withMinSdk = (config) => (0, config_plugins_1.withGradleProperties)(config, (config) => {
|
|
36
|
+
config.modResults = raiseMinSdk(config.modResults);
|
|
37
|
+
return config;
|
|
38
|
+
});
|
|
39
|
+
/** Plain http to the development hosts listed: a network security config naming only those. */
|
|
40
|
+
const withCleartextHosts = (config, hosts) => {
|
|
41
|
+
config = (0, config_plugins_1.withDangerousMod)(config, [
|
|
42
|
+
'android',
|
|
43
|
+
(config) => {
|
|
44
|
+
const dir = (0, node_path_1.join)(config.modRequest.platformProjectRoot, 'app', 'src', 'main', 'res', 'xml');
|
|
45
|
+
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
46
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, `${NETWORK_CONFIG}.xml`), networkSecurityConfig(hosts));
|
|
47
|
+
return config;
|
|
48
|
+
},
|
|
49
|
+
]);
|
|
50
|
+
return (0, config_plugins_1.withAndroidManifest)(config, (config) => {
|
|
51
|
+
const application = config_plugins_1.AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults);
|
|
52
|
+
const existing = application.$['android:networkSecurityConfig'];
|
|
53
|
+
if (existing && existing !== `@xml/${NETWORK_CONFIG}`) {
|
|
54
|
+
throw new Error(`@bubblsdk/react-native-sdk: the app already has a network security config (${existing}). ` +
|
|
55
|
+
'Add the cleartext hosts to it yourself and leave android.cleartextHosts out.');
|
|
56
|
+
}
|
|
57
|
+
application.$['android:networkSecurityConfig'] = `@xml/${NETWORK_CONFIG}`;
|
|
58
|
+
return config;
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
function networkSecurityConfig(hosts) {
|
|
62
|
+
const domains = hosts.map((host) => ` <domain includeSubdomains="false">${escapeXml(host)}</domain>`).join('\n');
|
|
63
|
+
return [
|
|
64
|
+
'<?xml version="1.0" encoding="utf-8"?>',
|
|
65
|
+
'<!-- Written by @bubblsdk/react-native-sdk (android.cleartextHosts): plain http for development only. -->',
|
|
66
|
+
'<network-security-config>',
|
|
67
|
+
' <domain-config cleartextTrafficPermitted="true">',
|
|
68
|
+
domains,
|
|
69
|
+
' </domain-config>',
|
|
70
|
+
'</network-security-config>',
|
|
71
|
+
'',
|
|
72
|
+
].join('\n');
|
|
73
|
+
}
|
|
74
|
+
function escapeXml(text) {
|
|
75
|
+
return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* The Bubbl iOS SDK's deployment target: iOS 13 and up install it (Bubbl runs on 17 and up and does
|
|
79
|
+
* nothing below). React Native's and Expo's own floors are higher, so this rarely changes anything.
|
|
80
|
+
*/
|
|
81
|
+
exports.IOS_DEPLOYMENT_TARGET = '13.0';
|
|
82
|
+
const DEFAULT_WHEN_IN_USE = 'Your location is used to show offers and messages for places near you.';
|
|
83
|
+
const DEFAULT_ALWAYS = 'Your location is used, even when the app is closed, to show offers and messages when you arrive at places.';
|
|
84
|
+
/** Info.plist with Bubbl's keys: the app's own usage strings win over Bubbl's defaults. */
|
|
85
|
+
function bubblInfoPlist(plist, ios = {}) {
|
|
86
|
+
const result = { ...plist };
|
|
87
|
+
result.NSLocationWhenInUseUsageDescription = ios.locationWhenInUse ?? plist.NSLocationWhenInUseUsageDescription ?? DEFAULT_WHEN_IN_USE;
|
|
88
|
+
result.NSLocationAlwaysAndWhenInUseUsageDescription =
|
|
89
|
+
ios.locationAlways ?? plist.NSLocationAlwaysAndWhenInUseUsageDescription ?? DEFAULT_ALWAYS;
|
|
90
|
+
if (ios.allowLocalNetworking) {
|
|
91
|
+
const ats = plist.NSAppTransportSecurity ?? {};
|
|
92
|
+
result.NSAppTransportSecurity = { ...ats, NSAllowsLocalNetworking: true };
|
|
93
|
+
}
|
|
94
|
+
if (ios.backgroundRefresh) {
|
|
95
|
+
result.BGTaskSchedulerPermittedIdentifiers = withItem(plist.BGTaskSchedulerPermittedIdentifiers, exports.BACKGROUND_REFRESH_TASK);
|
|
96
|
+
result.UIBackgroundModes = withItem(plist.UIBackgroundModes, 'fetch');
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
/** The Bubbl iOS SDK's background refresh task. */
|
|
101
|
+
exports.BACKGROUND_REFRESH_TASK = 'tech.bubbl.sdk.refresh';
|
|
102
|
+
/** A plist array with `item` in it once, the app's own entries kept in order. */
|
|
103
|
+
function withItem(list, item) {
|
|
104
|
+
const items = Array.isArray(list) ? list.filter((entry) => typeof entry === 'string') : [];
|
|
105
|
+
return items.includes(item) ? items : [...items, item];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The deployment target, at least the SDK's: a lower one is raised, a higher one kept, and none
|
|
109
|
+
* stays none (Expo's and React Native's own default, higher than the SDK's, applies then).
|
|
110
|
+
*/
|
|
111
|
+
function raiseDeploymentTarget(current) {
|
|
112
|
+
const version = (text) => text.split('.').map((part) => Number.parseInt(part, 10) || 0);
|
|
113
|
+
if (!current) {
|
|
114
|
+
return current;
|
|
115
|
+
}
|
|
116
|
+
const [major = 0, minor = 0] = version(current);
|
|
117
|
+
const [minMajor = 0, minMinor = 0] = version(exports.IOS_DEPLOYMENT_TARGET);
|
|
118
|
+
return major > minMajor || (major === minMajor && minor >= minMinor) ? current : exports.IOS_DEPLOYMENT_TARGET;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* The app target's and the project's IPHONEOS_DEPLOYMENT_TARGET at least the SDK's where they're
|
|
122
|
+
* set (a higher one is kept). Podfile.properties.json has the pods'; this is the app's own. Other
|
|
123
|
+
* targets (an app's widget, say) are left as they are.
|
|
124
|
+
*/
|
|
125
|
+
function raiseAppDeploymentTarget(project) {
|
|
126
|
+
const lists = project.pbxXCConfigurationList();
|
|
127
|
+
const configurations = project.pbxXCBuildConfigurationSection();
|
|
128
|
+
const app = project.getFirstTarget()?.firstTarget;
|
|
129
|
+
const projectList = project.getFirstProject().firstProject.buildConfigurationList;
|
|
130
|
+
for (const listId of [app?.buildConfigurationList, projectList]) {
|
|
131
|
+
for (const entry of (listId && lists[listId]?.buildConfigurations) || []) {
|
|
132
|
+
const settings = configurations[entry.value]?.buildSettings;
|
|
133
|
+
if (!settings) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const current = settings.IPHONEOS_DEPLOYMENT_TARGET;
|
|
137
|
+
if (current !== undefined) {
|
|
138
|
+
settings.IPHONEOS_DEPLOYMENT_TARGET = raiseDeploymentTarget(String(current).replace(/"/g, ''));
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return project;
|
|
143
|
+
}
|
|
144
|
+
const withBubblIos = (config, ios = {}) => {
|
|
145
|
+
config = (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
146
|
+
config.modResults = bubblInfoPlist(config.modResults, ios);
|
|
147
|
+
return config;
|
|
148
|
+
});
|
|
149
|
+
config = (0, config_plugins_1.withEntitlementsPlist)(config, (config) => {
|
|
150
|
+
config.modResults['aps-environment'] = ios.apsEnvironment ?? config.modResults['aps-environment'] ?? 'development';
|
|
151
|
+
return config;
|
|
152
|
+
});
|
|
153
|
+
config = (0, config_plugins_1.withPodfileProperties)(config, (config) => {
|
|
154
|
+
const target = raiseDeploymentTarget(config.modResults['ios.deploymentTarget']);
|
|
155
|
+
if (target) {
|
|
156
|
+
config.modResults['ios.deploymentTarget'] = target;
|
|
157
|
+
}
|
|
158
|
+
return config;
|
|
159
|
+
});
|
|
160
|
+
config = (0, config_plugins_1.withXcodeProject)(config, (config) => {
|
|
161
|
+
config.modResults = raiseAppDeploymentTarget(config.modResults);
|
|
162
|
+
return config;
|
|
163
|
+
});
|
|
164
|
+
return ios.imageNotifications ? (0, imageNotifications_1.withImageNotifications)(config) : config;
|
|
165
|
+
};
|
|
166
|
+
const withBubbl = (config, props) => {
|
|
167
|
+
config = withMinSdk(config);
|
|
168
|
+
const hosts = props?.android?.cleartextHosts?.filter((host) => host.trim() !== '') ?? [];
|
|
169
|
+
if (hosts.length > 0) {
|
|
170
|
+
config = withCleartextHosts(config, hosts);
|
|
171
|
+
}
|
|
172
|
+
return withBubblIos(config, props?.ios);
|
|
173
|
+
};
|
|
174
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
175
|
+
const pkg = require('../../package.json');
|
|
176
|
+
exports.default = (0, config_plugins_1.createRunOncePlugin)(withBubbl, pkg.name, pkg.version);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Spec } from './NativeBubblSdk';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* No native module on the web, and react-native-web has no TurboModuleRegistry to ask for one, so
|
|
5
|
+
* NativeBubblSdk.ts isn't loaded at all: Bubbl's calls do nothing there.
|
|
6
|
+
*/
|
|
7
|
+
const BubblModule: Spec | null = null;
|
|
8
|
+
|
|
9
|
+
export default BubblModule;
|