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 - (BOOL)enableSDFs {
105  NSNumber* enableSDFs = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"FLTEnableSDFs"];
106  if (enableSDFs != nil) {
107  return enableSDFs.boolValue;
108  }
109  return NO;
110 }
111 
112 - (NSString*)assetsPath {
113  if (_assetsPath) {
114  return _assetsPath;
115  }
116 
117  // If there's no App.framework, fall back to checking the main bundle for assets.
118  NSBundle* assetBundle = _dartBundle ?: [NSBundle mainBundle];
119  NSString* flutterAssetsName = [assetBundle objectForInfoDictionaryKey:@"FLTAssetsPath"];
120  if (flutterAssetsName == nil) {
121  flutterAssetsName = @"flutter_assets";
122  }
123  NSString* path = [assetBundle pathForResource:flutterAssetsName ofType:@""];
124  if (!path) {
125  NSLog(@"Failed to find path for \"%@\"", flutterAssetsName);
126  }
127  return path;
128 }
129 
130 - (NSString*)ICUDataPath {
131  if (_ICUDataPath) {
132  return _ICUDataPath;
133  }
134 
135  NSString* path = [[NSBundle bundleForClass:[self class]] pathForResource:kICUBundlePath
136  ofType:nil];
137  if (!path) {
138  NSLog(@"Failed to find path for \"%@\"", kICUBundlePath);
139  }
140  return path;
141 }
142 
143 + (NSString*)flutterAssetsNameWithBundle:(NSBundle*)bundle {
144  if (bundle == nil) {
146  }
147  if (bundle == nil) {
148  bundle = [NSBundle mainBundle];
149  }
150  NSString* flutterAssetsName = [bundle objectForInfoDictionaryKey:@"FLTAssetsPath"];
151  if (flutterAssetsName == nil) {
152  flutterAssetsName = @"Contents/Frameworks/App.framework/Resources/flutter_assets";
153  }
154  return flutterAssetsName;
155 }
156 
157 + (NSString*)lookupKeyForAsset:(NSString*)asset {
158  return [self lookupKeyForAsset:asset fromBundle:nil];
159 }
160 
161 + (NSString*)lookupKeyForAsset:(NSString*)asset fromBundle:(nullable NSBundle*)bundle {
162  NSString* flutterAssetsName = [FlutterDartProject flutterAssetsNameWithBundle:bundle];
163  return [NSString stringWithFormat:@"%@/%@", flutterAssetsName, asset];
164 }
165 
166 + (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
167  return [self lookupKeyForAsset:asset fromPackage:package fromBundle:nil];
168 }
169 
170 + (NSString*)lookupKeyForAsset:(NSString*)asset
171  fromPackage:(NSString*)package
172  fromBundle:(nullable NSBundle*)bundle {
173  return [self lookupKeyForAsset:[NSString stringWithFormat:@"packages/%@/%@", package, asset]
174  fromBundle:bundle];
175 }
176 
177 + (NSString*)defaultBundleIdentifier {
178  return kAppBundleIdentifier;
179 }
180 
181 @end
static NSString *const kAppBundleIdentifier
static NSString *const kICUBundlePath
NSString * _ICUDataPath
NSString * _assetsPath
static BOOL DoesHardwareSupportWideGamut()
NSBundle * FLTFrameworkBundleWithIdentifier(NSString *flutterFrameworkBundleID)