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