diff --git a/packages/react-native/React/Base/RCTBundleManager.h b/packages/react-native/React/Base/RCTBundleManager.h index db2c1813e2a8..aeef12c40f77 100644 --- a/packages/react-native/React/Base/RCTBundleManager.h +++ b/packages/react-native/React/Base/RCTBundleManager.h @@ -72,5 +72,13 @@ typedef NSMutableArray *_Nullable (^RCTPackagerOptionsUpdater) andDefaultGetter:(nullable RCTBridgelessBundleURLGetter)defaultGetter; - (void)resetBundleURL; @property (nonatomic, nullable) NSURL *bundleURL; + +/** + * File URL of the most recently downloaded bundle persisted to disk. Only set + * when the bundle was fetched from a remote URL (e.g. the packager); nil when + * the bundle was loaded from a local file, in which case its location on disk + * is the bundleURL itself. + */ +@property (nonatomic, nullable) NSURL *downloadedBundleFileURL; @property (nonatomic, nonnull) RCTBundleConfiguration *bundleConfig; @end diff --git a/packages/react-native/React/Base/RCTJavaScriptLoader.h b/packages/react-native/React/Base/RCTJavaScriptLoader.h index f6fcfc58a3b4..370af209a8f7 100755 --- a/packages/react-native/React/Base/RCTJavaScriptLoader.h +++ b/packages/react-native/React/Base/RCTJavaScriptLoader.h @@ -68,6 +68,14 @@ NS_ENUM(NSInteger){ */ @property (nonatomic, readonly) NSInteger filesChangedCount; +/** + * File URL of the downloaded bundle persisted to disk, from which self.data is + * memory-mapped. Only set when the bundle was fetched from a remote URL (e.g. the + * packager); nil when the bundle was loaded from a local file, in which case the + * bundle's location on disk is self.url. + */ +@property (strong, nonatomic, readonly) NSURL *downloadedBundleFileURL; + @end typedef void (^RCTSourceLoadProgressBlock)(RCTLoadingProgress *progressData); diff --git a/packages/react-native/React/Base/RCTJavaScriptLoader.mm b/packages/react-native/React/Base/RCTJavaScriptLoader.mm index 5f7d6e9b5efe..9c3e59570a48 100755 --- a/packages/react-native/React/Base/RCTJavaScriptLoader.mm +++ b/packages/react-native/React/Base/RCTJavaScriptLoader.mm @@ -25,6 +25,7 @@ @interface RCTSource () { NSData *_data; NSUInteger _length; NSInteger _filesChangedCount; + NSURL *_downloadedBundleFileURL; } @end @@ -210,6 +211,15 @@ static void parseHeaders(NSDictionary *headers, RCTSource *source) source->_filesChangedCount = [headers[@"X-Metro-Files-Changed-Count"] integerValue]; } +static NSURL *persistDownloadedBundle(NSData *data, NSError **error) +{ + NSString *bundlePath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"ReactNativeDevBundle.js"]; + if (![data writeToFile:bundlePath options:NSDataWritingAtomic error:error]) { + return nil; + } + return [NSURL fileURLWithPath:bundlePath]; +} + static void attemptAsynchronousLoadOfBundleAtURL( NSURL *scriptURL, RCTSourceLoadProgressBlock onProgress, @@ -314,7 +324,24 @@ static void attemptAsynchronousLoadOfBundleAtURL( } } - RCTSource *source = RCTSourceCreate(sourceURL, data, data.length); + NSError *persistError; + NSURL *downloadedBundleFileURL = persistDownloadedBundle(data, &persistError); + if (downloadedBundleFileURL == nil) { + onComplete(persistError, nil); + return; + } + + NSError *mappingError; + NSData *bundleData = [NSData dataWithContentsOfFile:downloadedBundleFileURL.path + options:NSDataReadingMappedIfSafe + error:&mappingError]; + if (bundleData == nil) { + onComplete(mappingError, nil); + return; + } + + RCTSource *source = RCTSourceCreate(sourceURL, bundleData, bundleData.length); + source->_downloadedBundleFileURL = downloadedBundleFileURL; parseHeaders(headers, source); onComplete(nil, source); } diff --git a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm index 0d5762dbaf67..815e469ec0c0 100644 --- a/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm +++ b/packages/react-native/ReactCommon/react/runtime/platform/ios/ReactCommon/RCTHost.mm @@ -537,10 +537,18 @@ - (void)loadBundleAtURL:(NSURL *)sourceURL onProgress:(RCTSourceLoadProgressBlock)onProgress onComplete:(RCTSourceLoadBlock)loadCallback { + __weak RCTHost *weakSelf = self; + RCTSourceLoadBlock onComplete = ^(NSError *error, RCTSource *source) { + RCTHost *strongSelf = weakSelf; + if (strongSelf != nil && source != nil) { + strongSelf->_bundleManager.downloadedBundleFileURL = source.downloadedBundleFileURL; + } + loadCallback(error, source); + }; if ([_hostDelegate respondsToSelector:@selector(loadBundleAtURL:onProgress:onComplete:)]) { - [_hostDelegate loadBundleAtURL:sourceURL onProgress:onProgress onComplete:loadCallback]; + [_hostDelegate loadBundleAtURL:sourceURL onProgress:onProgress onComplete:onComplete]; } else { - [RCTJavaScriptLoader loadBundleAtURL:sourceURL onProgress:onProgress onComplete:loadCallback]; + [RCTJavaScriptLoader loadBundleAtURL:sourceURL onProgress:onProgress onComplete:onComplete]; } } diff --git a/packages/rn-tester/Build/Intermediates.noindex/XCBuildData/PIFCache/project/PROJECT@v11_mod=4e9ef4a1896968794696d95d96e06272_hash=57ed08ac0db58769a2e543e57ef9df72plugins=1OJSG6M1FOV3XYQCBH7Z29RZ0FPR9XDE1-json b/packages/rn-tester/Build/Intermediates.noindex/XCBuildData/PIFCache/project/PROJECT@v11_mod=4e9ef4a1896968794696d95d96e06272_hash=57ed08ac0db58769a2e543e57ef9df72plugins=1OJSG6M1FOV3XYQCBH7Z29RZ0FPR9XDE1-json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/rn-tester/Build/Intermediates.noindex/XCBuildData/PIFCache/target/TARGET@v11_hash=45d9011e54ab66045cab07ec8ddeea6c-json b/packages/rn-tester/Build/Intermediates.noindex/XCBuildData/PIFCache/target/TARGET@v11_hash=45d9011e54ab66045cab07ec8ddeea6c-json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/rn-tester/Build/Intermediates.noindex/XCBuildData/PIFCache/target/TARGET@v11_hash=7a96253ea5b248aae35741026aaa8116-json b/packages/rn-tester/Build/Intermediates.noindex/XCBuildData/PIFCache/target/TARGET@v11_hash=7a96253ea5b248aae35741026aaa8116-json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/rn-tester/Build/Intermediates.noindex/XCBuildData/PIFCache/target/TARGET@v11_hash=cdea8b75b7d83c6301fe249f3d287f80-json b/packages/rn-tester/Build/Intermediates.noindex/XCBuildData/PIFCache/target/TARGET@v11_hash=cdea8b75b7d83c6301fe249f3d287f80-json new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/packages/rn-tester/Build/Intermediates.noindex/XCBuildData/PIFCache/workspace/WORKSPACE@v11_hash=c51127f0d5a4a2cd5bd45c57af017784_subobjects=84d40f8c62151d31548e0cdc4f2cab1c-json b/packages/rn-tester/Build/Intermediates.noindex/XCBuildData/PIFCache/workspace/WORKSPACE@v11_hash=c51127f0d5a4a2cd5bd45c57af017784_subobjects=84d40f8c62151d31548e0cdc4f2cab1c-json new file mode 100644 index 000000000000..e69de29bb2d1