mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Introduces FlutterPluginRegistrant protocol. (#169399)
design doc: https://docs.google.com/document/d/1ZfcQOs-UKRa9jsFG84-MTFeibZTLKCvPQLxF2eskx44/edit?tab=t.0 issue: https://github.com/flutter/flutter/issues/167267 This provides the proper long term API for registering plugins in lieu of `application:didFinishLaunching:withOptions:` no longer being a viable place. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --------- Co-authored-by: Victoria Ashworth <15619084+vashworth@users.noreply.github.com>
This commit is contained in:
parent
5d013c73ba
commit
0e536eb9fe
@ -27,7 +27,20 @@ FLUTTER_DARWIN_EXPORT
|
||||
@interface FlutterAppDelegate
|
||||
: UIResponder <UIApplicationDelegate, FlutterPluginRegistry, FlutterAppLifeCycleProvider>
|
||||
|
||||
@property(strong, nonatomic) UIWindow* window;
|
||||
@property(nonatomic, strong, nullable) UIWindow* window;
|
||||
|
||||
/**
|
||||
* The `FlutterPluginRegistrant` that will be used when FlutterViewControllers
|
||||
* are instantiated from nibs.
|
||||
*
|
||||
* The `FlutterAppDelegate` itself can be passed in without creating a retain
|
||||
* cycle.
|
||||
*
|
||||
* This was introduced to help users migrate code from the FlutterAppDelegate
|
||||
* when UISceneDelegate was adopted. Using
|
||||
* FlutterViewController.pluginRegistrant should be preferred.
|
||||
*/
|
||||
@property(nonatomic, strong, nullable) NSObject<FlutterPluginRegistrant>* pluginRegistrant;
|
||||
|
||||
@end
|
||||
|
||||
|
@ -425,6 +425,27 @@ typedef enum {
|
||||
- (nullable NSObject*)valuePublishedByPlugin:(NSString*)pluginKey;
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
/**
|
||||
* The target of registration of plugins.
|
||||
*
|
||||
* This often is hooked up to the GeneratedPluginRegistrant which is
|
||||
* automatically generated by Flutter for the dependencies listed in the
|
||||
* project.
|
||||
*/
|
||||
@protocol FlutterPluginRegistrant <NSObject>
|
||||
@required
|
||||
/**
|
||||
* Register all the plugins for the registrant.
|
||||
*
|
||||
* This will be called after a FlutterEngine has been instantiated, the registry
|
||||
* will connect any plugins to that engine.
|
||||
*
|
||||
* @param registry The registry where plugins will be registered.
|
||||
*/
|
||||
- (void)registerWithRegistry:(NSObject<FlutterPluginRegistry>*)registry;
|
||||
@end
|
||||
|
||||
#pragma mark -
|
||||
/**
|
||||
* Implement this in the `UIAppDelegate` of your app to enable Flutter plugins to register
|
||||
|
@ -254,6 +254,16 @@ FLUTTER_DARWIN_EXPORT
|
||||
*/
|
||||
@property(nonatomic, readonly) BOOL engineAllowHeadlessExecution;
|
||||
|
||||
/**
|
||||
* The plugin registrant that will be executed when the FlutterViewController is
|
||||
* created with a NIB.
|
||||
*
|
||||
* This is only necessary when working with NIBs (XIBs and Storyboards). When
|
||||
* programatically creating FlutterViewControllers, plugins can be registered
|
||||
* directly.
|
||||
*/
|
||||
@property(nonatomic, weak) IBOutlet NSObject<FlutterPluginRegistrant>* pluginRegistrant;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
@ -20,7 +20,10 @@ static NSString* const kRemoteNotificationCapabitiliy = @"remote-notification";
|
||||
static NSString* const kBackgroundFetchCapatibility = @"fetch";
|
||||
static NSString* const kRestorationStateAppModificationKey = @"mod-date";
|
||||
|
||||
@interface FlutterAppDelegate ()
|
||||
@interface FlutterAppDelegate () {
|
||||
__weak NSObject<FlutterPluginRegistrant>* _weakRegistrant;
|
||||
NSObject<FlutterPluginRegistrant>* _strongRegistrant;
|
||||
}
|
||||
@property(nonatomic, copy) FlutterViewController* (^rootFlutterViewControllerGetter)(void);
|
||||
@property(nonatomic, strong) FlutterPluginAppLifeCycleDelegate* lifeCycleDelegate;
|
||||
@end
|
||||
@ -228,6 +231,26 @@ static NSString* const kRestorationStateAppModificationKey = @"mod-date";
|
||||
|
||||
#pragma mark - FlutterPluginRegistry methods. All delegating to the rootViewController
|
||||
|
||||
- (NSObject<FlutterPluginRegistrant>*)pluginRegistrant {
|
||||
if (_weakRegistrant) {
|
||||
return _weakRegistrant;
|
||||
}
|
||||
if (_strongRegistrant) {
|
||||
return _strongRegistrant;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)setPluginRegistrant:(NSObject<FlutterPluginRegistrant>*)pluginRegistrant {
|
||||
if (pluginRegistrant == (id)self) {
|
||||
_weakRegistrant = pluginRegistrant;
|
||||
_strongRegistrant = nil;
|
||||
} else {
|
||||
_weakRegistrant = nil;
|
||||
_strongRegistrant = pluginRegistrant;
|
||||
}
|
||||
}
|
||||
|
||||
- (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
|
||||
FlutterViewController* flutterRootViewController = [self rootFlutterViewController];
|
||||
if (flutterRootViewController) {
|
||||
|
@ -198,4 +198,20 @@ FLUTTER_ASSERT_ARC
|
||||
completionHandler:[OCMArg any]]);
|
||||
}
|
||||
|
||||
- (void)testSetGetPluginRegistrant {
|
||||
id mockRegistrant = OCMProtocolMock(@protocol(FlutterPluginRegistrant));
|
||||
self.appDelegate.pluginRegistrant = mockRegistrant;
|
||||
XCTAssertEqual(self.appDelegate.pluginRegistrant, mockRegistrant);
|
||||
}
|
||||
|
||||
- (void)testSetGetPluginRegistrantSelf {
|
||||
__weak FlutterAppDelegate* appDelegate = self.appDelegate;
|
||||
@autoreleasepool {
|
||||
appDelegate.pluginRegistrant = (id)appDelegate;
|
||||
self.appDelegate = nil;
|
||||
}
|
||||
// A retain cycle would keep this alive.
|
||||
XCTAssertNil(appDelegate);
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -247,6 +247,9 @@ typedef struct MouseState {
|
||||
if (!self.engine) {
|
||||
[self sharedSetupWithProject:nil initialRoute:nil];
|
||||
}
|
||||
if (self.pluginRegistrant) {
|
||||
[self.pluginRegistrant registerWithRegistry:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
@ -281,6 +284,13 @@ typedef struct MouseState {
|
||||
// Eliminate method calls in initializers and dealloc.
|
||||
[self loadDefaultSplashScreenView];
|
||||
[self performCommonViewControllerInitialization];
|
||||
|
||||
if ([FlutterSharedApplication.application.delegate
|
||||
respondsToSelector:@selector(pluginRegistrant)]) {
|
||||
NSObject<FlutterPluginRegistrant>* pluginRegistrant =
|
||||
[FlutterSharedApplication.application.delegate performSelector:@selector(pluginRegistrant)];
|
||||
[pluginRegistrant registerWithRegistry:self];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)isViewOpaque {
|
||||
|
@ -22,6 +22,7 @@
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/UIViewController+FlutterScreenAndSceneIfLoaded.h"
|
||||
#import "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h"
|
||||
#import "flutter/shell/platform/embedder/embedder.h"
|
||||
#import "flutter/testing/ios/IosUnitTests/App/AppDelegate.h"
|
||||
#import "flutter/third_party/spring_animation/spring_animation.h"
|
||||
|
||||
FLUTTER_ASSERT_ARC
|
||||
@ -2471,4 +2472,22 @@ extern NSNotificationName const FlutterViewControllerWillDealloc;
|
||||
[mockVC stopMocking];
|
||||
}
|
||||
|
||||
- (void)testPluginRegistrant {
|
||||
id mockRegistrant = OCMProtocolMock(@protocol(FlutterPluginRegistrant));
|
||||
FlutterViewController* viewController = [[FlutterViewController alloc] init];
|
||||
viewController.pluginRegistrant = mockRegistrant;
|
||||
[viewController awakeFromNib];
|
||||
OCMVerify([mockRegistrant registerWithRegistry:viewController]);
|
||||
}
|
||||
|
||||
- (void)testAppDelegatePluginRegistrant {
|
||||
id mockRegistrant = OCMProtocolMock(@protocol(FlutterPluginRegistrant));
|
||||
id appDelegate = [[UIApplication sharedApplication] delegate];
|
||||
XCTAssertTrue([appDelegate respondsToSelector:@selector(setPluginRegistrant:)]);
|
||||
[appDelegate setPluginRegistrant:mockRegistrant];
|
||||
FlutterViewController* viewController = [[FlutterViewController alloc] init];
|
||||
[appDelegate setPluginRegistrant:nil];
|
||||
OCMVerify([mockRegistrant registerWithRegistry:viewController]);
|
||||
}
|
||||
|
||||
@end
|
||||
|
@ -7,9 +7,14 @@
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@protocol FlutterPluginRegistrant;
|
||||
|
||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
||||
|
||||
@property(strong, nonatomic) UIWindow* window;
|
||||
@property(nonatomic, strong, nullable) UIWindow* window;
|
||||
|
||||
// A mirror of the FlutterAppDelegate API for integration testing.
|
||||
@property(nonatomic, strong, nullable) NSObject<FlutterPluginRegistrant>* pluginRegistrant;
|
||||
|
||||
@end
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user