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
|
@interface FlutterAppDelegate
|
||||||
: UIResponder <UIApplicationDelegate, FlutterPluginRegistry, FlutterAppLifeCycleProvider>
|
: 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
|
@end
|
||||||
|
|
||||||
|
@ -425,6 +425,27 @@ typedef enum {
|
|||||||
- (nullable NSObject*)valuePublishedByPlugin:(NSString*)pluginKey;
|
- (nullable NSObject*)valuePublishedByPlugin:(NSString*)pluginKey;
|
||||||
@end
|
@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 -
|
#pragma mark -
|
||||||
/**
|
/**
|
||||||
* Implement this in the `UIAppDelegate` of your app to enable Flutter plugins to register
|
* 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;
|
@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
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
@ -20,7 +20,10 @@ static NSString* const kRemoteNotificationCapabitiliy = @"remote-notification";
|
|||||||
static NSString* const kBackgroundFetchCapatibility = @"fetch";
|
static NSString* const kBackgroundFetchCapatibility = @"fetch";
|
||||||
static NSString* const kRestorationStateAppModificationKey = @"mod-date";
|
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, copy) FlutterViewController* (^rootFlutterViewControllerGetter)(void);
|
||||||
@property(nonatomic, strong) FlutterPluginAppLifeCycleDelegate* lifeCycleDelegate;
|
@property(nonatomic, strong) FlutterPluginAppLifeCycleDelegate* lifeCycleDelegate;
|
||||||
@end
|
@end
|
||||||
@ -228,6 +231,26 @@ static NSString* const kRestorationStateAppModificationKey = @"mod-date";
|
|||||||
|
|
||||||
#pragma mark - FlutterPluginRegistry methods. All delegating to the rootViewController
|
#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 {
|
- (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
|
||||||
FlutterViewController* flutterRootViewController = [self rootFlutterViewController];
|
FlutterViewController* flutterRootViewController = [self rootFlutterViewController];
|
||||||
if (flutterRootViewController) {
|
if (flutterRootViewController) {
|
||||||
|
@ -198,4 +198,20 @@ FLUTTER_ASSERT_ARC
|
|||||||
completionHandler:[OCMArg any]]);
|
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
|
@end
|
||||||
|
@ -247,6 +247,9 @@ typedef struct MouseState {
|
|||||||
if (!self.engine) {
|
if (!self.engine) {
|
||||||
[self sharedSetupWithProject:nil initialRoute:nil];
|
[self sharedSetupWithProject:nil initialRoute:nil];
|
||||||
}
|
}
|
||||||
|
if (self.pluginRegistrant) {
|
||||||
|
[self.pluginRegistrant registerWithRegistry:self];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (instancetype)init {
|
- (instancetype)init {
|
||||||
@ -281,6 +284,13 @@ typedef struct MouseState {
|
|||||||
// Eliminate method calls in initializers and dealloc.
|
// Eliminate method calls in initializers and dealloc.
|
||||||
[self loadDefaultSplashScreenView];
|
[self loadDefaultSplashScreenView];
|
||||||
[self performCommonViewControllerInitialization];
|
[self performCommonViewControllerInitialization];
|
||||||
|
|
||||||
|
if ([FlutterSharedApplication.application.delegate
|
||||||
|
respondsToSelector:@selector(pluginRegistrant)]) {
|
||||||
|
NSObject<FlutterPluginRegistrant>* pluginRegistrant =
|
||||||
|
[FlutterSharedApplication.application.delegate performSelector:@selector(pluginRegistrant)];
|
||||||
|
[pluginRegistrant registerWithRegistry:self];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)isViewOpaque {
|
- (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/UIViewController+FlutterScreenAndSceneIfLoaded.h"
|
||||||
#import "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h"
|
#import "flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.h"
|
||||||
#import "flutter/shell/platform/embedder/embedder.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"
|
#import "flutter/third_party/spring_animation/spring_animation.h"
|
||||||
|
|
||||||
FLUTTER_ASSERT_ARC
|
FLUTTER_ASSERT_ARC
|
||||||
@ -2471,4 +2472,22 @@ extern NSNotificationName const FlutterViewControllerWillDealloc;
|
|||||||
[mockVC stopMocking];
|
[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
|
@end
|
||||||
|
@ -7,9 +7,14 @@
|
|||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
|
@protocol FlutterPluginRegistrant;
|
||||||
|
|
||||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
@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
|
@end
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user