Flutter macOS Embedder
FlutterDartProject.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
7 
8 #include <vector>
9 
10 #import <Metal/Metal.h>
11 
13 
14 static NSString* const kICUBundlePath = @"icudtl.dat";
15 
17  static BOOL result = NO;
18  static dispatch_once_t once_token = 0;
19  dispatch_once(&once_token, ^{
20  id<MTLDevice> device = MTLCreateSystemDefaultDevice();
21  // Wide gamut on macOS requires Apple3+ GPU family (Apple Silicon M1+).
22  // This uses 10-bit BGRA format (same as iOS) for consistency.
23  // Intel Macs (Mac1/Mac2 family) do not support wide gamut.
24  result = [device supportsFamily:MTLGPUFamilyApple3];
25  });
26  return result;
27 }
28 static NSString* const kAppBundleIdentifier = @"io.flutter.flutter.app";
29 
30 #pragma mark - Private interface declaration.
31 @interface FlutterDartProject ()
32 /**
33  Get the Flutter assets name path by pass the bundle. If bundle is nil, we use the main bundle as
34  default.
35  */
36 + (NSString*)flutterAssetsNameWithBundle:(NSBundle*)bundle;
37 @end
38 
39 @implementation FlutterDartProject {
40  NSBundle* _dartBundle;
41  NSString* _assetsPath;
42  NSString* _ICUDataPath;
43 }
44 
45 - (instancetype)init {
46  return [self initWithPrecompiledDartBundle:nil];
47 }
48 
49 - (instancetype)initWithPrecompiledDartBundle:(NSBundle*)bundle {
50  self = [super init];
51  NSAssert(self, @"Super init cannot be nil");
52 
54  if (_dartBundle == nil) {
55  // The bundle isn't loaded and can't be found by bundle ID. Find it by path.
56  _dartBundle = [NSBundle bundleWithURL:[NSBundle.mainBundle.privateFrameworksURL
57  URLByAppendingPathComponent:@"App.framework"]];
58  }
59  if (!_dartBundle.isLoaded) {
60  [_dartBundle load];
61  }
62  _dartEntrypointArguments = [[NSProcessInfo processInfo] arguments];
63  // Remove the first element as it's the binary name
64  _dartEntrypointArguments = [_dartEntrypointArguments
65  subarrayWithRange:NSMakeRange(1, _dartEntrypointArguments.count - 1)];
66  return self;
67 }
68 
69 - (instancetype)initWithAssetsPath:(NSString*)assets ICUDataPath:(NSString*)icuPath {
70  self = [super init];
71  NSAssert(self, @"Super init cannot be nil");
72  _assetsPath = assets;
73  _ICUDataPath = icuPath;
74  return self;
75 }
76 
77 - (BOOL)enableImpeller {
78  NSNumber* enableImpeller =
79  [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTEnableImpeller"];
80  if (enableImpeller != nil) {
81  return enableImpeller.boolValue;
82  }
83  return NO;
84 }
85 
86 - (BOOL)enableWideGamut {
87  NSNumber* enableWideGamut =
88  [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTEnableWideGamut"];
89  if (enableWideGamut != nil) {
90  return enableWideGamut.boolValue && DoesHardwareSupportWideGamut();
91  }
92  return NO;
93 }
94 
95 - (BOOL)enableFlutterGPU {
96  NSNumber* enableFlutterGPU =
97  [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTEnableFlutterGPU"];
98  if (enableFlutterGPU != nil) {
99  return enableFlutterGPU.boolValue;
100  }
101  return NO;
102 }
103 
104 - (NSString*)assetsPath {
105  if (_assetsPath) {
106  return _assetsPath;
107  }
108 
109  // If there's no App.framework, fall back to checking the main bundle for assets.
110  NSBundle* assetBundle = _dartBundle ?: [NSBundle mainBundle];
111  NSString* flutterAssetsName = [assetBundle objectForInfoDictionaryKey:@"FLTAssetsPath"];
112  if (flutterAssetsName == nil) {
113  flutterAssetsName = @"flutter_assets";
114  }
115  NSString* path = [assetBundle pathForResource:flutterAssetsName ofType:@""];
116  if (!path) {
117  NSLog(@"Failed to find path for \"%@\"", flutterAssetsName);
118  }
119  return path;
120 }
121 
122 - (NSString*)ICUDataPath {
123  if (_ICUDataPath) {
124  return _ICUDataPath;
125  }
126 
127  NSString* path = [[NSBundle bundleForClass:[self class]] pathForResource:kICUBundlePath
128  ofType:nil];
129  if (!path) {
130  NSLog(@"Failed to find path for \"%@\"", kICUBundlePath);
131  }
132  return path;
133 }
134 
135 + (NSString*)flutterAssetsNameWithBundle:(NSBundle*)bundle {
136  if (bundle == nil) {
138  }
139  if (bundle == nil) {
140  bundle = [NSBundle mainBundle];
141  }
142  NSString* flutterAssetsName = [bundle objectForInfoDictionaryKey:@"FLTAssetsPath"];
143  if (flutterAssetsName == nil) {
144  flutterAssetsName = @"Contents/Frameworks/App.framework/Resources/flutter_assets";
145  }
146  return flutterAssetsName;
147 }
148 
149 + (NSString*)lookupKeyForAsset:(NSString*)asset {
150  return [self lookupKeyForAsset:asset fromBundle:nil];
151 }
152 
153 + (NSString*)lookupKeyForAsset:(NSString*)asset fromBundle:(nullable NSBundle*)bundle {
154  NSString* flutterAssetsName = [FlutterDartProject flutterAssetsNameWithBundle:bundle];
155  return [NSString stringWithFormat:@"%@/%@", flutterAssetsName, asset];
156 }
157 
158 + (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
159  return [self lookupKeyForAsset:asset fromPackage:package fromBundle:nil];
160 }
161 
162 + (NSString*)lookupKeyForAsset:(NSString*)asset
163  fromPackage:(NSString*)package
164  fromBundle:(nullable NSBundle*)bundle {
165  return [self lookupKeyForAsset:[NSString stringWithFormat:@"packages/%@/%@", package, asset]
166  fromBundle:bundle];
167 }
168 
169 + (NSString*)defaultBundleIdentifier {
170  return kAppBundleIdentifier;
171 }
172 
173 @end
static NSString *const kAppBundleIdentifier
static NSString *const kICUBundlePath
NSString * _ICUDataPath
NSString * _assetsPath
static BOOL DoesHardwareSupportWideGamut()
NSBundle * FLTFrameworkBundleWithIdentifier(NSString *flutterFrameworkBundleID)