From 7f9c045c262a6a6247c403ab53510e3187134c54 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 11:34:11 -0700 Subject: [PATCH 01/35] iOS: Migrate FlutterViewController to ARC --- shell/platform/darwin/ios/BUILD.gn | 6 +- .../framework/Source/FlutterViewController.mm | 242 +++++++++--------- 2 files changed, 120 insertions(+), 128 deletions(-) diff --git a/shell/platform/darwin/ios/BUILD.gn b/shell/platform/darwin/ios/BUILD.gn index 37f6b3fe65eb0..02ea92bc9d500 100644 --- a/shell/platform/darwin/ios/BUILD.gn +++ b/shell/platform/darwin/ios/BUILD.gn @@ -105,6 +105,8 @@ source_set("flutter_framework_source_arc") { "framework/Source/FlutterUndoManagerPlugin.mm", "framework/Source/FlutterView.h", "framework/Source/FlutterView.mm", + "framework/Source/FlutterViewController.mm", + "framework/Source/FlutterViewController_Internal.h", "framework/Source/FlutterViewResponder.h", "framework/Source/KeyCodeMap.g.mm", "framework/Source/KeyCodeMap_Internal.h", @@ -175,6 +177,7 @@ source_set("flutter_framework_source_arc") { "//flutter/shell/platform/darwin/graphics", "//flutter/shell/platform/embedder:embedder_as_internal_library", "//flutter/shell/profiling:profiling", + "//flutter/third_party/spring_animation", ] } @@ -189,8 +192,6 @@ source_set("flutter_framework_source") { # iOS embedder is migrating to ARC. # New files are highly encouraged to be in ARC. # To add new files in ARC, add them to the `flutter_framework_source_arc` target. - "framework/Source/FlutterViewController.mm", - "framework/Source/FlutterViewController_Internal.h", "platform_view_ios.h", "platform_view_ios.mm", ] @@ -210,7 +211,6 @@ source_set("flutter_framework_source") { "//flutter/shell/platform/darwin/common:framework_common", "//flutter/shell/platform/embedder:embedder_as_internal_library", "//flutter/shell/profiling:profiling", - "//flutter/third_party/spring_animation", ] public_configs = [ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index ebf48bdd1076a..57fc72fd8864d 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -78,7 +78,7 @@ @interface FlutterViewController () > _weakFactory; fml::scoped_nsobject _engine; // We keep a separate reference to this and create it ahead of time because we want to be able to // set up a shell along with its platform view before the view has to appear. fml::scoped_nsobject _flutterView; - fml::scoped_nsobject _splashScreenView; fml::ScopedBlock _flutterViewRenderedCallback; UIInterfaceOrientationMask _orientationPreferences; UIStatusBarStyle _statusBarStyle; @@ -173,7 +174,7 @@ - (instancetype)initWithEngine:(FlutterEngine*)engine "FlutterViewController at a time. Set FlutterEngine.viewController " "to nil before attaching it to another FlutterViewController."; } - _engine.reset([engine retain]); + _engine.reset(engine); _engineNeedsLaunch = NO; _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque @@ -236,7 +237,7 @@ - (void)sharedSetupWithProject:(nullable FlutterDartProject*)project // Need the project to get settings for the view. Initializing it here means // the Engine class won't initialize it later. if (!project) { - project = [[[FlutterDartProject alloc] init] autorelease]; + project = [[FlutterDartProject alloc] init]; } FlutterView.forceSoftwareRendering = project.settings.enable_software_rendering; _weakFactory = std::make_unique>(self); @@ -465,7 +466,7 @@ - (void)pushRoute:(NSString*)route { return existing_view; } - auto placeholder = [[[UIView alloc] init] autorelease]; + auto placeholder = [[UIView alloc] init]; placeholder.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; if (@available(iOS 13.0, *)) { @@ -479,7 +480,7 @@ - (void)pushRoute:(NSString*)route { // Otherwise, a spurious warning will be shown in cases where an engine cannot be initialized for // other reasons. if (flutter::GetTracingResult() == flutter::TracingResult::kDisabled) { - auto messageLabel = [[[UILabel alloc] init] autorelease]; + auto messageLabel = [[UILabel alloc] init]; messageLabel.numberOfLines = 0u; messageLabel.textAlignment = NSTextAlignmentCenter; messageLabel.autoresizingMask = @@ -556,9 +557,9 @@ - (BOOL)scrollViewShouldScrollToTop:(UIScrollView*)scrollView { - (void)installSplashScreenViewIfNecessary { // Show the launch screen view again on top of the FlutterView if available. // This launch screen view will be removed once the first Flutter frame is rendered. - if (_splashScreenView && (self.isBeingPresented || self.isMovingToParentViewController)) { - [_splashScreenView.get() removeFromSuperview]; - _splashScreenView.reset(); + if (self.splashScreenView && (self.isBeingPresented || self.isMovingToParentViewController)) { + [self.splashScreenView removeFromSuperview]; + self.splashScreenView = nil; return; } @@ -597,16 +598,15 @@ - (void)callViewRenderedCallback { } - (void)removeSplashScreenView:(dispatch_block_t _Nullable)onComplete { - NSAssert(_splashScreenView, @"The splash screen view must not be null"); - UIView* splashScreen = [_splashScreenView.get() retain]; - _splashScreenView.reset(); + NSAssert(self.splashScreenView, @"The splash screen view must not be nil"); + UIView* splashScreen = self.splashScreenView; + _splashScreenView = nil; [UIView animateWithDuration:0.2 animations:^{ splashScreen.alpha = 0; } completion:^(BOOL finished) { [splashScreen removeFromSuperview]; - [splashScreen release]; if (onComplete) { onComplete(); } @@ -624,24 +624,24 @@ - (void)installFirstFrameCallback { } // Start on the platform thread. - weakPlatformView->SetNextFrameCallback([weakSelf = [self getWeakNSObject], + __weak FlutterViewController* weakSelf = self; + weakPlatformView->SetNextFrameCallback([weakSelf, platformTaskRunner = [_engine.get() platformTaskRunner], rasterTaskRunner = [_engine.get() rasterTaskRunner]]() { FML_DCHECK(rasterTaskRunner->RunsTasksOnCurrentThread()); // Get callback on raster thread and jump back to platform thread. platformTaskRunner->PostTask([weakSelf]() { - if (weakSelf) { - fml::scoped_nsobject flutterViewController( - [(FlutterViewController*)weakSelf.get() retain]); - if (flutterViewController) { - if (flutterViewController.get()->_splashScreenView) { - [flutterViewController removeSplashScreenView:^{ - [flutterViewController callViewRenderedCallback]; - }]; - } else { - [flutterViewController callViewRenderedCallback]; - } - } + FlutterViewController* strongSelf = weakSelf; + if (!strongSelf) { + return; + } + + if (strongSelf.splashScreenView) { + [strongSelf removeSplashScreenView:^{ + [strongSelf callViewRenderedCallback]; + }]; + } else { + [strongSelf callViewRenderedCallback]; } }); }); @@ -655,13 +655,6 @@ - (int64_t)viewIdentifier { return flutter::kFlutterImplicitViewId; } -- (UIView*)splashScreenView { - if (!_splashScreenView) { - return nil; - } - return _splashScreenView.get(); -} - - (UIView*)keyboardAnimationView { return _keyboardAnimationView.get(); } @@ -716,16 +709,20 @@ - (UIView*)splashScreenFromXib:(NSString*)name { } - (void)setSplashScreenView:(UIView*)view { + if (view == self.splashScreenView) { + return; + } + + // Special case: user wants to remove the splash screen view. if (!view) { - // Special case: user wants to remove the splash screen view. - if (_splashScreenView) { + if (self.splashScreenView) { [self removeSplashScreenView:nil]; } return; } - _splashScreenView.reset([view retain]); - _splashScreenView.get().autoresizingMask = + _splashScreenView = view; + _splashScreenView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; } @@ -812,7 +809,7 @@ - (void)viewDidLoad { } - (void)addInternalPlugins { - self.keyboardManager = [[[FlutterKeyboardManager alloc] init] autorelease]; + self.keyboardManager = [[FlutterKeyboardManager alloc] init]; fml::WeakNSObject weakSelf = [self getWeakNSObject]; FlutterSendKeyEvent sendEvent = ^(const FlutterKeyEvent& event, FlutterKeyEventCallback callback, void* userData) { @@ -820,10 +817,10 @@ - (void)addInternalPlugins { [weakSelf.get()->_engine.get() sendKeyEvent:event callback:callback userData:userData]; } }; - [self.keyboardManager addPrimaryResponder:[[[FlutterEmbedderKeyResponder alloc] - initWithSendEvent:sendEvent] autorelease]]; - FlutterChannelKeyResponder* responder = [[[FlutterChannelKeyResponder alloc] - initWithChannel:self.engine.keyEventChannel] autorelease]; + [self.keyboardManager + addPrimaryResponder:[[FlutterEmbedderKeyResponder alloc] initWithSendEvent:sendEvent]]; + FlutterChannelKeyResponder* responder = + [[FlutterChannelKeyResponder alloc] initWithChannel:self.engine.keyEventChannel]; [self.keyboardManager addPrimaryResponder:responder]; FlutterTextInputPlugin* textInputPlugin = self.engine.textInputPlugin; if (textInputPlugin != nil) { @@ -917,6 +914,7 @@ - (void)viewWillTransitionToSize:(CGSize)size return; } + __weak FlutterViewController* weakSelf = self; _shouldIgnoreViewportMetricsUpdatesDuringRotation = YES; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, static_cast(transitionDuration / 2.0 * NSEC_PER_SEC)), @@ -924,7 +922,7 @@ - (void)viewWillTransitionToSize:(CGSize)size // `viewWillTransitionToSize` is only called after the previous rotation is // complete. So there won't be race condition for this flag. _shouldIgnoreViewportMetricsUpdatesDuringRotation = NO; - [self updateViewportMetricsIfNeeded]; + [weakSelf updateViewportMetricsIfNeeded]; }); } @@ -976,18 +974,15 @@ - (void)dealloc { [self invalidateKeyboardAnimationVSyncClient]; [self invalidateTouchRateCorrectionVSyncClient]; + + // TODO(cbracken): https://github.com/flutter/flutter/issues/156222 + // Ensure all delegates are weak and remove this. _scrollView.get().delegate = nil; _hoverGestureRecognizer.delegate = nil; - [_hoverGestureRecognizer release]; _discreteScrollingPanGestureRecognizer.delegate = nil; - [_discreteScrollingPanGestureRecognizer release]; _continuousScrollingPanGestureRecognizer.delegate = nil; - [_continuousScrollingPanGestureRecognizer release]; _pinchGestureRecognizer.delegate = nil; - [_pinchGestureRecognizer release]; _rotationGestureRecognizer.delegate = nil; - [_rotationGestureRecognizer release]; - [super dealloc]; } #pragma mark - Application lifecycle notifications @@ -1367,7 +1362,6 @@ - (void)triggerTouchRateCorrectionIfNeeded:(NSSet*)touches { - (void)invalidateTouchRateCorrectionVSyncClient { [_touchRateCorrectionVSyncClient invalidate]; - [_touchRateCorrectionVSyncClient release]; _touchRateCorrectionVSyncClient = nil; } @@ -1718,74 +1712,79 @@ - (void)startKeyBoardAnimation:(NSTimeInterval)duration { // Invalidate old vsync client if old animation is not completed. [self invalidateKeyboardAnimationVSyncClient]; - fml::WeakNSObject weakSelf = [self getWeakNSObject]; - FlutterKeyboardAnimationCallback keyboardAnimationCallback = ^( - fml::TimePoint keyboardAnimationTargetTime) { - if (!weakSelf) { - return; - } - fml::scoped_nsobject flutterViewController( - [(FlutterViewController*)weakSelf.get() retain]); - if (!flutterViewController) { - return; - } + __weak FlutterViewController* weakSelf = self; + FlutterKeyboardAnimationCallback keyboardAnimationCallback = + ^(fml::TimePoint keyboardAnimationTargetTime) { + FlutterViewController* strongSelf = weakSelf; + if (!strongSelf) { + return; + } - // If the view controller's view is not loaded, bail out. - if (!flutterViewController.get().isViewLoaded) { - return; - } - // If the view for tracking keyboard animation is nil, means it is not - // created, bail out. - if ([flutterViewController keyboardAnimationView] == nil) { - return; - } - // If keyboardAnimationVSyncClient is nil, means the animation ends. - // And should bail out. - if (flutterViewController.get().keyboardAnimationVSyncClient == nil) { - return; - } + // If the view controller's view is not loaded, bail out. + if (!strongSelf.isViewLoaded) { + return; + } + // If the view for tracking keyboard animation is nil, means it is not + // created, bail out. + if (!strongSelf.keyboardAnimationView) { + return; + } + // If keyboardAnimationVSyncClient is nil, means the animation ends. + // And should bail out. + if (!strongSelf.keyboardAnimationVSyncClient) { + return; + } - if ([flutterViewController keyboardAnimationView].superview == nil) { - // Ensure the keyboardAnimationView is in view hierarchy when animation running. - [flutterViewController.get().view addSubview:[flutterViewController keyboardAnimationView]]; - } + if (!strongSelf.keyboardAnimationView.superview) { + // Ensure the keyboardAnimationView is in view hierarchy when animation running. + [strongSelf.view addSubview:strongSelf.keyboardAnimationView]; + } - if ([flutterViewController keyboardSpringAnimation] == nil) { - if (flutterViewController.get().keyboardAnimationView.layer.presentationLayer) { - flutterViewController.get()->_viewportMetrics.physical_view_inset_bottom = - flutterViewController.get() - .keyboardAnimationView.layer.presentationLayer.frame.origin.y; - [flutterViewController updateViewportMetricsIfNeeded]; - } - } else { - fml::TimeDelta timeElapsed = - keyboardAnimationTargetTime - flutterViewController.get().keyboardAnimationStartTime; - flutterViewController.get()->_viewportMetrics.physical_view_inset_bottom = - [[flutterViewController keyboardSpringAnimation] curveFunction:timeElapsed.ToSecondsF()]; - [flutterViewController updateViewportMetricsIfNeeded]; - } - }; + if (!strongSelf.keyboardSpringAnimation) { + if (strongSelf.keyboardAnimationView.layer.presentationLayer) { + strongSelf->_viewportMetrics.physical_view_inset_bottom = + strongSelf.keyboardAnimationView.layer.presentationLayer.frame.origin.y; + [strongSelf updateViewportMetricsIfNeeded]; + } + } else { + fml::TimeDelta timeElapsed = + keyboardAnimationTargetTime - strongSelf.keyboardAnimationStartTime; + strongSelf->_viewportMetrics.physical_view_inset_bottom = + [[strongSelf keyboardSpringAnimation] curveFunction:timeElapsed.ToSecondsF()]; + [strongSelf updateViewportMetricsIfNeeded]; + } + }; [self setUpKeyboardAnimationVsyncClient:keyboardAnimationCallback]; VSyncClient* currentVsyncClient = _keyboardAnimationVSyncClient; [UIView animateWithDuration:duration animations:^{ + FlutterViewController* strongSelf = weakSelf; + if (!strongSelf) { + return; + } + // Set end value. - [self keyboardAnimationView].frame = CGRectMake(0, self.targetViewInsetBottom, 0, 0); + [strongSelf keyboardAnimationView].frame = CGRectMake(0, self.targetViewInsetBottom, 0, 0); // Setup keyboard animation interpolation. CAAnimation* keyboardAnimation = - [[self keyboardAnimationView].layer animationForKey:@"position"]; - [self setUpKeyboardSpringAnimationIfNeeded:keyboardAnimation]; + [[strongSelf keyboardAnimationView].layer animationForKey:@"position"]; + [strongSelf setUpKeyboardSpringAnimationIfNeeded:keyboardAnimation]; } completion:^(BOOL finished) { if (_keyboardAnimationVSyncClient == currentVsyncClient) { + FlutterViewController* strongSelf = weakSelf; + if (!strongSelf) { + return; + } + // Indicates the vsync client captured by this block is the original one, which also // indicates the animation has not been interrupted from its beginning. Moreover, // indicates the animation is over and there is no more to execute. - [self invalidateKeyboardAnimationVSyncClient]; - [self removeKeyboardAnimationView]; - [self ensureViewportMetricsIsCorrect]; + [strongSelf invalidateKeyboardAnimationVSyncClient]; + [strongSelf removeKeyboardAnimationView]; + [strongSelf ensureViewportMetricsIsCorrect]; } }]; } @@ -1835,7 +1834,6 @@ - (void)setUpKeyboardAnimationVsyncClient: - (void)invalidateKeyboardAnimationVSyncClient { [_keyboardAnimationVSyncClient invalidate]; - [_keyboardAnimationVSyncClient release]; _keyboardAnimationVSyncClient = nil; } @@ -1909,8 +1907,7 @@ - (void)pressesBegan:(NSSet*)presses withEvent:(UIPressesEvent*)event API_AVAILABLE(ios(9.0)) { if (@available(iOS 13.4, *)) { for (UIPress* press in presses) { - [self handlePressEvent:[[[FlutterUIPressProxy alloc] initWithPress:press - withEvent:event] autorelease] + [self handlePressEvent:[[FlutterUIPressProxy alloc] initWithPress:press withEvent:event] nextAction:^() { [super pressesBegan:[NSSet setWithObject:press] withEvent:event]; }]; @@ -1924,8 +1921,7 @@ - (void)pressesChanged:(NSSet*)presses withEvent:(UIPressesEvent*)event API_AVAILABLE(ios(9.0)) { if (@available(iOS 13.4, *)) { for (UIPress* press in presses) { - [self handlePressEvent:[[[FlutterUIPressProxy alloc] initWithPress:press - withEvent:event] autorelease] + [self handlePressEvent:[[FlutterUIPressProxy alloc] initWithPress:press withEvent:event] nextAction:^() { [super pressesChanged:[NSSet setWithObject:press] withEvent:event]; }]; @@ -1939,8 +1935,7 @@ - (void)pressesEnded:(NSSet*)presses withEvent:(UIPressesEvent*)event API_AVAILABLE(ios(9.0)) { if (@available(iOS 13.4, *)) { for (UIPress* press in presses) { - [self handlePressEvent:[[[FlutterUIPressProxy alloc] initWithPress:press - withEvent:event] autorelease] + [self handlePressEvent:[[FlutterUIPressProxy alloc] initWithPress:press withEvent:event] nextAction:^() { [super pressesEnded:[NSSet setWithObject:press] withEvent:event]; }]; @@ -1954,8 +1949,7 @@ - (void)pressesCancelled:(NSSet*)presses withEvent:(UIPressesEvent*)event API_AVAILABLE(ios(9.0)) { if (@available(iOS 13.4, *)) { for (UIPress* press in presses) { - [self handlePressEvent:[[[FlutterUIPressProxy alloc] initWithPress:press - withEvent:event] autorelease] + [self handlePressEvent:[[FlutterUIPressProxy alloc] initWithPress:press withEvent:event] nextAction:^() { [super pressesCancelled:[NSSet setWithObject:press] withEvent:event]; }]; @@ -1969,15 +1963,14 @@ - (void)pressesCancelled:(NSSet*)presses - (void)onOrientationPreferencesUpdated:(NSNotification*)notification { // Notifications may not be on the iOS UI thread + __weak FlutterViewController* weakSelf = self; dispatch_async(dispatch_get_main_queue(), ^{ NSDictionary* info = notification.userInfo; - NSNumber* update = info[@(flutter::kOrientationUpdateNotificationKey)]; - if (update == nil) { return; } - [self performOrientationUpdate:update.unsignedIntegerValue]; + [weakSelf performOrientationUpdate:update.unsignedIntegerValue]; }); } @@ -1985,8 +1978,8 @@ - (void)requestGeometryUpdateForWindowScenes:(NSSet*)windowScenes API_AVAILABLE(ios(16.0)) { for (UIScene* windowScene in windowScenes) { FML_DCHECK([windowScene isKindOfClass:[UIWindowScene class]]); - UIWindowSceneGeometryPreferencesIOS* preference = [[[UIWindowSceneGeometryPreferencesIOS alloc] - initWithInterfaceOrientations:_orientationPreferences] autorelease]; + UIWindowSceneGeometryPreferencesIOS* preference = [[UIWindowSceneGeometryPreferencesIOS alloc] + initWithInterfaceOrientations:_orientationPreferences]; [(UIWindowScene*)windowScene requestGeometryUpdateWithPreferences:preference errorHandler:^(NSError* error) { @@ -2274,20 +2267,18 @@ - (UIStatusBarStyle)preferredStatusBarStyle { - (void)onPreferredStatusBarStyleUpdated:(NSNotification*)notification { // Notifications may not be on the iOS UI thread + __weak FlutterViewController* weakSelf = self; dispatch_async(dispatch_get_main_queue(), ^{ NSDictionary* info = notification.userInfo; - NSNumber* update = info[@(flutter::kOverlayStyleUpdateNotificationKey)]; - if (update == nil) { return; } NSInteger style = update.integerValue; - if (style != _statusBarStyle) { _statusBarStyle = static_cast(style); - [self setNeedsStatusBarAppearanceUpdate]; + [weakSelf setNeedsStatusBarAppearanceUpdate]; } }); } @@ -2398,10 +2389,11 @@ - (void)presentViewController:(UIViewController*)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion { self.isPresentingViewControllerAnimating = YES; + __weak FlutterViewController* weakSelf = self; [super presentViewController:viewControllerToPresent animated:flag completion:^{ - self.isPresentingViewControllerAnimating = NO; + weakSelf.isPresentingViewControllerAnimating = NO; if (completion) { completion(); } From 60ea3e5957a8411b87403fe8ae9eccf56e9e7427 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 12:17:15 -0700 Subject: [PATCH 02/35] Migrate _engine to property --- .../framework/Source/FlutterViewController.mm | 235 +++++++++--------- 1 file changed, 115 insertions(+), 120 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 57fc72fd8864d..23f2e2b573c73 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -121,7 +121,6 @@ @implementation FlutterViewController { // TODO(cbracken): https://github.com/flutter/flutter/issues/155943 // Eliminate once we can use weak pointers in platform_view_ios.h. std::unique_ptr> _weakFactory; - fml::scoped_nsobject _engine; // We keep a separate reference to this and create it ahead of time because we want to be able to // set up a shell along with its platform view before the view has to appear. @@ -174,7 +173,7 @@ - (instancetype)initWithEngine:(FlutterEngine*)engine "FlutterViewController at a time. Set FlutterEngine.viewController " "to nil before attaching it to another FlutterViewController."; } - _engine.reset(engine); + _engine = engine; _engineNeedsLaunch = NO; _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque @@ -223,7 +222,7 @@ - (instancetype)initWithCoder:(NSCoder*)aDecoder { - (void)awakeFromNib { [super awakeFromNib]; - if (!_engine) { + if (!self.engine) { [self sharedSetupWithProject:nil initialRoute:nil]; } } @@ -245,7 +244,7 @@ - (void)sharedSetupWithProject:(nullable FlutterDartProject*)project initWithName:@"io.flutter" project:project allowHeadlessExecution:self.engineAllowHeadlessExecution - restorationEnabled:[self restorationIdentifier] != nil]}; + restorationEnabled:self.restorationIdentifier != nil]}; if (!engine) { return; @@ -253,10 +252,10 @@ - (void)sharedSetupWithProject:(nullable FlutterDartProject*)project _viewOpaque = YES; _engine = engine; - _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine + _flutterView.reset([[FlutterView alloc] initWithDelegate:self.engine opaque:self.isViewOpaque enableWideGamut:project.isWideGamutEnabled]); - [_engine.get() createShell:nil libraryURI:nil initialRoute:initialRoute]; + [self.engine createShell:nil libraryURI:nil initialRoute:initialRoute]; _engineNeedsLaunch = YES; _ongoingTouches.reset([[NSMutableSet alloc] init]); [self loadDefaultSplashScreenView]; @@ -290,10 +289,6 @@ - (void)performCommonViewControllerInitialization { [self setUpNotificationCenterObservers]; } -- (FlutterEngine*)engine { - return _engine.get(); -} - - (fml::WeakNSObject)getWeakNSObject { return _weakFactory->GetWeakNSObject(); } @@ -448,15 +443,15 @@ - (void)setUpApplicationLifecycleNotifications:(NSNotificationCenter*)center { } - (void)setInitialRoute:(NSString*)route { - [[_engine.get() navigationChannel] invokeMethod:@"setInitialRoute" arguments:route]; + [self.engine.navigationChannel invokeMethod:@"setInitialRoute" arguments:route]; } - (void)popRoute { - [[_engine.get() navigationChannel] invokeMethod:@"popRoute" arguments:nil]; + [self.engine.navigationChannel invokeMethod:@"popRoute" arguments:nil]; } - (void)pushRoute:(NSString*)route { - [[_engine.get() navigationChannel] invokeMethod:@"pushRoute" arguments:route]; + [self.engine.navigationChannel invokeMethod:@"pushRoute" arguments:route]; } #pragma mark - Loading the view @@ -540,14 +535,14 @@ static void SendFakeTouchEvent(UIScreen* screen, } - (BOOL)scrollViewShouldScrollToTop:(UIScrollView*)scrollView { - if (!_engine) { + if (!self.engine) { return NO; } CGPoint statusBarPoint = CGPointZero; - UIScreen* screen = [self flutterScreenIfViewLoaded]; + UIScreen* screen = self.flutterScreenIfViewLoaded; if (screen) { - SendFakeTouchEvent(screen, _engine.get(), statusBarPoint, flutter::PointerData::Change::kDown); - SendFakeTouchEvent(screen, _engine.get(), statusBarPoint, flutter::PointerData::Change::kUp); + SendFakeTouchEvent(screen, self.engine, statusBarPoint, flutter::PointerData::Change::kDown); + SendFakeTouchEvent(screen, self.engine, statusBarPoint, flutter::PointerData::Change::kUp); } return NO; } @@ -614,11 +609,11 @@ - (void)removeSplashScreenView:(dispatch_block_t _Nullable)onComplete { } - (void)installFirstFrameCallback { - if (!_engine) { + if (!self.engine) { return; } - fml::WeakPtr weakPlatformView = [_engine.get() platformView]; + fml::WeakPtr weakPlatformView = self.engine.platformView; if (!weakPlatformView) { return; } @@ -626,8 +621,8 @@ - (void)installFirstFrameCallback { // Start on the platform thread. __weak FlutterViewController* weakSelf = self; weakPlatformView->SetNextFrameCallback([weakSelf, - platformTaskRunner = [_engine.get() platformTaskRunner], - rasterTaskRunner = [_engine.get() rasterTaskRunner]]() { + platformTaskRunner = self.engine.platformTaskRunner, + rasterTaskRunner = self.engine.rasterTaskRunner]() { FML_DCHECK(rasterTaskRunner->RunsTasksOnCurrentThread()); // Get callback on raster thread and jump back to platform thread. platformTaskRunner->PostTask([weakSelf]() { @@ -733,7 +728,7 @@ - (void)setFlutterViewDidRenderCallback:(void (^)(void))callback { #pragma mark - Surface creation and teardown updates - (void)surfaceUpdated:(BOOL)appeared { - if (!_engine) { + if (!self.engine) { return; } @@ -741,14 +736,14 @@ - (void)surfaceUpdated:(BOOL)appeared { // thread. if (appeared) { [self installFirstFrameCallback]; - [_engine.get() platformViewsController]->SetFlutterView(_flutterView.get()); - [_engine.get() platformViewsController]->SetFlutterViewController(self); - [_engine.get() iosPlatformView]->NotifyCreated(); + [self.engine platformViewsController]->SetFlutterView(_flutterView.get()); + [self.engine platformViewsController]->SetFlutterViewController(self); + [self.engine iosPlatformView]->NotifyCreated(); } else { self.displayingFlutterUI = NO; - [_engine.get() iosPlatformView]->NotifyDestroyed(); - [_engine.get() platformViewsController]->SetFlutterView(nullptr); - [_engine.get() platformViewsController]->SetFlutterViewController(nullptr); + [self.engine iosPlatformView]->NotifyDestroyed(); + [self.engine platformViewsController]->SetFlutterView(nullptr); + [self.engine platformViewsController]->SetFlutterViewController(nullptr); } } @@ -757,12 +752,12 @@ - (void)surfaceUpdated:(BOOL)appeared { - (void)viewDidLoad { TRACE_EVENT0("flutter", "viewDidLoad"); - if (_engine && _engineNeedsLaunch) { - [_engine.get() launchEngine:nil libraryURI:nil entrypointArgs:nil]; - [_engine.get() setViewController:self]; + if (self.engine && _engineNeedsLaunch) { + [self.engine launchEngine:nil libraryURI:nil entrypointArgs:nil]; + [self.engine setViewController:self]; _engineNeedsLaunch = NO; - } else if ([_engine.get() viewController] == self) { - [_engine.get() attachView]; + } else if (self.engine.viewController == self) { + [self.engine attachView]; } // Register internal plugins. @@ -810,12 +805,10 @@ - (void)viewDidLoad { - (void)addInternalPlugins { self.keyboardManager = [[FlutterKeyboardManager alloc] init]; - fml::WeakNSObject weakSelf = [self getWeakNSObject]; + __weak FlutterViewController* weakSelf = self; FlutterSendKeyEvent sendEvent = ^(const FlutterKeyEvent& event, FlutterKeyEventCallback callback, void* userData) { - if (weakSelf) { - [weakSelf.get()->_engine.get() sendKeyEvent:event callback:callback userData:userData]; - } + [weakSelf.engine sendKeyEvent:event callback:callback userData:userData]; }; [self.keyboardManager addPrimaryResponder:[[FlutterEmbedderKeyResponder alloc] initWithSendEvent:sendEvent]]; @@ -826,7 +819,7 @@ - (void)addInternalPlugins { if (textInputPlugin != nil) { [self.keyboardManager addSecondaryResponder:textInputPlugin]; } - if ([_engine.get() viewController] == self) { + if (self.engine.viewController == self) { [textInputPlugin setUpIndirectScribbleInteraction:self]; } } @@ -837,7 +830,7 @@ - (void)removeInternalPlugins { - (void)viewWillAppear:(BOOL)animated { TRACE_EVENT0("flutter", "viewWillAppear"); - if ([_engine.get() viewController] == self) { + if (self.engine.viewController == self) { // Send platform settings to Flutter, e.g., platform brightness. [self onUserSettingsChanged:nil]; @@ -846,8 +839,8 @@ - (void)viewWillAppear:(BOOL)animated { if (_viewportMetrics.physical_width) { [self surfaceUpdated:YES]; } - [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.inactive"]; - [[_engine.get() restorationPlugin] markRestorationComplete]; + [[self.engine lifecycleChannel] sendMessage:@"AppLifecycleState.inactive"]; + [[self.engine restorationPlugin] markRestorationComplete]; } [super viewWillAppear:animated]; @@ -855,7 +848,7 @@ - (void)viewWillAppear:(BOOL)animated { - (void)viewDidAppear:(BOOL)animated { TRACE_EVENT0("flutter", "viewDidAppear"); - if ([_engine.get() viewController] == self) { + if (self.engine.viewController == self) { [self onUserSettingsChanged:nil]; [self onAccessibilityStatusChanged:nil]; BOOL stateIsActive = YES; @@ -868,7 +861,7 @@ - (void)viewDidAppear:(BOOL)animated { stateIsActive = UIApplication.sharedApplication.applicationState == UIApplicationStateActive; #endif if (stateIsActive) { - [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.resumed"]; + [self.engine.lifecycleChannel sendMessage:@"AppLifecycleState.resumed"]; } } [super viewDidAppear:animated]; @@ -876,21 +869,21 @@ - (void)viewDidAppear:(BOOL)animated { - (void)viewWillDisappear:(BOOL)animated { TRACE_EVENT0("flutter", "viewWillDisappear"); - if ([_engine.get() viewController] == self) { - [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.inactive"]; + if (self.engine.viewController == self) { + [self.engine.lifecycleChannel sendMessage:@"AppLifecycleState.inactive"]; } [super viewWillDisappear:animated]; } - (void)viewDidDisappear:(BOOL)animated { TRACE_EVENT0("flutter", "viewDidDisappear"); - if ([_engine.get() viewController] == self) { + if (self.engine.viewController == self) { [self invalidateKeyboardAnimationVSyncClient]; [self ensureViewportMetricsIsCorrect]; [self surfaceUpdated:NO]; - [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.paused"]; + [self.engine.lifecycleChannel sendMessage:@"AppLifecycleState.paused"]; [self flushOngoingTouches]; - [_engine.get() notifyLowMemory]; + [self.engine notifyLowMemory]; } [super viewDidDisappear:animated]; @@ -927,7 +920,7 @@ - (void)viewWillTransitionToSize:(CGSize)size } - (void)flushOngoingTouches { - if (_engine && _ongoingTouches.get().count > 0) { + if (self.engine && _ongoingTouches.get().count > 0) { auto packet = std::make_unique(_ongoingTouches.get().count); size_t pointer_index = 0; // If the view controller is going away, we want to flush cancel all the ongoing @@ -953,7 +946,7 @@ - (void)flushOngoingTouches { } [_ongoingTouches removeAllObjects]; - [_engine.get() dispatchPointerDataPacket:std::move(packet)]; + [self.engine dispatchPointerDataPacket:std::move(packet)]; } } @@ -1076,7 +1069,7 @@ - (void)goToApplicationLifecycle:(nonnull NSString*)state { // Accessing self.view will create the view. Instead use viewIfLoaded // to check whether the view is attached to window. if (self.viewIfLoaded.window) { - [[_engine.get() lifecycleChannel] sendMessage:state]; + [self.engine.lifecycleChannel sendMessage:state]; } } @@ -1128,7 +1121,7 @@ - (void)goToApplicationLifecycle:(nonnull NSString*)state { - (void)dispatchTouches:(NSSet*)touches pointerDataChangeOverride:(flutter::PointerData::Change*)overridden_change event:(UIEvent*)event { - if (!_engine) { + if (!self.engine) { return; } @@ -1162,7 +1155,7 @@ - (void)dispatchTouches:(NSSet*)touches // Activate or pause the correction of delivery frame rate of touch events. [self triggerTouchRateCorrectionIfNeeded:touches]; - const CGFloat scale = [self flutterScreenIfViewLoaded].scale; + const CGFloat scale = self.flutterScreenIfViewLoaded.scale; auto packet = std::make_unique(touches.count + touches_to_remove_count); @@ -1285,7 +1278,7 @@ - (void)dispatchTouches:(NSSet*)touches } } - [_engine.get() dispatchPointerDataPacket:std::move(packet)]; + [self.engine dispatchPointerDataPacket:std::move(packet)]; } - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { @@ -1326,7 +1319,8 @@ - (void)createTouchRateCorrectionVSyncClientIfNeeded { return; } - flutter::Shell& shell = [_engine.get() shell]; + // XXX: Errr, shouldn't we be ensuring self.engine isn't nil here? + flutter::Shell& shell = self.engine.shell; auto callback = [](std::unique_ptr recorder) { // Do nothing in this block. Just trigger system to callback touch events with correct rate. }; @@ -1353,7 +1347,7 @@ - (void)triggerTouchRateCorrectionIfNeeded:(NSSet*)touches { } } - if (isUserInteracting && [_engine.get() viewController] == self) { + if (isUserInteracting && self.engine.viewController == self) { [_touchRateCorrectionVSyncClient await]; } else { [_touchRateCorrectionVSyncClient pause]; @@ -1371,14 +1365,14 @@ - (void)updateViewportMetricsIfNeeded { if (_shouldIgnoreViewportMetricsUpdatesDuringRotation) { return; } - if ([_engine.get() viewController] == self) { - [_engine.get() updateViewportMetrics:_viewportMetrics]; + if (self.engine.viewController == self) { + [self.engine updateViewportMetrics:_viewportMetrics]; } } - (void)viewDidLayoutSubviews { CGRect viewBounds = self.view.bounds; - CGFloat scale = [self flutterScreenIfViewLoaded].scale; + CGFloat scale = self.flutterScreenIfViewLoaded.scale; // Purposefully place this not visible. _scrollView.get().frame = CGRectMake(0.0, 0.0, viewBounds.size.width, 0.0); @@ -1407,10 +1401,11 @@ - (void)viewDidLayoutSubviews { // This must run after updateViewportMetrics so that the surface creation tasks are queued after // the viewport metrics update tasks. - if (firstViewBoundsUpdate && applicationOrSceneIsActive && _engine) { + if (firstViewBoundsUpdate && applicationOrSceneIsActive && self.engine) { [self surfaceUpdated:YES]; - flutter::Shell& shell = [_engine.get() shell]; + // XXX: Check self.engine isn't nil... + flutter::Shell& shell = self.engine.shell; fml::TimeDelta waitTime = #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG fml::TimeDelta::FromMilliseconds(200); @@ -1433,7 +1428,7 @@ - (void)viewSafeAreaInsetsDidChange { // Set _viewportMetrics physical size. - (void)setViewportMetricsSize { - UIScreen* screen = [self flutterScreenIfViewLoaded]; + UIScreen* screen = self.flutterScreenIfViewLoaded; if (!screen) { return; } @@ -1447,7 +1442,7 @@ - (void)setViewportMetricsSize { // // Viewport paddings represent the iOS safe area insets. - (void)setViewportMetricsPaddings { - UIScreen* screen = [self flutterScreenIfViewLoaded]; + UIScreen* screen = self.flutterScreenIfViewLoaded; if (!screen) { return; } @@ -1519,8 +1514,8 @@ - (void)handleKeyboardNotification:(NSNotification*)notification { if (!keyboardAnimationIsCompounding) { [self startKeyBoardAnimation:duration]; - } else if ([self keyboardSpringAnimation]) { - [self keyboardSpringAnimation].toValue = self.targetViewInsetBottom; + } else if (self.keyboardSpringAnimation) { + self.keyboardSpringAnimation.toValue = self.targetViewInsetBottom; } } @@ -1581,11 +1576,9 @@ - (BOOL)isKeyboardNotificationForDifferentView:(NSNotification*)notification { if (isLocal && ![isLocal boolValue]) { return YES; } + // Engine’s viewController is not current viewController. - if ([_engine.get() viewController] != self) { - return YES; - } - return NO; + return self.engine.viewController != self; } - (FlutterKeyboardMode)calculateKeyboardAttachMode:(NSNotification*)notification { @@ -1613,7 +1606,7 @@ - (FlutterKeyboardMode)calculateKeyboardAttachMode:(NSNotification*)notification return FlutterKeyboardModeHidden; } - CGRect screenRect = [self flutterScreenIfViewLoaded].bounds; + CGRect screenRect = self.flutterScreenIfViewLoaded.bounds; CGRect adjustedKeyboardFrame = keyboardFrame; adjustedKeyboardFrame.origin.y += [self calculateMultitaskingAdjustment:screenRect keyboardFrame:keyboardFrame]; @@ -1653,7 +1646,7 @@ - (CGFloat)calculateMultitaskingAdjustment:(CGRect)screenRect keyboardFrame:(CGR } CGRect viewRectRelativeToScreen = [self.viewIfLoaded convertRect:self.viewIfLoaded.frame - toCoordinateSpace:[self flutterScreenIfViewLoaded].coordinateSpace]; + toCoordinateSpace:self.flutterScreenIfViewLoaded.coordinateSpace]; CGFloat viewBottom = CGRectGetMaxY(viewRectRelativeToScreen); CGFloat offset = screenHeight - viewBottom; if (offset > 0) { @@ -1669,14 +1662,14 @@ - (CGFloat)calculateKeyboardInset:(CGRect)keyboardFrame keyboardMode:(NSInteger) // Calculate how much of the keyboard intersects with the view. CGRect viewRectRelativeToScreen = [self.viewIfLoaded convertRect:self.viewIfLoaded.frame - toCoordinateSpace:[self flutterScreenIfViewLoaded].coordinateSpace]; + toCoordinateSpace:self.flutterScreenIfViewLoaded.coordinateSpace]; CGRect intersection = CGRectIntersection(keyboardFrame, viewRectRelativeToScreen); CGFloat portionOfKeyboardInView = CGRectGetHeight(intersection); // The keyboard is treated as an inset since we want to effectively reduce the window size by // the keyboard height. The Dart side will compute a value accounting for the keyboard-consuming // bottom padding. - CGFloat scale = [self flutterScreenIfViewLoaded].scale; + CGFloat scale = self.flutterScreenIfViewLoaded.scale; return portionOfKeyboardInView * scale; } return 0; @@ -1690,21 +1683,21 @@ - (void)startKeyBoardAnimation:(NSTimeInterval)duration { // When this method is called for the first time, // initialize the keyboardAnimationView to get animation interpolation during animation. - if ([self keyboardAnimationView] == nil) { + if (!self.keyboardAnimationView) { UIView* keyboardAnimationView = [[UIView alloc] init]; [keyboardAnimationView setHidden:YES]; _keyboardAnimationView.reset(keyboardAnimationView); } - if ([self keyboardAnimationView].superview == nil) { - [self.view addSubview:[self keyboardAnimationView]]; + if (!self.keyboardAnimationView.superview) { + [self.view addSubview:self.keyboardAnimationView]; } // Remove running animation when start another animation. - [[self keyboardAnimationView].layer removeAllAnimations]; + [self.keyboardAnimationView.layer removeAllAnimations]; // Set animation begin value and DisplayLink tracking values. - [self keyboardAnimationView].frame = + self.keyboardAnimationView.frame = CGRectMake(0, _viewportMetrics.physical_view_inset_bottom, 0, 0); self.keyboardAnimationStartTime = fml::TimePoint().Now(); self.originalViewInsetBottom = _viewportMetrics.physical_view_inset_bottom; @@ -1750,7 +1743,7 @@ - (void)startKeyBoardAnimation:(NSTimeInterval)duration { fml::TimeDelta timeElapsed = keyboardAnimationTargetTime - strongSelf.keyboardAnimationStartTime; strongSelf->_viewportMetrics.physical_view_inset_bottom = - [[strongSelf keyboardSpringAnimation] curveFunction:timeElapsed.ToSecondsF()]; + [strongSelf.keyboardSpringAnimation curveFunction:timeElapsed.ToSecondsF()]; [strongSelf updateViewportMetricsIfNeeded]; } }; @@ -1826,7 +1819,7 @@ - (void)setUpKeyboardAnimationVsyncClient: }); }; - _keyboardAnimationVSyncClient = [[VSyncClient alloc] initWithTaskRunner:[_engine uiTaskRunner] + _keyboardAnimationVSyncClient = [[VSyncClient alloc] initWithTaskRunner:[self.engine uiTaskRunner] callback:uiCallback]; _keyboardAnimationVSyncClient.allowPauseAfterVsync = NO; [_keyboardAnimationVSyncClient await]; @@ -1838,8 +1831,8 @@ - (void)invalidateKeyboardAnimationVSyncClient { } - (void)removeKeyboardAnimationView { - if ([self keyboardAnimationView].superview != nil) { - [[self keyboardAnimationView] removeFromSuperview]; + if (self.keyboardAnimationView.superview != nil) { + [self.keyboardAnimationView removeFromSuperview]; } } @@ -1862,7 +1855,8 @@ - (void)handlePressEvent:(FlutterUIPressProxy*)press } - (void)sendDeepLinkToFramework:(NSURL*)url completionHandler:(void (^)(BOOL success))completion { - [_engine.get() + __weak FlutterViewController* weakSelf = self; + [self.engine waitForFirstFrame:3.0 callback:^(BOOL didTimeout) { if (didTimeout) { @@ -1870,7 +1864,7 @@ - (void)sendDeepLinkToFramework:(NSURL*)url completionHandler:(void (^)(BOOL suc completion(NO); } else { // invove the method and get the result - [[_engine.get() navigationChannel] + [weakSelf.engine.navigationChannel invokeMethod:@"pushRouteInformation" arguments:@{ @"location" : url.absoluteString ?: [NSNull null], @@ -2011,7 +2005,7 @@ - (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences { } else { UIInterfaceOrientationMask currentInterfaceOrientation = 0; if (@available(iOS 13.0, *)) { - UIWindowScene* windowScene = [self flutterWindowSceneIfViewLoaded]; + UIWindowScene* windowScene = self.flutterWindowSceneIfViewLoaded; if (!windowScene) { FML_LOG(WARNING) << "Accessing the interface orientation when the window scene is unavailable."; @@ -2081,11 +2075,12 @@ - (NSUInteger)supportedInterfaceOrientations { #pragma mark - Accessibility - (void)onAccessibilityStatusChanged:(NSNotification*)notification { - if (!_engine) { + if (!self.engine) { return; } - auto platformView = [_engine.get() platformView]; - int32_t flags = [self accessibilityFlags]; + // XXX: Explicit type here. + auto platformView = self.engine.platformView; + int32_t flags = self.accessibilityFlags; #if TARGET_OS_SIMULATOR // There doesn't appear to be any way to determine whether the accessibility // inspector is enabled on the simulator. We conservatively always turn on the @@ -2125,7 +2120,7 @@ - (int32_t)accessibilityFlags { } - (BOOL)accessibilityPerformEscape { - FlutterMethodChannel* navigationChannel = [_engine.get() navigationChannel]; + FlutterMethodChannel* navigationChannel = self.engine.navigationChannel; if (navigationChannel) { [self popRoute]; return YES; @@ -2149,13 +2144,13 @@ - (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection { } - (void)onUserSettingsChanged:(NSNotification*)notification { - [[_engine.get() settingsChannel] sendMessage:@{ - @"textScaleFactor" : @([self textScaleFactor]), + [self.engine.settingsChannel sendMessage:@{ + @"textScaleFactor" : @(self.textScaleFactor), @"alwaysUse24HourFormat" : @([FlutterHourFormat isAlwaysUse24HourFormat]), - @"platformBrightness" : [self brightnessMode], - @"platformContrast" : [self contrastMode], + @"platformBrightness" : self.brightnessMode, + @"platformContrast" : self.contrastMode, @"nativeSpellCheckServiceDefined" : @true, - @"supportsShowingSystemContextMenu" : @([self supportsShowingSystemContextMenu]) + @"supportsShowingSystemContextMenu" : @(self.supportsShowingSystemContextMenu) }]; } @@ -2297,28 +2292,28 @@ - (BOOL)prefersStatusBarHidden { #pragma mark - Platform views - (std::shared_ptr&)platformViewsController { - return [_engine.get() platformViewsController]; + return self.engine.platformViewsController; } - (NSObject*)binaryMessenger { - return _engine.get().binaryMessenger; + return self.engine.binaryMessenger; } #pragma mark - FlutterBinaryMessenger - (void)sendOnChannel:(NSString*)channel message:(NSData*)message { - [_engine.get().binaryMessenger sendOnChannel:channel message:message]; + [self.engine.binaryMessenger sendOnChannel:channel message:message]; } - (void)sendOnChannel:(NSString*)channel message:(NSData*)message binaryReply:(FlutterBinaryReply)callback { NSAssert(channel, @"The channel must not be null"); - [_engine.get().binaryMessenger sendOnChannel:channel message:message binaryReply:callback]; + [self.engine.binaryMessenger sendOnChannel:channel message:message binaryReply:callback]; } - (NSObject*)makeBackgroundTaskQueue { - return [_engine.get().binaryMessenger makeBackgroundTaskQueue]; + return [self.engine.binaryMessenger makeBackgroundTaskQueue]; } - (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString*)channel @@ -2332,27 +2327,27 @@ - (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString*)channe binaryMessageHandler:(FlutterBinaryMessageHandler _Nullable)handler taskQueue:(NSObject* _Nullable)taskQueue { NSAssert(channel, @"The channel must not be null"); - return [_engine.get().binaryMessenger setMessageHandlerOnChannel:channel + return [self.engine.binaryMessenger setMessageHandlerOnChannel:channel binaryMessageHandler:handler taskQueue:taskQueue]; } - (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection { - [_engine.get().binaryMessenger cleanUpConnection:connection]; + [self.engine.binaryMessenger cleanUpConnection:connection]; } #pragma mark - FlutterTextureRegistry - (int64_t)registerTexture:(NSObject*)texture { - return [_engine.get().textureRegistry registerTexture:texture]; + return [self.engine.textureRegistry registerTexture:texture]; } - (void)unregisterTexture:(int64_t)textureId { - [_engine.get().textureRegistry unregisterTexture:textureId]; + [self.engine.textureRegistry unregisterTexture:textureId]; } - (void)textureFrameAvailable:(int64_t)textureId { - [_engine.get().textureRegistry textureFrameAvailable:textureId]; + [self.engine.textureRegistry textureFrameAvailable:textureId]; } - (NSString*)lookupKeyForAsset:(NSString*)asset { @@ -2364,7 +2359,7 @@ - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package { } - (id)pluginRegistry { - return _engine; + return self.engine; } + (BOOL)isUIAccessibilityIsVoiceOverRunning { @@ -2374,15 +2369,15 @@ + (BOOL)isUIAccessibilityIsVoiceOverRunning { #pragma mark - FlutterPluginRegistry - (NSObject*)registrarForPlugin:(NSString*)pluginKey { - return [_engine.get() registrarForPlugin:pluginKey]; + return [self.engine registrarForPlugin:pluginKey]; } - (BOOL)hasPlugin:(NSString*)pluginKey { - return [_engine.get() hasPlugin:pluginKey]; + return [self.engine hasPlugin:pluginKey]; } - (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey { - return [_engine.get() valuePublishedByPlugin:pluginKey]; + return [self.engine valuePublishedByPlugin:pluginKey]; } - (void)presentViewController:(UIViewController*)viewControllerToPresent @@ -2407,7 +2402,7 @@ - (BOOL)isPresentingViewController { - (flutter::PointerData)updateMousePointerDataFrom:(UIGestureRecognizer*)gestureRecognizer API_AVAILABLE(ios(13.4)) { CGPoint location = [gestureRecognizer locationInView:self.view]; - CGFloat scale = [self flutterScreenIfViewLoaded].scale; + CGFloat scale = self.flutterScreenIfViewLoaded.scale; _mouseState.location = {location.x * scale, location.y * scale}; flutter::PointerData pointer_data; pointer_data.Clear(); @@ -2439,7 +2434,7 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer // If received after the deadline, it's not likely the event is from a user-initiated cancel. auto packet = std::make_unique(1); packet->SetPointerData(/*i=*/0, pointer_data); - [_engine.get() dispatchPointerDataPacket:std::move(packet)]; + [self.engine dispatchPointerDataPacket:std::move(packet)]; _scrollInertiaEventAppKitDeadline = 0; } } @@ -2494,18 +2489,18 @@ - (void)hoverEvent:(UIHoverGestureRecognizer*)recognizer API_AVAILABLE(ios(13.4) inertia_cancel.signal_kind = flutter::PointerData::SignalKind::kScrollInertiaCancel; inertia_cancel.view_id = self.viewIdentifier; packet->SetPointerData(/*i=*/1, inertia_cancel); - [_engine.get() dispatchPointerDataPacket:std::move(packet)]; + [self.engine dispatchPointerDataPacket:std::move(packet)]; _scrollInertiaEventStartline = DBL_MAX; } else { auto packet = std::make_unique(1); packet->SetPointerData(/*i=*/0, pointer_data); - [_engine.get() dispatchPointerDataPacket:std::move(packet)]; + [self.engine dispatchPointerDataPacket:std::move(packet)]; } } - (void)discreteScrollEvent:(UIPanGestureRecognizer*)recognizer API_AVAILABLE(ios(13.4)) { CGPoint translation = [recognizer translationInView:self.view]; - const CGFloat scale = [self flutterScreenIfViewLoaded].scale; + const CGFloat scale = self.flutterScreenIfViewLoaded.scale; translation.x *= scale; translation.y *= scale; @@ -2530,12 +2525,12 @@ - (void)discreteScrollEvent:(UIPanGestureRecognizer*)recognizer API_AVAILABLE(io auto packet = std::make_unique(1); packet->SetPointerData(/*i=*/0, pointer_data); - [_engine.get() dispatchPointerDataPacket:std::move(packet)]; + [self.engine dispatchPointerDataPacket:std::move(packet)]; } - (void)continuousScrollEvent:(UIPanGestureRecognizer*)recognizer API_AVAILABLE(ios(13.4)) { CGPoint translation = [recognizer translationInView:self.view]; - const CGFloat scale = [self flutterScreenIfViewLoaded].scale; + const CGFloat scale = self.flutterScreenIfViewLoaded.scale; flutter::PointerData pointer_data = [self updateMousePointerDataFrom:recognizer]; pointer_data.device = reinterpret_cast(recognizer); @@ -2582,7 +2577,7 @@ - (void)continuousScrollEvent:(UIPanGestureRecognizer*)recognizer API_AVAILABLE( auto packet = std::make_unique(1); packet->SetPointerData(/*i=*/0, pointer_data); - [_engine.get() dispatchPointerDataPacket:std::move(packet)]; + [self.engine dispatchPointerDataPacket:std::move(packet)]; } - (void)pinchEvent:(UIPinchGestureRecognizer*)recognizer API_AVAILABLE(ios(13.4)) { @@ -2612,13 +2607,13 @@ - (void)pinchEvent:(UIPinchGestureRecognizer*)recognizer API_AVAILABLE(ios(13.4) auto packet = std::make_unique(1); packet->SetPointerData(/*i=*/0, pointer_data); - [_engine.get() dispatchPointerDataPacket:std::move(packet)]; + [self.engine dispatchPointerDataPacket:std::move(packet)]; } #pragma mark - State Restoration - (void)encodeRestorableStateWithCoder:(NSCoder*)coder { - NSData* restorationData = [[_engine.get() restorationPlugin] restorationData]; + NSData* restorationData = [self.engine.restorationPlugin restorationData]; [coder encodeBytes:(const unsigned char*)restorationData.bytes length:restorationData.length forKey:kFlutterRestorationStateAppData]; @@ -2630,11 +2625,11 @@ - (void)decodeRestorableStateWithCoder:(NSCoder*)coder { const unsigned char* restorationBytes = [coder decodeBytesForKey:kFlutterRestorationStateAppData returnedLength:&restorationDataLength]; NSData* restorationData = [NSData dataWithBytes:restorationBytes length:restorationDataLength]; - [[_engine.get() restorationPlugin] setRestorationData:restorationData]; + [self.engine.restorationPlugin setRestorationData:restorationData]; } - (FlutterRestorationPlugin*)restorationPlugin { - return [_engine.get() restorationPlugin]; + return self.engine.restorationPlugin; } @end From 7e46ed2c6e36a6e9e2592a91b6e46b42cf5a85e1 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 13:17:26 -0700 Subject: [PATCH 03/35] Migrate _flutterView to property --- .../framework/Source/FlutterViewController.mm | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 23f2e2b573c73..a773e47c7a463 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -64,6 +64,10 @@ @interface FlutterViewController () > _weakFactory; - // We keep a separate reference to this and create it ahead of time because we want to be able to - // set up a shell along with its platform view before the view has to appear. - fml::scoped_nsobject _flutterView; fml::ScopedBlock _flutterViewRenderedCallback; UIInterfaceOrientationMask _orientationPreferences; UIStatusBarStyle _statusBarStyle; @@ -175,9 +176,9 @@ - (instancetype)initWithEngine:(FlutterEngine*)engine } _engine = engine; _engineNeedsLaunch = NO; - _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine + _flutterView = [[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque - enableWideGamut:engine.project.isWideGamutEnabled]); + enableWideGamut:engine.project.isWideGamutEnabled]; _weakFactory = std::make_unique>(self); _ongoingTouches.reset([[NSMutableSet alloc] init]); @@ -252,9 +253,9 @@ - (void)sharedSetupWithProject:(nullable FlutterDartProject*)project _viewOpaque = YES; _engine = engine; - _flutterView.reset([[FlutterView alloc] initWithDelegate:self.engine + _flutterView = [[FlutterView alloc] initWithDelegate:self.engine opaque:self.isViewOpaque - enableWideGamut:project.isWideGamutEnabled]); + enableWideGamut:project.isWideGamutEnabled]; [self.engine createShell:nil libraryURI:nil initialRoute:initialRoute]; _engineNeedsLaunch = YES; _ongoingTouches.reset([[NSMutableSet alloc] init]); @@ -268,9 +269,9 @@ - (BOOL)isViewOpaque { - (void)setViewOpaque:(BOOL)value { _viewOpaque = value; - if (_flutterView.get().layer.opaque != value) { - _flutterView.get().layer.opaque = value; - [_flutterView.get().layer setNeedsLayout]; + if (self.flutterView.layer.opaque != value) { + self.flutterView.layer.opaque = value; + [self.flutterView.layer setNeedsLayout]; } } @@ -491,7 +492,7 @@ - (void)pushRoute:(NSString*)route { } - (void)loadView { - self.view = GetViewOrPlaceholder(_flutterView.get()); + self.view = GetViewOrPlaceholder(self.flutterView); self.view.multipleTouchEnabled = YES; self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; @@ -736,7 +737,7 @@ - (void)surfaceUpdated:(BOOL)appeared { // thread. if (appeared) { [self installFirstFrameCallback]; - [self.engine platformViewsController]->SetFlutterView(_flutterView.get()); + [self.engine platformViewsController]->SetFlutterView(self.flutterView); [self.engine platformViewsController]->SetFlutterViewController(self); [self.engine iosPlatformView]->NotifyCreated(); } else { @@ -770,7 +771,7 @@ - (void)viewDidLoad { _hoverGestureRecognizer = [[UIHoverGestureRecognizer alloc] initWithTarget:self action:@selector(hoverEvent:)]; _hoverGestureRecognizer.delegate = self; - [_flutterView.get() addGestureRecognizer:_hoverGestureRecognizer]; + [self.flutterView addGestureRecognizer:_hoverGestureRecognizer]; _discreteScrollingPanGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(discreteScrollEvent:)]; @@ -781,23 +782,23 @@ - (void)viewDidLoad { // than touch events, so they will still be received. _discreteScrollingPanGestureRecognizer.allowedTouchTypes = @[]; _discreteScrollingPanGestureRecognizer.delegate = self; - [_flutterView.get() addGestureRecognizer:_discreteScrollingPanGestureRecognizer]; + [self.flutterView addGestureRecognizer:_discreteScrollingPanGestureRecognizer]; _continuousScrollingPanGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(continuousScrollEvent:)]; _continuousScrollingPanGestureRecognizer.allowedScrollTypesMask = UIScrollTypeMaskContinuous; _continuousScrollingPanGestureRecognizer.allowedTouchTypes = @[]; _continuousScrollingPanGestureRecognizer.delegate = self; - [_flutterView.get() addGestureRecognizer:_continuousScrollingPanGestureRecognizer]; + [self.flutterView addGestureRecognizer:_continuousScrollingPanGestureRecognizer]; _pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinchEvent:)]; _pinchGestureRecognizer.allowedTouchTypes = @[]; _pinchGestureRecognizer.delegate = self; - [_flutterView.get() addGestureRecognizer:_pinchGestureRecognizer]; + [self.flutterView addGestureRecognizer:_pinchGestureRecognizer]; _rotationGestureRecognizer = [[UIRotationGestureRecognizer alloc] init]; _rotationGestureRecognizer.allowedTouchTypes = @[]; _rotationGestureRecognizer.delegate = self; - [_flutterView.get() addGestureRecognizer:_rotationGestureRecognizer]; + [self.flutterView addGestureRecognizer:_rotationGestureRecognizer]; } [super viewDidLoad]; From b20610cc3b5898411d40176c65062b37dde3a7d0 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 13:28:56 -0700 Subject: [PATCH 04/35] Migrate _flutterViewRenderedCallback to property --- .../ios/framework/Source/FlutterViewController.mm | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index a773e47c7a463..7ff136ae03e2e 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -67,6 +67,7 @@ @interface FlutterViewController () > _weakFactory; - fml::ScopedBlock _flutterViewRenderedCallback; UIInterfaceOrientationMask _orientationPreferences; UIStatusBarStyle _statusBarStyle; flutter::ViewportMetrics _viewportMetrics; @@ -587,9 +587,9 @@ - (void)setDisplayingFlutterUI:(BOOL)displayingFlutterUI { - (void)callViewRenderedCallback { self.displayingFlutterUI = YES; - if (_flutterViewRenderedCallback != nil) { - _flutterViewRenderedCallback.get()(); - _flutterViewRenderedCallback.reset(); + if (self.flutterViewRenderedCallback) { + self.flutterViewRenderedCallback(); + self.flutterViewRenderedCallback = nil; } } @@ -723,7 +723,7 @@ - (void)setSplashScreenView:(UIView*)view { } - (void)setFlutterViewDidRenderCallback:(void (^)(void))callback { - _flutterViewRenderedCallback.reset(callback, fml::scoped_policy::OwnershipPolicy::kRetain); + _flutterViewRenderedCallback = callback; } #pragma mark - Surface creation and teardown updates From 385e7c7f1db59dabbdc82762157ccd5984b2dc37 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 13:34:41 -0700 Subject: [PATCH 05/35] Migrate _statusBarStyle to property --- .../framework/Source/FlutterViewController.mm | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 7ff136ae03e2e..fa83ee00968c9 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -69,6 +69,9 @@ @interface FlutterViewController () > _weakFactory; - UIInterfaceOrientationMask _orientationPreferences; - UIStatusBarStyle _statusBarStyle; flutter::ViewportMetrics _viewportMetrics; BOOL _initialized; BOOL _viewOpaque; @@ -1974,7 +1975,7 @@ - (void)requestGeometryUpdateForWindowScenes:(NSSet*)windowScenes for (UIScene* windowScene in windowScenes) { FML_DCHECK([windowScene isKindOfClass:[UIWindowScene class]]); UIWindowSceneGeometryPreferencesIOS* preference = [[UIWindowSceneGeometryPreferencesIOS alloc] - initWithInterfaceOrientations:_orientationPreferences]; + initWithInterfaceOrientations:self.orientationPreferences]; [(UIWindowScene*)windowScene requestGeometryUpdateWithPreferences:preference errorHandler:^(NSError* error) { @@ -1986,8 +1987,8 @@ - (void)requestGeometryUpdateForWindowScenes:(NSSet*)windowScenes } - (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences { - if (new_preferences != _orientationPreferences) { - _orientationPreferences = new_preferences; + if (new_preferences != self.orientationPreferences) { + self.orientationPreferences = new_preferences; if (@available(iOS 16.0, *)) { NSSet* scenes = @@ -2022,22 +2023,22 @@ - (void)performOrientationUpdate:(UIInterfaceOrientationMask)new_preferences { currentInterfaceOrientation = 1 << [[UIApplication sharedApplication] statusBarOrientation]; #endif } - if (!(_orientationPreferences & currentInterfaceOrientation)) { + if (!(self.orientationPreferences & currentInterfaceOrientation)) { [UIViewController attemptRotationToDeviceOrientation]; // Force orientation switch if the current orientation is not allowed - if (_orientationPreferences & UIInterfaceOrientationMaskPortrait) { + if (self.orientationPreferences & UIInterfaceOrientationMaskPortrait) { // This is no official API but more like a workaround / hack (using // key-value coding on a read-only property). This might break in // the future, but currently it´s the only way to force an orientation change [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortrait) forKey:@"orientation"]; - } else if (_orientationPreferences & UIInterfaceOrientationMaskPortraitUpsideDown) { + } else if (self.orientationPreferences & UIInterfaceOrientationMaskPortraitUpsideDown) { [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationPortraitUpsideDown) forKey:@"orientation"]; - } else if (_orientationPreferences & UIInterfaceOrientationMaskLandscapeLeft) { + } else if (self.orientationPreferences & UIInterfaceOrientationMaskLandscapeLeft) { [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeLeft) forKey:@"orientation"]; - } else if (_orientationPreferences & UIInterfaceOrientationMaskLandscapeRight) { + } else if (self.orientationPreferences & UIInterfaceOrientationMaskLandscapeRight) { [[UIDevice currentDevice] setValue:@(UIInterfaceOrientationLandscapeRight) forKey:@"orientation"]; } @@ -2070,7 +2071,7 @@ - (BOOL)shouldAutorotate { } - (NSUInteger)supportedInterfaceOrientations { - return _orientationPreferences; + return self.orientationPreferences; } #pragma mark - Accessibility @@ -2258,7 +2259,7 @@ - (NSString*)contrastMode { #pragma mark - Status bar style - (UIStatusBarStyle)preferredStatusBarStyle { - return _statusBarStyle; + return self.statusBarStyle; } - (void)onPreferredStatusBarStyleUpdated:(NSNotification*)notification { @@ -2271,9 +2272,9 @@ - (void)onPreferredStatusBarStyleUpdated:(NSNotification*)notification { return; } - NSInteger style = update.integerValue; - if (style != _statusBarStyle) { - _statusBarStyle = static_cast(style); + UIStatusBarStyle style = static_cast(update.integerValue); + if (style != self.statusBarStyle) { + self.statusBarStyle = style; [weakSelf setNeedsStatusBarAppearanceUpdate]; } }); From e702f443a5325b5e927817574018471319d85e35 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 13:45:06 -0700 Subject: [PATCH 06/35] Migrate _initialized, _viewOpaque, _engineNeedsLaunch to properties --- .../ios/framework/Source/FlutterViewController.mm | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index fa83ee00968c9..e593257c863a7 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -71,6 +71,8 @@ @interface FlutterViewController () > _weakFactory; flutter::ViewportMetrics _viewportMetrics; - BOOL _initialized; - BOOL _viewOpaque; - BOOL _engineNeedsLaunch; fml::scoped_nsobject> _ongoingTouches; // This scroll view is a workaround to accommodate iOS 13 and higher. There isn't a way to get // touches on the status bar to trigger scrolling to the top of a scroll view. We place a @@ -154,6 +153,7 @@ @implementation FlutterViewController { NSTimeInterval _scrollInertiaEventAppKitDeadline; } +@synthesize viewOpaque = _viewOpaque; @synthesize displayingFlutterUI = _displayingFlutterUI; @synthesize prefersStatusBarHidden = _flutterPrefersStatusBarHidden; @dynamic viewIdentifier; @@ -284,7 +284,6 @@ - (void)performCommonViewControllerInitialization { } _initialized = YES; - _orientationPreferences = UIInterfaceOrientationMaskAll; _statusBarStyle = UIStatusBarStyleDefault; @@ -754,10 +753,10 @@ - (void)surfaceUpdated:(BOOL)appeared { - (void)viewDidLoad { TRACE_EVENT0("flutter", "viewDidLoad"); - if (self.engine && _engineNeedsLaunch) { + if (self.engine && self.engineNeedsLaunch) { [self.engine launchEngine:nil libraryURI:nil entrypointArgs:nil]; [self.engine setViewController:self]; - _engineNeedsLaunch = NO; + self.engineNeedsLaunch = NO; } else if (self.engine.viewController == self) { [self.engine attachView]; } From e9b7438ca0970ccc5ffdaf80daaebf75f4e25903 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 13:51:29 -0700 Subject: [PATCH 07/35] Migrate _ongoingTouches to property --- .../framework/Source/FlutterViewController.mm | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index e593257c863a7..200070ca25a77 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -78,6 +78,7 @@ @interface FlutterViewController () * ongoingTouches; /** * Whether we should ignore viewport metrics updates during rotation transition. */ @@ -133,7 +134,6 @@ @implementation FlutterViewController { std::unique_ptr> _weakFactory; flutter::ViewportMetrics _viewportMetrics; - fml::scoped_nsobject> _ongoingTouches; // This scroll view is a workaround to accommodate iOS 13 and higher. There isn't a way to get // touches on the status bar to trigger scrolling to the top of a scroll view. We place a // UIScrollView with height zero and a content offset so we can get those events. See also: @@ -181,7 +181,7 @@ - (instancetype)initWithEngine:(FlutterEngine*)engine opaque:self.isViewOpaque enableWideGamut:engine.project.isWideGamutEnabled]; _weakFactory = std::make_unique>(self); - _ongoingTouches.reset([[NSMutableSet alloc] init]); + _ongoingTouches = [[NSMutableSet alloc] init]; [self performCommonViewControllerInitialization]; [engine setViewController:self]; @@ -259,7 +259,7 @@ - (void)sharedSetupWithProject:(nullable FlutterDartProject*)project enableWideGamut:project.isWideGamutEnabled]; [self.engine createShell:nil libraryURI:nil initialRoute:initialRoute]; _engineNeedsLaunch = YES; - _ongoingTouches.reset([[NSMutableSet alloc] init]); + _ongoingTouches = [[NSMutableSet alloc] init]; [self loadDefaultSplashScreenView]; [self performCommonViewControllerInitialization]; } @@ -921,12 +921,12 @@ - (void)viewWillTransitionToSize:(CGSize)size } - (void)flushOngoingTouches { - if (self.engine && _ongoingTouches.get().count > 0) { - auto packet = std::make_unique(_ongoingTouches.get().count); + if (self.engine && self.ongoingTouches.count > 0) { + auto packet = std::make_unique(self.ongoingTouches.count); size_t pointer_index = 0; // If the view controller is going away, we want to flush cancel all the ongoing // touches to the framework so nothing gets orphaned. - for (NSNumber* device in _ongoingTouches.get()) { + for (NSNumber* device in self.ongoingTouches) { // Create fake PointerData to balance out each previously started one for the framework. flutter::PointerData pointer_data = [self generatePointerDataForFake]; @@ -946,7 +946,7 @@ - (void)flushOngoingTouches { packet->SetPointerData(pointer_index++, pointer_data); } - [_ongoingTouches removeAllObjects]; + [self.ongoingTouches removeAllObjects]; [self.engine dispatchPointerDataPacket:std::move(packet)]; } } @@ -1196,11 +1196,11 @@ - (void)dispatchTouches:(NSSet*)touches // if the view controller goes away. switch (pointer_data.change) { case flutter::PointerData::Change::kDown: - [_ongoingTouches addObject:deviceKey]; + [self.ongoingTouches addObject:deviceKey]; break; case flutter::PointerData::Change::kCancel: case flutter::PointerData::Change::kUp: - [_ongoingTouches removeObject:deviceKey]; + [self.ongoingTouches removeObject:deviceKey]; break; case flutter::PointerData::Change::kHover: case flutter::PointerData::Change::kMove: From 42451200217e278d4a3e1018734ab50928e7b5a1 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 13:54:49 -0700 Subject: [PATCH 08/35] Migrate _scrollView to property --- .../framework/Source/FlutterViewController.mm | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 200070ca25a77..6b05188b74357 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -79,6 +79,11 @@ @interface FlutterViewController () * ongoingTouches; +// This scroll view is a workaround to accommodate iOS 13 and higher. There isn't a way to get +// touches on the status bar to trigger scrolling to the top of a scroll view. We place a +// UIScrollView with height zero and a content offset so we can get those events. See also: +// https://github.com/flutter/flutter/issues/35050 +@property(nonatomic, strong) UIScrollView* scrollView; /** * Whether we should ignore viewport metrics updates during rotation transition. */ @@ -134,11 +139,6 @@ @implementation FlutterViewController { std::unique_ptr> _weakFactory; flutter::ViewportMetrics _viewportMetrics; - // This scroll view is a workaround to accommodate iOS 13 and higher. There isn't a way to get - // touches on the status bar to trigger scrolling to the top of a scroll view. We place a - // UIScrollView with height zero and a content offset so we can get those events. See also: - // https://github.com/flutter/flutter/issues/35050 - fml::scoped_nsobject _scrollView; fml::scoped_nsobject _keyboardAnimationView; fml::scoped_nsobject _keyboardSpringAnimation; MouseState _mouseState; @@ -497,17 +497,16 @@ - (void)loadView { self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [self installSplashScreenViewIfNecessary]; - UIScrollView* scrollView = [[UIScrollView alloc] init]; - scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth; + self.scrollView = [[UIScrollView alloc] init]; + self.scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth; // The color shouldn't matter since it is offscreen. - scrollView.backgroundColor = UIColor.whiteColor; - scrollView.delegate = self; + self.scrollView.backgroundColor = UIColor.whiteColor; + self.scrollView.delegate = self; // This is an arbitrary small size. - scrollView.contentSize = CGSizeMake(kScrollViewContentSize, kScrollViewContentSize); + self.scrollView.contentSize = CGSizeMake(kScrollViewContentSize, kScrollViewContentSize); // This is an arbitrary offset that is not CGPointZero. - scrollView.contentOffset = CGPointMake(kScrollViewContentSize, kScrollViewContentSize); - [self.view addSubview:scrollView]; - _scrollView.reset(scrollView); + self.scrollView.contentOffset = CGPointMake(kScrollViewContentSize, kScrollViewContentSize); + [self.view addSubview:self.scrollView]; } - (flutter::PointerData)generatePointerDataForFake { @@ -971,7 +970,7 @@ - (void)dealloc { // TODO(cbracken): https://github.com/flutter/flutter/issues/156222 // Ensure all delegates are weak and remove this. - _scrollView.get().delegate = nil; + _scrollView.delegate = nil; _hoverGestureRecognizer.delegate = nil; _discreteScrollingPanGestureRecognizer.delegate = nil; _continuousScrollingPanGestureRecognizer.delegate = nil; @@ -1376,8 +1375,8 @@ - (void)viewDidLayoutSubviews { CGFloat scale = self.flutterScreenIfViewLoaded.scale; // Purposefully place this not visible. - _scrollView.get().frame = CGRectMake(0.0, 0.0, viewBounds.size.width, 0.0); - _scrollView.get().contentOffset = CGPointMake(kScrollViewContentSize, kScrollViewContentSize); + self.scrollView.frame = CGRectMake(0.0, 0.0, viewBounds.size.width, 0.0); + self.scrollView.contentOffset = CGPointMake(kScrollViewContentSize, kScrollViewContentSize); // First time since creation that the dimensions of its view is known. bool firstViewBoundsUpdate = !_viewportMetrics.physical_width; From a451b9feb3bcb69d8a1b0a186dbd23452a13ee93 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 13:57:11 -0700 Subject: [PATCH 09/35] Migrate _keyboardAnimationView to property --- .../ios/framework/Source/FlutterViewController.mm | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 6b05188b74357..e7147a6165742 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -84,6 +84,8 @@ @interface FlutterViewController () > _weakFactory; flutter::ViewportMetrics _viewportMetrics; - fml::scoped_nsobject _keyboardAnimationView; fml::scoped_nsobject _keyboardSpringAnimation; MouseState _mouseState; // Timestamp after which a scroll inertia cancel event should be inferred. @@ -650,10 +651,6 @@ - (int64_t)viewIdentifier { return flutter::kFlutterImplicitViewId; } -- (UIView*)keyboardAnimationView { - return _keyboardAnimationView.get(); -} - - (SpringAnimation*)keyboardSpringAnimation { return _keyboardSpringAnimation.get(); } @@ -1685,8 +1682,8 @@ - (void)startKeyBoardAnimation:(NSTimeInterval)duration { // initialize the keyboardAnimationView to get animation interpolation during animation. if (!self.keyboardAnimationView) { UIView* keyboardAnimationView = [[UIView alloc] init]; - [keyboardAnimationView setHidden:YES]; - _keyboardAnimationView.reset(keyboardAnimationView); + keyboardAnimationView.hidden = YES; + self.keyboardAnimationView = keyboardAnimationView; } if (!self.keyboardAnimationView.superview) { @@ -1758,11 +1755,11 @@ - (void)startKeyBoardAnimation:(NSTimeInterval)duration { } // Set end value. - [strongSelf keyboardAnimationView].frame = CGRectMake(0, self.targetViewInsetBottom, 0, 0); + strongSelf.keyboardAnimationView.frame = CGRectMake(0, self.targetViewInsetBottom, 0, 0); // Setup keyboard animation interpolation. CAAnimation* keyboardAnimation = - [[strongSelf keyboardAnimationView].layer animationForKey:@"position"]; + [strongSelf.keyboardAnimationView.layer animationForKey:@"position"]; [strongSelf setUpKeyboardSpringAnimationIfNeeded:keyboardAnimation]; } completion:^(BOOL finished) { From b4f201e18b38ef228fe62707fb2e8addce12cc27 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 13:58:52 -0700 Subject: [PATCH 10/35] Migrate _keyboardSpringAnimation to property --- .../framework/Source/FlutterViewController.mm | 34 ++++++++----------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index e7147a6165742..879f3ec864cf7 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -85,6 +85,7 @@ @interface FlutterViewController () > _weakFactory; flutter::ViewportMetrics _viewportMetrics; - fml::scoped_nsobject _keyboardSpringAnimation; MouseState _mouseState; // Timestamp after which a scroll inertia cancel event should be inferred. NSTimeInterval _scrollInertiaEventStartline; @@ -179,8 +179,8 @@ - (instancetype)initWithEngine:(FlutterEngine*)engine _engine = engine; _engineNeedsLaunch = NO; _flutterView = [[FlutterView alloc] initWithDelegate:_engine - opaque:self.isViewOpaque - enableWideGamut:engine.project.isWideGamutEnabled]; + opaque:self.isViewOpaque + enableWideGamut:engine.project.isWideGamutEnabled]; _weakFactory = std::make_unique>(self); _ongoingTouches = [[NSMutableSet alloc] init]; @@ -256,8 +256,8 @@ - (void)sharedSetupWithProject:(nullable FlutterDartProject*)project _viewOpaque = YES; _engine = engine; _flutterView = [[FlutterView alloc] initWithDelegate:self.engine - opaque:self.isViewOpaque - enableWideGamut:project.isWideGamutEnabled]; + opaque:self.isViewOpaque + enableWideGamut:project.isWideGamutEnabled]; [self.engine createShell:nil libraryURI:nil initialRoute:initialRoute]; _engineNeedsLaunch = YES; _ongoingTouches = [[NSMutableSet alloc] init]; @@ -651,10 +651,6 @@ - (int64_t)viewIdentifier { return flutter::kFlutterImplicitViewId; } -- (SpringAnimation*)keyboardSpringAnimation { - return _keyboardSpringAnimation.get(); -} - - (BOOL)loadDefaultSplashScreenView { NSString* launchscreenName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"UILaunchStoryboardName"]; @@ -1782,19 +1778,19 @@ - (void)startKeyBoardAnimation:(NSTimeInterval)duration { - (void)setUpKeyboardSpringAnimationIfNeeded:(CAAnimation*)keyboardAnimation { // If keyboard animation is null or not a spring animation, fallback to DisplayLink tracking. if (keyboardAnimation == nil || ![keyboardAnimation isKindOfClass:[CASpringAnimation class]]) { - _keyboardSpringAnimation.reset(); + _keyboardSpringAnimation = nil; return; } // Setup keyboard spring animation details for spring curve animation calculation. CASpringAnimation* keyboardCASpringAnimation = (CASpringAnimation*)keyboardAnimation; - _keyboardSpringAnimation.reset([[SpringAnimation alloc] - initWithStiffness:keyboardCASpringAnimation.stiffness - damping:keyboardCASpringAnimation.damping - mass:keyboardCASpringAnimation.mass - initialVelocity:keyboardCASpringAnimation.initialVelocity - fromValue:self.originalViewInsetBottom - toValue:self.targetViewInsetBottom]); + _keyboardSpringAnimation = + [[SpringAnimation alloc] initWithStiffness:keyboardCASpringAnimation.stiffness + damping:keyboardCASpringAnimation.damping + mass:keyboardCASpringAnimation.mass + initialVelocity:keyboardCASpringAnimation.initialVelocity + fromValue:self.originalViewInsetBottom + toValue:self.targetViewInsetBottom]; } - (void)setUpKeyboardAnimationVsyncClient: @@ -2325,8 +2321,8 @@ - (FlutterBinaryMessengerConnection)setMessageHandlerOnChannel:(NSString*)channe taskQueue:(NSObject* _Nullable)taskQueue { NSAssert(channel, @"The channel must not be null"); return [self.engine.binaryMessenger setMessageHandlerOnChannel:channel - binaryMessageHandler:handler - taskQueue:taskQueue]; + binaryMessageHandler:handler + taskQueue:taskQueue]; } - (void)cleanUpConnection:(FlutterBinaryMessengerConnection)connection { From cbb842669d1afd716ed463fb8527efd97861227d Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 14:03:04 -0700 Subject: [PATCH 11/35] Migrate scrollInertiaEvent ivars to properties --- .../framework/Source/FlutterViewController.mm | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 879f3ec864cf7..4c8d63cf7abea 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -102,6 +102,17 @@ @interface FlutterViewController () (1); packet->SetPointerData(/*i=*/0, pointer_data); [self.engine dispatchPointerDataPacket:std::move(packet)]; - _scrollInertiaEventAppKitDeadline = 0; + self.scrollInertiaEventAppKitDeadline = 0; } } // This method is also called for UITouches, should return YES to process all touches. @@ -2470,7 +2472,7 @@ - (void)hoverEvent:(UIHoverGestureRecognizer*)recognizer API_AVAILABLE(ios(13.4) isRunningOnMac = [NSProcessInfo processInfo].iOSAppOnMac; } if (!isRunningOnMac && CGPointEqualToPoint(oldLocation, _mouseState.location) && - time > _scrollInertiaEventStartline) { + time > self.scrollInertiaEventStartline) { // iPadOS reports trackpad movements events with high (sub-pixel) precision. When an event // is received with the same position as the previous one, it can only be from a finger // making or breaking contact with the trackpad surface. @@ -2483,7 +2485,7 @@ - (void)hoverEvent:(UIHoverGestureRecognizer*)recognizer API_AVAILABLE(ios(13.4) inertia_cancel.view_id = self.viewIdentifier; packet->SetPointerData(/*i=*/1, inertia_cancel); [self.engine dispatchPointerDataPacket:std::move(packet)]; - _scrollInertiaEventStartline = DBL_MAX; + self.scrollInertiaEventStartline = DBL_MAX; } else { auto packet = std::make_unique(1); packet->SetPointerData(/*i=*/0, pointer_data); @@ -2543,7 +2545,7 @@ - (void)continuousScrollEvent:(UIPanGestureRecognizer*)recognizer API_AVAILABLE( break; case UIGestureRecognizerStateEnded: case UIGestureRecognizerStateCancelled: - _scrollInertiaEventStartline = + self.scrollInertiaEventStartline = [[NSProcessInfo processInfo] systemUptime] + 0.1; // Time to lift fingers off trackpad (experimentally determined) // When running an iOS app on an Apple Silicon Mac, AppKit will send an event @@ -2554,7 +2556,7 @@ - (void)continuousScrollEvent:(UIPanGestureRecognizer*)recognizer API_AVAILABLE( // The following (curve-fitted) calculation provides a cutoff point after which any // UIEventTypeScroll event will likely be from the system instead of the user. // See https://github.com/flutter/engine/pull/34929. - _scrollInertiaEventAppKitDeadline = + self.scrollInertiaEventAppKitDeadline = [[NSProcessInfo processInfo] systemUptime] + (0.1821 * log(fmax([recognizer velocityInView:self.view].x, [recognizer velocityInView:self.view].y))) - From 26067541507c9d10c3ea3c2e452d7c60577cc8c1 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 14:17:07 -0700 Subject: [PATCH 12/35] Make a couple more references to self weak --- .../framework/Source/FlutterViewController.mm | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 4c8d63cf7abea..c61bce6de69a7 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -1804,13 +1804,12 @@ - (void)setUpKeyboardAnimationVsyncClient: @"_keyboardAnimationVSyncClient must be nil when setting up."); // Make sure the new viewport metrics get sent after the begin frame event has processed. - fml::scoped_nsprotocol animationCallback( - [keyboardAnimationCallback copy]); + FlutterKeyboardAnimationCallback animationCallback = [keyboardAnimationCallback copy]; auto uiCallback = [animationCallback](std::unique_ptr recorder) { fml::TimeDelta frameInterval = recorder->GetVsyncTargetTime() - recorder->GetVsyncStartTime(); fml::TimePoint keyboardAnimationTargetTime = recorder->GetVsyncTargetTime() + frameInterval; dispatch_async(dispatch_get_main_queue(), ^(void) { - animationCallback.get()(keyboardAnimationTargetTime); + animationCallback(keyboardAnimationTargetTime); }); }; @@ -1895,10 +1894,11 @@ - (void)sendDeepLinkToFramework:(NSURL*)url completionHandler:(void (^)(BOOL suc - (void)pressesBegan:(NSSet*)presses withEvent:(UIPressesEvent*)event API_AVAILABLE(ios(9.0)) { if (@available(iOS 13.4, *)) { + __weak FlutterViewController* weakSelf = self; for (UIPress* press in presses) { [self handlePressEvent:[[FlutterUIPressProxy alloc] initWithPress:press withEvent:event] nextAction:^() { - [super pressesBegan:[NSSet setWithObject:press] withEvent:event]; + [weakSelf->super pressesBegan:[NSSet setWithObject:press] withEvent:event]; }]; } } else { @@ -1909,10 +1909,11 @@ - (void)pressesBegan:(NSSet*)presses - (void)pressesChanged:(NSSet*)presses withEvent:(UIPressesEvent*)event API_AVAILABLE(ios(9.0)) { if (@available(iOS 13.4, *)) { + __weak FlutterViewController* weakSelf = self; for (UIPress* press in presses) { [self handlePressEvent:[[FlutterUIPressProxy alloc] initWithPress:press withEvent:event] nextAction:^() { - [super pressesChanged:[NSSet setWithObject:press] withEvent:event]; + [weakSelf->super pressesChanged:[NSSet setWithObject:press] withEvent:event]; }]; } } else { @@ -1923,10 +1924,11 @@ - (void)pressesChanged:(NSSet*)presses - (void)pressesEnded:(NSSet*)presses withEvent:(UIPressesEvent*)event API_AVAILABLE(ios(9.0)) { if (@available(iOS 13.4, *)) { + __weak FlutterViewController* weakSelf = self; for (UIPress* press in presses) { [self handlePressEvent:[[FlutterUIPressProxy alloc] initWithPress:press withEvent:event] nextAction:^() { - [super pressesEnded:[NSSet setWithObject:press] withEvent:event]; + [weakSelf->super pressesEnded:[NSSet setWithObject:press] withEvent:event]; }]; } } else { @@ -1937,10 +1939,11 @@ - (void)pressesEnded:(NSSet*)presses - (void)pressesCancelled:(NSSet*)presses withEvent:(UIPressesEvent*)event API_AVAILABLE(ios(9.0)) { if (@available(iOS 13.4, *)) { + __weak FlutterViewController* weakSelf = self; for (UIPress* press in presses) { [self handlePressEvent:[[FlutterUIPressProxy alloc] initWithPress:press withEvent:event] nextAction:^() { - [super pressesCancelled:[NSSet setWithObject:press] withEvent:event]; + [weakSelf->super pressesCancelled:[NSSet setWithObject:press] withEvent:event]; }]; } } else { @@ -2266,8 +2269,8 @@ - (void)onPreferredStatusBarStyleUpdated:(NSNotification*)notification { } UIStatusBarStyle style = static_cast(update.integerValue); - if (style != self.statusBarStyle) { - self.statusBarStyle = style; + if (style != weakSelf.statusBarStyle) { + weakSelf.statusBarStyle = style; [weakSelf setNeedsStatusBarAppearanceUpdate]; } }); From 950ef839118317d06730811a8a061f13fea85abc Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 14:29:58 -0700 Subject: [PATCH 13/35] Avoid capturing self via super --- .../framework/Source/FlutterViewController.mm | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index c61bce6de69a7..9011645d413b1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -1,4 +1,4 @@ -// Copyright 2013 The Flutter Authors. All rights reserved. +// Copyright 2013 The Flutter Authors. All rights reservedj;えr // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -1885,11 +1885,30 @@ - (void)sendDeepLinkToFramework:(NSURL*)url completionHandler:(void (^)(BOOL suc // the wild, however, so I suspect that the API is built for a tvOS remote or // something, and perhaps only one ever appears in the set on iOS from a // keyboard. +// +// We define separate superPresses* overrides to avoid implicitly capturing self in the blocks +// passed to the presses* methods below. + +- (void)superPressesBegan:(NSSet*)presses withEvent:(UIPressesEvent*)event { + [super pressesBegan:presses withEvent:event]; +} + +- (void)superPressesChanged:(NSSet*)presses withEvent:(UIPressesEvent*)event { + [super pressesChanged:presses withEvent:event]; +} + +- (void)superPressesEnded:(NSSet*)presses withEvent:(UIPressesEvent*)event { + [super pressesEnded:presses withEvent:event]; +} + +- (void)superPressesCancelled:(NSSet*)presses withEvent:(UIPressesEvent*)event { + [super pressesCancelled:presses withEvent:event]; +} // If you substantially change these presses overrides, consider also changing // the similar ones in FlutterTextInputPlugin. They need to be overridden in // both places to capture keys both inside and outside of a text field, but have -// slightly different implmentations. +// slightly different implementations. - (void)pressesBegan:(NSSet*)presses withEvent:(UIPressesEvent*)event API_AVAILABLE(ios(9.0)) { @@ -1898,7 +1917,7 @@ - (void)pressesBegan:(NSSet*)presses for (UIPress* press in presses) { [self handlePressEvent:[[FlutterUIPressProxy alloc] initWithPress:press withEvent:event] nextAction:^() { - [weakSelf->super pressesBegan:[NSSet setWithObject:press] withEvent:event]; + [weakSelf superPressesBegan:[NSSet setWithObject:press] withEvent:event]; }]; } } else { @@ -1913,7 +1932,7 @@ - (void)pressesChanged:(NSSet*)presses for (UIPress* press in presses) { [self handlePressEvent:[[FlutterUIPressProxy alloc] initWithPress:press withEvent:event] nextAction:^() { - [weakSelf->super pressesChanged:[NSSet setWithObject:press] withEvent:event]; + [weakSelf superPressesChanged:[NSSet setWithObject:press] withEvent:event]; }]; } } else { @@ -1928,7 +1947,7 @@ - (void)pressesEnded:(NSSet*)presses for (UIPress* press in presses) { [self handlePressEvent:[[FlutterUIPressProxy alloc] initWithPress:press withEvent:event] nextAction:^() { - [weakSelf->super pressesEnded:[NSSet setWithObject:press] withEvent:event]; + [weakSelf superPressesEnded:[NSSet setWithObject:press] withEvent:event]; }]; } } else { @@ -1943,7 +1962,7 @@ - (void)pressesCancelled:(NSSet*)presses for (UIPress* press in presses) { [self handlePressEvent:[[FlutterUIPressProxy alloc] initWithPress:press withEvent:event] nextAction:^() { - [weakSelf->super pressesCancelled:[NSSet setWithObject:press] withEvent:event]; + [weakSelf superPressesCancelled:[NSSet setWithObject:press] withEvent:event]; }]; } } else { From 65129c2cd2e4d0f7e7386991f7065cea203f8e60 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 14:34:23 -0700 Subject: [PATCH 14/35] Eliminate use of scoped_nsobject --- .../framework/Source/FlutterViewController.mm | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 9011645d413b1..9bb01aa956767 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -13,7 +13,6 @@ #include "flutter/fml/memory/weak_ptr.h" #include "flutter/fml/message_loop.h" #include "flutter/fml/platform/darwin/platform_version.h" -#include "flutter/fml/platform/darwin/scoped_nsobject.h" #include "flutter/runtime/ptrace_check.h" #include "flutter/shell/common/thread_host.h" #import "flutter/shell/platform/darwin/common/framework/Source/FlutterBinaryMessengerRelay.h" @@ -245,22 +244,20 @@ - (void)sharedSetupWithProject:(nullable FlutterDartProject*)project } FlutterView.forceSoftwareRendering = project.settings.enable_software_rendering; _weakFactory = std::make_unique>(self); - auto engine = fml::scoped_nsobject{[[FlutterEngine alloc] - initWithName:@"io.flutter" - project:project - allowHeadlessExecution:self.engineAllowHeadlessExecution - restorationEnabled:self.restorationIdentifier != nil]}; - + FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"io.flutter" + project:project + allowHeadlessExecution:self.engineAllowHeadlessExecution + restorationEnabled:self.restorationIdentifier != nil]; if (!engine) { return; } _viewOpaque = YES; _engine = engine; - _flutterView = [[FlutterView alloc] initWithDelegate:self.engine - opaque:self.isViewOpaque + _flutterView = [[FlutterView alloc] initWithDelegate:_engine + opaque:_viewOpaque enableWideGamut:project.isWideGamutEnabled]; - [self.engine createShell:nil libraryURI:nil initialRoute:initialRoute]; + [_engine createShell:nil libraryURI:nil initialRoute:initialRoute]; _engineNeedsLaunch = YES; _ongoingTouches = [[NSMutableSet alloc] init]; [self loadDefaultSplashScreenView]; From b5b49780dd977c06651320911b88439a1c6b442d Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 15:34:03 -0700 Subject: [PATCH 15/35] Update TODO --- .../darwin/ios/framework/Source/FlutterViewController.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 9bb01aa956767..f0d3a4cb4cca3 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -147,7 +147,7 @@ - (void)deregisterNotifications; @end @implementation FlutterViewController { - // TODO(cbracken): https://github.com/flutter/flutter/issues/155943 + // TODO(cbracken): https://github.com/flutter/flutter/issues/137801 // Eliminate once we can use weak pointers in platform_view_ios.h. std::unique_ptr> _weakFactory; From f274f240a0685a259ba013da339a31de80b163ef Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 15:35:28 -0700 Subject: [PATCH 16/35] Fix typo --- .../darwin/ios/framework/Source/FlutterViewController.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index f0d3a4cb4cca3..57aa4fbfd61cd 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -1,4 +1,4 @@ -// Copyright 2013 The Flutter Authors. All rights reservedj;えr +// Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. From 134e6d3c3455f36387fd3cedc282742ac029a531 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 15:45:38 -0700 Subject: [PATCH 17/35] ivar stuff --- .../darwin/ios/framework/Source/FlutterViewController.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 57aa4fbfd61cd..cefcfa5b8ff86 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -696,13 +696,13 @@ - (UIView*)splashScreenFromXib:(NSString*)name { } - (void)setSplashScreenView:(UIView*)view { - if (view == self.splashScreenView) { + if (view == _splashScreenView) { return; } // Special case: user wants to remove the splash screen view. if (!view) { - if (self.splashScreenView) { + if (_splashScreenView) { [self removeSplashScreenView:nil]; } return; From 03ec71bed22cfe9bb6e5b59406514cc0c53920f7 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 15:51:58 -0700 Subject: [PATCH 18/35] Moar property syntax --- .../darwin/ios/framework/Source/FlutterViewController.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index cefcfa5b8ff86..e592b94fb8b82 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -831,8 +831,8 @@ - (void)viewWillAppear:(BOOL)animated { if (_viewportMetrics.physical_width) { [self surfaceUpdated:YES]; } - [[self.engine lifecycleChannel] sendMessage:@"AppLifecycleState.inactive"]; - [[self.engine restorationPlugin] markRestorationComplete]; + [self.engine.lifecycleChannel sendMessage:@"AppLifecycleState.inactive"]; + [self.engine.restorationPlugin markRestorationComplete]; } [super viewWillAppear:animated]; From 258e7e6fd12205605e3eae15908fa344705096b2 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 16:00:34 -0700 Subject: [PATCH 19/35] Add engine non-nil asserts --- .../darwin/ios/framework/Source/FlutterViewController.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index e592b94fb8b82..29f68a9316abc 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -1311,7 +1311,7 @@ - (void)createTouchRateCorrectionVSyncClientIfNeeded { return; } - // XXX: Errr, shouldn't we be ensuring self.engine isn't nil here? + NSAssert(self.engine, @"engine must not be nil"); flutter::Shell& shell = self.engine.shell; auto callback = [](std::unique_ptr recorder) { // Do nothing in this block. Just trigger system to callback touch events with correct rate. @@ -1396,7 +1396,7 @@ - (void)viewDidLayoutSubviews { if (firstViewBoundsUpdate && applicationOrSceneIsActive && self.engine) { [self surfaceUpdated:YES]; - // XXX: Check self.engine isn't nil... + NSAssert(self.engine, @"engine must not be nil"); flutter::Shell& shell = self.engine.shell; fml::TimeDelta waitTime = #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG From 5c9923b14e7d3bef0318d4c5ff771d3c00716157 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 16:01:44 -0700 Subject: [PATCH 20/35] Set explicit type for my own sanity --- .../darwin/ios/framework/Source/FlutterViewController.mm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 29f68a9316abc..9ab8d23e82b40 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -2092,8 +2092,7 @@ - (void)onAccessibilityStatusChanged:(NSNotification*)notification { if (!self.engine) { return; } - // XXX: Explicit type here. - auto platformView = self.engine.platformView; + fml::WeakPtr platformView = self.engine.platformView; int32_t flags = self.accessibilityFlags; #if TARGET_OS_SIMULATOR // There doesn't appear to be any way to determine whether the accessibility From 445202b46877840dc7a7957859de0b6ff598585e Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 16:17:26 -0700 Subject: [PATCH 21/35] One more property --- .../darwin/ios/framework/Source/FlutterViewController.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 9ab8d23e82b40..2f122e0705cb8 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -2159,7 +2159,7 @@ - (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection { - (void)onUserSettingsChanged:(NSNotification*)notification { [self.engine.settingsChannel sendMessage:@{ @"textScaleFactor" : @(self.textScaleFactor), - @"alwaysUse24HourFormat" : @([FlutterHourFormat isAlwaysUse24HourFormat]), + @"alwaysUse24HourFormat" : @(FlutterHourFormat.isAlwaysUse24HourFormat), @"platformBrightness" : self.brightnessMode, @"platformContrast" : self.contrastMode, @"nativeSpellCheckServiceDefined" : @true, From bd4af677267b47b21b23c29914edc6b6fc5e55ae Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 16:21:43 -0700 Subject: [PATCH 22/35] err YES, not true. NO, not false. --- .../darwin/ios/framework/Source/FlutterViewController.mm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 2f122e0705cb8..b520838faf52a 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -2162,7 +2162,7 @@ - (void)onUserSettingsChanged:(NSNotification*)notification { @"alwaysUse24HourFormat" : @(FlutterHourFormat.isAlwaysUse24HourFormat), @"platformBrightness" : self.brightnessMode, @"platformContrast" : self.contrastMode, - @"nativeSpellCheckServiceDefined" : @true, + @"nativeSpellCheckServiceDefined" : @YES, @"supportsShowingSystemContextMenu" : @(self.supportsShowingSystemContextMenu) }]; } @@ -2583,7 +2583,7 @@ - (void)continuousScrollEvent:(UIPanGestureRecognizer*)recognizer API_AVAILABLE( break; default: // continuousScrollEvent: should only ever be triggered with the above phases - NSAssert(false, @"Trackpad pan event occured with unexpected phase 0x%lx", + NSAssert(NO, @"Trackpad pan event occured with unexpected phase 0x%lx", (long)recognizer.state); break; } @@ -2613,7 +2613,7 @@ - (void)pinchEvent:(UIPinchGestureRecognizer*)recognizer API_AVAILABLE(ios(13.4) break; default: // pinchEvent: should only ever be triggered with the above phases - NSAssert(false, @"Trackpad pinch event occured with unexpected phase 0x%lx", + NSAssert(NO, @"Trackpad pinch event occured with unexpected phase 0x%lx", (long)recognizer.state); break; } From e4d63082e9e10614b8a649f150271626985dcd44 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 16:30:10 -0700 Subject: [PATCH 23/35] Let's maybe assert ARC --- .../darwin/ios/framework/Source/FlutterViewController.mm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index b520838faf52a..d16bfcfe6870c 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -33,6 +33,8 @@ #import "flutter/shell/platform/embedder/embedder.h" #import "flutter/third_party/spring_animation/spring_animation.h" +FLUTTER_ASSERT_ARC + static constexpr int kMicrosecondsPerSecond = 1000 * 1000; static constexpr CGFloat kScrollViewContentSize = 2.0; From e7360f418a94dcf024299eb4556ad9d96049b15f Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 17:12:57 -0700 Subject: [PATCH 24/35] retain to strong --- .../ios/framework/Source/FlutterViewController_Internal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h index c41533ff9c041..db9fc37bfd75f 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h @@ -46,7 +46,7 @@ typedef void (^FlutterKeyboardAnimationCallback)(fml::TimePoint); @property(class, nonatomic, readonly) BOOL accessibilityIsOnOffSwitchLabelsEnabled; @property(nonatomic, readonly) BOOL isPresentingViewController; @property(nonatomic, readonly) BOOL isVoiceOverRunning; -@property(nonatomic, retain) FlutterKeyboardManager* keyboardManager; +@property(nonatomic, strong) FlutterKeyboardManager* keyboardManager; /** * @brief Whether the status bar is preferred hidden. From be400a2cc7027d334e146feaa7971158078b6ed8 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 17:19:05 -0700 Subject: [PATCH 25/35] Fix self capture --- .../darwin/ios/framework/Source/FlutterViewController.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index d16bfcfe6870c..cf364102897a1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -908,7 +908,7 @@ - (void)viewWillTransitionToSize:(CGSize)size dispatch_get_main_queue(), ^{ // `viewWillTransitionToSize` is only called after the previous rotation is // complete. So there won't be race condition for this flag. - _shouldIgnoreViewportMetricsUpdatesDuringRotation = NO; + weakSelf.shouldIgnoreViewportMetricsUpdatesDuringRotation = NO; [weakSelf updateViewportMetricsIfNeeded]; }); } From 25a6263a3d98836c39c16213779051a9fc9df1b5 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 17:19:45 -0700 Subject: [PATCH 26/35] Revert to previous setup local then assign --- .../framework/Source/FlutterViewController.mm | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index cf364102897a1..bb7dbed88ead4 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -499,16 +499,20 @@ - (void)loadView { self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; [self installSplashScreenViewIfNecessary]; - self.scrollView = [[UIScrollView alloc] init]; - self.scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth; + + // Create and set up the scroll view. + UIScrollView* scrollView = [[UIScrollView alloc] init]; + scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth; // The color shouldn't matter since it is offscreen. - self.scrollView.backgroundColor = UIColor.whiteColor; - self.scrollView.delegate = self; + scrollView.backgroundColor = UIColor.whiteColor; + scrollView.delegate = self; // This is an arbitrary small size. - self.scrollView.contentSize = CGSizeMake(kScrollViewContentSize, kScrollViewContentSize); + scrollView.contentSize = CGSizeMake(kScrollViewContentSize, kScrollViewContentSize); // This is an arbitrary offset that is not CGPointZero. - self.scrollView.contentOffset = CGPointMake(kScrollViewContentSize, kScrollViewContentSize); + scrollView.contentOffset = CGPointMake(kScrollViewContentSize, kScrollViewContentSize); + [self.view addSubview:self.scrollView]; + self.scrollView = scrollView; } - (flutter::PointerData)generatePointerDataForFake { From 496e4f5d8e29a327297671cf5b16cddcdd39800f Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 4 Oct 2024 17:28:09 -0700 Subject: [PATCH 27/35] Remove NSAsserts on self.engine -- though we probably do want em eventually --- .../darwin/ios/framework/Source/FlutterViewController.mm | 2 -- 1 file changed, 2 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index bb7dbed88ead4..e486c92982b62 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -1317,7 +1317,6 @@ - (void)createTouchRateCorrectionVSyncClientIfNeeded { return; } - NSAssert(self.engine, @"engine must not be nil"); flutter::Shell& shell = self.engine.shell; auto callback = [](std::unique_ptr recorder) { // Do nothing in this block. Just trigger system to callback touch events with correct rate. @@ -1402,7 +1401,6 @@ - (void)viewDidLayoutSubviews { if (firstViewBoundsUpdate && applicationOrSceneIsActive && self.engine) { [self surfaceUpdated:YES]; - NSAssert(self.engine, @"engine must not be nil"); flutter::Shell& shell = self.engine.shell; fml::TimeDelta waitTime = #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG From f757a08cc7e4e471f612b048cd6e0108bd0921bf Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Thu, 17 Oct 2024 18:32:24 -0700 Subject: [PATCH 28/35] review feedback: comments and todos --- .../framework/Source/FlutterViewController.mm | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index e486c92982b62..88d7295be78e5 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -187,6 +187,8 @@ - (instancetype)initWithEngine:(FlutterEngine*)engine _weakFactory = std::make_unique>(self); _ongoingTouches = [[NSMutableSet alloc] init]; + // TODO(cbracken): https://github.com/flutter/flutter/issues/157140 + // Eliminate method calls in initializers and dealloc. [self performCommonViewControllerInitialization]; [engine setViewController:self]; } @@ -199,6 +201,8 @@ - (instancetype)initWithProject:(FlutterDartProject*)project bundle:(NSBundle*)nibBundle { self = [super initWithNibName:nibName bundle:nibBundle]; if (self) { + // TODO(cbracken): https://github.com/flutter/flutter/issues/157140 + // Eliminate method calls in initializers and dealloc. [self sharedSetupWithProject:project initialRoute:nil]; } @@ -211,6 +215,8 @@ - (instancetype)initWithProject:(FlutterDartProject*)project bundle:(NSBundle*)nibBundle { self = [super initWithNibName:nibName bundle:nibBundle]; if (self) { + // TODO(cbracken): https://github.com/flutter/flutter/issues/157140 + // Eliminate method calls in initializers and dealloc. [self sharedSetupWithProject:project initialRoute:initialRoute]; } @@ -262,6 +268,9 @@ - (void)sharedSetupWithProject:(nullable FlutterDartProject*)project [_engine createShell:nil libraryURI:nil initialRoute:initialRoute]; _engineNeedsLaunch = YES; _ongoingTouches = [[NSMutableSet alloc] init]; + + // TODO(cbracken): https://github.com/flutter/flutter/issues/157140 + // Eliminate method calls in initializers and dealloc. [self loadDefaultSplashScreenView]; [self performCommonViewControllerInitialization]; } @@ -289,6 +298,8 @@ - (void)performCommonViewControllerInitialization { _orientationPreferences = UIInterfaceOrientationMaskAll; _statusBarStyle = UIStatusBarStyleDefault; + // TODO(cbracken): https://github.com/flutter/flutter/issues/157140 + // Eliminate method calls in initializers and dealloc. [self setUpNotificationCenterObservers]; } @@ -601,6 +612,7 @@ - (void)callViewRenderedCallback { - (void)removeSplashScreenView:(dispatch_block_t _Nullable)onComplete { NSAssert(self.splashScreenView, @"The splash screen view must not be nil"); UIView* splashScreen = self.splashScreenView; + // setSplashScreenView calls this method. Assign directly to ivar to avoid an infinite loop. _splashScreenView = nil; [UIView animateWithDuration:0.2 animations:^{ @@ -960,6 +972,8 @@ - (void)dealloc { // before any other members are destroyed. _weakFactory.reset(); + // TODO(cbracken): https://github.com/flutter/flutter/issues/157140 + // Eliminate method calls in initializers and dealloc. [self removeInternalPlugins]; [self deregisterNotifications]; @@ -1572,8 +1586,6 @@ - (BOOL)isKeyboardNotificationForDifferentView:(NSNotification*)notification { if (isLocal && ![isLocal boolValue]) { return YES; } - - // Engine’s viewController is not current viewController. return self.engine.viewController != self; } From f3c808e530a51af171f42bfa7b4e028923b18d23 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Thu, 17 Oct 2024 18:33:08 -0700 Subject: [PATCH 29/35] review feedback: moar strongSelf --- .../framework/Source/FlutterViewController.mm | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 88d7295be78e5..37bfea4d91ce1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -922,10 +922,15 @@ - (void)viewWillTransitionToSize:(CGSize)size dispatch_after(dispatch_time(DISPATCH_TIME_NOW, static_cast(transitionDuration / 2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + FlutterViewController* strongSelf = weakSelf; + if (!strongSelf) { + return; + } + // `viewWillTransitionToSize` is only called after the previous rotation is // complete. So there won't be race condition for this flag. - weakSelf.shouldIgnoreViewportMetricsUpdatesDuringRotation = NO; - [weakSelf updateViewportMetricsIfNeeded]; + strongSelf.shouldIgnoreViewportMetricsUpdatesDuringRotation = NO; + [strongSelf updateViewportMetricsIfNeeded]; }); } @@ -2293,6 +2298,11 @@ - (void)onPreferredStatusBarStyleUpdated:(NSNotification*)notification { // Notifications may not be on the iOS UI thread __weak FlutterViewController* weakSelf = self; dispatch_async(dispatch_get_main_queue(), ^{ + FlutterViewController* strongSelf = weakSelf; + if (!strongSelf) { + return; + } + NSDictionary* info = notification.userInfo; NSNumber* update = info[@(flutter::kOverlayStyleUpdateNotificationKey)]; if (update == nil) { @@ -2300,9 +2310,9 @@ - (void)onPreferredStatusBarStyleUpdated:(NSNotification*)notification { } UIStatusBarStyle style = static_cast(update.integerValue); - if (style != weakSelf.statusBarStyle) { - weakSelf.statusBarStyle = style; - [weakSelf setNeedsStatusBarAppearanceUpdate]; + if (style != strongSelf.statusBarStyle) { + strongSelf.statusBarStyle = style; + [strongSelf setNeedsStatusBarAppearanceUpdate]; } }); } From c492fcccfcfaf920033e28e752b430efb0e736ec Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 18 Oct 2024 10:17:22 -0700 Subject: [PATCH 30/35] Extract keyboardAnimationCallback helper --- .../framework/Source/FlutterViewController.mm | 87 +++++++++---------- 1 file changed, 43 insertions(+), 44 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 37bfea4d91ce1..fde90f38357bf 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -146,6 +146,9 @@ @interface FlutterViewController () _viewportMetrics.physical_view_inset_bottom = - strongSelf.keyboardAnimationView.layer.presentationLayer.frame.origin.y; - [strongSelf updateViewportMetricsIfNeeded]; - } - } else { - fml::TimeDelta timeElapsed = - keyboardAnimationTargetTime - strongSelf.keyboardAnimationStartTime; - strongSelf->_viewportMetrics.physical_view_inset_bottom = - [strongSelf.keyboardSpringAnimation curveFunction:timeElapsed.ToSecondsF()]; - [strongSelf updateViewportMetricsIfNeeded]; - } - }; - [self setUpKeyboardAnimationVsyncClient:keyboardAnimationCallback]; + [self setUpKeyboardAnimationVsyncClient:^(fml::TimePoint targetTime) { + [weakSelf handleKeyboardAnimationCallback:targetTime]; + }]; VSyncClient* currentVsyncClient = _keyboardAnimationVSyncClient; [UIView animateWithDuration:duration @@ -1813,6 +1777,41 @@ - (void)setUpKeyboardSpringAnimationIfNeeded:(CAAnimation*)keyboardAnimation { toValue:self.targetViewInsetBottom]; } +- (void)handleKeyboardAnimationCallback:(fml::TimePoint)targetTime { + // If the view controller's view is not loaded, bail out. + if (!self.isViewLoaded) { + return; + } + // If the view for tracking keyboard animation is nil, means it is not + // created, bail out. + if (!self.keyboardAnimationView) { + return; + } + // If keyboardAnimationVSyncClient is nil, means the animation ends. + // And should bail out. + if (!self.keyboardAnimationVSyncClient) { + return; + } + + if (!self.keyboardAnimationView.superview) { + // Ensure the keyboardAnimationView is in view hierarchy when animation running. + [self.view addSubview:self.keyboardAnimationView]; + } + + if (!self.keyboardSpringAnimation) { + if (self.keyboardAnimationView.layer.presentationLayer) { + self->_viewportMetrics.physical_view_inset_bottom = + self.keyboardAnimationView.layer.presentationLayer.frame.origin.y; + [self updateViewportMetricsIfNeeded]; + } + } else { + fml::TimeDelta timeElapsed = targetTime - self.keyboardAnimationStartTime; + self->_viewportMetrics.physical_view_inset_bottom = + [self.keyboardSpringAnimation curveFunction:timeElapsed.ToSecondsF()]; + [self updateViewportMetricsIfNeeded]; + } +} + - (void)setUpKeyboardAnimationVsyncClient: (FlutterKeyboardAnimationCallback)keyboardAnimationCallback { if (!keyboardAnimationCallback) { @@ -1825,9 +1824,9 @@ - (void)setUpKeyboardAnimationVsyncClient: FlutterKeyboardAnimationCallback animationCallback = [keyboardAnimationCallback copy]; auto uiCallback = [animationCallback](std::unique_ptr recorder) { fml::TimeDelta frameInterval = recorder->GetVsyncTargetTime() - recorder->GetVsyncStartTime(); - fml::TimePoint keyboardAnimationTargetTime = recorder->GetVsyncTargetTime() + frameInterval; + fml::TimePoint targetTime = recorder->GetVsyncTargetTime() + frameInterval; dispatch_async(dispatch_get_main_queue(), ^(void) { - animationCallback(keyboardAnimationTargetTime); + animationCallback(targetTime); }); }; From 861d6a06b27d0ca57a0a0c4b13b6fee55453f464 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 18 Oct 2024 10:31:05 -0700 Subject: [PATCH 31/35] Extract onFirstFrameRendered --- .../framework/Source/FlutterViewController.mm | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index fde90f38357bf..f40f57570f1a2 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -141,13 +141,14 @@ @interface FlutterViewController () RunsTasksOnCurrentThread()); // Get callback on raster thread and jump back to platform thread. platformTaskRunner->PostTask([weakSelf]() { - FlutterViewController* strongSelf = weakSelf; - if (!strongSelf) { - return; - } - - if (strongSelf.splashScreenView) { - [strongSelf removeSplashScreenView:^{ - [strongSelf callViewRenderedCallback]; - }]; - } else { - [strongSelf callViewRenderedCallback]; - } + [weakSelf onFirstFrameRendered]; }); }); } @@ -724,7 +725,7 @@ - (void)setSplashScreenView:(UIView*)view { // Special case: user wants to remove the splash screen view. if (!view) { if (_splashScreenView) { - [self removeSplashScreenView:nil]; + [self removeSplashScreenWithCompletion:nil]; } return; } From be718951b76819dd83c4bf055b654f0cbcc40da2 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 18 Oct 2024 10:33:40 -0700 Subject: [PATCH 32/35] Format --- .../darwin/ios/framework/Source/FlutterViewController.mm | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index f40f57570f1a2..76aa4be104ee9 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -658,9 +658,7 @@ - (void)installFirstFrameCallback { rasterTaskRunner = self.engine.rasterTaskRunner]() { FML_DCHECK(rasterTaskRunner->RunsTasksOnCurrentThread()); // Get callback on raster thread and jump back to platform thread. - platformTaskRunner->PostTask([weakSelf]() { - [weakSelf onFirstFrameRendered]; - }); + platformTaskRunner->PostTask([weakSelf]() { [weakSelf onFirstFrameRendered]; }); }); } From 18781e1b9d3a096ec81bb27253bee0d78cea4707 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 18 Oct 2024 14:20:08 -0700 Subject: [PATCH 33/35] review feedback: method name --- .../darwin/ios/framework/Source/FlutterViewController.mm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 76aa4be104ee9..03a642ebdbcd1 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -149,7 +149,7 @@ - (void)deregisterNotifications; - (void)onFirstFrameRendered; /// Handles updating viewport metrics on keyboard animation. -- (void)handleKeyboardAnimationCallback:(fml::TimePoint)targetTime; +- (void)handleKeyboardAnimationCallbackWithTargetTime:(fml::TimePoint)targetTime; @end @implementation FlutterViewController { @@ -1722,7 +1722,7 @@ - (void)startKeyBoardAnimation:(NSTimeInterval)duration { __weak FlutterViewController* weakSelf = self; [self setUpKeyboardAnimationVsyncClient:^(fml::TimePoint targetTime) { - [weakSelf handleKeyboardAnimationCallback:targetTime]; + [weakSelf handleKeyboardAnimationCallbackWithTargetTime:targetTime]; }]; VSyncClient* currentVsyncClient = _keyboardAnimationVSyncClient; @@ -1776,7 +1776,7 @@ - (void)setUpKeyboardSpringAnimationIfNeeded:(CAAnimation*)keyboardAnimation { toValue:self.targetViewInsetBottom]; } -- (void)handleKeyboardAnimationCallback:(fml::TimePoint)targetTime { +- (void)handleKeyboardAnimationCallbackWithTargetTime:(fml::TimePoint)targetTime { // If the view controller's view is not loaded, bail out. if (!self.isViewLoaded) { return; From 49800bb1119f7f1ee3e58b6d3c72860ad982f5f6 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Tue, 22 Oct 2024 16:56:23 -0700 Subject: [PATCH 34/35] Hangs head in shame Commit crime... then immediately cover my tracks so it looks like the scrollView is set. I mean it is... it's just never added to the view hierarchy, which is really the most important part. --- .../darwin/ios/framework/Source/FlutterViewController.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 03a642ebdbcd1..dfb1708c120a8 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -526,7 +526,7 @@ - (void)loadView { // This is an arbitrary offset that is not CGPointZero. scrollView.contentOffset = CGPointMake(kScrollViewContentSize, kScrollViewContentSize); - [self.view addSubview:self.scrollView]; + [self.view addSubview:scrollView]; self.scrollView = scrollView; } From 4f9cf856c84f54ddad46b710072583ae967a194b Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 25 Oct 2024 16:31:23 -0700 Subject: [PATCH 35/35] Weak engine is strong Previously we held a scoped_nsobject as a strong reference ivar. This restores that behaviour. --- .../darwin/ios/framework/Source/FlutterViewController.mm | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index dfb1708c120a8..2ef686974f793 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -156,6 +156,7 @@ @implementation FlutterViewController { // TODO(cbracken): https://github.com/flutter/flutter/issues/137801 // Eliminate once we can use weak pointers in platform_view_ios.h. std::unique_ptr> _weakFactory; + FlutterEngine* _engine; flutter::ViewportMetrics _viewportMetrics; MouseState _mouseState; @@ -307,6 +308,10 @@ - (void)performCommonViewControllerInitialization { [self setUpNotificationCenterObservers]; } +- (FlutterEngine*)engine { + return _engine; +} + - (fml::WeakNSObject)getWeakNSObject { return _weakFactory->GetWeakNSObject(); }