airborne-react-native 0.2.0 → 0.3.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/AirborneReact.podspec +4 -7
- package/README.md +6 -6
- package/android/build.gradle +30 -1
- package/android/gradle.properties +1 -1
- package/android/src/latest/java/in/juspay/airborneplugin/AirborneReactHostDelegate.kt +65 -0
- package/android/src/main/AndroidManifest.xml +4 -2
- package/android/src/main/java/in/juspay/airborneplugin/Airborne.kt +53 -19
- package/android/src/main/java/in/juspay/airborneplugin/AirborneInterface.kt +2 -6
- package/android/src/main/java/in/juspay/airborneplugin/AirborneModule.kt +6 -6
- package/android/src/main/java/in/juspay/airborneplugin/AirborneModuleImpl.kt +6 -6
- package/android/src/main/java/in/juspay/airborneplugin/AirbornePackage.kt +25 -25
- package/android/src/main/java/in/juspay/airborneplugin/AirborneReactActivityDelegate.kt +81 -0
- package/android/src/main/java/in/juspay/airborneplugin/AirborneReactNativeHost.kt +49 -0
- package/android/src/main/java/in/juspay/airborneplugin/AirborneTurboModule.kt +7 -7
- package/android/src/main/res/raw/juspay_airborne_react_res.xml +1 -0
- package/android/src/rn77/java/in/juspay/airborneplugin/AirborneReactHostDelegate.kt +71 -0
- package/ios/Airborne.h +11 -18
- package/ios/Airborne.m +80 -0
- package/ios/AirborneReact.h +26 -0
- package/ios/{Airborne.mm → AirborneReact.mm} +10 -12
- package/lib/module/index.js +6 -6
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/NativeAirborne.d.ts +3 -3
- package/lib/typescript/src/NativeAirborne.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +3 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/NativeAirborne.ts +3 -3
- package/src/index.tsx +6 -6
- package/ios/AirborneiOS.h +0 -75
- package/ios/AirborneiOS.m +0 -114
- package/lib/typescript/ExampleOldArch/App.d.ts +0 -4
- package/lib/typescript/ExampleOldArch/App.d.ts.map +0 -1
package/AirborneReact.podspec
CHANGED
|
@@ -28,15 +28,12 @@ Pod::Spec.new do |s|
|
|
|
28
28
|
|
|
29
29
|
s.platforms = { :ios => "12.0"}
|
|
30
30
|
s.source = { :git => "https://github.com/juspay/airborne.git", :tag => "#{s.version}" }
|
|
31
|
-
s.resource_bundles = {
|
|
32
|
-
'AirborneReactResources' => ['ios/**/*.{xib,storyboard,xcassets,json}']
|
|
33
|
-
}
|
|
34
31
|
|
|
35
32
|
s.source_files = "ios/**/*.{h,m,mm,cpp}"
|
|
36
33
|
s.public_header_files = "ios/**/*.h"
|
|
37
34
|
s.static_framework = false
|
|
38
|
-
|
|
39
|
-
s.dependency "Airborne", "0.
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
|
|
36
|
+
s.dependency "Airborne", "0.3.2"
|
|
37
|
+
|
|
38
|
+
install_modules_dependencies(s)
|
|
42
39
|
end
|
package/README.md
CHANGED
|
@@ -53,9 +53,9 @@ This implementation provides a React Native module for Airborne that:
|
|
|
53
53
|
|
|
54
54
|
## API Methods
|
|
55
55
|
|
|
56
|
-
1. **readReleaseConfig()** - Returns the release configuration as a stringified JSON
|
|
57
|
-
2. **getFileContent(filePath)** - Reads content from a file in the OTA bundle
|
|
58
|
-
3. **getBundlePath()** - Returns the path to the JavaScript bundle
|
|
56
|
+
1. **readReleaseConfig(namespace/appId)** - Returns the release configuration as a stringified JSON
|
|
57
|
+
2. **getFileContent(namespace/appId, filePath)** - Reads content from a file in the OTA bundle
|
|
58
|
+
3. **getBundlePath(namespace/appId)** - Returns the path to the JavaScript bundle
|
|
59
59
|
|
|
60
60
|
## Usage
|
|
61
61
|
|
|
@@ -68,13 +68,13 @@ This implementation provides a React Native module for Airborne that:
|
|
|
68
68
|
import { readReleaseConfig, getFileContent, getBundlePath } from 'airborne-react-native';
|
|
69
69
|
|
|
70
70
|
// Read configuration
|
|
71
|
-
const config = await readReleaseConfig();
|
|
71
|
+
const config = await readReleaseConfig(namespace/appId);
|
|
72
72
|
|
|
73
73
|
// Get file content
|
|
74
|
-
const content = await getFileContent('path/to/file.json');
|
|
74
|
+
const content = await getFileContent(namespace/appId, 'path/to/file.json');
|
|
75
75
|
|
|
76
76
|
// Get bundle path
|
|
77
|
-
const bundlePath = await getBundlePath();
|
|
77
|
+
const bundlePath = await getBundlePath(namespace/appId);
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
## Implementation Notes
|
package/android/build.gradle
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
// limitations under the License.
|
|
14
14
|
|
|
15
15
|
import com.android.Version
|
|
16
|
+
import groovy.json.JsonSlurper
|
|
16
17
|
|
|
17
18
|
buildscript {
|
|
18
19
|
ext.getExtOrDefault = { name ->
|
|
@@ -35,6 +36,22 @@ def isNewArchitectureEnabled() {
|
|
|
35
36
|
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
def findReactNativeVersion() {
|
|
40
|
+
// Locate app's node_modules/react-native/package.json
|
|
41
|
+
def packageJson = file("${rootProject.projectDir}/../node_modules/react-native/package.json")
|
|
42
|
+
if (!packageJson.exists()) {
|
|
43
|
+
packageJson = file("${rootProject.projectDir}/node_modules/react-native/package.json")
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (!packageJson.exists()) {
|
|
47
|
+
println("[AirborneReact] Could not find react-native/package.json. Defaulting to latest.")
|
|
48
|
+
return "999.0.0"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
def json = new JsonSlurper().parseText(packageJson.text)
|
|
52
|
+
return json.version
|
|
53
|
+
}
|
|
54
|
+
|
|
38
55
|
apply plugin: "com.android.library"
|
|
39
56
|
apply plugin: "kotlin-android"
|
|
40
57
|
|
|
@@ -67,6 +84,18 @@ android {
|
|
|
67
84
|
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
68
85
|
}
|
|
69
86
|
|
|
87
|
+
sourceSets {
|
|
88
|
+
main {
|
|
89
|
+
java.srcDirs = ["src/main/java"]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (findReactNativeVersion().tokenize('.')[1].toInteger() >= 78) {
|
|
94
|
+
sourceSets.main.java.srcDirs += "src/latest/java"
|
|
95
|
+
} else {
|
|
96
|
+
sourceSets.main.java.srcDirs += "src/rn77/java"
|
|
97
|
+
}
|
|
98
|
+
|
|
70
99
|
buildFeatures {
|
|
71
100
|
buildConfig true
|
|
72
101
|
}
|
|
@@ -114,7 +143,7 @@ dependencies {
|
|
|
114
143
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
115
144
|
|
|
116
145
|
// Airborne SDK dependency
|
|
117
|
-
api "in.juspay:airborne:
|
|
146
|
+
api "in.juspay:airborne:2.2.5-xota.03"
|
|
118
147
|
}
|
|
119
148
|
|
|
120
149
|
react {
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
package `in`.juspay.airborneplugin
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import com.facebook.react.JSEngineResolutionAlgorithm
|
|
6
|
+
import com.facebook.react.ReactNativeHost
|
|
7
|
+
import com.facebook.react.ReactPackage
|
|
8
|
+
import com.facebook.react.ReactPackageTurboModuleManagerDelegate
|
|
9
|
+
import com.facebook.react.bridge.JSBundleLoader
|
|
10
|
+
import com.facebook.react.common.annotations.UnstableReactNativeAPI
|
|
11
|
+
import com.facebook.react.defaults.DefaultTurboModuleManagerDelegate
|
|
12
|
+
import com.facebook.react.runtime.BindingsInstaller
|
|
13
|
+
import com.facebook.react.runtime.JSCInstance
|
|
14
|
+
import com.facebook.react.runtime.JSRuntimeFactory
|
|
15
|
+
import com.facebook.react.runtime.ReactHostDelegate
|
|
16
|
+
import com.facebook.react.runtime.hermes.HermesInstance
|
|
17
|
+
import java.lang.ref.WeakReference
|
|
18
|
+
|
|
19
|
+
@OptIn(UnstableReactNativeAPI::class)
|
|
20
|
+
class AirborneReactHostDelegate(
|
|
21
|
+
private val weakContext: WeakReference<Context>,
|
|
22
|
+
private val reactNativeHostWrapper: ReactNativeHost,
|
|
23
|
+
override val bindingsInstaller: BindingsInstaller? = null,
|
|
24
|
+
override val turboModuleManagerDelegateBuilder: ReactPackageTurboModuleManagerDelegate.Builder =
|
|
25
|
+
DefaultTurboModuleManagerDelegate.Builder()
|
|
26
|
+
) : ReactHostDelegate {
|
|
27
|
+
|
|
28
|
+
override val jsBundleLoader: JSBundleLoader
|
|
29
|
+
get() {
|
|
30
|
+
val bundleName =
|
|
31
|
+
(reactNativeHostWrapper as AirborneReactNativeHost).jsBundleFile
|
|
32
|
+
bundleName?.let {
|
|
33
|
+
return if (bundleName.startsWith("assets://")) {
|
|
34
|
+
JSBundleLoader.createAssetLoader(
|
|
35
|
+
weakContext.get(),
|
|
36
|
+
bundleName,
|
|
37
|
+
false
|
|
38
|
+
)
|
|
39
|
+
} else {
|
|
40
|
+
JSBundleLoader.createFileLoader(bundleName)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return JSBundleLoader.createAssetLoader(
|
|
44
|
+
weakContext.get(),
|
|
45
|
+
"assets://index.android.bundle",
|
|
46
|
+
false
|
|
47
|
+
)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
override val jsMainModulePath: String
|
|
51
|
+
get() = (reactNativeHostWrapper as AirborneReactNativeHost).jsMainModuleName
|
|
52
|
+
|
|
53
|
+
override val jsRuntimeFactory: JSRuntimeFactory
|
|
54
|
+
get() = if ((reactNativeHostWrapper as AirborneReactNativeHost).jsEngineResolutionAlgorithm == JSEngineResolutionAlgorithm.HERMES) {
|
|
55
|
+
HermesInstance()
|
|
56
|
+
} else {
|
|
57
|
+
JSCInstance()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
override val reactPackages: List<ReactPackage>
|
|
61
|
+
get() = (reactNativeHostWrapper as AirborneReactNativeHost).packages
|
|
62
|
+
|
|
63
|
+
override fun handleInstanceException(error: Exception) {
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -5,11 +5,11 @@ import androidx.annotation.Keep
|
|
|
5
5
|
import `in`.juspay.airborne.HyperOTAServices
|
|
6
6
|
import `in`.juspay.airborne.LazyDownloadCallback
|
|
7
7
|
import `in`.juspay.airborne.TrackerCallback
|
|
8
|
-
import `in`.juspay.
|
|
8
|
+
import `in`.juspay.hyperutil.constants.LogLevel
|
|
9
9
|
import org.json.JSONObject
|
|
10
10
|
|
|
11
11
|
@Keep
|
|
12
|
-
class Airborne
|
|
12
|
+
class Airborne(
|
|
13
13
|
context: Context,
|
|
14
14
|
releaseConfigUrl: String,
|
|
15
15
|
private val airborneInterface: AirborneInterface
|
|
@@ -50,23 +50,27 @@ class Airborne (
|
|
|
50
50
|
"",
|
|
51
51
|
releaseConfigUrl,
|
|
52
52
|
trackerCallback,
|
|
53
|
-
|
|
53
|
+
this::bootComplete
|
|
54
54
|
)
|
|
55
55
|
|
|
56
56
|
private val applicationManager = hyperOTAServices.createApplicationManager(airborneInterface.getDimensions())
|
|
57
57
|
|
|
58
58
|
init {
|
|
59
|
-
|
|
59
|
+
airborneObjectMap.put(airborneInterface.getNamespace(), this)
|
|
60
60
|
applicationManager.loadApplication(airborneInterface.getNamespace(), airborneInterface.getLazyDownloadCallback())
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
private fun bootComplete(filePath: String) {
|
|
64
|
+
airborneInterface.startApp(filePath.ifEmpty { "assets://${applicationManager.getBundledIndexPath().ifEmpty { "index.android.bundle" }}" })
|
|
65
|
+
}
|
|
66
|
+
|
|
63
67
|
/**
|
|
64
68
|
* @return The path of the index bundle, or asset path fallback if empty.
|
|
65
69
|
*/
|
|
66
70
|
@Keep
|
|
67
71
|
fun getBundlePath(): String {
|
|
68
72
|
val filePath = applicationManager.getIndexBundlePath()
|
|
69
|
-
return filePath.ifEmpty { "assets://${
|
|
73
|
+
return filePath.ifEmpty { "assets://${applicationManager.getBundledIndexPath().ifEmpty { "index.android.bundle" }}" }
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
/**
|
|
@@ -88,16 +92,46 @@ class Airborne (
|
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
companion object {
|
|
91
|
-
private var initializer: (() -> Airborne)? = null
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Lazily initialized singleton instance.
|
|
95
|
-
*/
|
|
96
|
-
@JvmStatic
|
|
97
|
-
val instance: Airborne by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
|
|
98
|
-
initializer?.invoke()
|
|
99
|
-
?: throw IllegalStateException("
|
|
100
|
-
}
|
|
95
|
+
// private var initializer: (() -> Airborne)? = null
|
|
96
|
+
//
|
|
97
|
+
// /**
|
|
98
|
+
// * Lazily initialized singleton instance.
|
|
99
|
+
// */
|
|
100
|
+
// @JvmStatic
|
|
101
|
+
// val instance: Airborne by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
|
|
102
|
+
// initializer?.invoke()
|
|
103
|
+
// ?: throw IllegalStateException("AirborneReact initializer not set. Call init() first.")
|
|
104
|
+
// }
|
|
105
|
+
//
|
|
106
|
+
// /**
|
|
107
|
+
// * Initializes the AirborneReact singleton.
|
|
108
|
+
// */
|
|
109
|
+
// @JvmStatic
|
|
110
|
+
// fun init(
|
|
111
|
+
// context: Context,
|
|
112
|
+
// appId: String,
|
|
113
|
+
// indexFileName: String,
|
|
114
|
+
// appVersion: String,
|
|
115
|
+
// releaseConfigTemplateUrl: String,
|
|
116
|
+
// headers: Map<String, String>? = null,
|
|
117
|
+
// lazyDownloadCallback: LazyDownloadCallback? = null,
|
|
118
|
+
// trackerCallback: TrackerCallback? = null
|
|
119
|
+
// ) {
|
|
120
|
+
// initializer = {
|
|
121
|
+
// Airborne(
|
|
122
|
+
// context,
|
|
123
|
+
// appId,
|
|
124
|
+
// indexFileName,
|
|
125
|
+
// appVersion,
|
|
126
|
+
// releaseConfigTemplateUrl,
|
|
127
|
+
// headers,
|
|
128
|
+
// lazyDownloadCallback ?: defaultLazyCallback,
|
|
129
|
+
// trackerCallback ?: defaultTrackerCallback
|
|
130
|
+
// )
|
|
131
|
+
// }
|
|
132
|
+
// }
|
|
133
|
+
|
|
134
|
+
public val airborneObjectMap: MutableMap<String, Airborne> = mutableMapOf()
|
|
101
135
|
|
|
102
136
|
/**
|
|
103
137
|
* Default LazyDownloadCallback implementation.
|
|
@@ -106,18 +140,18 @@ class Airborne (
|
|
|
106
140
|
override fun fileInstalled(filePath: String, success: Boolean) {
|
|
107
141
|
// Default implementation: log the file installation status
|
|
108
142
|
if (success) {
|
|
109
|
-
println("
|
|
143
|
+
println("AirborneReact: File installed successfully: $filePath")
|
|
110
144
|
} else {
|
|
111
|
-
println("
|
|
145
|
+
println("AirborneReact: File installation failed: $filePath")
|
|
112
146
|
}
|
|
113
147
|
}
|
|
114
148
|
|
|
115
149
|
override fun lazySplitsInstalled(success: Boolean) {
|
|
116
150
|
// Default implementation: log the lazy splits installation status
|
|
117
151
|
if (success) {
|
|
118
|
-
println("
|
|
152
|
+
println("AirborneReact: Lazy splits installed successfully")
|
|
119
153
|
} else {
|
|
120
|
-
println("
|
|
154
|
+
println("AirborneReact: Lazy splits installation failed")
|
|
121
155
|
}
|
|
122
156
|
}
|
|
123
157
|
}
|
|
@@ -6,18 +6,14 @@ import org.json.JSONObject
|
|
|
6
6
|
|
|
7
7
|
abstract class AirborneInterface {
|
|
8
8
|
open fun getNamespace(): String {
|
|
9
|
-
return "
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
open fun getIndexBundlePath(): String {
|
|
13
|
-
return "index.android.bundle"
|
|
9
|
+
return "airborne-example"
|
|
14
10
|
}
|
|
15
11
|
|
|
16
12
|
open fun getDimensions(): HashMap<String, String> {
|
|
17
13
|
return hashMapOf()
|
|
18
14
|
}
|
|
19
15
|
|
|
20
|
-
open fun
|
|
16
|
+
open fun startApp(indexPath: String) {
|
|
21
17
|
}
|
|
22
18
|
|
|
23
19
|
open fun onEvent(level: String, label: String, key: String, value: JSONObject, category: String, subCategory: String) {
|
|
@@ -17,18 +17,18 @@ class AirborneModule(reactContext: ReactApplicationContext) :
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
@ReactMethod
|
|
20
|
-
fun readReleaseConfig(promise: Promise) {
|
|
21
|
-
implementation.readReleaseConfig(promise)
|
|
20
|
+
fun readReleaseConfig(namespace: String, promise: Promise) {
|
|
21
|
+
implementation.readReleaseConfig(namespace, promise)
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
@ReactMethod
|
|
25
|
-
fun getFileContent(filePath: String, promise: Promise) {
|
|
26
|
-
implementation.getFileContent(filePath, promise)
|
|
25
|
+
fun getFileContent(namespace: String, filePath: String, promise: Promise) {
|
|
26
|
+
implementation.getFileContent(namespace, filePath, promise)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
@ReactMethod
|
|
30
|
-
fun getBundlePath(promise: Promise) {
|
|
31
|
-
implementation.getBundlePath(promise)
|
|
30
|
+
fun getBundlePath(namespace: String, promise: Promise) {
|
|
31
|
+
implementation.getBundlePath(namespace, promise)
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
companion object {
|
|
@@ -42,27 +42,27 @@ class AirborneModuleImpl(private val reactContext: ReactApplicationContext) {
|
|
|
42
42
|
// }
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
fun readReleaseConfig(promise: Promise) {
|
|
45
|
+
fun readReleaseConfig(namespace: String, promise: Promise) {
|
|
46
46
|
try {
|
|
47
|
-
val config = Airborne.
|
|
47
|
+
val config = Airborne.airborneObjectMap[namespace]?.getReleaseConfig()
|
|
48
48
|
promise.resolve(config)
|
|
49
49
|
} catch (e: Exception) {
|
|
50
50
|
promise.reject("AIRBORNE_ERROR", "Failed to read release config: ${e.message}", e)
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
fun getFileContent(filePath: String, promise: Promise) {
|
|
54
|
+
fun getFileContent(namespace: String, filePath: String, promise: Promise) {
|
|
55
55
|
try {
|
|
56
|
-
val content = Airborne.
|
|
56
|
+
val content = Airborne.airborneObjectMap[namespace]?.getFileContent(filePath)
|
|
57
57
|
promise.resolve(content)
|
|
58
58
|
} catch (e: Exception) {
|
|
59
59
|
promise.reject("AIRBORNE_ERROR", "Failed to read file content: ${e.message}", e)
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
fun getBundlePath(promise: Promise) {
|
|
63
|
+
fun getBundlePath(namespace: String, promise: Promise) {
|
|
64
64
|
try {
|
|
65
|
-
val path = Airborne.
|
|
65
|
+
val path = Airborne.airborneObjectMap[namespace]?.getBundlePath()
|
|
66
66
|
promise.resolve(path)
|
|
67
67
|
} catch (e: Exception) {
|
|
68
68
|
promise.reject("AIRBORNE_ERROR", "Failed to get bundle path: ${e.message}", e)
|
|
@@ -8,32 +8,32 @@ import com.facebook.react.module.model.ReactModuleInfoProvider
|
|
|
8
8
|
import java.util.HashMap
|
|
9
9
|
|
|
10
10
|
class AirbornePackage : TurboReactPackage() {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
11
|
+
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
|
|
12
|
+
return if (name == AirborneModule.NAME) {
|
|
13
|
+
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
|
14
|
+
AirborneTurboModule(reactContext)
|
|
15
|
+
} else {
|
|
16
|
+
AirborneModule(reactContext)
|
|
17
|
+
}
|
|
18
|
+
} else {
|
|
19
|
+
null
|
|
20
|
+
}
|
|
20
21
|
}
|
|
21
|
-
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
23
|
+
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
|
|
24
|
+
return ReactModuleInfoProvider {
|
|
25
|
+
val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
|
|
26
|
+
val isTurboModule: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
|
|
27
|
+
moduleInfos[AirborneModule.NAME] = ReactModuleInfo(
|
|
28
|
+
AirborneModule.NAME,
|
|
29
|
+
if (isTurboModule) AirborneTurboModule::class.java.name else AirborneModule::class.java.name,
|
|
30
|
+
false, // canOverrideExistingModule
|
|
31
|
+
false, // needsEagerInit
|
|
32
|
+
true, // hasConstants
|
|
33
|
+
false, // isCxxModule
|
|
34
|
+
isTurboModule // isTurboModule
|
|
35
|
+
)
|
|
36
|
+
moduleInfos
|
|
37
|
+
}
|
|
37
38
|
}
|
|
38
|
-
}
|
|
39
39
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
package `in`.juspay.airborneplugin
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import com.facebook.react.ReactActivity
|
|
5
|
+
import com.facebook.react.defaults.DefaultReactActivityDelegate
|
|
6
|
+
import kotlinx.coroutines.CoroutineScope
|
|
7
|
+
import kotlinx.coroutines.Dispatchers
|
|
8
|
+
import kotlinx.coroutines.launch
|
|
9
|
+
|
|
10
|
+
class AirborneReactActivityDelegate(
|
|
11
|
+
activity: ReactActivity,
|
|
12
|
+
mainComponentName: String,
|
|
13
|
+
fabricEnabled: Boolean
|
|
14
|
+
) : DefaultReactActivityDelegate(activity, mainComponentName, fabricEnabled) {
|
|
15
|
+
|
|
16
|
+
private var appState = AppState.BEFORE_APPLOAD;
|
|
17
|
+
override fun loadApp(appKey: String) {
|
|
18
|
+
if (reactNativeHost is AirborneReactNativeHost) {
|
|
19
|
+
CoroutineScope(Dispatchers.Default).launch {
|
|
20
|
+
|
|
21
|
+
// The wait for bundle update
|
|
22
|
+
(reactNativeHost as AirborneReactNativeHost).jsBundleFile
|
|
23
|
+
|
|
24
|
+
CoroutineScope(Dispatchers.Main).launch {
|
|
25
|
+
callLoadApp(appKey)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
} else {
|
|
29
|
+
callLoadApp(appKey)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private fun callLoadApp(appKey: String) {
|
|
34
|
+
super.loadApp(appKey)
|
|
35
|
+
appState = AppState.APP_LOADED
|
|
36
|
+
onResume()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
override fun onPause() {
|
|
40
|
+
try {
|
|
41
|
+
if (appState == AppState.ONRESUME_CALLED) {
|
|
42
|
+
super.onPause()
|
|
43
|
+
} else {
|
|
44
|
+
Log.d(AirborneReactNativeHost.toString(), "skipping onPause as onResume is not yet called")
|
|
45
|
+
}
|
|
46
|
+
} catch (e: Exception) {
|
|
47
|
+
Log.e( AirborneReactNativeHost.toString(), "Exception in onPause: ${e.message}")
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
override fun onResume() {
|
|
52
|
+
try {
|
|
53
|
+
if (appState == AppState.APP_LOADED) {
|
|
54
|
+
super.onResume()
|
|
55
|
+
appState = AppState.ONRESUME_CALLED
|
|
56
|
+
} else {
|
|
57
|
+
Log.d(AirborneReactNativeHost.toString(), "skipping onResume as app is not yet loaded")
|
|
58
|
+
}
|
|
59
|
+
} catch (e: Exception) {
|
|
60
|
+
Log.e(AirborneReactNativeHost.toString(), "Exception in onResume: ${e.message}")
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
override fun onDestroy() {
|
|
65
|
+
try {
|
|
66
|
+
if (appState == AppState.ONRESUME_CALLED) {
|
|
67
|
+
super.onDestroy()
|
|
68
|
+
} else {
|
|
69
|
+
Log.d(AirborneReactNativeHost.toString(), "skipping onDestroy as onResume is not yet called")
|
|
70
|
+
}
|
|
71
|
+
} catch (e: Exception) {
|
|
72
|
+
Log.e(AirborneReactNativeHost.toString(), "Exception in onDestroy: ${e.message}")
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
enum class AppState {
|
|
77
|
+
BEFORE_APPLOAD,
|
|
78
|
+
APP_LOADED,
|
|
79
|
+
ONRESUME_CALLED
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
package `in`.juspay.airborneplugin
|
|
2
|
+
|
|
3
|
+
import android.app.Application
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.util.Log
|
|
6
|
+
import com.facebook.react.JSEngineResolutionAlgorithm
|
|
7
|
+
import com.facebook.react.ReactHost
|
|
8
|
+
import com.facebook.react.ReactNativeHost
|
|
9
|
+
import com.facebook.react.ReactPackage
|
|
10
|
+
import com.facebook.react.bridge.JSExceptionHandler
|
|
11
|
+
import com.facebook.react.common.annotations.UnstableReactNativeAPI
|
|
12
|
+
import com.facebook.react.defaults.DefaultComponentsRegistry
|
|
13
|
+
import com.facebook.react.defaults.DefaultReactNativeHost
|
|
14
|
+
import com.facebook.react.fabric.ComponentFactory
|
|
15
|
+
import com.facebook.react.runtime.ReactHostImpl
|
|
16
|
+
import java.lang.ref.WeakReference
|
|
17
|
+
|
|
18
|
+
abstract class AirborneReactNativeHost(application: Application) :
|
|
19
|
+
DefaultReactNativeHost(application) {
|
|
20
|
+
|
|
21
|
+
public override fun getPackages(): List<ReactPackage> {
|
|
22
|
+
return this.packages
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
public override fun getJSBundleFile(): String? {
|
|
26
|
+
return super.getJSBundleFile()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public override fun getJSEngineResolutionAlgorithm(): JSEngineResolutionAlgorithm? {
|
|
30
|
+
return super.getJSEngineResolutionAlgorithm()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public override fun getJSMainModuleName(): String {
|
|
34
|
+
return super.getJSMainModuleName()
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
companion object {
|
|
38
|
+
@OptIn(UnstableReactNativeAPI::class)
|
|
39
|
+
fun getReactHost(context: Context, reactNativeHost: ReactNativeHost): ReactHost {
|
|
40
|
+
val reactHostDelegate =
|
|
41
|
+
AirborneReactHostDelegate(WeakReference(context), reactNativeHost)
|
|
42
|
+
val componentFactory = ComponentFactory()
|
|
43
|
+
DefaultComponentsRegistry.register(componentFactory)
|
|
44
|
+
val reactHostImpl =
|
|
45
|
+
ReactHostImpl(context, reactHostDelegate, componentFactory, true, true)
|
|
46
|
+
return reactHostImpl
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -14,19 +14,19 @@ class AirborneTurboModule(reactContext: ReactApplicationContext) :
|
|
|
14
14
|
return NAME
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
override fun readReleaseConfig(promise: Promise) {
|
|
18
|
-
implementation.readReleaseConfig(promise)
|
|
17
|
+
override fun readReleaseConfig(nameSpace: String, promise: Promise) {
|
|
18
|
+
implementation.readReleaseConfig(nameSpace, promise)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
override fun getFileContent(filePath: String, promise: Promise) {
|
|
22
|
-
implementation.getFileContent(filePath, promise)
|
|
21
|
+
override fun getFileContent(nameSpace: String, filePath: String, promise: Promise) {
|
|
22
|
+
implementation.getFileContent(nameSpace, filePath, promise)
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
override fun getBundlePath(promise: Promise) {
|
|
26
|
-
implementation.getBundlePath(promise)
|
|
25
|
+
override fun getBundlePath(nameSpace: String, promise: Promise) {
|
|
26
|
+
implementation.getBundlePath(nameSpace, promise)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
companion object {
|
|
30
|
-
const val NAME = "
|
|
30
|
+
const val NAME = "AirborneReact"
|
|
31
31
|
}
|
|
32
32
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?><resources xmlns:tools="http://schemas.android.com/tools" tools:keep="@string/airborne_react_version,@string/airborne_react_build_version" />
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
package `in`.juspay.airborneplugin
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.util.Log
|
|
5
|
+
import com.facebook.react.JSEngineResolutionAlgorithm
|
|
6
|
+
import com.facebook.react.ReactNativeHost
|
|
7
|
+
import com.facebook.react.ReactPackage
|
|
8
|
+
import com.facebook.react.ReactPackageTurboModuleManagerDelegate
|
|
9
|
+
import com.facebook.react.bridge.JSBundleLoader
|
|
10
|
+
import com.facebook.react.common.annotations.UnstableReactNativeAPI
|
|
11
|
+
import com.facebook.react.defaults.DefaultTurboModuleManagerDelegate
|
|
12
|
+
import com.facebook.react.fabric.ReactNativeConfig
|
|
13
|
+
import com.facebook.react.runtime.BindingsInstaller
|
|
14
|
+
import com.facebook.react.runtime.JSCInstance
|
|
15
|
+
import com.facebook.react.runtime.JSRuntimeFactory
|
|
16
|
+
import com.facebook.react.runtime.ReactHostDelegate
|
|
17
|
+
import com.facebook.react.runtime.hermes.HermesInstance
|
|
18
|
+
import java.lang.ref.WeakReference
|
|
19
|
+
|
|
20
|
+
@OptIn(UnstableReactNativeAPI::class)
|
|
21
|
+
class AirborneReactHostDelegate(
|
|
22
|
+
private val weakContext: WeakReference<Context>,
|
|
23
|
+
private val reactNativeHostWrapper: ReactNativeHost,
|
|
24
|
+
override val bindingsInstaller: BindingsInstaller? = null,
|
|
25
|
+
private val reactNativeConfig: ReactNativeConfig = ReactNativeConfig.DEFAULT_CONFIG,
|
|
26
|
+
override val turboModuleManagerDelegateBuilder: ReactPackageTurboModuleManagerDelegate.Builder =
|
|
27
|
+
DefaultTurboModuleManagerDelegate.Builder()
|
|
28
|
+
) : ReactHostDelegate {
|
|
29
|
+
|
|
30
|
+
override val jsBundleLoader: JSBundleLoader
|
|
31
|
+
get() {
|
|
32
|
+
val bundleName =
|
|
33
|
+
(reactNativeHostWrapper as AirborneReactNativeHost).jsBundleFile
|
|
34
|
+
bundleName?.let {
|
|
35
|
+
return if (bundleName.startsWith("assets://")) {
|
|
36
|
+
JSBundleLoader.createAssetLoader(
|
|
37
|
+
weakContext.get(),
|
|
38
|
+
bundleName,
|
|
39
|
+
false
|
|
40
|
+
)
|
|
41
|
+
} else {
|
|
42
|
+
JSBundleLoader.createFileLoader(bundleName)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return JSBundleLoader.createAssetLoader(
|
|
46
|
+
weakContext.get(),
|
|
47
|
+
"assets://index.android.bundle",
|
|
48
|
+
false
|
|
49
|
+
)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
override val jsMainModulePath: String
|
|
53
|
+
get() = (reactNativeHostWrapper as AirborneReactNativeHost).jsMainModuleName
|
|
54
|
+
|
|
55
|
+
override val jsRuntimeFactory: JSRuntimeFactory
|
|
56
|
+
get() = if ((reactNativeHostWrapper as AirborneReactNativeHost).jsEngineResolutionAlgorithm == JSEngineResolutionAlgorithm.HERMES) {
|
|
57
|
+
HermesInstance()
|
|
58
|
+
} else {
|
|
59
|
+
JSCInstance()
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
override val reactPackages: List<ReactPackage>
|
|
63
|
+
get() = (reactNativeHostWrapper as AirborneReactNativeHost).packages
|
|
64
|
+
|
|
65
|
+
override fun getReactNativeConfig(): ReactNativeConfig {
|
|
66
|
+
return reactNativeConfig
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
override fun handleInstanceException(error: Exception) {
|
|
70
|
+
}
|
|
71
|
+
}
|
package/ios/Airborne.h
CHANGED
|
@@ -1,25 +1,18 @@
|
|
|
1
|
-
#import
|
|
2
|
-
#
|
|
3
|
-
#import <AirborneSpec/AirborneSpec.h>
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
#import <Airborne/Airborne-Swift.h>
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
#else
|
|
7
|
-
#import <React/RCTBridgeModule.h>
|
|
4
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
8
5
|
|
|
6
|
+
@interface Airborne : NSObject
|
|
9
7
|
|
|
10
|
-
|
|
11
|
-
#endif
|
|
8
|
+
+ (instancetype)sharedInstanceWithNamespace:(NSString *)aNamespace;
|
|
12
9
|
|
|
13
|
-
|
|
10
|
+
- (instancetype)initWithReleaseConfigURL:(NSString *)releaseConfigURL delegate:(id<AirborneDelegate>)delegate;
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
+ (void)initializeAirborneWithReleaseConfigUrl:(NSString *)releaseConfigUrl
|
|
19
|
-
delegate:delegate;
|
|
20
|
-
|
|
21
|
-
+ (void)initializeAirborneWithReleaseConfigUrl:(NSString *) releaseConfigUrl
|
|
22
|
-
inNamespace:(NSString *) ns
|
|
23
|
-
delegate:(id<AirborneReactDelegate>) delegate;
|
|
12
|
+
- (NSString *)getBundlePath;
|
|
13
|
+
- (NSString *)getFileContent:(NSString *)filePath;
|
|
14
|
+
- (NSString *)getReleaseConfig;
|
|
24
15
|
|
|
25
16
|
@end
|
|
17
|
+
|
|
18
|
+
NS_ASSUME_NONNULL_END
|
package/ios/Airborne.m
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
|
|
2
|
+
#import "Airborne.h"
|
|
3
|
+
|
|
4
|
+
@interface Airborne() <AirborneDelegate>
|
|
5
|
+
|
|
6
|
+
@property (nonatomic, strong) NSString* namespace;
|
|
7
|
+
@property (nonatomic, strong) AirborneServices* airborne;
|
|
8
|
+
@property (nonatomic, weak) id <AirborneDelegate> delegate;
|
|
9
|
+
|
|
10
|
+
@end
|
|
11
|
+
|
|
12
|
+
@implementation Airborne
|
|
13
|
+
|
|
14
|
+
+ (instancetype)sharedInstanceWithNamespace:(NSString *)aNamespace {
|
|
15
|
+
static NSMutableDictionary<NSString *, Airborne *> *instances = nil;
|
|
16
|
+
static dispatch_queue_t syncQueue;
|
|
17
|
+
static dispatch_once_t onceToken;
|
|
18
|
+
|
|
19
|
+
// Initialize dictionary and queue once
|
|
20
|
+
dispatch_once(&onceToken, ^{
|
|
21
|
+
instances = [NSMutableDictionary dictionary];
|
|
22
|
+
syncQueue = dispatch_queue_create("in.juspay.Airborne.singleton", DISPATCH_QUEUE_CONCURRENT);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
__block Airborne *instance = nil;
|
|
26
|
+
|
|
27
|
+
// Read existing instance (concurrent)
|
|
28
|
+
dispatch_sync(syncQueue, ^{
|
|
29
|
+
instance = instances[aNamespace];
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
if (instance == nil) {
|
|
33
|
+
// Write new instance (barrier to prevent concurrent writes)
|
|
34
|
+
dispatch_barrier_sync(syncQueue, ^{
|
|
35
|
+
if (!instances[aNamespace]) {
|
|
36
|
+
instances[aNamespace] = [[self alloc] initWithNamespace:aNamespace];
|
|
37
|
+
}
|
|
38
|
+
instance = instances[aNamespace];
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return instance;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
- (instancetype)initWithNamespace:(NSString *)namespace {
|
|
46
|
+
self = [super init];
|
|
47
|
+
if (self) {
|
|
48
|
+
self.namespace = namespace;
|
|
49
|
+
}
|
|
50
|
+
return self;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
- (instancetype)initWithReleaseConfigURL:(NSString *)releaseConfigURL delegate:(id<AirborneDelegate>)delegate {
|
|
54
|
+
self = [super init];
|
|
55
|
+
if (self) {
|
|
56
|
+
self.airborne = [[AirborneServices alloc] initWithReleaseConfigURL:releaseConfigURL delegate:delegate ?: self];
|
|
57
|
+
}
|
|
58
|
+
return self;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
- (NSString *)getBundlePath {
|
|
62
|
+
return [self.airborne getIndexBundlePath].absoluteString;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
- (NSString *)getFileContent:(NSString *)filePath {
|
|
66
|
+
return [self.airborne getFileContentAtPath:filePath];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
- (NSString *)getReleaseConfig {
|
|
70
|
+
return [self.airborne getReleaseConfig];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#pragma mark - AirborneDelegate
|
|
74
|
+
|
|
75
|
+
- (NSString *)namespace {
|
|
76
|
+
return @"default";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@end
|
|
80
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#import "Airborne.h"
|
|
2
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
3
|
+
#import <AirborneSpec/AirborneSpec.h>
|
|
4
|
+
#import <Airborne/Airborne-Swift.h>
|
|
5
|
+
|
|
6
|
+
@interface AirborneReact : NSObject <NativeAirborneSpec>
|
|
7
|
+
#else
|
|
8
|
+
#import <React/RCTBridgeModule.h>
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@interface AirborneReact : NSObject <RCTBridgeModule>
|
|
12
|
+
#endif
|
|
13
|
+
|
|
14
|
+
+ (void)initializeAirborneWithReleaseConfigUrl:(NSString *) releaseConfigUrl;
|
|
15
|
+
|
|
16
|
+
+ (void)initializeAirborneWithReleaseConfigUrl:(NSString *) releaseConfigUrl
|
|
17
|
+
inNamespace:(NSString *) ns;
|
|
18
|
+
|
|
19
|
+
+ (void)initializeAirborneWithReleaseConfigUrl:(NSString *)releaseConfigUrl
|
|
20
|
+
delegate:delegate;
|
|
21
|
+
|
|
22
|
+
+ (void)initializeAirborneWithReleaseConfigUrl:(NSString *) releaseConfigUrl
|
|
23
|
+
inNamespace:(NSString *) ns
|
|
24
|
+
delegate:(id<AirborneDelegate>) delegate;
|
|
25
|
+
|
|
26
|
+
@end
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
#import "AirborneReact.h"
|
|
1
2
|
#import "Airborne.h"
|
|
2
|
-
#import "AirborneiOS.h"
|
|
3
3
|
#import <React/RCTLog.h>
|
|
4
|
-
#import <Airborne/
|
|
4
|
+
#import <Airborne/AJPLoggerDelegate.h>
|
|
5
|
+
#import <Airborne/AJPApplicationManager.h>
|
|
5
6
|
#import <Airborne/Airborne-Swift.h>
|
|
6
7
|
|
|
7
|
-
@implementation
|
|
8
|
+
@implementation AirborneReact
|
|
8
9
|
|
|
9
10
|
RCT_EXPORT_MODULE(Airborne)
|
|
10
11
|
|
|
@@ -15,25 +16,22 @@ static NSString * const defaultNamespace = @"default";
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
+ (void)initializeAirborneWithReleaseConfigUrl:(NSString *)releaseConfigUrl inNamespace:ns {
|
|
18
|
-
|
|
19
|
-
[air loadWithReleaseConfig:releaseConfigUrl delegate:nil];
|
|
19
|
+
AJPApplicationManager* manager = [AJPApplicationManager getSharedInstanceWithWorkspace:ns delegate:nil logger:nil];
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
+ (void)initializeAirborneWithReleaseConfigUrl:(NSString *)releaseConfigUrl delegate:delegate {
|
|
23
|
-
|
|
24
|
-
[air loadWithReleaseConfig:releaseConfigUrl delegate:delegate];
|
|
23
|
+
AJPApplicationManager* manager = [AJPApplicationManager getSharedInstanceWithWorkspace:defaultNamespace delegate:delegate logger:nil];
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
+ (void)initializeAirborneWithReleaseConfigUrl:(NSString *)releaseConfigUrl inNamespace:ns delegate:delegate {
|
|
28
|
-
|
|
29
|
-
[air loadWithReleaseConfig:releaseConfigUrl delegate:delegate];
|
|
27
|
+
AJPApplicationManager* manager = [AJPApplicationManager getSharedInstanceWithWorkspace:ns delegate:delegate logger:nil];
|
|
30
28
|
}
|
|
31
29
|
|
|
32
30
|
#ifdef RCT_NEW_ARCH_ENABLED
|
|
33
31
|
- (void)readReleaseConfig:(RCTPromiseResolveBlock)resolve
|
|
34
32
|
reject:(RCTPromiseRejectBlock)reject {
|
|
35
33
|
@try {
|
|
36
|
-
NSString *config = [[
|
|
34
|
+
NSString *config = [[Airborne sharedInstanceWithNamespace:defaultNamespace] getReleaseConfig];
|
|
37
35
|
resolve(config);
|
|
38
36
|
} @catch (NSException *exception) {
|
|
39
37
|
reject(@"AIRBORNE_ERROR", exception.reason, nil);
|
|
@@ -44,7 +42,7 @@ static NSString * const defaultNamespace = @"default";
|
|
|
44
42
|
resolve:(RCTPromiseResolveBlock)resolve
|
|
45
43
|
reject:(RCTPromiseRejectBlock)reject {
|
|
46
44
|
@try {
|
|
47
|
-
NSString *content = [[
|
|
45
|
+
NSString *content = [[Airborne sharedInstanceWithNamespace:defaultNamespace] getFileContent:filePath];
|
|
48
46
|
resolve(content);
|
|
49
47
|
} @catch (NSException *exception) {
|
|
50
48
|
reject(@"AIRBORNE_ERROR", exception.reason, nil);
|
|
@@ -54,7 +52,7 @@ static NSString * const defaultNamespace = @"default";
|
|
|
54
52
|
- (void)getBundlePath:(RCTPromiseResolveBlock)resolve
|
|
55
53
|
reject:(RCTPromiseRejectBlock)reject {
|
|
56
54
|
@try {
|
|
57
|
-
NSString *bundlePath = [[
|
|
55
|
+
NSString *bundlePath = [[Airborne sharedInstanceWithNamespace:defaultNamespace] getBundlePath];
|
|
58
56
|
resolve(bundlePath);
|
|
59
57
|
} @catch (NSException *exception) {
|
|
60
58
|
reject(@"AIRBORNE_ERROR", exception.reason, nil);
|
package/lib/module/index.js
CHANGED
|
@@ -28,14 +28,14 @@ const Airborne = AirborneModule ? AirborneModule : new Proxy({}, {
|
|
|
28
28
|
throw new Error(LINKING_ERROR);
|
|
29
29
|
}
|
|
30
30
|
});
|
|
31
|
-
export function readReleaseConfig() {
|
|
32
|
-
return Airborne.readReleaseConfig();
|
|
31
|
+
export function readReleaseConfig(nameSpace) {
|
|
32
|
+
return Airborne.readReleaseConfig(nameSpace);
|
|
33
33
|
}
|
|
34
|
-
export function getFileContent(filePath) {
|
|
35
|
-
return Airborne.getFileContent(filePath);
|
|
34
|
+
export function getFileContent(nameSpace, filePath) {
|
|
35
|
+
return Airborne.getFileContent(nameSpace, filePath);
|
|
36
36
|
}
|
|
37
|
-
export function getBundlePath() {
|
|
38
|
-
return Airborne.getBundlePath();
|
|
37
|
+
export function getBundlePath(nameSpace) {
|
|
38
|
+
return Airborne.getBundlePath(nameSpace);
|
|
39
39
|
}
|
|
40
40
|
export default Airborne;
|
|
41
41
|
//# sourceMappingURL=index.js.map
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["NativeModules","Platform","LINKING_ERROR","select","ios","default","isTurboModuleEnabled","global","__turboModuleProxy","AirborneModule","require","Airborne","Proxy","get","Error","readReleaseConfig","getFileContent","filePath","getBundlePath"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,aAAa,EAAEC,QAAQ,QAAQ,cAAc;AAEtD,MAAMC,aAAa,GACjB,gFAAgF,GAChFD,QAAQ,CAACE,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,+BAA+B;;AAEjC;AACA,MAAMC,oBAAoB,GAAGC,MAAM,CAACC,kBAAkB,IAAI,IAAI;AAE9D,MAAMC,cAAc,GAAGH,oBAAoB,GACvCI,OAAO,CAAC,kBAAkB,CAAC,CAACL,OAAO,GACnCL,aAAa,CAACW,QAAQ;AAE1B,MAAMA,QAAQ,GAAGF,cAAc,GAC3BA,cAAc,GACd,IAAIG,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACZ,aAAa,CAAC;EAChC;AACF,CACF,CAAC;AAEL,OAAO,SAASa,iBAAiBA,
|
|
1
|
+
{"version":3,"names":["NativeModules","Platform","LINKING_ERROR","select","ios","default","isTurboModuleEnabled","global","__turboModuleProxy","AirborneModule","require","Airborne","Proxy","get","Error","readReleaseConfig","nameSpace","getFileContent","filePath","getBundlePath"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SAASA,aAAa,EAAEC,QAAQ,QAAQ,cAAc;AAEtD,MAAMC,aAAa,GACjB,gFAAgF,GAChFD,QAAQ,CAACE,MAAM,CAAC;EAAEC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAG,CAAC,CAAC,GACvE,sDAAsD,GACtD,+BAA+B;;AAEjC;AACA,MAAMC,oBAAoB,GAAGC,MAAM,CAACC,kBAAkB,IAAI,IAAI;AAE9D,MAAMC,cAAc,GAAGH,oBAAoB,GACvCI,OAAO,CAAC,kBAAkB,CAAC,CAACL,OAAO,GACnCL,aAAa,CAACW,QAAQ;AAE1B,MAAMA,QAAQ,GAAGF,cAAc,GAC3BA,cAAc,GACd,IAAIG,KAAK,CACP,CAAC,CAAC,EACF;EACEC,GAAGA,CAAA,EAAG;IACJ,MAAM,IAAIC,KAAK,CAACZ,aAAa,CAAC;EAChC;AACF,CACF,CAAC;AAEL,OAAO,SAASa,iBAAiBA,CAACC,SAAiB,EAAmB;EACpE,OAAOL,QAAQ,CAACI,iBAAiB,CAACC,SAAS,CAAC;AAC9C;AAEA,OAAO,SAASC,cAAcA,CAACD,SAAiB,EAAEE,QAAgB,EAAmB;EACnF,OAAOP,QAAQ,CAACM,cAAc,CAACD,SAAS,EAAEE,QAAQ,CAAC;AACrD;AAEA,OAAO,SAASC,aAAaA,CAACH,SAAiB,EAAmB;EAChE,OAAOL,QAAQ,CAACQ,aAAa,CAACH,SAAS,CAAC;AAC1C;AAEA,eAAeL,QAAQ","ignoreList":[]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { TurboModule } from 'react-native';
|
|
2
2
|
export interface Spec extends TurboModule {
|
|
3
|
-
readReleaseConfig(): Promise<string>;
|
|
4
|
-
getFileContent(filePath: string): Promise<string>;
|
|
5
|
-
getBundlePath(): Promise<string>;
|
|
3
|
+
readReleaseConfig(nameSpace: string): Promise<string>;
|
|
4
|
+
getFileContent(nameSpace: string, filePath: string): Promise<string>;
|
|
5
|
+
getBundlePath(nameSpace: string): Promise<string>;
|
|
6
6
|
}
|
|
7
7
|
declare const _default: Spec;
|
|
8
8
|
export default _default;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NativeAirborne.d.ts","sourceRoot":"","sources":["../../../src/NativeAirborne.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,iBAAiB,
|
|
1
|
+
{"version":3,"file":"NativeAirborne.d.ts","sourceRoot":"","sources":["../../../src/NativeAirborne.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAGhD,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACtD,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrE,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACnD;;AAED,wBAAkE"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
declare const Airborne: any;
|
|
2
|
-
export declare function readReleaseConfig(): Promise<string>;
|
|
3
|
-
export declare function getFileContent(filePath: string): Promise<string>;
|
|
4
|
-
export declare function getBundlePath(): Promise<string>;
|
|
2
|
+
export declare function readReleaseConfig(nameSpace: string): Promise<string>;
|
|
3
|
+
export declare function getFileContent(nameSpace: string, filePath: string): Promise<string>;
|
|
4
|
+
export declare function getBundlePath(nameSpace: string): Promise<string>;
|
|
5
5
|
export default Airborne;
|
|
6
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AA6BA,QAAA,MAAM,QAAQ,KAST,CAAC;AAEN,wBAAgB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AA6BA,QAAA,MAAM,QAAQ,KAST,CAAC;AAEN,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEpE;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEnF;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEhE;AAED,eAAe,QAAQ,CAAC"}
|
package/package.json
CHANGED
package/src/NativeAirborne.ts
CHANGED
|
@@ -16,9 +16,9 @@ import type { TurboModule } from 'react-native';
|
|
|
16
16
|
import { TurboModuleRegistry } from 'react-native';
|
|
17
17
|
|
|
18
18
|
export interface Spec extends TurboModule {
|
|
19
|
-
readReleaseConfig(): Promise<string>;
|
|
20
|
-
getFileContent(filePath: string): Promise<string>;
|
|
21
|
-
getBundlePath(): Promise<string>;
|
|
19
|
+
readReleaseConfig(nameSpace: string): Promise<string>;
|
|
20
|
+
getFileContent(nameSpace: string, filePath: string): Promise<string>;
|
|
21
|
+
getBundlePath(nameSpace: string): Promise<string>;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export default TurboModuleRegistry.getEnforcing<Spec>('Airborne');
|
package/src/index.tsx
CHANGED
|
@@ -38,16 +38,16 @@ const Airborne = AirborneModule
|
|
|
38
38
|
}
|
|
39
39
|
);
|
|
40
40
|
|
|
41
|
-
export function readReleaseConfig(): Promise<string> {
|
|
42
|
-
return Airborne.readReleaseConfig();
|
|
41
|
+
export function readReleaseConfig(nameSpace: string): Promise<string> {
|
|
42
|
+
return Airborne.readReleaseConfig(nameSpace);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
export function getFileContent(filePath: string): Promise<string> {
|
|
46
|
-
return Airborne.getFileContent(filePath);
|
|
45
|
+
export function getFileContent(nameSpace: string, filePath: string): Promise<string> {
|
|
46
|
+
return Airborne.getFileContent(nameSpace, filePath);
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
export function getBundlePath(): Promise<string> {
|
|
50
|
-
return Airborne.getBundlePath();
|
|
49
|
+
export function getBundlePath(nameSpace: string): Promise<string> {
|
|
50
|
+
return Airborne.getBundlePath(nameSpace);
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
export default Airborne;
|
package/ios/AirborneiOS.h
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
#import <Foundation/Foundation.h>
|
|
2
|
-
|
|
3
|
-
NS_ASSUME_NONNULL_BEGIN
|
|
4
|
-
|
|
5
|
-
@protocol AirborneReactDelegate <NSObject>
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Returns custom dimensions/metadata to include with release configuration requests.
|
|
9
|
-
*
|
|
10
|
-
* These dimensions are sent as HTTP headers when fetching the release configuration
|
|
11
|
-
* and can be used for:
|
|
12
|
-
* - A/B testing and feature flags
|
|
13
|
-
* - Device-specific configurations
|
|
14
|
-
* - User segmentation
|
|
15
|
-
* - Analytics and debugging context
|
|
16
|
-
*
|
|
17
|
-
* @return A dictionary of header field names and values to include in network requests.
|
|
18
|
-
* If not implemented, defaults to an empty dictionary.
|
|
19
|
-
*/
|
|
20
|
-
- (NSDictionary<NSString *, NSString *> *)getDimensions;
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Called when the OTA boot process has completed successfully.
|
|
24
|
-
*
|
|
25
|
-
* This callback indicates that the application is ready to load the packages & resources
|
|
26
|
-
*
|
|
27
|
-
* @note This method is called on a background queue. Dispatch UI updates
|
|
28
|
-
* to the main queue if needed.
|
|
29
|
-
* @note Boot completion occurs even if some downloads failed or timed out.
|
|
30
|
-
* Check the release configuration for actual status.
|
|
31
|
-
*/
|
|
32
|
-
- (void)onBootComplete:(NSString *) bundlePath;
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Called when significant events occur during the OTA update process.
|
|
36
|
-
*
|
|
37
|
-
* This callback provides detailed information about:
|
|
38
|
-
* - Download progress and completion
|
|
39
|
-
* - Error conditions and failures
|
|
40
|
-
* - Performance metrics and timing
|
|
41
|
-
* - State transitions in the update process
|
|
42
|
-
*
|
|
43
|
-
* @param level The severity level of the event ("info", "error", "warning")
|
|
44
|
-
* @param label A category label for the event (e.g., "ota_update")
|
|
45
|
-
* @param key A specific identifier for the event type
|
|
46
|
-
* @param value Additional structured data about the event
|
|
47
|
-
* @param category The broad category of the event (e.g., "lifecycle")
|
|
48
|
-
* @param subcategory The specific subcategory (e.g., "hyperota")
|
|
49
|
-
*
|
|
50
|
-
* @note Use this for logging, analytics, debugging, and monitoring OTA performance.
|
|
51
|
-
*/
|
|
52
|
-
- (void)onEventWithLevel:(NSString *)level
|
|
53
|
-
label:(NSString *)label
|
|
54
|
-
key:(NSString *)key
|
|
55
|
-
value:(NSDictionary<NSString *, id> *)value
|
|
56
|
-
category:(NSString *)category
|
|
57
|
-
subcategory:(NSString *)subcategory;
|
|
58
|
-
|
|
59
|
-
@end
|
|
60
|
-
|
|
61
|
-
typedef void (^HyperOTALazyDownloadCallback)(NSString *filePath, BOOL success);
|
|
62
|
-
typedef void (^HyperOTALazySplitsCallback)(BOOL success);
|
|
63
|
-
|
|
64
|
-
@interface AirborneiOS : NSObject
|
|
65
|
-
|
|
66
|
-
+ (instancetype)sharedInstanceWithNamespace:(NSString *)ns;
|
|
67
|
-
|
|
68
|
-
- (void) loadWithReleaseConfig:(NSString *) rcurl delegate:(id<AirborneReactDelegate>) delegate;
|
|
69
|
-
- (NSString *)getBundlePath;
|
|
70
|
-
- (NSString *)getFileContent:(NSString *)filePath;
|
|
71
|
-
- (NSString *)getReleaseConfig;
|
|
72
|
-
|
|
73
|
-
@end
|
|
74
|
-
|
|
75
|
-
NS_ASSUME_NONNULL_END
|
package/ios/AirborneiOS.m
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
#import "AirborneiOS.h"
|
|
2
|
-
#import "Airborne/Airborne.h"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
@interface AirborneLocalDelegate : NSObject<AirborneDelegate>
|
|
6
|
-
@property (nonatomic, weak) NSString* ns;
|
|
7
|
-
@property (nonatomic, weak) id<AirborneReactDelegate> delegate;
|
|
8
|
-
-(instancetype)initWithNamespace:(NSString*) ns
|
|
9
|
-
delegate:(id<AirborneReactDelegate>) delegate;
|
|
10
|
-
@end
|
|
11
|
-
|
|
12
|
-
@implementation AirborneLocalDelegate
|
|
13
|
-
|
|
14
|
-
-(instancetype)initWithNamespace:(NSString*) ns delegate:(id<AirborneReactDelegate>) del {
|
|
15
|
-
self = [super init];
|
|
16
|
-
if (self) {
|
|
17
|
-
_ns = ns;
|
|
18
|
-
_delegate = del;
|
|
19
|
-
}
|
|
20
|
-
return self;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
- (NSString *)namespace{
|
|
24
|
-
return self.ns;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
- (NSDictionary *)dimensions{
|
|
28
|
-
if(_delegate == nil) return @{};
|
|
29
|
-
return [_delegate getDimensions];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
- (void)onBootCompleteWithIndexBundlePath:(NSString *)indexBundlePath{
|
|
33
|
-
if (_delegate == nil) return;
|
|
34
|
-
[_delegate onBootComplete:indexBundlePath];
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
-(void)onEventWithLevel:(NSString *)level label:(NSString *)label key:(NSString *)key value:(NSDictionary<NSString *,id> *)value category:(NSString *)category subcategory:(NSString *)subcategory{
|
|
38
|
-
if (_delegate == nil) return;
|
|
39
|
-
[_delegate onEventWithLevel:level label:label key:key value:value category:category subcategory:subcategory];
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
@end
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
@interface AirborneiOS ()
|
|
46
|
-
@property (nonatomic, strong) NSString* ns;
|
|
47
|
-
@property (nonatomic, strong) AirborneServices* air;
|
|
48
|
-
@property (nonatomic, strong) id<AirborneDelegate> delegate;
|
|
49
|
-
@property (nonatomic, strong) AirborneLocalDelegate* delegateproxy;
|
|
50
|
-
|
|
51
|
-
@end
|
|
52
|
-
|
|
53
|
-
@implementation AirborneiOS
|
|
54
|
-
|
|
55
|
-
+ (instancetype)sharedInstanceWithNamespace:(NSString *)namespace{
|
|
56
|
-
static NSMutableDictionary<NSString *, AirborneiOS *> *instances = nil;
|
|
57
|
-
static dispatch_queue_t syncQueue;
|
|
58
|
-
static dispatch_once_t onceToken;
|
|
59
|
-
|
|
60
|
-
// Initialize dictionary and queue once
|
|
61
|
-
dispatch_once(&onceToken, ^{
|
|
62
|
-
instances = [NSMutableDictionary dictionary];
|
|
63
|
-
syncQueue = dispatch_queue_create("in.juspay.Airborne.singleton", DISPATCH_QUEUE_CONCURRENT);
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
__block AirborneiOS *instance = nil;
|
|
67
|
-
|
|
68
|
-
// Read existing instance (concurrent)
|
|
69
|
-
dispatch_sync(syncQueue, ^{
|
|
70
|
-
instance = instances[namespace];
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
if (instance == nil) {
|
|
74
|
-
// Write new instance (barrier to prevent concurrent writes)
|
|
75
|
-
dispatch_barrier_sync(syncQueue, ^{
|
|
76
|
-
if (!instances[namespace]) {
|
|
77
|
-
instances[namespace] = [[self alloc] initWithNamespace:namespace];
|
|
78
|
-
}
|
|
79
|
-
instance = instances[namespace];
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return instance;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
- (instancetype)initWithNamespace:(NSString *) ns{
|
|
88
|
-
self = [super init];
|
|
89
|
-
if (self) {
|
|
90
|
-
_ns = ns;
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
return self;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
- (void)loadWithReleaseConfig:(NSString *) rcurl delegate:(id<AirborneReactDelegate>) delegate{
|
|
97
|
-
_delegateproxy = [[AirborneLocalDelegate alloc] initWithNamespace: self.ns delegate:delegate];
|
|
98
|
-
_air = [[AirborneServices alloc] initWithReleaseConfigURL:rcurl delegate:_delegateproxy];
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
- (NSString *)getBundlePath {
|
|
102
|
-
return [_air getIndexBundlePath] ;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
- (NSString *)getFileContent:(NSString *)filePath {
|
|
106
|
-
return [_air getFileContentAtPath:filePath];
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
- (NSString *)getReleaseConfig {
|
|
110
|
-
return [_air getReleaseConfig];
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
@end
|
|
114
|
-
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../../ExampleOldArch/App.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAYnD,iBAAS,GAAG,IAAI,KAAK,CAAC,GAAG,CAAC,OAAO,CAuFhC;AA+ED,eAAe,GAAG,CAAC"}
|