airborne-react-native 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/AirborneReact.podspec +4 -1
  2. package/README.md +6 -6
  3. package/android/build.gradle +30 -1
  4. package/android/gradle.properties +1 -1
  5. package/android/src/latest/java/in/juspay/airborneplugin/AirborneReactHostDelegate.kt +65 -0
  6. package/android/src/main/AndroidManifest.xml +4 -2
  7. package/android/src/main/java/in/juspay/airborneplugin/Airborne.kt +53 -19
  8. package/android/src/main/java/in/juspay/airborneplugin/AirborneInterface.kt +2 -6
  9. package/android/src/main/java/in/juspay/airborneplugin/AirborneModule.kt +6 -6
  10. package/android/src/main/java/in/juspay/airborneplugin/AirborneModuleImpl.kt +6 -6
  11. package/android/src/main/java/in/juspay/airborneplugin/AirbornePackage.kt +25 -25
  12. package/android/src/main/java/in/juspay/airborneplugin/AirborneReactActivityDelegate.kt +81 -0
  13. package/android/src/main/java/in/juspay/airborneplugin/AirborneReactNativeHost.kt +49 -0
  14. package/android/src/main/java/in/juspay/airborneplugin/AirborneTurboModule.kt +7 -7
  15. package/android/src/main/res/raw/juspay_airborne_react_res.xml +1 -0
  16. package/android/src/rn77/java/in/juspay/airborneplugin/AirborneReactHostDelegate.kt +71 -0
  17. package/ios/Airborne.h +13 -5
  18. package/ios/Airborne.mm +27 -22
  19. package/ios/AirborneiOS.h +72 -9
  20. package/ios/AirborneiOS.m +90 -98
  21. package/lib/module/index.js +6 -6
  22. package/lib/module/index.js.map +1 -1
  23. package/lib/typescript/src/NativeAirborne.d.ts +3 -3
  24. package/lib/typescript/src/NativeAirborne.d.ts.map +1 -1
  25. package/lib/typescript/src/index.d.ts +3 -3
  26. package/lib/typescript/src/index.d.ts.map +1 -1
  27. package/package.json +4 -1
  28. package/src/NativeAirborne.ts +3 -3
  29. package/src/index.tsx +6 -6
  30. package/lib/typescript/ExampleOldArch/App.d.ts +0 -4
  31. package/lib/typescript/ExampleOldArch/App.d.ts.map +0 -1
@@ -33,7 +33,10 @@ Pod::Spec.new do |s|
33
33
  }
34
34
 
35
35
  s.source_files = "ios/**/*.{h,m,mm,cpp}"
36
- s.private_header_files = "ios/**/*.h"
36
+ s.public_header_files = "ios/**/*.h"
37
+ s.static_framework = false
38
+
39
+ s.dependency "Airborne", "0.0.4"
37
40
 
38
41
  install_modules_dependencies(s)
39
42
  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
@@ -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:0.0.1-xota.15"
146
+ api "in.juspay:airborne:2.2.5-xota.03"
118
147
  }
119
148
 
120
149
  react {
@@ -14,6 +14,6 @@
14
14
 
15
15
  Airborne_kotlinVersion=2.0.21
16
16
  Airborne_minSdkVersion=24
17
- Airborne_targetSdkVersion=34
17
+ Airborne_targetSdkVersion=35
18
18
  Airborne_compileSdkVersion=35
19
19
  Airborne_ndkVersion=27.1.12297006
@@ -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
+ }
@@ -1,2 +1,4 @@
1
- <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
- </manifest>
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <manifest>
3
+
4
+ </manifest>
@@ -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.airborne.constants.LogLevel
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
- airborneInterface::onBootComplete
53
+ this::bootComplete
54
54
  )
55
55
 
56
56
  private val applicationManager = hyperOTAServices.createApplicationManager(airborneInterface.getDimensions())
57
57
 
58
58
  init {
59
- initializer = { this }
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://${airborneInterface.getIndexBundlePath()}" }
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("Airborne initializer not set. Call init() first.")
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("HyperOTAReact: File installed successfully: $filePath")
143
+ println("AirborneReact: File installed successfully: $filePath")
110
144
  } else {
111
- println("HyperOTAReact: File installation failed: $filePath")
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("HyperOTAReact: Lazy splits installed successfully")
152
+ println("AirborneReact: Lazy splits installed successfully")
119
153
  } else {
120
- println("HyperOTAReact: Lazy splits installation failed")
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 "default"
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 onBootComplete(indexPath: String) {
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.instance.getReleaseConfig()
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.instance.getFileContent(filePath)
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.instance.getBundlePath()
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
- 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
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
- 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
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 = "HyperOta"
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,3 +1,4 @@
1
+ #import "AirborneiOS.h"
1
2
  #ifdef RCT_NEW_ARCH_ENABLED
2
3
  #import <AirborneSpec/AirborneSpec.h>
3
4
 
@@ -5,13 +6,20 @@
5
6
  #else
6
7
  #import <React/RCTBridgeModule.h>
7
8
 
9
+
8
10
  @interface Airborne : NSObject <RCTBridgeModule>
9
11
  #endif
10
12
 
11
- + (void)initializeAirborneWithAppId:(NSString *)appId
12
- indexFileName:(NSString *)indexFileName
13
- appVersion:(NSString *)appVersion
14
- releaseConfigTemplateUrl:(NSString *)releaseConfigTemplateUrl
15
- headers:(nullable NSDictionary<NSString *, NSString *> *)headers;
13
+ + (void)initializeAirborneWithReleaseConfigUrl:(NSString *) releaseConfigUrl;
14
+
15
+ + (void)initializeAirborneWithReleaseConfigUrl:(NSString *) releaseConfigUrl
16
+ inNamespace:(NSString *) ns;
17
+
18
+ + (void)initializeAirborneWithReleaseConfigUrl:(NSString *)releaseConfigUrl
19
+ delegate:delegate;
20
+
21
+ + (void)initializeAirborneWithReleaseConfigUrl:(NSString *) releaseConfigUrl
22
+ inNamespace:(NSString *) ns
23
+ delegate:(id<AirborneReactDelegate>) delegate;
16
24
 
17
25
  @end
package/ios/Airborne.mm CHANGED
@@ -1,34 +1,39 @@
1
1
  #import "Airborne.h"
2
2
  #import "AirborneiOS.h"
3
3
  #import <React/RCTLog.h>
4
+ #import <Airborne/Airborne.h>
5
+ #import <Airborne/Airborne-Swift.h>
4
6
 
5
7
  @implementation Airborne
6
8
 
7
9
  RCT_EXPORT_MODULE(Airborne)
8
10
 
9
- + (void)initializeAirborneWithAppId:(NSString *)appId
10
- indexFileName:(NSString *)indexFileName
11
- appVersion:(NSString *)appVersion
12
- releaseConfigTemplateUrl:(NSString *)releaseConfigTemplateUrl
13
- headers:(nullable NSDictionary<NSString *, NSString *> *)headers {
14
- [[AirborneiOS sharedInstance] initializeWithAppId:appId
15
- indexFileName:indexFileName
16
- appVersion:appVersion
17
- releaseConfigTemplateUrl:releaseConfigTemplateUrl
18
- headers:headers
19
- lazyDownloadCallback:^(NSString *filePath, BOOL success) {
20
- RCTLogInfo(@"Airborne: File %@ - %@", filePath, success ? @"installed" : @"failed");
21
- }
22
- lazySplitsCallback:^(BOOL success) {
23
- RCTLogInfo(@"Airborne: Lazy splits - %@", success ? @"installed" : @"failed");
24
- }];
11
+ static NSString * const defaultNamespace = @"default";
12
+
13
+ + (void)initializeAirborneWithReleaseConfigUrl:(NSString *)releaseConfigUrl {
14
+ [self initializeAirborneWithReleaseConfigUrl:releaseConfigUrl inNamespace:defaultNamespace];
15
+ }
16
+
17
+ + (void)initializeAirborneWithReleaseConfigUrl:(NSString *)releaseConfigUrl inNamespace:ns {
18
+ AirborneiOS* air = [AirborneiOS sharedInstanceWithNamespace:ns];
19
+ [air loadWithReleaseConfig:releaseConfigUrl delegate:nil];
20
+ }
21
+
22
+ + (void)initializeAirborneWithReleaseConfigUrl:(NSString *)releaseConfigUrl delegate:delegate {
23
+ AirborneiOS* air = [AirborneiOS sharedInstanceWithNamespace:defaultNamespace];
24
+ [air loadWithReleaseConfig:releaseConfigUrl delegate:delegate];
25
+ }
26
+
27
+ + (void)initializeAirborneWithReleaseConfigUrl:(NSString *)releaseConfigUrl inNamespace:ns delegate:delegate {
28
+ AirborneiOS* air = [AirborneiOS sharedInstanceWithNamespace:ns];
29
+ [air loadWithReleaseConfig:releaseConfigUrl delegate:delegate];
25
30
  }
26
31
 
27
32
  #ifdef RCT_NEW_ARCH_ENABLED
28
33
  - (void)readReleaseConfig:(RCTPromiseResolveBlock)resolve
29
34
  reject:(RCTPromiseRejectBlock)reject {
30
35
  @try {
31
- NSString *config = [[AirborneiOS sharedInstance] getReleaseConfig];
36
+ NSString *config = [[AirborneiOS sharedInstanceWithNamespace:defaultNamespace] getReleaseConfig];
32
37
  resolve(config);
33
38
  } @catch (NSException *exception) {
34
39
  reject(@"AIRBORNE_ERROR", exception.reason, nil);
@@ -39,7 +44,7 @@ RCT_EXPORT_MODULE(Airborne)
39
44
  resolve:(RCTPromiseResolveBlock)resolve
40
45
  reject:(RCTPromiseRejectBlock)reject {
41
46
  @try {
42
- NSString *content = [[AirborneiOS sharedInstance] getFileContent:filePath];
47
+ NSString *content = [[AirborneiOS sharedInstanceWithNamespace:defaultNamespace] getFileContent:filePath];
43
48
  resolve(content);
44
49
  } @catch (NSException *exception) {
45
50
  reject(@"AIRBORNE_ERROR", exception.reason, nil);
@@ -49,7 +54,7 @@ RCT_EXPORT_MODULE(Airborne)
49
54
  - (void)getBundlePath:(RCTPromiseResolveBlock)resolve
50
55
  reject:(RCTPromiseRejectBlock)reject {
51
56
  @try {
52
- NSString *bundlePath = [[AirborneiOS sharedInstance] getBundlePath];
57
+ NSString *bundlePath = [[AirborneiOS sharedInstanceWithNamespace:defaultNamespace] getBundlePath];
53
58
  resolve(bundlePath);
54
59
  } @catch (NSException *exception) {
55
60
  reject(@"AIRBORNE_ERROR", exception.reason, nil);
@@ -59,7 +64,7 @@ RCT_EXPORT_MODULE(Airborne)
59
64
  RCT_EXPORT_METHOD(readReleaseConfig:(RCTPromiseResolveBlock)resolve
60
65
  rejecter:(RCTPromiseRejectBlock)reject) {
61
66
  @try {
62
- NSString *config = [[AirborneiOS sharedInstance] getReleaseConfig];
67
+ NSString *config = [[AirborneiOS sharedInstanceWithNamespace:defaultNamespace] getReleaseConfig];
63
68
  resolve(config);
64
69
  } @catch (NSException *exception) {
65
70
  reject(@"AIRBORNE_ERROR", exception.reason, nil);
@@ -70,7 +75,7 @@ RCT_EXPORT_METHOD(getFileContent:(NSString *)filePath
70
75
  resolver:(RCTPromiseResolveBlock)resolve
71
76
  rejecter:(RCTPromiseRejectBlock)reject) {
72
77
  @try {
73
- NSString *content = [[AirborneiOS sharedInstance] getFileContent:filePath];
78
+ NSString *content = [[AirborneiOS sharedInstanceWithNamespace:defaultNamespace] getFileContent:filePath];
74
79
  resolve(content);
75
80
  } @catch (NSException *exception) {
76
81
  reject(@"AIRBORNE_ERROR", exception.reason, nil);
@@ -80,7 +85,7 @@ RCT_EXPORT_METHOD(getFileContent:(NSString *)filePath
80
85
  RCT_EXPORT_METHOD(getBundlePath:(RCTPromiseResolveBlock)resolve
81
86
  rejecter:(RCTPromiseRejectBlock)reject) {
82
87
  @try {
83
- NSString *bundlePath = [[AirborneiOS sharedInstance] getBundlePath];
88
+ NSString *bundlePath = [[AirborneiOS sharedInstanceWithNamespace:defaultNamespace] getBundlePath];
84
89
  resolve(bundlePath);
85
90
  } @catch (NSException *exception) {
86
91
  reject(@"AIRBORNE_ERROR", exception.reason, nil);
package/ios/AirborneiOS.h CHANGED
@@ -2,21 +2,84 @@
2
2
 
3
3
  NS_ASSUME_NONNULL_BEGIN
4
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
+ * Returns the namespace, an unique identifier of the app/sdk.
24
+ *
25
+ * This namespace is used to store the files in the internal storage.
26
+ * and also to read the bundled release config.
27
+ *
28
+ * @return the namespace, an unique identifier of the app/sdk.
29
+ * If not implemented, defaults to an default.
30
+ */
31
+ - (NSString *)getNamespace;
32
+
33
+
34
+ - (NSBundle *)getBundle;
35
+
36
+ /**
37
+ * Called when the OTA boot process has completed successfully.
38
+ *
39
+ * This callback indicates that the application is ready to load the packages & resources
40
+ *
41
+ * @note This method is called on a background queue. Dispatch UI updates
42
+ * to the main queue if needed.
43
+ * @note Boot completion occurs even if some downloads failed or timed out.
44
+ * Check the release configuration for actual status.
45
+ */
46
+ - (void)startApp:(NSString *) bundlePath;
47
+
48
+ /**
49
+ * Called when significant events occur during the OTA update process.
50
+ *
51
+ * This callback provides detailed information about:
52
+ * - Download progress and completion
53
+ * - Error conditions and failures
54
+ * - Performance metrics and timing
55
+ * - State transitions in the update process
56
+ *
57
+ * @param level The severity level of the event ("info", "error", "warning")
58
+ * @param label A category label for the event (e.g., "ota_update")
59
+ * @param key A specific identifier for the event type
60
+ * @param value Additional structured data about the event
61
+ * @param category The broad category of the event (e.g., "lifecycle")
62
+ * @param subcategory The specific subcategory (e.g., "hyperota")
63
+ *
64
+ * @note Use this for logging, analytics, debugging, and monitoring OTA performance.
65
+ */
66
+ - (void)onEventWithLevel:(NSString *)level
67
+ label:(NSString *)label
68
+ key:(NSString *)key
69
+ value:(NSDictionary<NSString *, id> *)value
70
+ category:(NSString *)category
71
+ subcategory:(NSString *)subcategory;
72
+
73
+ @end
74
+
5
75
  typedef void (^HyperOTALazyDownloadCallback)(NSString *filePath, BOOL success);
6
76
  typedef void (^HyperOTALazySplitsCallback)(BOOL success);
7
77
 
8
78
  @interface AirborneiOS : NSObject
9
79
 
10
- + (instancetype)sharedInstance;
11
-
12
- - (void)initializeWithAppId:(NSString *)appId
13
- indexFileName:(NSString *)indexFileName
14
- appVersion:(NSString *)appVersion
15
- releaseConfigTemplateUrl:(NSString *)releaseConfigTemplateUrl
16
- headers:(nullable NSDictionary<NSString *, NSString *> *)headers
17
- lazyDownloadCallback:(nullable HyperOTALazyDownloadCallback)lazyDownloadCallback
18
- lazySplitsCallback:(nullable HyperOTALazySplitsCallback)lazySplitsCallback;
80
+ + (instancetype)sharedInstanceWithNamespace:(NSString *)ns;
19
81
 
82
+ - (void) loadWithReleaseConfig:(NSString *) rcurl delegate:(id<AirborneReactDelegate>) delegate;
20
83
  - (NSString *)getBundlePath;
21
84
  - (NSString *)getFileContent:(NSString *)filePath;
22
85
  - (NSString *)getReleaseConfig;
package/ios/AirborneiOS.m CHANGED
@@ -1,128 +1,120 @@
1
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
+ if(_delegate == nil) return @"default";
25
+ return [_delegate getNamespace];
26
+ }
27
+
28
+ - (NSBundle *)bundle{
29
+ if(_delegate == nil) return NSBundle.mainBundle;
30
+ return [_delegate getBundle];
31
+ }
32
+
33
+ - (NSDictionary *)dimensions{
34
+ if(_delegate == nil) return @{};
35
+ return [_delegate getDimensions];
36
+ }
37
+
38
+ - (void)onBootCompleteWithIndexBundlePath:(NSString *)indexBundlePath{
39
+ if (_delegate == nil) return;
40
+ [_delegate startApp:indexBundlePath];
41
+ }
42
+
43
+ -(void)onEventWithLevel:(NSString *)level label:(NSString *)label key:(NSString *)key value:(NSDictionary<NSString *,id> *)value category:(NSString *)category subcategory:(NSString *)subcategory{
44
+ if (_delegate == nil) return;
45
+ [_delegate onEventWithLevel:level label:label key:key value:value category:category subcategory:subcategory];
46
+ }
47
+
48
+ @end
49
+
2
50
 
3
51
  @interface AirborneiOS ()
4
- @property (nonatomic, assign) BOOL isInitialized;
5
- @property (nonatomic, strong) NSString *indexFileName;
6
- // In a real implementation, you would have references to the actual Airborne SDK objects here
52
+ @property (nonatomic, strong) NSString* ns;
53
+ @property (nonatomic, strong) AirborneServices* air;
54
+ @property (nonatomic, strong) id<AirborneDelegate> delegate;
55
+ @property (nonatomic, strong) AirborneLocalDelegate* delegateproxy;
56
+
7
57
  @end
8
58
 
9
59
  @implementation AirborneiOS
10
60
 
11
- + (instancetype)sharedInstance {
12
- static AirborneiOS *sharedInstance = nil;
61
+ + (instancetype)sharedInstanceWithNamespace:(NSString *)namespace{
62
+ static NSMutableDictionary<NSString *, AirborneiOS *> *instances = nil;
63
+ static dispatch_queue_t syncQueue;
13
64
  static dispatch_once_t onceToken;
65
+
66
+ // Initialize dictionary and queue once
14
67
  dispatch_once(&onceToken, ^{
15
- sharedInstance = [[self alloc] init];
68
+ instances = [NSMutableDictionary dictionary];
69
+ syncQueue = dispatch_queue_create("in.juspay.Airborne.singleton", DISPATCH_QUEUE_CONCURRENT);
16
70
  });
17
- return sharedInstance;
71
+
72
+ __block AirborneiOS *instance = nil;
73
+
74
+ // Read existing instance (concurrent)
75
+ dispatch_sync(syncQueue, ^{
76
+ instance = instances[namespace];
77
+ });
78
+
79
+ if (instance == nil) {
80
+ // Write new instance (barrier to prevent concurrent writes)
81
+ dispatch_barrier_sync(syncQueue, ^{
82
+ if (!instances[namespace]) {
83
+ instances[namespace] = [[self alloc] initWithNamespace:namespace];
84
+ }
85
+ instance = instances[namespace];
86
+ });
87
+ }
88
+
89
+ return instance;
18
90
  }
19
91
 
20
- - (instancetype)init {
92
+
93
+ - (instancetype)initWithNamespace:(NSString *) ns{
21
94
  self = [super init];
22
95
  if (self) {
23
- _isInitialized = NO;
96
+ _ns = ns;
97
+
24
98
  }
25
99
  return self;
26
100
  }
27
101
 
28
- - (void)initializeWithAppId:(NSString *)appId
29
- indexFileName:(NSString *)indexFileName
30
- appVersion:(NSString *)appVersion
31
- releaseConfigTemplateUrl:(NSString *)releaseConfigTemplateUrl
32
- headers:(nullable NSDictionary<NSString *, NSString *> *)headers
33
- lazyDownloadCallback:(nullable HyperOTALazyDownloadCallback)lazyDownloadCallback
34
- lazySplitsCallback:(nullable HyperOTALazySplitsCallback)lazySplitsCallback {
35
-
36
- if (self.isInitialized) {
37
- NSLog(@"AirborneiOS: Already initialized");
38
- return;
39
- }
40
-
41
- self.indexFileName = indexFileName;
42
-
43
- // TODO: Initialize the actual Airborne SDK here
44
- // This is a placeholder implementation
45
- // In a real implementation, you would:
46
- // 1. Import the HyperOTA iOS SDK
47
- // 2. Initialize HyperOTAServices with the provided parameters
48
- // 3. Create an ApplicationManager
49
- // 4. Load the application
50
-
51
- NSLog(@"AirborneiOS: Initializing with appId: %@, indexFileName: %@, appVersion: %@",
52
- appId, indexFileName, appVersion);
53
-
54
- self.isInitialized = YES;
55
-
56
- // Simulate callbacks for demo purposes
57
- if (lazyDownloadCallback) {
58
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
59
- lazyDownloadCallback(@"demo/file.js", YES);
60
- });
61
- }
62
-
63
- if (lazySplitsCallback) {
64
- dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
65
- lazySplitsCallback(YES);
66
- });
67
- }
102
+ - (void)loadWithReleaseConfig:(NSString *) rcurl delegate:(id<AirborneReactDelegate>) delegate{
103
+ _delegateproxy = [[AirborneLocalDelegate alloc] initWithNamespace: self.ns delegate:delegate];
104
+ _air = [[AirborneServices alloc] initWithReleaseConfigURL:rcurl delegate:_delegateproxy];
68
105
  }
69
106
 
70
107
  - (NSString *)getBundlePath {
71
- // if (!self.isInitialized) {
72
- // @throw [NSException exceptionWithName:@"HyperOTANotInitialized"
73
- // reason:@"HyperOTA is not initialized. Call initialize first."
74
- // userInfo:nil];
75
- // }
76
- //
77
- // // TODO: Get the actual bundle path from HyperOTA SDK //
78
- // // This is a placeholder implementation
79
- // NSString *bundlePath = [[NSBundle mainBundle] pathForResource:self.indexFileName ofType:nil];
80
- // if (!bundlePath) {
81
- // bundlePath = [NSString stringWithFormat:@"assets://%@", self.indexFileName];
82
- // }
83
- //
84
- // return bundlePath;
85
- return @"";
108
+ return [_air getIndexBundlePath] ;
86
109
  }
87
110
 
88
111
  - (NSString *)getFileContent:(NSString *)filePath {
89
- // if (!self.isInitialized) {
90
- // @throw [NSException exceptionWithName:@"HyperOTANotInitialized"
91
- // reason:@"HyperOTA is not initialized. Call initialize first."
92
- // userInfo:nil];
93
- // }
94
- //
95
- // // TODO: Read the actual file content from HyperOTA SDK //
96
- // // This is a placeholder implementation
97
- // return [NSString stringWithFormat:@"File content for: %@", filePath];
98
- return @"";
112
+ return [_air getFileContentAtPath:filePath];
99
113
  }
100
114
 
101
115
  - (NSString *)getReleaseConfig {
102
- // if (!self.isInitialized) {
103
- // @throw [NSException exceptionWithName:@"HyperOTANotInitialized"
104
- // reason:@"HyperOTA is not initialized. Call initialize first."
105
- // userInfo:nil];
106
- // }
107
- //
108
- // // TODO: Get the actual release config from HyperOTA SDK //
109
- // // This is a placeholder implementation
110
- // NSDictionary *config = @{
111
- // @"version": @"1.0.0",
112
- // @"environment": @"production",
113
- // @"features": @{
114
- // @"featureA": @YES,
115
- // @"featureB": @NO
116
- // }
117
- // };
118
- //
119
- // NSError *error;
120
- // NSData *jsonData = [NSJSONSerialization dataWithJSONObject:config options:0 error:&error];
121
- // if (jsonData) {
122
- // return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
123
- // }
124
-
125
- return @"{}";
116
+ return [_air getReleaseConfig];
126
117
  }
127
118
 
128
119
  @end
120
+
@@ -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
@@ -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,CAAA,EAAoB;EACnD,OAAOJ,QAAQ,CAACI,iBAAiB,CAAC,CAAC;AACrC;AAEA,OAAO,SAASC,cAAcA,CAACC,QAAgB,EAAmB;EAChE,OAAON,QAAQ,CAACK,cAAc,CAACC,QAAQ,CAAC;AAC1C;AAEA,OAAO,SAASC,aAAaA,CAAA,EAAoB;EAC/C,OAAOP,QAAQ,CAACO,aAAa,CAAC,CAAC;AACjC;AAEA,eAAeP,QAAQ","ignoreList":[]}
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,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAClD,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAClC;;AAED,wBAAkE"}
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,IAAI,OAAO,CAAC,MAAM,CAAC,CAEnD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEhE;AAED,wBAAgB,aAAa,IAAI,OAAO,CAAC,MAAM,CAAC,CAE/C;AAED,eAAe,QAAQ,CAAC"}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "airborne-react-native",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Airborne",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -58,6 +58,9 @@
58
58
  "publishConfig": {
59
59
  "registry": "https://registry.npmjs.org/"
60
60
  },
61
+ "dependencies": {
62
+ "airborne-cli-react-native": "file:../airborne_cli"
63
+ },
61
64
  "devDependencies": {
62
65
  "@commitlint/config-conventional": "^19.6.0",
63
66
  "@eslint/compat": "^1.2.7",
@@ -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;
@@ -1,4 +0,0 @@
1
- import React from 'react';
2
- declare function App(): React.JSX.Element;
3
- export default App;
4
- //# sourceMappingURL=App.d.ts.map
@@ -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"}