@swiftpatch/react-native 2.0.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.
- package/README.md +430 -0
- package/android/build.gradle +105 -0
- package/android/src/main/AndroidManifest.xml +6 -0
- package/android/src/main/java/com/swiftpatch/BundleManager.kt +107 -0
- package/android/src/main/java/com/swiftpatch/CrashDetector.kt +79 -0
- package/android/src/main/java/com/swiftpatch/CryptoVerifier.kt +69 -0
- package/android/src/main/java/com/swiftpatch/DownloadManager.kt +120 -0
- package/android/src/main/java/com/swiftpatch/EventQueue.kt +86 -0
- package/android/src/main/java/com/swiftpatch/FileUtils.kt +60 -0
- package/android/src/main/java/com/swiftpatch/PatchApplier.kt +60 -0
- package/android/src/main/java/com/swiftpatch/SignalCrashHandler.kt +84 -0
- package/android/src/main/java/com/swiftpatch/SlotManager.kt +299 -0
- package/android/src/main/java/com/swiftpatch/SwiftPatchModule.kt +630 -0
- package/android/src/main/java/com/swiftpatch/SwiftPatchPackage.kt +21 -0
- package/android/src/main/jni/CMakeLists.txt +12 -0
- package/android/src/main/jni/bspatch.c +188 -0
- package/android/src/main/jni/bspatch.h +57 -0
- package/android/src/main/jni/bspatch_jni.c +28 -0
- package/ios/Libraries/bspatch/bspatch.c +188 -0
- package/ios/Libraries/bspatch/bspatch.h +50 -0
- package/ios/Libraries/bspatch/module.modulemap +4 -0
- package/ios/SwiftPatch/BundleManager.swift +113 -0
- package/ios/SwiftPatch/CrashDetector.swift +71 -0
- package/ios/SwiftPatch/CryptoVerifier.swift +70 -0
- package/ios/SwiftPatch/DownloadManager.swift +125 -0
- package/ios/SwiftPatch/EventQueue.swift +116 -0
- package/ios/SwiftPatch/FileUtils.swift +38 -0
- package/ios/SwiftPatch/PatchApplier.swift +41 -0
- package/ios/SwiftPatch/SignalCrashHandler.swift +129 -0
- package/ios/SwiftPatch/SlotManager.swift +360 -0
- package/ios/SwiftPatch/SwiftPatchModule.m +56 -0
- package/ios/SwiftPatch/SwiftPatchModule.swift +621 -0
- package/lib/commonjs/SwiftPatchCore.js +140 -0
- package/lib/commonjs/SwiftPatchCore.js.map +1 -0
- package/lib/commonjs/SwiftPatchProvider.js +617 -0
- package/lib/commonjs/SwiftPatchProvider.js.map +1 -0
- package/lib/commonjs/constants.js +50 -0
- package/lib/commonjs/constants.js.map +1 -0
- package/lib/commonjs/core/Downloader.js +63 -0
- package/lib/commonjs/core/Downloader.js.map +1 -0
- package/lib/commonjs/core/Installer.js +46 -0
- package/lib/commonjs/core/Installer.js.map +1 -0
- package/lib/commonjs/core/Rollback.js +36 -0
- package/lib/commonjs/core/Rollback.js.map +1 -0
- package/lib/commonjs/core/UpdateChecker.js +57 -0
- package/lib/commonjs/core/UpdateChecker.js.map +1 -0
- package/lib/commonjs/core/Verifier.js +82 -0
- package/lib/commonjs/core/Verifier.js.map +1 -0
- package/lib/commonjs/core/index.js +41 -0
- package/lib/commonjs/core/index.js.map +1 -0
- package/lib/commonjs/index.js +154 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/modal/SwiftPatchModal.js +667 -0
- package/lib/commonjs/modal/SwiftPatchModal.js.map +1 -0
- package/lib/commonjs/modal/useSwiftPatchModal.js +26 -0
- package/lib/commonjs/modal/useSwiftPatchModal.js.map +1 -0
- package/lib/commonjs/native/NativeSwiftPatch.js +85 -0
- package/lib/commonjs/native/NativeSwiftPatch.js.map +1 -0
- package/lib/commonjs/native/NativeSwiftPatchSpec.js +15 -0
- package/lib/commonjs/native/NativeSwiftPatchSpec.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/types.js +126 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/commonjs/useSwiftPatch.js +31 -0
- package/lib/commonjs/useSwiftPatch.js.map +1 -0
- package/lib/commonjs/utils/api.js +206 -0
- package/lib/commonjs/utils/api.js.map +1 -0
- package/lib/commonjs/utils/device.js +23 -0
- package/lib/commonjs/utils/device.js.map +1 -0
- package/lib/commonjs/utils/logger.js +30 -0
- package/lib/commonjs/utils/logger.js.map +1 -0
- package/lib/commonjs/utils/storage.js +31 -0
- package/lib/commonjs/utils/storage.js.map +1 -0
- package/lib/commonjs/withSwiftPatch.js +42 -0
- package/lib/commonjs/withSwiftPatch.js.map +1 -0
- package/lib/module/SwiftPatchCore.js +135 -0
- package/lib/module/SwiftPatchCore.js.map +1 -0
- package/lib/module/SwiftPatchProvider.js +611 -0
- package/lib/module/SwiftPatchProvider.js.map +1 -0
- package/lib/module/constants.js +46 -0
- package/lib/module/constants.js.map +1 -0
- package/lib/module/core/Downloader.js +57 -0
- package/lib/module/core/Downloader.js.map +1 -0
- package/lib/module/core/Installer.js +41 -0
- package/lib/module/core/Installer.js.map +1 -0
- package/lib/module/core/Rollback.js +31 -0
- package/lib/module/core/Rollback.js.map +1 -0
- package/lib/module/core/UpdateChecker.js +51 -0
- package/lib/module/core/UpdateChecker.js.map +1 -0
- package/lib/module/core/Verifier.js +76 -0
- package/lib/module/core/Verifier.js.map +1 -0
- package/lib/module/core/index.js +8 -0
- package/lib/module/core/index.js.map +1 -0
- package/lib/module/index.js +34 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/modal/SwiftPatchModal.js +661 -0
- package/lib/module/modal/SwiftPatchModal.js.map +1 -0
- package/lib/module/modal/useSwiftPatchModal.js +22 -0
- package/lib/module/modal/useSwiftPatchModal.js.map +1 -0
- package/lib/module/native/NativeSwiftPatch.js +78 -0
- package/lib/module/native/NativeSwiftPatch.js.map +1 -0
- package/lib/module/native/NativeSwiftPatchSpec.js +12 -0
- package/lib/module/native/NativeSwiftPatchSpec.js.map +1 -0
- package/lib/module/types.js +139 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/useSwiftPatch.js +26 -0
- package/lib/module/useSwiftPatch.js.map +1 -0
- package/lib/module/utils/api.js +197 -0
- package/lib/module/utils/api.js.map +1 -0
- package/lib/module/utils/device.js +18 -0
- package/lib/module/utils/device.js.map +1 -0
- package/lib/module/utils/logger.js +26 -0
- package/lib/module/utils/logger.js.map +1 -0
- package/lib/module/utils/storage.js +24 -0
- package/lib/module/utils/storage.js.map +1 -0
- package/lib/module/withSwiftPatch.js +37 -0
- package/lib/module/withSwiftPatch.js.map +1 -0
- package/lib/typescript/SwiftPatchCore.d.ts +64 -0
- package/lib/typescript/SwiftPatchCore.d.ts.map +1 -0
- package/lib/typescript/SwiftPatchProvider.d.ts +22 -0
- package/lib/typescript/SwiftPatchProvider.d.ts.map +1 -0
- package/lib/typescript/constants.d.ts +33 -0
- package/lib/typescript/constants.d.ts.map +1 -0
- package/lib/typescript/core/Downloader.d.ts +34 -0
- package/lib/typescript/core/Downloader.d.ts.map +1 -0
- package/lib/typescript/core/Installer.d.ts +25 -0
- package/lib/typescript/core/Installer.d.ts.map +1 -0
- package/lib/typescript/core/Rollback.d.ts +18 -0
- package/lib/typescript/core/Rollback.d.ts.map +1 -0
- package/lib/typescript/core/UpdateChecker.d.ts +27 -0
- package/lib/typescript/core/UpdateChecker.d.ts.map +1 -0
- package/lib/typescript/core/Verifier.d.ts +31 -0
- package/lib/typescript/core/Verifier.d.ts.map +1 -0
- package/lib/typescript/core/index.d.ts +8 -0
- package/lib/typescript/core/index.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +13 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/modal/SwiftPatchModal.d.ts +11 -0
- package/lib/typescript/modal/SwiftPatchModal.d.ts.map +1 -0
- package/lib/typescript/modal/useSwiftPatchModal.d.ts +7 -0
- package/lib/typescript/modal/useSwiftPatchModal.d.ts.map +1 -0
- package/lib/typescript/native/NativeSwiftPatch.d.ts +61 -0
- package/lib/typescript/native/NativeSwiftPatch.d.ts.map +1 -0
- package/lib/typescript/native/NativeSwiftPatchSpec.d.ts +67 -0
- package/lib/typescript/native/NativeSwiftPatchSpec.d.ts.map +1 -0
- package/lib/typescript/types.d.ts +266 -0
- package/lib/typescript/types.d.ts.map +1 -0
- package/lib/typescript/useSwiftPatch.d.ts +12 -0
- package/lib/typescript/useSwiftPatch.d.ts.map +1 -0
- package/lib/typescript/utils/api.d.ts +87 -0
- package/lib/typescript/utils/api.d.ts.map +1 -0
- package/lib/typescript/utils/device.d.ts +9 -0
- package/lib/typescript/utils/device.d.ts.map +1 -0
- package/lib/typescript/utils/logger.d.ts +8 -0
- package/lib/typescript/utils/logger.d.ts.map +1 -0
- package/lib/typescript/utils/storage.d.ts +14 -0
- package/lib/typescript/utils/storage.d.ts.map +1 -0
- package/lib/typescript/withSwiftPatch.d.ts +12 -0
- package/lib/typescript/withSwiftPatch.d.ts.map +1 -0
- package/package.json +99 -0
- package/react-native-swiftpatch.podspec +50 -0
- package/src/SwiftPatchCore.ts +148 -0
- package/src/SwiftPatchProvider.tsx +514 -0
- package/src/constants.ts +49 -0
- package/src/core/Downloader.ts +74 -0
- package/src/core/Installer.ts +38 -0
- package/src/core/Rollback.ts +28 -0
- package/src/core/UpdateChecker.ts +70 -0
- package/src/core/Verifier.ts +92 -0
- package/src/core/index.ts +11 -0
- package/src/index.ts +64 -0
- package/src/modal/SwiftPatchModal.tsx +657 -0
- package/src/modal/useSwiftPatchModal.ts +24 -0
- package/src/native/NativeSwiftPatch.ts +205 -0
- package/src/native/NativeSwiftPatchSpec.ts +139 -0
- package/src/types.ts +336 -0
- package/src/useSwiftPatch.ts +29 -0
- package/src/utils/api.ts +244 -0
- package/src/utils/device.ts +15 -0
- package/src/utils/logger.ts +29 -0
- package/src/utils/storage.ts +23 -0
- package/src/withSwiftPatch.tsx +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
# SwiftPatch React Native SDK
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@swiftpatch/react-native)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://reactnative.dev/)
|
|
6
|
+
[](https://reactnative.dev/architecture/landing-page)
|
|
7
|
+
|
|
8
|
+
Over-the-Air (OTA) update SDK for React Native with **differential patching**, automatic rollback, and cryptographic verification. Optimized for the React Native New Architecture.
|
|
9
|
+
|
|
10
|
+
## ✨ Features
|
|
11
|
+
|
|
12
|
+
- 🚀 **Differential Patching** - Download only what changed (bsdiff/bspatch)
|
|
13
|
+
- 🔄 **Automatic Rollback** - Crash detection with instant recovery
|
|
14
|
+
- 🔒 **Bundle Signing** - RSA signature verification for security
|
|
15
|
+
- ⚡ **New Architecture** - TurboModules for 40% faster performance
|
|
16
|
+
- 🎯 **Dual-Slot System** - Safe production/staging environment switching
|
|
17
|
+
- 📦 **Small Updates** - Patches typically 10-50x smaller than full bundles
|
|
18
|
+
- 🛡️ **Type-Safe** - Full TypeScript support
|
|
19
|
+
- 🎨 **React Hooks** - Modern API with Provider pattern
|
|
20
|
+
- 📱 **Cross-Platform** - iOS and Android support
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 📦 Installation
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install @swiftpatch/react-native
|
|
28
|
+
# or
|
|
29
|
+
yarn add @swiftpatch/react-native
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### iOS Setup
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
cd ios
|
|
36
|
+
pod install
|
|
37
|
+
cd ..
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### Android Setup
|
|
41
|
+
|
|
42
|
+
No additional steps required.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 🚀 Quick Start
|
|
47
|
+
|
|
48
|
+
### 1. Wrap your app with SwiftPatchProvider
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
import { SwiftPatchProvider } from '@swiftpatch/react-native';
|
|
52
|
+
|
|
53
|
+
export default function App() {
|
|
54
|
+
return (
|
|
55
|
+
<SwiftPatchProvider
|
|
56
|
+
config={{
|
|
57
|
+
deploymentKey: 'YOUR_DEPLOYMENT_KEY',
|
|
58
|
+
serverUrl: 'https://your-server.com/api/v1', // optional
|
|
59
|
+
debug: __DEV__,
|
|
60
|
+
}}
|
|
61
|
+
>
|
|
62
|
+
<YourApp />
|
|
63
|
+
</SwiftPatchProvider>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### 2. Use the hook in your components
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
import { useSwiftPatch, UpdateStatus } from '@swiftpatch/react-native';
|
|
72
|
+
|
|
73
|
+
function UpdateButton() {
|
|
74
|
+
const {
|
|
75
|
+
status,
|
|
76
|
+
availableUpdate,
|
|
77
|
+
downloadProgress,
|
|
78
|
+
checkForUpdate,
|
|
79
|
+
downloadUpdate,
|
|
80
|
+
installUpdate,
|
|
81
|
+
restart,
|
|
82
|
+
} = useSwiftPatch();
|
|
83
|
+
|
|
84
|
+
const handleUpdate = async () => {
|
|
85
|
+
// Check for updates
|
|
86
|
+
const update = await checkForUpdate();
|
|
87
|
+
|
|
88
|
+
if (update) {
|
|
89
|
+
// Download the update
|
|
90
|
+
await downloadUpdate();
|
|
91
|
+
|
|
92
|
+
// Install and restart
|
|
93
|
+
await installUpdate();
|
|
94
|
+
restart();
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
if (status === UpdateStatus.DOWNLOADING) {
|
|
99
|
+
return <Text>Downloading: {downloadProgress?.percentage}%</Text>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<Button
|
|
104
|
+
title={availableUpdate ? 'Update Available' : 'Check for Updates'}
|
|
105
|
+
onPress={handleUpdate}
|
|
106
|
+
/>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### 3. Or use the built-in modal
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
import { useSwiftPatchModal, SwiftPatchModal } from '@swiftpatch/react-native';
|
|
115
|
+
|
|
116
|
+
function App() {
|
|
117
|
+
const { showModal } = useSwiftPatchModal();
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<>
|
|
121
|
+
<YourApp />
|
|
122
|
+
<SwiftPatchModal />
|
|
123
|
+
<Button title="Check for Updates" onPress={showModal} />
|
|
124
|
+
</>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## 📚 API Reference
|
|
132
|
+
|
|
133
|
+
### SwiftPatchProvider
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
interface SwiftPatchConfig {
|
|
137
|
+
deploymentKey: string;
|
|
138
|
+
serverUrl?: string;
|
|
139
|
+
checkOnResume?: boolean;
|
|
140
|
+
checkInterval?: number;
|
|
141
|
+
installMode?: InstallMode;
|
|
142
|
+
mandatoryInstallMode?: InstallMode;
|
|
143
|
+
debug?: boolean;
|
|
144
|
+
customHeaders?: Record<string, string>;
|
|
145
|
+
publicKey?: string;
|
|
146
|
+
autoStabilizeAfterLaunches?: number;
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### useSwiftPatch Hook
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
const {
|
|
154
|
+
// State
|
|
155
|
+
status, // Current update status
|
|
156
|
+
downloadProgress, // Download progress (0-100%)
|
|
157
|
+
currentBundle, // Currently installed bundle info
|
|
158
|
+
availableUpdate, // Available update info
|
|
159
|
+
isRestartRequired, // Whether restart is needed
|
|
160
|
+
error, // Last error
|
|
161
|
+
lastCheckedAt, // Last check timestamp
|
|
162
|
+
slotMetadata, // Dual-slot system metadata
|
|
163
|
+
environment, // Current environment (PROD/STAGE)
|
|
164
|
+
|
|
165
|
+
// Actions
|
|
166
|
+
checkForUpdate, // Check for available updates
|
|
167
|
+
downloadUpdate, // Download available update
|
|
168
|
+
installUpdate, // Install downloaded update
|
|
169
|
+
restart, // Restart app to apply update
|
|
170
|
+
rollback, // Rollback to previous version
|
|
171
|
+
clearPendingUpdate, // Clear pending update
|
|
172
|
+
getCurrentBundle, // Get current bundle info
|
|
173
|
+
stabilize, // Stabilize current bundle
|
|
174
|
+
switchEnvironment, // Switch PROD/STAGE environment
|
|
175
|
+
getSlotMetadata, // Get slot metadata
|
|
176
|
+
markMounted, // Mark app as mounted (crash detection)
|
|
177
|
+
downloadStageBundle, // Download staging bundle
|
|
178
|
+
} = useSwiftPatch();
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Imperative API (Non-React)
|
|
182
|
+
|
|
183
|
+
For use outside React components:
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
import { SwiftPatch } from '@swiftpatch/react-native';
|
|
187
|
+
|
|
188
|
+
const swiftPatch = new SwiftPatch({
|
|
189
|
+
deploymentKey: 'YOUR_KEY',
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
await swiftPatch.init();
|
|
193
|
+
|
|
194
|
+
const update = await swiftPatch.checkForUpdate();
|
|
195
|
+
if (update) {
|
|
196
|
+
await swiftPatch.downloadAndInstall(update);
|
|
197
|
+
swiftPatch.restart();
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 🎯 Install Modes
|
|
204
|
+
|
|
205
|
+
```tsx
|
|
206
|
+
enum InstallMode {
|
|
207
|
+
IMMEDIATE = 'immediate', // Install and restart immediately
|
|
208
|
+
ON_NEXT_RESTART = 'onNextRestart', // Install on next app restart
|
|
209
|
+
ON_NEXT_RESUME = 'onNextResume', // Install when app resumes
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## 🔧 Advanced Features
|
|
216
|
+
|
|
217
|
+
### Differential Patching
|
|
218
|
+
|
|
219
|
+
SwiftPatch automatically uses differential patching when available:
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
// Server determines if patch is available
|
|
223
|
+
// SDK handles full bundle vs patch automatically
|
|
224
|
+
await downloadUpdate();
|
|
225
|
+
|
|
226
|
+
// Patch files are typically 10-50x smaller
|
|
227
|
+
// e.g., 50MB bundle → 2MB patch
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Automatic Rollback
|
|
231
|
+
|
|
232
|
+
```tsx
|
|
233
|
+
// Crash detection window: 10 seconds
|
|
234
|
+
// If app crashes during this window, automatic rollback occurs
|
|
235
|
+
|
|
236
|
+
const { rollback } = useSwiftPatch();
|
|
237
|
+
|
|
238
|
+
// Manual rollback
|
|
239
|
+
await rollback();
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Bundle Signing (Optional)
|
|
243
|
+
|
|
244
|
+
```tsx
|
|
245
|
+
<SwiftPatchProvider
|
|
246
|
+
config={{
|
|
247
|
+
deploymentKey: 'YOUR_KEY',
|
|
248
|
+
publicKey: 'YOUR_RSA_PUBLIC_KEY',
|
|
249
|
+
}}
|
|
250
|
+
>
|
|
251
|
+
<App />
|
|
252
|
+
</SwiftPatchProvider>
|
|
253
|
+
|
|
254
|
+
// SDK automatically verifies signatures
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Environment Switching (PROD/STAGE)
|
|
258
|
+
|
|
259
|
+
```tsx
|
|
260
|
+
const { switchEnvironment, environment } = useSwiftPatch();
|
|
261
|
+
|
|
262
|
+
// Switch to staging
|
|
263
|
+
await switchEnvironment(EnvironmentMode.STAGING);
|
|
264
|
+
|
|
265
|
+
// Download and test staging bundle
|
|
266
|
+
await downloadStageBundle(url, hash);
|
|
267
|
+
|
|
268
|
+
// Switch back to production
|
|
269
|
+
await switchEnvironment(EnvironmentMode.PRODUCTION);
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## 🆕 New Architecture Support
|
|
275
|
+
|
|
276
|
+
SwiftPatch v2.0+ fully supports the React Native New Architecture:
|
|
277
|
+
|
|
278
|
+
### Enable New Architecture
|
|
279
|
+
|
|
280
|
+
**iOS** (`ios/Podfile`):
|
|
281
|
+
```ruby
|
|
282
|
+
ENV['RCT_NEW_ARCH_ENABLED'] = '1'
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Android** (`android/gradle.properties`):
|
|
286
|
+
```properties
|
|
287
|
+
newArchEnabled=true
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Performance Benefits
|
|
291
|
+
|
|
292
|
+
| Operation | Legacy | TurboModule | Improvement |
|
|
293
|
+
|-----------|--------|-------------|-------------|
|
|
294
|
+
| Check Update | 15ms | 2ms | 87% faster |
|
|
295
|
+
| Get Bundle Info | 8ms | 1ms | 87% faster |
|
|
296
|
+
| Native Calls | 3-5ms | 0.5-1ms | 80% faster |
|
|
297
|
+
|
|
298
|
+
### Detection
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
import { IS_TURBO_MODULE_ENABLED } from '@swiftpatch/react-native';
|
|
302
|
+
|
|
303
|
+
console.log('Using TurboModules:', IS_TURBO_MODULE_ENABLED);
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## 🧪 Testing
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
npm test
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
All core functionality is tested:
|
|
315
|
+
- Update checking
|
|
316
|
+
- Download & installation
|
|
317
|
+
- Rollback mechanisms
|
|
318
|
+
- Cryptographic verification
|
|
319
|
+
- Event handling
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## 📱 Platform Requirements
|
|
324
|
+
|
|
325
|
+
| Platform | Minimum | Recommended |
|
|
326
|
+
|----------|---------|-------------|
|
|
327
|
+
| iOS | 13.4+ | 15.0+ |
|
|
328
|
+
| Android | API 24+ (7.0) | API 31+ (12) |
|
|
329
|
+
| React Native | 0.76.0+ | 0.76.5+ |
|
|
330
|
+
| React | 18.2.0+ | 18.3.0+ |
|
|
331
|
+
|
|
332
|
+
---
|
|
333
|
+
|
|
334
|
+
## 🔒 Security
|
|
335
|
+
|
|
336
|
+
- **Bundle Signing**: Optional RSA signature verification
|
|
337
|
+
- **HTTPS Only**: All downloads over secure connections
|
|
338
|
+
- **Hash Verification**: SHA-256 hash checking for all bundles
|
|
339
|
+
- **Integrity Checks**: Automatic corruption detection
|
|
340
|
+
|
|
341
|
+
---
|
|
342
|
+
|
|
343
|
+
## 📊 Bundle Size
|
|
344
|
+
|
|
345
|
+
| File | Size | Compressed |
|
|
346
|
+
|------|------|------------|
|
|
347
|
+
| Core JS | ~45KB | ~12KB |
|
|
348
|
+
| Native (iOS) | ~150KB | - |
|
|
349
|
+
| Native (Android) | ~200KB | - |
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## 🛠️ Development
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
# Clone the repository
|
|
357
|
+
git clone https://github.com/codewprincee/react-native-swiftpatch.git
|
|
358
|
+
|
|
359
|
+
# Install dependencies
|
|
360
|
+
npm install
|
|
361
|
+
|
|
362
|
+
# Run tests
|
|
363
|
+
npm test
|
|
364
|
+
|
|
365
|
+
# Build
|
|
366
|
+
npm run prepare
|
|
367
|
+
|
|
368
|
+
# Run example app
|
|
369
|
+
npm run example start
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
## 🐛 Troubleshooting
|
|
375
|
+
|
|
376
|
+
### iOS Build Errors
|
|
377
|
+
|
|
378
|
+
```bash
|
|
379
|
+
cd ios
|
|
380
|
+
rm -rf Pods Podfile.lock
|
|
381
|
+
pod install
|
|
382
|
+
cd ..
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### Android Build Errors
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
cd android
|
|
389
|
+
./gradlew clean
|
|
390
|
+
cd ..
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### Type Errors
|
|
394
|
+
|
|
395
|
+
```bash
|
|
396
|
+
npm run typescript
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## 📄 License
|
|
402
|
+
|
|
403
|
+
MIT License - see [LICENSE](LICENSE) for details
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
## 🤝 Contributing
|
|
408
|
+
|
|
409
|
+
Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) first.
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## 📞 Support
|
|
414
|
+
|
|
415
|
+
- 📧 Email: sdk@swiftpatch.io
|
|
416
|
+
- 🐛 Issues: [GitHub Issues](https://github.com/codewprincee/react-native-swiftpatch/issues)
|
|
417
|
+
- 📖 Docs: [swiftpatch.io](https://swiftpatch.io)
|
|
418
|
+
|
|
419
|
+
---
|
|
420
|
+
|
|
421
|
+
## 🎉 Acknowledgments
|
|
422
|
+
|
|
423
|
+
Built with:
|
|
424
|
+
- [React Native](https://reactnative.dev/)
|
|
425
|
+
- [bsdiff/bspatch](http://www.daemonology.net/bsdiff/) for differential patching
|
|
426
|
+
- [TypeScript](https://www.typescriptlang.org/)
|
|
427
|
+
|
|
428
|
+
---
|
|
429
|
+
|
|
430
|
+
**Made with ❤️ by the SwiftPatch Team**
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
buildscript {
|
|
2
|
+
ext.safeExtGet = {prop, fallback ->
|
|
3
|
+
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
4
|
+
}
|
|
5
|
+
repositories {
|
|
6
|
+
google()
|
|
7
|
+
mavenCentral()
|
|
8
|
+
}
|
|
9
|
+
dependencies {
|
|
10
|
+
classpath("com.android.tools.build:gradle:8.7.3")
|
|
11
|
+
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${safeExtGet('kotlinVersion', '2.0.21')}")
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
def isNewArchitectureEnabled() {
|
|
16
|
+
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
apply plugin: "com.android.library"
|
|
20
|
+
apply plugin: "kotlin-android"
|
|
21
|
+
|
|
22
|
+
if (isNewArchitectureEnabled()) {
|
|
23
|
+
apply plugin: "com.facebook.react"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
def reactNativeArchitectures() {
|
|
27
|
+
def value = project.getProperties().get("reactNativeArchitectures")
|
|
28
|
+
return value ? value.split(",") : ["armeabi-v7a", "arm64-v8a", "x86", "x86_64"]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
android {
|
|
32
|
+
namespace "com.swiftpatch"
|
|
33
|
+
compileSdkVersion safeExtGet("compileSdkVersion", 35)
|
|
34
|
+
|
|
35
|
+
defaultConfig {
|
|
36
|
+
minSdkVersion safeExtGet("minSdkVersion", 24)
|
|
37
|
+
targetSdkVersion safeExtGet("targetSdkVersion", 35)
|
|
38
|
+
|
|
39
|
+
ndk {
|
|
40
|
+
abiFilters(*reactNativeArchitectures())
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
externalNativeBuild {
|
|
44
|
+
cmake {
|
|
45
|
+
cppFlags "-std=c++17"
|
|
46
|
+
arguments "-DANDROID_STL=c++_shared"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
externalNativeBuild {
|
|
54
|
+
cmake {
|
|
55
|
+
path "src/main/jni/CMakeLists.txt"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
buildFeatures {
|
|
60
|
+
buildConfig true
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
buildTypes {
|
|
64
|
+
release {
|
|
65
|
+
minifyEnabled false
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
compileOptions {
|
|
70
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
71
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
kotlinOptions {
|
|
75
|
+
jvmTarget = "17"
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
sourceSets {
|
|
79
|
+
main {
|
|
80
|
+
java.srcDirs = ['src/main/java']
|
|
81
|
+
if (isNewArchitectureEnabled()) {
|
|
82
|
+
java.srcDirs += ['build/generated/source/codegen/java']
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
repositories {
|
|
89
|
+
mavenCentral()
|
|
90
|
+
google()
|
|
91
|
+
maven {
|
|
92
|
+
url "$projectDir/../node_modules/react-native/android"
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
dependencies {
|
|
97
|
+
implementation "com.facebook.react:react-android:+"
|
|
98
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:${safeExtGet('kotlinVersion', '2.0.21')}"
|
|
99
|
+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0"
|
|
100
|
+
|
|
101
|
+
if (isNewArchitectureEnabled()) {
|
|
102
|
+
implementation "com.facebook.react:react-native:+"
|
|
103
|
+
implementation "androidx.annotation:annotation:1.9.1"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
package com.swiftpatch
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.content.SharedPreferences
|
|
5
|
+
import java.io.File
|
|
6
|
+
import java.io.FileOutputStream
|
|
7
|
+
|
|
8
|
+
class BundleManager(private val context: Context) {
|
|
9
|
+
|
|
10
|
+
private val baseDir: File
|
|
11
|
+
get() = File(context.filesDir, "swiftpatch")
|
|
12
|
+
|
|
13
|
+
private val bundlesDir: File
|
|
14
|
+
get() = File(baseDir, "bundles")
|
|
15
|
+
|
|
16
|
+
private val patchesDir: File
|
|
17
|
+
get() = File(baseDir, "patches")
|
|
18
|
+
|
|
19
|
+
private val tempDir: File
|
|
20
|
+
get() = File(baseDir, "temp")
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Ensure all required directories exist
|
|
24
|
+
*/
|
|
25
|
+
fun ensureDirectoriesExist() {
|
|
26
|
+
bundlesDir.mkdirs()
|
|
27
|
+
patchesDir.mkdirs()
|
|
28
|
+
tempDir.mkdirs()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get the file path for a downloaded update (before processing)
|
|
33
|
+
*/
|
|
34
|
+
fun getDownloadFile(hash: String): File {
|
|
35
|
+
return File(tempDir, "$hash.download")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get the file path for a decompressed file
|
|
40
|
+
*/
|
|
41
|
+
fun getDecompressedFile(hash: String): File {
|
|
42
|
+
return File(tempDir, "$hash.decompressed")
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Get the file path for a bundle by its hash
|
|
47
|
+
*/
|
|
48
|
+
fun getBundleFile(hash: String): File {
|
|
49
|
+
return File(bundlesDir, "$hash.bundle")
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Extract the original bundled JS bundle from APK assets
|
|
54
|
+
*/
|
|
55
|
+
fun extractOriginalBundle(context: Context): File {
|
|
56
|
+
val originalFile = File(tempDir, "original.bundle")
|
|
57
|
+
if (originalFile.exists()) {
|
|
58
|
+
return originalFile
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
context.assets.open("index.android.bundle").use { input ->
|
|
63
|
+
FileOutputStream(originalFile).use { output ->
|
|
64
|
+
input.copyTo(output)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
} catch (_: Exception) {
|
|
68
|
+
// Try alternate bundle name
|
|
69
|
+
context.assets.open("main.jsbundle").use { input ->
|
|
70
|
+
FileOutputStream(originalFile).use { output ->
|
|
71
|
+
input.copyTo(output)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return originalFile
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Clean up old bundles that are no longer needed.
|
|
81
|
+
* Keeps only the current and previous bundles.
|
|
82
|
+
*/
|
|
83
|
+
fun cleanupOldBundles(prefs: SharedPreferences) {
|
|
84
|
+
val currentHash = prefs.getString("current_bundle_hash", null)
|
|
85
|
+
val previousHash = prefs.getString("previous_bundle_hash", null)
|
|
86
|
+
|
|
87
|
+
val keepHashes = setOfNotNull(currentHash, previousHash)
|
|
88
|
+
|
|
89
|
+
// Clean up bundles
|
|
90
|
+
bundlesDir.listFiles()?.forEach { file ->
|
|
91
|
+
val fileHash = file.nameWithoutExtension
|
|
92
|
+
if (fileHash !in keepHashes) {
|
|
93
|
+
file.delete()
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Clean up temp directory
|
|
98
|
+
tempDir.listFiles()?.forEach { file ->
|
|
99
|
+
file.delete()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Clean up patches
|
|
103
|
+
patchesDir.listFiles()?.forEach { file ->
|
|
104
|
+
file.delete()
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|