@hot-updater/react-native 0.11.0 → 0.12.1-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/android/src/main/java/com/hotupdater/HotUpdater.kt +91 -24
- package/dist/index.d.ts +1 -1
- package/dist/native.d.ts +1 -1
- package/dist/runUpdateProcess.d.ts +1 -1
- package/dist/wrap.d.ts +1 -1
- package/ios/HotUpdater/HotUpdater.mm +159 -48
- package/package.json +3 -3
- package/src/index.ts +1 -1
- package/src/native.ts +1 -1
- package/src/runUpdateProcess.ts +1 -1
- package/src/wrap.tsx +1 -1
- /package/dist/{checkUpdate.d.ts → checkForUpdate.d.ts} +0 -0
- /package/src/{checkUpdate.ts → checkForUpdate.ts} +0 -0
|
@@ -128,13 +128,34 @@ class HotUpdater : ReactPackage {
|
|
|
128
128
|
|
|
129
129
|
val baseDir = context.getExternalFilesDir(null)
|
|
130
130
|
val bundleStoreDir = File(baseDir, "bundle-store")
|
|
131
|
-
|
|
131
|
+
if (!bundleStoreDir.exists()) {
|
|
132
|
+
bundleStoreDir.mkdirs()
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
val finalBundleDir = File(bundleStoreDir, bundleId)
|
|
136
|
+
if (finalBundleDir.exists()) {
|
|
137
|
+
Log.d("HotUpdater", "Bundle for bundleId $bundleId already exists. Using cached bundle.")
|
|
138
|
+
val existingIndexFile = finalBundleDir.walk().find { it.name == "index.android.bundle" }
|
|
139
|
+
if (existingIndexFile != null) {
|
|
140
|
+
// Update directory modification time to current time after update
|
|
141
|
+
finalBundleDir.setLastModified(System.currentTimeMillis())
|
|
142
|
+
setBundleURL(context, existingIndexFile.absolutePath)
|
|
143
|
+
cleanupOldBundles(bundleStoreDir)
|
|
144
|
+
return true
|
|
145
|
+
} else {
|
|
146
|
+
finalBundleDir.deleteRecursively()
|
|
147
|
+
}
|
|
148
|
+
}
|
|
132
149
|
|
|
133
|
-
|
|
134
|
-
if (
|
|
135
|
-
|
|
150
|
+
val tempDir = File(baseDir, "bundle-temp")
|
|
151
|
+
if (tempDir.exists()) {
|
|
152
|
+
tempDir.deleteRecursively()
|
|
136
153
|
}
|
|
137
|
-
|
|
154
|
+
tempDir.mkdirs()
|
|
155
|
+
|
|
156
|
+
val tempZipFile = File(tempDir, "build.zip")
|
|
157
|
+
val extractedDir = File(tempDir, "extracted")
|
|
158
|
+
extractedDir.mkdirs()
|
|
138
159
|
|
|
139
160
|
val isSuccess =
|
|
140
161
|
withContext(Dispatchers.IO) {
|
|
@@ -144,6 +165,7 @@ class HotUpdater : ReactPackage {
|
|
|
144
165
|
downloadUrl.openConnection() as HttpURLConnection
|
|
145
166
|
} catch (e: Exception) {
|
|
146
167
|
Log.d("HotUpdater", "Failed to open connection: ${e.message}")
|
|
168
|
+
tempDir.deleteRecursively()
|
|
147
169
|
return@withContext false
|
|
148
170
|
}
|
|
149
171
|
|
|
@@ -152,12 +174,11 @@ class HotUpdater : ReactPackage {
|
|
|
152
174
|
val totalSize = conn.contentLength
|
|
153
175
|
if (totalSize <= 0) {
|
|
154
176
|
Log.d("HotUpdater", "Invalid content length: $totalSize")
|
|
177
|
+
tempDir.deleteRecursively()
|
|
155
178
|
return@withContext false
|
|
156
179
|
}
|
|
157
|
-
|
|
158
|
-
// File download
|
|
159
180
|
conn.inputStream.use { input ->
|
|
160
|
-
|
|
181
|
+
tempZipFile.outputStream().use { output ->
|
|
161
182
|
val buffer = ByteArray(8 * 1024)
|
|
162
183
|
var bytesRead: Int
|
|
163
184
|
var totalRead = 0L
|
|
@@ -178,34 +199,80 @@ class HotUpdater : ReactPackage {
|
|
|
178
199
|
}
|
|
179
200
|
} catch (e: Exception) {
|
|
180
201
|
Log.d("HotUpdater", "Failed to download data from URL: $zipUrl, Error: ${e.message}")
|
|
202
|
+
tempDir.deleteRecursively()
|
|
181
203
|
return@withContext false
|
|
182
204
|
} finally {
|
|
183
205
|
conn.disconnect()
|
|
184
206
|
}
|
|
185
207
|
|
|
186
|
-
|
|
187
|
-
if (!extractZipFileAtPath(zipFilePath.absolutePath, bundleStoreDir.absolutePath)) {
|
|
208
|
+
if (!extractZipFileAtPath(tempZipFile.absolutePath, extractedDir.absolutePath)) {
|
|
188
209
|
Log.d("HotUpdater", "Failed to extract zip file.")
|
|
210
|
+
tempDir.deleteRecursively()
|
|
189
211
|
return@withContext false
|
|
190
212
|
}
|
|
213
|
+
true
|
|
214
|
+
}
|
|
191
215
|
|
|
192
|
-
|
|
193
|
-
|
|
216
|
+
if (!isSuccess) {
|
|
217
|
+
tempDir.deleteRecursively()
|
|
218
|
+
return false
|
|
219
|
+
}
|
|
194
220
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
return@withContext false
|
|
202
|
-
}
|
|
221
|
+
val indexFileExtracted = extractedDir.walk().find { it.name == "index.android.bundle" }
|
|
222
|
+
if (indexFileExtracted == null) {
|
|
223
|
+
Log.d("HotUpdater", "index.android.bundle not found in extracted files.")
|
|
224
|
+
tempDir.deleteRecursively()
|
|
225
|
+
return false
|
|
226
|
+
}
|
|
203
227
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
228
|
+
// Move (or copy) contents from temp folder to finalBundleDir
|
|
229
|
+
if (finalBundleDir.exists()) {
|
|
230
|
+
finalBundleDir.deleteRecursively()
|
|
231
|
+
}
|
|
232
|
+
if (!extractedDir.renameTo(finalBundleDir)) {
|
|
233
|
+
extractedDir.copyRecursively(finalBundleDir, overwrite = true)
|
|
234
|
+
extractedDir.deleteRecursively()
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
val finalIndexFile = finalBundleDir.walk().find { it.name == "index.android.bundle" }
|
|
238
|
+
if (finalIndexFile == null) {
|
|
239
|
+
Log.d("HotUpdater", "index.android.bundle not found in final directory.")
|
|
240
|
+
tempDir.deleteRecursively()
|
|
241
|
+
return false
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Update final bundle directory modification time to current time after bundle update
|
|
245
|
+
finalBundleDir.setLastModified(System.currentTimeMillis())
|
|
246
|
+
|
|
247
|
+
val bundlePath = finalIndexFile.absolutePath
|
|
248
|
+
Log.d("HotUpdater", "Setting bundle URL: $bundlePath")
|
|
249
|
+
setBundleURL(context, bundlePath)
|
|
250
|
+
|
|
251
|
+
// Clean up old bundles in the bundle store to keep only up to 2 bundles
|
|
252
|
+
cleanupOldBundles(bundleStoreDir)
|
|
207
253
|
|
|
208
|
-
|
|
254
|
+
// Clean up temp directory
|
|
255
|
+
tempDir.deleteRecursively()
|
|
256
|
+
|
|
257
|
+
Log.d("HotUpdater", "Downloaded and extracted file successfully.")
|
|
258
|
+
return true
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Helper function to delete old bundles, keeping only up to 2 bundles in the bundle-store folder
|
|
262
|
+
private fun cleanupOldBundles(bundleStoreDir: File) {
|
|
263
|
+
// Get list of all directories in bundle-store folder
|
|
264
|
+
val bundles = bundleStoreDir.listFiles { file -> file.isDirectory }?.toList() ?: return
|
|
265
|
+
|
|
266
|
+
// Sort by last modified time in descending order to keep most recently updated bundles at the top
|
|
267
|
+
val sortedBundles = bundles.sortedByDescending { it.lastModified() }
|
|
268
|
+
|
|
269
|
+
// Delete all bundles except the top 2
|
|
270
|
+
if (sortedBundles.size > 2) {
|
|
271
|
+
sortedBundles.drop(2).forEach { oldBundle ->
|
|
272
|
+
Log.d("HotUpdater", "Removing old bundle: ${oldBundle.name}")
|
|
273
|
+
oldBundle.deleteRecursively()
|
|
274
|
+
}
|
|
275
|
+
}
|
|
209
276
|
}
|
|
210
277
|
}
|
|
211
278
|
}
|
package/dist/index.d.ts
CHANGED
package/dist/native.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export declare const addListener: <T extends keyof HotUpdaterEvent>(eventName: T
|
|
|
7
7
|
/**
|
|
8
8
|
* Downloads files from given URLs.
|
|
9
9
|
*
|
|
10
|
-
* @param {string} bundleId - identifier for the bundle
|
|
10
|
+
* @param {string} bundleId - identifier for the bundle id.
|
|
11
11
|
* @param {string | null} zipUrl - zip file URL. If null, it means rolling back to the built-in bundle
|
|
12
12
|
* @returns {Promise<boolean>} Resolves with true if download was successful, otherwise rejects with an error.
|
|
13
13
|
*/
|
package/dist/wrap.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type React from "react";
|
|
2
|
-
import { type CheckForUpdateConfig } from "./
|
|
2
|
+
import { type CheckForUpdateConfig } from "./checkForUpdate";
|
|
3
3
|
import { HotUpdaterError } from "./error";
|
|
4
4
|
import type { RunUpdateProcessResponse } from "./runUpdateProcess";
|
|
5
5
|
type UpdateStatus = "CHECK_FOR_UPDATE" | "UPDATING" | "UPDATE_PROCESS_COMPLETED";
|
|
@@ -49,7 +49,6 @@ RCT_EXPORT_MODULE();
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
+ (NSURL *)fallbackURL {
|
|
52
|
-
// This supports React Native 0.72.6
|
|
53
52
|
#if DEBUG
|
|
54
53
|
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
|
|
55
54
|
#else
|
|
@@ -72,6 +71,48 @@ RCT_EXPORT_MODULE();
|
|
|
72
71
|
return success;
|
|
73
72
|
}
|
|
74
73
|
|
|
74
|
+
#pragma mark - Cleanup Old Bundles
|
|
75
|
+
|
|
76
|
+
- (void)cleanupOldBundlesAtDirectory:(NSString *)bundleStoreDir {
|
|
77
|
+
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
78
|
+
NSError *error = nil;
|
|
79
|
+
NSArray *contents = [fileManager contentsOfDirectoryAtPath:bundleStoreDir error:&error];
|
|
80
|
+
if (error) {
|
|
81
|
+
NSLog(@"Failed to list bundle store directory: %@", error);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
NSMutableArray *bundleDirs = [NSMutableArray array];
|
|
86
|
+
for (NSString *item in contents) {
|
|
87
|
+
NSString *fullPath = [bundleStoreDir stringByAppendingPathComponent:item];
|
|
88
|
+
BOOL isDir = NO;
|
|
89
|
+
if ([fileManager fileExistsAtPath:fullPath isDirectory:&isDir] && isDir) {
|
|
90
|
+
[bundleDirs addObject:fullPath];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Sort in descending order by modification time (keep latest 2)
|
|
95
|
+
[bundleDirs sortUsingComparator:^NSComparisonResult(NSString *path1, NSString *path2) {
|
|
96
|
+
NSDictionary *attr1 = [fileManager attributesOfItemAtPath:path1 error:nil];
|
|
97
|
+
NSDictionary *attr2 = [fileManager attributesOfItemAtPath:path2 error:nil];
|
|
98
|
+
NSDate *date1 = attr1[NSFileModificationDate] ?: [NSDate dateWithTimeIntervalSince1970:0];
|
|
99
|
+
NSDate *date2 = attr2[NSFileModificationDate] ?: [NSDate dateWithTimeIntervalSince1970:0];
|
|
100
|
+
return [date2 compare:date1];
|
|
101
|
+
}];
|
|
102
|
+
|
|
103
|
+
if (bundleDirs.count > 2) {
|
|
104
|
+
NSArray *oldBundles = [bundleDirs subarrayWithRange:NSMakeRange(2, bundleDirs.count - 2)];
|
|
105
|
+
for (NSString *oldBundle in oldBundles) {
|
|
106
|
+
NSError *delError = nil;
|
|
107
|
+
if ([fileManager removeItemAtPath:oldBundle error:&delError]) {
|
|
108
|
+
NSLog(@"Removed old bundle: %@", oldBundle);
|
|
109
|
+
} else {
|
|
110
|
+
NSLog(@"Failed to remove old bundle %@: %@", oldBundle, delError);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
75
116
|
#pragma mark - Update Bundle Method
|
|
76
117
|
|
|
77
118
|
- (void)updateBundle:(NSString *)bundleId zipUrl:(NSURL *)zipUrl completion:(void (^)(BOOL success))completion {
|
|
@@ -83,88 +124,158 @@ RCT_EXPORT_MODULE();
|
|
|
83
124
|
return;
|
|
84
125
|
}
|
|
85
126
|
|
|
86
|
-
// Set
|
|
127
|
+
// Set document directory path and bundle store path
|
|
87
128
|
NSString *documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
|
|
88
129
|
NSString *bundleStoreDir = [documentsPath stringByAppendingPathComponent:@"bundle-store"];
|
|
89
|
-
NSString *zipFilePath = [bundleStoreDir stringByAppendingPathComponent:@"build.zip"];
|
|
90
130
|
|
|
91
|
-
|
|
92
|
-
[
|
|
93
|
-
|
|
94
|
-
|
|
131
|
+
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
132
|
+
if (![fileManager fileExistsAtPath:bundleStoreDir]) {
|
|
133
|
+
[fileManager createDirectoryAtPath:bundleStoreDir withIntermediateDirectories:YES attributes:nil error:nil];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Final bundle path (bundle-store/<bundleId>)
|
|
137
|
+
NSString *finalBundleDir = [bundleStoreDir stringByAppendingPathComponent:bundleId];
|
|
138
|
+
|
|
139
|
+
// Check if cached bundle exists
|
|
140
|
+
if ([fileManager fileExistsAtPath:finalBundleDir]) {
|
|
141
|
+
NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtPath:finalBundleDir];
|
|
142
|
+
NSString *foundBundle = nil;
|
|
143
|
+
for (NSString *file in enumerator) {
|
|
144
|
+
if ([file isEqualToString:@"index.ios.bundle"]) {
|
|
145
|
+
foundBundle = file;
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (foundBundle) {
|
|
150
|
+
// Update modification time of final bundle
|
|
151
|
+
NSDictionary *attributes = @{NSFileModificationDate: [NSDate date]};
|
|
152
|
+
[fileManager setAttributes:attributes ofItemAtPath:finalBundleDir error:nil];
|
|
153
|
+
NSString *bundlePath = [finalBundleDir stringByAppendingPathComponent:foundBundle];
|
|
154
|
+
NSLog(@"Using cached bundle at path: %@", bundlePath);
|
|
155
|
+
[self setBundleURL:bundlePath];
|
|
156
|
+
[self cleanupOldBundlesAtDirectory:bundleStoreDir];
|
|
157
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
158
|
+
if (completion) completion(YES);
|
|
159
|
+
});
|
|
160
|
+
return;
|
|
161
|
+
} else {
|
|
162
|
+
[fileManager removeItemAtPath:finalBundleDir error:nil];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Set up temporary folder (for download and extraction)
|
|
167
|
+
NSString *tempDir = [documentsPath stringByAppendingPathComponent:@"bundle-temp"];
|
|
168
|
+
if ([fileManager fileExistsAtPath:tempDir]) {
|
|
169
|
+
[fileManager removeItemAtPath:tempDir error:nil];
|
|
170
|
+
}
|
|
171
|
+
[fileManager createDirectoryAtPath:tempDir withIntermediateDirectories:YES attributes:nil error:nil];
|
|
172
|
+
|
|
173
|
+
NSString *tempZipFile = [tempDir stringByAppendingPathComponent:@"build.zip"];
|
|
174
|
+
NSString *extractedDir = [tempDir stringByAppendingPathComponent:@"extracted"];
|
|
175
|
+
[fileManager createDirectoryAtPath:extractedDir withIntermediateDirectories:YES attributes:nil error:nil];
|
|
95
176
|
|
|
96
177
|
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
|
|
97
178
|
NSURLSession *session = [NSURLSession sessionWithConfiguration:configuration];
|
|
98
179
|
|
|
99
|
-
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:zipUrl
|
|
100
|
-
completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
|
|
180
|
+
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:zipUrl completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
|
|
101
181
|
if (error) {
|
|
102
182
|
NSLog(@"Failed to download data from URL: %@, error: %@", zipUrl, error);
|
|
103
183
|
if (completion) completion(NO);
|
|
104
184
|
return;
|
|
105
185
|
}
|
|
106
186
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if ([fileManager fileExistsAtPath:zipFilePath]) {
|
|
111
|
-
[fileManager removeItemAtPath:zipFilePath error:nil];
|
|
187
|
+
// Save temporary zip file
|
|
188
|
+
if ([fileManager fileExistsAtPath:tempZipFile]) {
|
|
189
|
+
[fileManager removeItemAtPath:tempZipFile error:nil];
|
|
112
190
|
}
|
|
113
191
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
NSLog(@"Failed to save data: %@", moveError);
|
|
192
|
+
NSError *moveError = nil;
|
|
193
|
+
if (![fileManager moveItemAtURL:location toURL:[NSURL fileURLWithPath:tempZipFile] error:&moveError]) {
|
|
194
|
+
NSLog(@"Failed to save downloaded file: %@", moveError);
|
|
118
195
|
if (completion) completion(NO);
|
|
119
196
|
return;
|
|
120
197
|
}
|
|
121
198
|
|
|
122
199
|
// Extract zip
|
|
123
|
-
if (![self extractZipFileAtPath:
|
|
200
|
+
if (![self extractZipFileAtPath:tempZipFile toDestination:extractedDir]) {
|
|
124
201
|
NSLog(@"Failed to extract zip file.");
|
|
125
202
|
if (completion) completion(NO);
|
|
126
203
|
return;
|
|
127
204
|
}
|
|
128
205
|
|
|
129
|
-
// Search for
|
|
130
|
-
NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtPath:
|
|
131
|
-
NSString *
|
|
206
|
+
// Search for index.ios.bundle in extracted folder
|
|
207
|
+
NSDirectoryEnumerator *enumerator = [fileManager enumeratorAtPath:extractedDir];
|
|
208
|
+
NSString *foundBundle = nil;
|
|
132
209
|
for (NSString *file in enumerator) {
|
|
133
210
|
if ([file isEqualToString:@"index.ios.bundle"]) {
|
|
134
|
-
|
|
211
|
+
foundBundle = file;
|
|
135
212
|
break;
|
|
136
213
|
}
|
|
137
214
|
}
|
|
138
215
|
|
|
139
|
-
if (
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
216
|
+
if (!foundBundle) {
|
|
217
|
+
NSLog(@"index.ios.bundle not found in extracted files.");
|
|
218
|
+
if (completion) completion(NO);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Move extracted folder to final bundle folder
|
|
223
|
+
if ([fileManager fileExistsAtPath:finalBundleDir]) {
|
|
224
|
+
[fileManager removeItemAtPath:finalBundleDir error:nil];
|
|
225
|
+
}
|
|
226
|
+
NSError *moveFinalError = nil;
|
|
227
|
+
BOOL moved = [fileManager moveItemAtPath:extractedDir toPath:finalBundleDir error:&moveFinalError];
|
|
228
|
+
if (!moved) {
|
|
229
|
+
// Try copy and delete if move fails
|
|
230
|
+
BOOL copied = [fileManager copyItemAtPath:extractedDir toPath:finalBundleDir error:&moveFinalError];
|
|
231
|
+
if (copied) {
|
|
232
|
+
[fileManager removeItemAtPath:extractedDir error:nil];
|
|
233
|
+
} else {
|
|
234
|
+
NSLog(@"Failed to move or copy extracted bundle: %@", moveFinalError);
|
|
235
|
+
if (completion) completion(NO);
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Recheck index.ios.bundle in final folder
|
|
241
|
+
NSDirectoryEnumerator *finalEnum = [fileManager enumeratorAtPath:finalBundleDir];
|
|
242
|
+
NSString *finalFoundBundle = nil;
|
|
243
|
+
for (NSString *file in finalEnum) {
|
|
244
|
+
if ([file isEqualToString:@"index.ios.bundle"]) {
|
|
245
|
+
finalFoundBundle = file;
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (!finalFoundBundle) {
|
|
251
|
+
NSLog(@"index.ios.bundle not found in final directory.");
|
|
148
252
|
if (completion) completion(NO);
|
|
253
|
+
return;
|
|
149
254
|
}
|
|
255
|
+
|
|
256
|
+
// Update modification time of final bundle
|
|
257
|
+
NSDictionary *attributes = @{NSFileModificationDate: [NSDate date]};
|
|
258
|
+
[fileManager setAttributes:attributes ofItemAtPath:finalBundleDir error:nil];
|
|
259
|
+
|
|
260
|
+
NSString *bundlePath = [finalBundleDir stringByAppendingPathComponent:finalFoundBundle];
|
|
261
|
+
NSLog(@"Setting bundle URL: %@", bundlePath);
|
|
262
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
263
|
+
[self setBundleURL:bundlePath];
|
|
264
|
+
[self cleanupOldBundlesAtDirectory:bundleStoreDir];
|
|
265
|
+
[fileManager removeItemAtPath:tempDir error:nil];
|
|
266
|
+
if (completion) completion(YES);
|
|
267
|
+
});
|
|
150
268
|
}];
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
[downloadTask addObserver:self
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
context:nil];
|
|
158
|
-
[downloadTask addObserver:self
|
|
159
|
-
forKeyPath:@"countOfBytesExpectedToReceive"
|
|
160
|
-
options:NSKeyValueObservingOptionNew
|
|
161
|
-
context:nil];
|
|
162
|
-
|
|
163
|
-
__block HotUpdater *weakSelf = self;
|
|
269
|
+
|
|
270
|
+
// Register KVO for progress updates
|
|
271
|
+
[downloadTask addObserver:self forKeyPath:@"countOfBytesReceived" options:NSKeyValueObservingOptionNew context:nil];
|
|
272
|
+
[downloadTask addObserver:self forKeyPath:@"countOfBytesExpectedToReceive" options:NSKeyValueObservingOptionNew context:nil];
|
|
273
|
+
|
|
274
|
+
__weak HotUpdater *weakSelf = self;
|
|
164
275
|
[[NSNotificationCenter defaultCenter] addObserverForName:@"NSURLSessionDownloadTaskDidFinishDownloading"
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
276
|
+
object:downloadTask
|
|
277
|
+
queue:[NSOperationQueue mainQueue]
|
|
278
|
+
usingBlock:^(NSNotification * _Nonnull note) {
|
|
168
279
|
[weakSelf removeObserversForTask:downloadTask];
|
|
169
280
|
}];
|
|
170
281
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1-0",
|
|
4
4
|
"description": "React Native OTA solution for self-hosted",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -78,8 +78,8 @@
|
|
|
78
78
|
"react-native-builder-bob": "^0.33.1"
|
|
79
79
|
},
|
|
80
80
|
"dependencies": {
|
|
81
|
-
"@hot-updater/js": "0.
|
|
82
|
-
"@hot-updater/core": "0.
|
|
81
|
+
"@hot-updater/js": "0.12.1-0",
|
|
82
|
+
"@hot-updater/core": "0.12.1-0"
|
|
83
83
|
},
|
|
84
84
|
"scripts": {
|
|
85
85
|
"build": "rslib build",
|
package/src/index.ts
CHANGED
package/src/native.ts
CHANGED
|
@@ -52,7 +52,7 @@ export const addListener = <T extends keyof HotUpdaterEvent>(
|
|
|
52
52
|
/**
|
|
53
53
|
* Downloads files from given URLs.
|
|
54
54
|
*
|
|
55
|
-
* @param {string} bundleId - identifier for the bundle
|
|
55
|
+
* @param {string} bundleId - identifier for the bundle id.
|
|
56
56
|
* @param {string | null} zipUrl - zip file URL. If null, it means rolling back to the built-in bundle
|
|
57
57
|
* @returns {Promise<boolean>} Resolves with true if download was successful, otherwise rejects with an error.
|
|
58
58
|
*/
|
package/src/runUpdateProcess.ts
CHANGED
package/src/wrap.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type React from "react";
|
|
2
2
|
import { useEffect, useLayoutEffect, useState } from "react";
|
|
3
|
-
import { type CheckForUpdateConfig, checkForUpdate } from "./
|
|
3
|
+
import { type CheckForUpdateConfig, checkForUpdate } from "./checkForUpdate";
|
|
4
4
|
import { HotUpdaterError } from "./error";
|
|
5
5
|
import { useEventCallback } from "./hooks/useEventCallback";
|
|
6
6
|
import { reload, updateBundle } from "./native";
|
|
File without changes
|
|
File without changes
|