Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 00a9958

Browse files
authored
Optimize search for the default bundle (#39975)
Optimize search for the default bundle
1 parent 6045e6e commit 00a9958

File tree

3 files changed

+61
-2
lines changed

3 files changed

+61
-2
lines changed

shell/platform/darwin/ios/framework/Source/FlutterDartProject.mm

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,50 @@
3232

3333
static const char* kApplicationKernelSnapshotFileName = "kernel_blob.bin";
3434

35+
// Finds a bundle with the named `bundleID` within `searchURL`.
36+
//
37+
// Returns `nil` if the bundle cannot be found or if errors are encountered.
38+
NSBundle* FLTFrameworkBundleInternal(NSString* bundleID, NSURL* searchURL) {
39+
NSDirectoryEnumerator<NSURL*>* frameworkEnumerator = [NSFileManager.defaultManager
40+
enumeratorAtURL:searchURL
41+
includingPropertiesForKeys:nil
42+
options:NSDirectoryEnumerationSkipsSubdirectoryDescendants |
43+
NSDirectoryEnumerationSkipsHiddenFiles
44+
// Skip directories where errors are encountered.
45+
errorHandler:nil];
46+
47+
for (NSURL* candidate in frameworkEnumerator) {
48+
NSBundle* bundle = [NSBundle bundleWithURL:candidate];
49+
if ([bundle.bundleIdentifier isEqualToString:bundleID]) {
50+
return bundle;
51+
}
52+
}
53+
return nil;
54+
}
55+
56+
// Finds a bundle with the named `bundleID`.
57+
//
58+
// `+[NSBundle bundleWithIdentifier:]` is slow, and can take in the order of
59+
// tens of milliseconds in a minimal flutter app, and closer to 100 milliseconds
60+
// in a medium sized Flutter app on an iPhone 13. It is likely that the slowness
61+
// comes from having to traverse and load all bundles known to the process.
62+
// Using `+[NSBundle allframeworks]` and filtering also suffers from the same
63+
// problem.
64+
//
65+
// This implementation is an optimization to first limit the search space to
66+
// `+[NSBundle privateFrameworksURL]` of the main bundle, which is usually where
67+
// frameworks used by this file are placed. If the desired bundle cannot be
68+
// found here, the implementation falls back to
69+
// `+[NSBundle bundleWithIdentifier:]`.
70+
NS_INLINE NSBundle* FLTFrameworkBundleWithIdentifier(NSString* bundleID) {
71+
NSBundle* bundle = FLTFrameworkBundleInternal(bundleID, NSBundle.mainBundle.privateFrameworksURL);
72+
if (bundle != nil) {
73+
return bundle;
74+
}
75+
// Fallback to slow implementation.
76+
return [NSBundle bundleWithIdentifier:bundleID];
77+
}
78+
3579
flutter::Settings FLTDefaultSettingsForBundle(NSBundle* bundle) {
3680
auto command_line = flutter::CommandLineFromNSProcessInfo();
3781

@@ -46,7 +90,7 @@
4690

4791
bool hasExplicitBundle = bundle != nil;
4892
if (bundle == nil) {
49-
bundle = [NSBundle bundleWithIdentifier:[FlutterDartProject defaultBundleIdentifier]];
93+
bundle = FLTFrameworkBundleWithIdentifier([FlutterDartProject defaultBundleIdentifier]);
5094
}
5195
if (bundle == nil) {
5296
bundle = mainBundle;
@@ -317,7 +361,7 @@ - (instancetype)initWithSettings:(const flutter::Settings&)settings {
317361

318362
+ (NSString*)flutterAssetsName:(NSBundle*)bundle {
319363
if (bundle == nil) {
320-
bundle = [NSBundle bundleWithIdentifier:[FlutterDartProject defaultBundleIdentifier]];
364+
bundle = FLTFrameworkBundleWithIdentifier([FlutterDartProject defaultBundleIdentifier]);
321365
}
322366
if (bundle == nil) {
323367
bundle = [NSBundle mainBundle];

shell/platform/darwin/ios/framework/Source/FlutterDartProjectTest.mm

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,19 @@ - (void)testLeakDartVMSettingsAreCorrectlyParsed {
6060
XCTAssertEqual(settings.leak_vm, NO);
6161
}
6262

63+
- (void)testFLTFrameworkBundleInternalWhenBundleIsNotPresent {
64+
NSBundle* found =
65+
FLTFrameworkBundleInternal(@"doesNotExist", NSBundle.mainBundle.privateFrameworksURL);
66+
XCTAssertNil(found);
67+
}
68+
69+
- (void)testFLTFrameworkBundleInternalWhenBundleIsPresent {
70+
NSString* presentBundleID = @"io.flutter.flutter";
71+
NSBundle* found =
72+
FLTFrameworkBundleInternal(presentBundleID, NSBundle.mainBundle.privateFrameworksURL);
73+
XCTAssertNotNil(found);
74+
}
75+
6376
- (void)testEnableImpellerSettingIsCorrectlyParsed {
6477
// The FLTEnableImpeller's value is defined in Info.plist
6578
NSBundle* mainBundle = [NSBundle mainBundle];

shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
NS_ASSUME_NONNULL_BEGIN
1414

15+
NSBundle* FLTFrameworkBundleInternal(NSString* bundleID, NSURL* searchURL);
16+
1517
flutter::Settings FLTDefaultSettingsForBundle(NSBundle* bundle = nil);
1618

1719
@interface FlutterDartProject ()

0 commit comments

Comments
 (0)