Skip to content

Commit 69aadfd

Browse files
committed
refactor(iOS): persist bundle as local file on par with Android
1 parent b33de75 commit 69aadfd

9 files changed

Lines changed: 54 additions & 3 deletions

packages/react-native/React/Base/RCTBundleManager.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,13 @@ typedef NSMutableArray<NSURLQueryItem *> *_Nullable (^RCTPackagerOptionsUpdater)
7272
andDefaultGetter:(nullable RCTBridgelessBundleURLGetter)defaultGetter;
7373
- (void)resetBundleURL;
7474
@property (nonatomic, nullable) NSURL *bundleURL;
75+
76+
/**
77+
* File URL of the most recently downloaded bundle persisted to disk. Only set
78+
* when the bundle was fetched from a remote URL (e.g. the packager); nil when
79+
* the bundle was loaded from a local file, in which case its location on disk
80+
* is the bundleURL itself.
81+
*/
82+
@property (nonatomic, nullable) NSURL *downloadedBundleFileURL;
7583
@property (nonatomic, nonnull) RCTBundleConfiguration *bundleConfig;
7684
@end

packages/react-native/React/Base/RCTJavaScriptLoader.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,14 @@ NS_ENUM(NSInteger){
6868
*/
6969
@property (nonatomic, readonly) NSInteger filesChangedCount;
7070

71+
/**
72+
* File URL of the downloaded bundle persisted to disk, from which self.data is
73+
* memory-mapped. Only set when the bundle was fetched from a remote URL (e.g. the
74+
* packager); nil when the bundle was loaded from a local file, in which case the
75+
* bundle's location on disk is self.url.
76+
*/
77+
@property (strong, nonatomic, readonly) NSURL *downloadedBundleFileURL;
78+
7179
@end
7280

7381
typedef void (^RCTSourceLoadProgressBlock)(RCTLoadingProgress *progressData);

packages/react-native/React/Base/RCTJavaScriptLoader.mm

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ @interface RCTSource () {
2525
NSData *_data;
2626
NSUInteger _length;
2727
NSInteger _filesChangedCount;
28+
NSURL *_downloadedBundleFileURL;
2829
}
2930

3031
@end
@@ -210,6 +211,15 @@ static void parseHeaders(NSDictionary *headers, RCTSource *source)
210211
source->_filesChangedCount = [headers[@"X-Metro-Files-Changed-Count"] integerValue];
211212
}
212213

214+
static NSURL *persistDownloadedBundle(NSData *data, NSError **error)
215+
{
216+
NSString *bundlePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"ReactNativeDevBundle.js"];
217+
if (![data writeToFile:bundlePath options:NSDataWritingAtomic error:error]) {
218+
return nil;
219+
}
220+
return [NSURL fileURLWithPath:bundlePath];
221+
}
222+
213223
static void attemptAsynchronousLoadOfBundleAtURL(
214224
NSURL *scriptURL,
215225
RCTSourceLoadProgressBlock onProgress,
@@ -314,7 +324,24 @@ static void attemptAsynchronousLoadOfBundleAtURL(
314324
}
315325
}
316326

317-
RCTSource *source = RCTSourceCreate(sourceURL, data, data.length);
327+
NSError *persistError;
328+
NSURL *downloadedBundleFileURL = persistDownloadedBundle(data, &persistError);
329+
if (downloadedBundleFileURL == nil) {
330+
onComplete(persistError, nil);
331+
return;
332+
}
333+
334+
NSError *mappingError;
335+
NSData *bundleData = [NSData dataWithContentsOfFile:downloadedBundleFileURL.path
336+
options:NSDataReadingMappedIfSafe
337+
error:&mappingError];
338+
if (bundleData == nil) {
339+
onComplete(mappingError, nil);
340+
return;
341+
}
342+
343+
RCTSource *source = RCTSourceCreate(sourceURL, bundleData, bundleData.length);
344+
source->_downloadedBundleFileURL = downloadedBundleFileURL;
318345
parseHeaders(headers, source);
319346
onComplete(nil, source);
320347
}

packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -537,10 +537,18 @@ - (void)loadBundleAtURL:(NSURL *)sourceURL
537537
onProgress:(RCTSourceLoadProgressBlock)onProgress
538538
onComplete:(RCTSourceLoadBlock)loadCallback
539539
{
540+
__weak RCTHost *weakSelf = self;
541+
RCTSourceLoadBlock onComplete = ^(NSError *error, RCTSource *source) {
542+
RCTHost *strongSelf = weakSelf;
543+
if (strongSelf != nil && source != nil) {
544+
strongSelf->_bundleManager.downloadedBundleFileURL = source.downloadedBundleFileURL;
545+
}
546+
loadCallback(error, source);
547+
};
540548
if ([_hostDelegate respondsToSelector:@selector(loadBundleAtURL:onProgress:onComplete:)]) {
541-
[_hostDelegate loadBundleAtURL:sourceURL onProgress:onProgress onComplete:loadCallback];
549+
[_hostDelegate loadBundleAtURL:sourceURL onProgress:onProgress onComplete:onComplete];
542550
} else {
543-
[RCTJavaScriptLoader loadBundleAtURL:sourceURL onProgress:onProgress onComplete:loadCallback];
551+
[RCTJavaScriptLoader loadBundleAtURL:sourceURL onProgress:onProgress onComplete:onComplete];
544552
}
545553
}
546554

packages/rn-tester/Build/Intermediates.noindex/XCBuildData/PIFCache/project/PROJECT@v11_mod=4e9ef4a1896968794696d95d96e06272_hash=57ed08ac0db58769a2e543e57ef9df72plugins=1OJSG6M1FOV3XYQCBH7Z29RZ0FPR9XDE1-json

Whitespace-only changes.

packages/rn-tester/Build/Intermediates.noindex/XCBuildData/PIFCache/target/TARGET@v11_hash=45d9011e54ab66045cab07ec8ddeea6c-json

Whitespace-only changes.

packages/rn-tester/Build/Intermediates.noindex/XCBuildData/PIFCache/target/TARGET@v11_hash=7a96253ea5b248aae35741026aaa8116-json

Whitespace-only changes.

packages/rn-tester/Build/Intermediates.noindex/XCBuildData/PIFCache/target/TARGET@v11_hash=cdea8b75b7d83c6301fe249f3d287f80-json

Whitespace-only changes.

packages/rn-tester/Build/Intermediates.noindex/XCBuildData/PIFCache/workspace/WORKSPACE@v11_hash=c51127f0d5a4a2cd5bd45c57af017784_subobjects=84d40f8c62151d31548e0cdc4f2cab1c-json

Whitespace-only changes.

0 commit comments

Comments
 (0)