FlutterDartProject.mm 9.6 KB
Newer Older
1 2 3 4
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h"
6

A
Adam Barth 已提交
7
#include "flutter/common/threads.h"
8
#include "flutter/shell/common/shell.h"
9
#include "flutter/shell/common/switches.h"
A
Adam Barth 已提交
10
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartSource.h"
11
#include "flutter/shell/platform/darwin/ios/framework/Source/flutter_main_ios.h"
12
#include "lib/fxl/strings/string_view.h"
13
#include "third_party/dart/runtime/include/dart_api.h"
14

15
static NSURL* URLForSwitch(const fxl::StringView name) {
16
  const auto& cmd = shell::Shell::Shared().GetCommandLine();
17 18
  NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];

19 20 21
  std::string switch_value;
  if (cmd.GetOptionValue(name, &switch_value)) {
    auto url = [NSURL fileURLWithPath:@(switch_value.c_str())];
22
    [defaults setURL:url forKey:@(name.data())];
23 24 25 26
    [defaults synchronize];
    return url;
  }

27
  return [defaults URLForKey:@(name.data())];
28 29
}

30 31 32 33 34 35 36
@implementation FlutterDartProject {
  NSBundle* _precompiledDartBundle;
  FlutterDartSource* _dartSource;

  VMType _vmTypeRequirement;
}

37 38 39 40 41 42
+ (void)initialize {
  if (self == [FlutterDartProject class]) {
    shell::FlutterMain();
  }
}

43 44 45
#pragma mark - Override base class designated initializers

- (instancetype)init {
46
  return [self initWithFLXArchive:nil dartMain:nil packages:nil];
47 48 49 50 51 52 53 54
}

#pragma mark - Designated initializers

- (instancetype)initWithPrecompiledDartBundle:(NSBundle*)bundle {
  self = [super init];

  if (self) {
55
    _precompiledDartBundle = [bundle retain];
56 57 58 59 60 61 62 63 64

    [self checkReadiness];
  }

  return self;
}

- (instancetype)initWithFLXArchive:(NSURL*)archiveURL
                          dartMain:(NSURL*)dartMainURL
65
                          packages:(NSURL*)dartPackages {
66 67 68 69
  self = [super init];

  if (self) {
    _dartSource = [[FlutterDartSource alloc] initWithDartMain:dartMainURL
70
                                                     packages:dartPackages
71 72 73 74 75 76 77 78
                                                   flxArchive:archiveURL];

    [self checkReadiness];
  }

  return self;
}

79 80 81 82
- (instancetype)initWithFLXArchiveWithScriptSnapshot:(NSURL*)archiveURL {
  self = [super init];

  if (self) {
83
    _dartSource = [[FlutterDartSource alloc] initWithFLXArchiveWithScriptSnapshot:archiveURL];
84 85 86 87 88 89 90

    [self checkReadiness];
  }

  return self;
}

91 92 93
#pragma mark - Convenience initializers

- (instancetype)initFromDefaultSourceForConfiguration {
94
  NSBundle* bundle = [NSBundle mainBundle];
95 96 97

  if (Dart_IsPrecompiledRuntime()) {
    // Load from an AOTC snapshot.
98
    return [self initWithPrecompiledDartBundle:bundle];
99 100 101 102
  } else {
    // Load directly from sources if the appropriate command line flags are
    // specified. If not, try loading from a script snapshot in the framework
    // bundle.
103
    NSURL* flxURL = URLForSwitch(shell::FlagForSwitch(shell::Switch::FLX));
104 105 106 107

    if (flxURL == nil) {
      // If the URL was not specified on the command line, look inside the
      // FlutterApplication bundle.
108 109 110 111 112 113 114 115 116 117
      NSString* flxPath = [self pathForFLXFromBundle:bundle];
      if (flxPath != nil) {
        flxURL = [NSURL fileURLWithPath:flxPath isDirectory:NO];
      }
    }

    if (flxURL == nil) {
      NSLog(@"Error: FLX file not present in bundle; unable to start app.");
      [self release];
      return nil;
118 119
    }

120 121
    NSURL* dartMainURL = URLForSwitch(shell::FlagForSwitch(shell::Switch::MainDartFile));
    NSURL* dartPackagesURL = URLForSwitch(shell::FlagForSwitch(shell::Switch::Packages));
122

123
    return [self initWithFLXArchive:flxURL dartMain:dartMainURL packages:dartPackagesURL];
124 125 126 127 128
  }

  NSAssert(NO, @"Unreachable");
  [self release];
  return nil;
129 130
}

131 132 133 134 135 136 137 138 139 140 141 142 143 144
#pragma mark - Common initialization tasks

- (void)checkReadiness {
  if (_precompiledDartBundle != nil) {
    _vmTypeRequirement = VMTypePrecompilation;
    return;
  }

  if (_dartSource != nil) {
    _vmTypeRequirement = VMTypeInterpreter;
    return;
  }
}

145 146 147 148 149 150 151 152 153 154
- (NSString*)pathForFLXFromBundle:(NSBundle*)bundle {
  NSString* flxName = [bundle objectForInfoDictionaryKey:@"FLTFlxName"];
  if (flxName == nil) {
    // Default to "app.flx"
    flxName = @"app";
  }

  return [bundle pathForResource:flxName ofType:@"flx"];
}

155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
#pragma mark - Launching the project in a preconfigured engine.

static NSString* NSStringFromVMType(VMType type) {
  switch (type) {
    case VMTypeInvalid:
      return @"Invalid";
    case VMTypeInterpreter:
      return @"Interpreter";
    case VMTypePrecompilation:
      return @"Precompilation";
  }

  return @"Unknown";
}

A
Adam Barth 已提交
170
- (void)launchInEngine:(shell::Engine*)engine
171
        withEntrypoint:(NSString*)entrypoint
172 173 174 175 176 177 178 179 180 181 182 183 184
        embedderVMType:(VMType)embedderVMType
                result:(LaunchResult)result {
  if (_vmTypeRequirement == VMTypeInvalid) {
    result(NO, @"The Dart project is invalid and cannot be loaded by any VM.");
    return;
  }

  if (embedderVMType == VMTypeInvalid) {
    result(NO, @"The embedder is invalid.");
    return;
  }

  if (_vmTypeRequirement != embedderVMType) {
185
    NSString* message =
186 187 188 189 190
        [NSString stringWithFormat:
                      @"Could not load the project because of differing project type. "
                      @"The project can run in '%@' but the embedder is configured as "
                      @"'%@'",
                      NSStringFromVMType(_vmTypeRequirement), NSStringFromVMType(embedderVMType)];
191 192 193 194 195 196
    result(NO, message);
    return;
  }

  switch (_vmTypeRequirement) {
    case VMTypeInterpreter:
197
      [self runFromSourceInEngine:engine withEntrypoint:entrypoint result:result];
198 199
      return;
    case VMTypePrecompilation:
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
      [self runFromPrecompiledSourceInEngine:engine withEntrypoint:entrypoint result:result];
      return;
    case VMTypeInvalid:
      break;
  }

  return result(NO, @"Internal error");
}

- (void)launchInEngine:(shell::Engine*)engine
        embedderVMType:(VMType)embedderVMType
                result:(LaunchResult)result {
  if (_vmTypeRequirement == VMTypeInvalid) {
    result(NO, @"The Dart project is invalid and cannot be loaded by any VM.");
    return;
  }

  if (embedderVMType == VMTypeInvalid) {
    result(NO, @"The embedder is invalid.");
    return;
  }

  if (_vmTypeRequirement != embedderVMType) {
    NSString* message =
        [NSString stringWithFormat:
                      @"Could not load the project because of differing project type. "
                      @"The project can run in '%@' but the embedder is configured as "
                      @"'%@'",
                      NSStringFromVMType(_vmTypeRequirement), NSStringFromVMType(embedderVMType)];
    result(NO, message);
    return;
  }

  switch (_vmTypeRequirement) {
    case VMTypeInterpreter:
      [self runFromSourceInEngine:engine withEntrypoint:@"main" result:result];
      return;
    case VMTypePrecompilation:
      [self runFromPrecompiledSourceInEngine:engine withEntrypoint:@"main" result:result];
239
      return;
240 241 242 243 244 245 246 247 248
    case VMTypeInvalid:
      break;
  }

  return result(NO, @"Internal error");
}

#pragma mark - Running from precompiled application bundles

249 250 251
- (void)runFromPrecompiledSourceInEngine:(shell::Engine*)engine
                          withEntrypoint:(NSString*)entrypoint
                                  result:(LaunchResult)result {
252 253
  if (![_precompiledDartBundle load]) {
    NSString* message = [NSString
254 255
        stringWithFormat:@"Could not load the framework ('%@') containing precompiled code.",
                         _precompiledDartBundle.bundleIdentifier];
256 257 258 259
    result(NO, message);
    return;
  }

260
  NSString* path = [self pathForFLXFromBundle:_precompiledDartBundle];
261
  if (path.length == 0) {
262 263 264 265
    NSString* message = [NSString stringWithFormat:
                                      @"Could not find the 'app.flx' archive in "
                                      @"the precompiled Dart bundle with ID '%@'",
                                      _precompiledDartBundle.bundleIdentifier];
266 267 268 269
    result(NO, message);
    return;
  }

A
Adam Barth 已提交
270
  std::string bundle_path = path.UTF8String;
271 272 273
  blink::Threads::UI()->PostTask([
    engine = engine->GetWeakPtr(), bundle_path, entrypoint = std::string([entrypoint UTF8String])
  ] {
274
    if (engine)
275
      engine->RunBundle(bundle_path, entrypoint);
276
  });
A
Adam Barth 已提交
277

278 279 280 281 282
  result(YES, @"Success");
}

#pragma mark - Running from source

283 284 285
- (void)runFromSourceInEngine:(shell::Engine*)engine
               withEntrypoint:(NSString*)entrypoint
                       result:(LaunchResult)result {
286 287 288 289 290 291 292 293 294 295
  if (_dartSource == nil) {
    result(NO, @"Dart source not specified.");
    return;
  }

  [_dartSource validate:^(BOOL success, NSString* message) {
    if (!success) {
      return result(NO, message);
    }

296
    std::string bundle_path = _dartSource.flxArchive.absoluteURL.path.UTF8String;
A
Adam Barth 已提交
297

298
    if (_dartSource.archiveContainsScriptSnapshot) {
299 300 301 302
      blink::Threads::UI()->PostTask([
        engine = engine->GetWeakPtr(), bundle_path,
        entrypoint = std::string([entrypoint UTF8String])
      ] {
303
        if (engine)
304
          engine->RunBundle(bundle_path, entrypoint);
305
      });
306
    } else {
A
Adam Barth 已提交
307 308
      std::string main = _dartSource.dartMain.absoluteURL.path.UTF8String;
      std::string packages = _dartSource.packages.absoluteURL.path.UTF8String;
309 310 311 312 313
      blink::Threads::UI()->PostTask(
          [ engine = engine->GetWeakPtr(), bundle_path, main, packages ] {
            if (engine)
              engine->RunBundleAndSource(bundle_path, main, packages);
          });
314 315
    }

316 317 318 319 320 321 322
    result(YES, @"Success");
  }];
}

#pragma mark - Misc.

- (void)dealloc {
323
  [_precompiledDartBundle unload];
324 325 326 327 328 329 330
  [_precompiledDartBundle release];
  [_dartSource release];

  [super dealloc];
}

@end