@vanikya/ota-react-native 0.1.4 → 0.1.5
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/README.md +3 -1
- package/app.plugin.js +104 -0
- package/ios/OTAUpdate.swift +3 -3
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
# @ota-
|
|
1
|
+
# @vanikya/ota-react-native
|
|
2
|
+
|
|
3
|
+
> **Beta Notice**: This package is currently in beta. Testing is in progress and APIs may change. Use in production at your own discretion. We welcome feedback and bug reports via [GitHub Issues](https://github.com/vanikya/ota-update/issues).
|
|
2
4
|
|
|
3
5
|
React Native SDK for OTA (Over-The-Air) updates. A self-hosted alternative to CodePush and EAS Updates.
|
|
4
6
|
|
package/app.plugin.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
const { withMainApplication, withAppDelegate, withDangerousMod } = require('@expo/config-plugins');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
function withOTAUpdateAndroid(config) {
|
|
6
|
+
return withMainApplication(config, (config) => {
|
|
7
|
+
let contents = config.modResults.contents;
|
|
8
|
+
|
|
9
|
+
// Check if already modified
|
|
10
|
+
if (contents.includes('getJSBundleFile')) {
|
|
11
|
+
return config;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Add imports if not present
|
|
15
|
+
if (!contents.includes('import android.content.SharedPreferences')) {
|
|
16
|
+
contents = contents.replace(
|
|
17
|
+
/^(package .+?\n)/m,
|
|
18
|
+
`$1\nimport android.content.SharedPreferences\nimport java.io.File\n`
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// For Kotlin-based MainApplication (Expo SDK 50+)
|
|
23
|
+
// Find the ReactNativeHost and add getJSBundleFile override
|
|
24
|
+
const kotlinPattern = /override\s+fun\s+getUseDeveloperSupport\(\):\s+Boolean\s*=\s*BuildConfig\.DEBUG/;
|
|
25
|
+
|
|
26
|
+
if (kotlinPattern.test(contents)) {
|
|
27
|
+
contents = contents.replace(
|
|
28
|
+
kotlinPattern,
|
|
29
|
+
`override fun getJSBundleFile(): String? {
|
|
30
|
+
val prefs: SharedPreferences = applicationContext.getSharedPreferences("OTAUpdate", android.content.Context.MODE_PRIVATE)
|
|
31
|
+
val bundlePath = prefs.getString("BundlePath", null)
|
|
32
|
+
if (bundlePath != null && File(bundlePath).exists()) {
|
|
33
|
+
return bundlePath
|
|
34
|
+
}
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG`
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
config.modResults.contents = contents;
|
|
43
|
+
return config;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function withOTAUpdateIOS(config) {
|
|
48
|
+
return withAppDelegate(config, (config) => {
|
|
49
|
+
let contents = config.modResults.contents;
|
|
50
|
+
|
|
51
|
+
// Check if already modified
|
|
52
|
+
if (contents.includes('OTAUpdateBundlePath')) {
|
|
53
|
+
return config;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// For Swift AppDelegate
|
|
57
|
+
if (config.modResults.language === 'swift') {
|
|
58
|
+
// Add helper function before the class closing brace
|
|
59
|
+
const helperFunction = `
|
|
60
|
+
// OTA Update: Check for downloaded bundle
|
|
61
|
+
private func getOTABundleURL() -> URL? {
|
|
62
|
+
let defaults = UserDefaults.standard
|
|
63
|
+
if let bundlePath = defaults.string(forKey: "OTAUpdateBundlePath") {
|
|
64
|
+
let fileURL = URL(fileURLWithPath: bundlePath)
|
|
65
|
+
if FileManager.default.fileExists(atPath: bundlePath) {
|
|
66
|
+
return fileURL
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return nil
|
|
70
|
+
}
|
|
71
|
+
`;
|
|
72
|
+
|
|
73
|
+
// Find bundleURL method and modify it
|
|
74
|
+
const bundleURLPattern = /func\s+bundleURL\(\)\s*->\s*URL\?\s*\{[\s\S]*?\n\s*\}/;
|
|
75
|
+
|
|
76
|
+
if (bundleURLPattern.test(contents)) {
|
|
77
|
+
contents = contents.replace(
|
|
78
|
+
bundleURLPattern,
|
|
79
|
+
`func bundleURL() -> URL? {
|
|
80
|
+
// OTA Update: Check for downloaded bundle first
|
|
81
|
+
if let otaBundle = getOTABundleURL() {
|
|
82
|
+
return otaBundle
|
|
83
|
+
}
|
|
84
|
+
#if DEBUG
|
|
85
|
+
return RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
|
|
86
|
+
#else
|
|
87
|
+
return Bundle.main.url(forResource: "main", withExtension: "jsbundle")
|
|
88
|
+
#endif
|
|
89
|
+
}
|
|
90
|
+
${helperFunction}`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
config.modResults.contents = contents;
|
|
96
|
+
return config;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = function withOTAUpdate(config) {
|
|
101
|
+
config = withOTAUpdateAndroid(config);
|
|
102
|
+
config = withOTAUpdateIOS(config);
|
|
103
|
+
return config;
|
|
104
|
+
};
|
package/ios/OTAUpdate.swift
CHANGED
|
@@ -135,7 +135,7 @@ class OTAUpdate: NSObject {
|
|
|
135
135
|
@objc
|
|
136
136
|
func applyBundle(_ bundlePath: String, restart: Bool, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
|
137
137
|
// Store the bundle path for next launch
|
|
138
|
-
UserDefaults.standard.set(bundlePath, forKey: "
|
|
138
|
+
UserDefaults.standard.set(bundlePath, forKey: "OTAUpdateBundlePath")
|
|
139
139
|
UserDefaults.standard.synchronize()
|
|
140
140
|
|
|
141
141
|
if restart {
|
|
@@ -151,13 +151,13 @@ class OTAUpdate: NSObject {
|
|
|
151
151
|
|
|
152
152
|
@objc
|
|
153
153
|
func getPendingBundlePath(_ resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
|
154
|
-
let path = UserDefaults.standard.string(forKey: "
|
|
154
|
+
let path = UserDefaults.standard.string(forKey: "OTAUpdateBundlePath")
|
|
155
155
|
resolver(path)
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
@objc
|
|
159
159
|
func clearPendingBundle(_ resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
|
160
|
-
UserDefaults.standard.removeObject(forKey: "
|
|
160
|
+
UserDefaults.standard.removeObject(forKey: "OTAUpdateBundlePath")
|
|
161
161
|
UserDefaults.standard.synchronize()
|
|
162
162
|
resolver(nil)
|
|
163
163
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vanikya/ota-react-native",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
4
|
"description": "OTA Update SDK for React Native apps - self-hosted CodePush/EAS Updates alternative",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"ios",
|
|
15
15
|
"*.podspec",
|
|
16
16
|
"react-native.config.js",
|
|
17
|
+
"app.plugin.js",
|
|
17
18
|
"README.md"
|
|
18
19
|
],
|
|
19
20
|
"scripts": {
|