Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor and migrate FlutterUndoManagerPlugin to ARC #52234

Merged
merged 4 commits into from
Apr 22, 2024

Conversation

jmagman
Copy link
Member

@jmagman jmagman commented Apr 18, 2024

Smart pointers support ARC as of #47612, and the unit tests were migrated in #48162.

Migrate FlutterUndoManagerPlugin from MRC to ARC.

  1. Refactor so the plugin and its tests don't need to understand the details of FlutterViewController or FlutterEngine (its delegate). This decouples the plugin, and means it doesn't depend on any MRC classes.
  2. Change the delegate so conforming only requires the objects the undo plugin actually needs.

Part of flutter/flutter#137801.

@jmagman jmagman self-assigned this Apr 18, 2024
id<FlutterUndoManagerDelegate> _undoManagerDelegate;
}
@interface FlutterUndoManagerPlugin ()
@property(nonatomic, weak, readonly) id<FlutterUndoManagerDelegate> undoManagerDelegate;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From below:

// `_undoManagerDelegate` is a weak reference because it should retain FlutterUndoManagerPlugin.

: FlutterUndoRedoDirectionRedo;
[target registerUndoWithDirection:newDirection];
// Invoke method on delegate.
[target.undoManagerDelegate flutterUndoManagerPlugin:target
Copy link
Member Author

@jmagman jmagman Apr 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed _undoManagerDelegate to target.undoManagerDelegate and passed target into flutterUndoManagerPlugin so self isn't retained in this block.

@jmagman jmagman marked this pull request as ready for review April 18, 2024 21:37
Copy link
Contributor

@stuartmorgan stuartmorgan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! Some minor comments, but LGTM.

@@ -19,8 +21,14 @@ typedef NS_ENUM(NSInteger, FlutterUndoRedoDirection) {
@class FlutterUndoManagerPlugin;

@protocol FlutterUndoManagerDelegate <NSObject>

@property(nonatomic, weak) UIResponder* viewController;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add declaration comments to these explaining their role in the delegation? (Per style guide, all declarations should be commented. I know the engine is awful about this, but it would be good to break the cycle in new code.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documenting how this delegate works led me to rewrite it.

  1. The -flutterUndoManagerPlugin:handleUndoWithDirection: engine implementation doesn't use the undoManagerPlugin, so there's no need to pass it back through.

  2. I realized we were exposing the view controller just to get the undo manager, and exposing the text input plugin just to get the active text input view, which was a message chain code smell. I changed the delegate to just require the objects the undo manager plugin actually needs, and updated FlutterEngine's implementation of this protocol. This also allowed me to reduce the mocking in the tests.

id<FlutterUndoManagerDelegate> _undoManagerDelegate;
}
@interface FlutterUndoManagerPlugin ()
@property(nonatomic, weak, readonly) id<FlutterUndoManagerDelegate> undoManagerDelegate;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should also have a declaration comment (maybe a short summary of what kinds of things are delegated).

@@ -34,7 +31,6 @@ - (instancetype)initWithDelegate:(id<FlutterUndoManagerDelegate>)undoManagerDele

- (void)dealloc {
[self resetUndoManager];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional since it's pre-existing: it would be good to inline the chain of calls (which is still only one line) to use _undoManagerDelegate directly instead of calling a self method in dealloc in violation of style guide/best practices.

Copy link
Member Author

@jmagman jmagman Apr 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, and my refactor of the delegate reduced the chaining. It still calls self since that's what being passed into -removeAllActionsWithTarget but at least it isn't calling its own properties.

[_undoManagerDelegate.undoManager removeAllActionsWithTarget:self];

handleUndoWithDirection:direction];
}];
[[self undoManager] endUndoGrouping];
NSUndoManager* undoManager = [self undoManager];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional nit: self.undoManager

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now it's a method, but I can add the readonly property. I was trying to limit the number of changes to this file, but I've touched everything at this point.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scratch that. I since removed the -undoManager method and changed the callsites to self.undoManagerDelegate.undoManager.

@@ -99,16 +97,15 @@ - (void)setUndoState:(NSDictionary*)dictionary API_AVAILABLE(ios(9.0)) {
if (canRedo) {
[self registerRedo];
}

if (_viewController.engine.textInputPlugin.textInputView != nil) {
UIView<UITextInput>* textInputView = [self.undoManagerDelegate.textInputPlugin textInputView];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is textInputView not declared as a property?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No:

At the moment I don't want to tackle changing that in a public header.

@@ -1189,12 +1187,19 @@ - (void)flutterTextInputView:(FlutterTextInputView*)textInputView

#pragma mark - Undo Manager Delegate

- (void)flutterUndoManagerPlugin:(FlutterUndoManagerPlugin*)undoManagerPlugin
handleUndoWithDirection:(FlutterUndoRedoDirection)direction {
- (void)handleUndoWithDirection:(FlutterUndoRedoDirection)direction {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

undoManagerPlugin was unused.

Comment on lines +1195 to +1201
- (UIView<UITextInput>*)activeTextInputView {
return [[self textInputPlugin] textInputView];
}

- (NSUndoManager*)undoManager {
return self.viewController.undoManager;
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two new properties to conform to the delegate, to reduce how much the plugin needs to understand this ownership model.


- (void)tearDown {
[self.undoManager removeAllActionsWithTarget:self.undoManagerPlugin];
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was only class mocked, I don't think this would do anything.

@@ -74,18 +97,6 @@ - (void)testSetUndoState {
.andDo(^(NSInvocation* invocation) {
removeAllActionsCount++;
});
__block int delegateUndoCount = 0;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now tracked in the FakeFlutterUndoManagerDelegate test object, which is not mocked anywhere.

handleUndoWithDirection:direction];
}];
[[self undoManager] endUndoGrouping];
NSUndoManager* undoManager = [self undoManager];
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Scratch that. I since removed the -undoManager method and changed the callsites to self.undoManagerDelegate.undoManager.

return _viewController.undoManager;
}

- (void)resetUndoManager API_AVAILABLE(ios(9.0)) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed API_AVAILABLE(ios(9.0)) since the min is iOS 12. Must have missed that in a cleanup.

Copy link
Contributor

@stuartmorgan stuartmorgan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

@jmagman jmagman added the autosubmit Merge PR when tree becomes green via auto submit App label Apr 22, 2024
@auto-submit auto-submit bot merged commit caf81dd into flutter:main Apr 22, 2024
29 checks passed
@jmagman jmagman deleted the undo-manager-arc branch April 22, 2024 17:19
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Apr 22, 2024
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Apr 22, 2024
auto-submit bot pushed a commit to flutter/flutter that referenced this pull request Apr 23, 2024
…147190)

flutter/engine@a4b71f0...33c828f

2024-04-22 skia-flutter-autoroll@skia.org Roll Skia from eb29b46236e6 to 3b32e3280b67 (1 revision) (flutter/engine#52298)
2024-04-22 magder@google.com Break dependency cycle of FlutterViewController <-> FlutterPlatformView (flutter/engine#52271)
2024-04-22 chris@bracken.jp [et] Lookup output filesystem path, not label (flutter/engine#52248)
2024-04-22 skia-flutter-autoroll@skia.org Roll Skia from 975859a96f8f to eb29b46236e6 (9 revisions) (flutter/engine#52297)
2024-04-22 1961493+harryterkelsen@users.noreply.github.com [canvaskit] Add configuration for maximum canvases (flutter/engine#51735)
2024-04-22 120297255+PurplePolyhedron@users.noreply.github.com Fix link in BlendMode.saturation (flutter/engine#52156)
2024-04-22 magder@google.com Refactor and migrate FlutterUndoManagerPlugin to ARC (flutter/engine#52234)
2024-04-22 skia-flutter-autoroll@skia.org Roll Fuchsia Linux SDK from Rr9lFiKCPhMXDGa89... to L533ubzhjWwW7jxbc... (flutter/engine#52293)
2024-04-22 skia-flutter-autoroll@skia.org Roll Dart SDK from 0a83dd7e61b1 to 0ed66a4d77cb (1 revision) (flutter/engine#52294)

Also rolling transitive DEPS:
  fuchsia/sdk/core/linux-amd64 from Rr9lFiKCPhMX to L533ubzhjWwW

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC jsimmons@google.com,rmistry@google.com,zra@google.com on the revert to ensure that a human
is aware of the problem.

To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose

To report a problem with the AutoRoller itself, please file a bug:
https://issues.skia.org/issues/new?component=1389291&template=1850622

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
autosubmit Merge PR when tree becomes green via auto submit App platform-ios
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants