Skip to content

Commit 40f248d

Browse files
[image_picker] Update for UIScene compatibility (#10677)
Replaces the code that used the key window's root view controller with a call to the new registrar viewController method to get the actual Flutter content's view controller. Introduces the same protocol abstraction now used in a number of our plugins, so that a stub can be injected without having to mock (which would be a barrier to Swift migration) or fake (which is fragile since it would have to be complete, and methods can be added over time) the entire Flutter plugin registrar. Fixes flutter/flutter#174418 ## Pre-Review Checklist [^1]: Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling.
1 parent 9b0239d commit 40f248d

10 files changed

Lines changed: 172 additions & 86 deletions

File tree

packages/image_picker/image_picker_ios/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 0.8.13+4
2+
3+
* Improves compatibility with `UIScene`.
4+
* Updates minimum supported SDK version to Flutter 3.38/Dart 3.10.
5+
16
## 0.8.13+3
27

38
* Fixes a performance regression on iOS where picking videos could cause a long delay due to transcoding. The picker is now configured to request the original asset to avoid conversion.

packages/image_picker/image_picker_ios/example/ios/RunnerTests/ImagePickerPluginTests.m

Lines changed: 78 additions & 46 deletions
Large diffs are not rendered by default.

packages/image_picker/image_picker_ios/example/pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ description: Demonstrates how to use the image_picker plugin.
33
publish_to: none
44

55
environment:
6-
sdk: ^3.9.0
7-
flutter: ">=3.35.0"
6+
sdk: ^3.10.0
7+
flutter: ">=3.38.0"
88

99
dependencies:
1010
flutter:
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright 2013 The Flutter Authors
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#import "./include/image_picker_ios/FIPViewProvider.h"
6+
7+
@import UIKit;
8+
9+
@interface FIPDefaultViewProvider ()
10+
/// The backing registrar.
11+
@property(nonatomic) NSObject<FlutterPluginRegistrar> *registrar;
12+
@end
13+
14+
@implementation FIPDefaultViewProvider
15+
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
16+
self = [super init];
17+
if (self) {
18+
_registrar = registrar;
19+
}
20+
return self;
21+
}
22+
23+
- (UIViewController *)viewController {
24+
return self.registrar.viewController;
25+
}
26+
@end

packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/FLTImagePickerPlugin.m

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -35,20 +35,32 @@ @interface FLTImagePickerPlugin ()
3535
/// controller would normally be created. Each call to
3636
/// createImagePickerController will remove the current first element from
3737
/// the array.
38-
@property(strong, nonatomic)
38+
@property(nonatomic, nullable)
3939
NSMutableArray<UIImagePickerController *> *imagePickerControllerOverrides;
4040

41+
/// The view provider to use for displaying native view controllers.
42+
@property(nonatomic, nonnull) NSObject<FIPViewProvider> *viewProvider;
43+
4144
@end
4245

4346
typedef NS_ENUM(NSInteger, ImagePickerClassType) { UIImagePickerClassType, PHPickerClassType };
4447

4548
@implementation FLTImagePickerPlugin
4649

4750
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
48-
FLTImagePickerPlugin *instance = [[FLTImagePickerPlugin alloc] init];
51+
FLTImagePickerPlugin *instance = [[FLTImagePickerPlugin alloc]
52+
initWithViewProvider:[[FIPDefaultViewProvider alloc] initWithRegistrar:registrar]];
4953
SetUpFLTImagePickerApi(registrar.messenger, instance);
5054
}
5155

56+
- (instancetype)initWithViewProvider:(NSObject<FIPViewProvider> *)viewProvider {
57+
self = [super init];
58+
if (self) {
59+
_viewProvider = viewProvider;
60+
}
61+
return self;
62+
}
63+
5264
- (UIImagePickerController *)createImagePickerController {
5365
if ([self.imagePickerControllerOverrides count] > 0) {
5466
UIImagePickerController *controller = [self.imagePickerControllerOverrides firstObject];
@@ -64,24 +76,6 @@ - (void)setImagePickerControllerOverrides:
6476
_imagePickerControllerOverrides = [imagePickerControllers mutableCopy];
6577
}
6678

67-
- (UIViewController *)viewControllerWithWindow:(UIWindow *)window {
68-
UIWindow *windowToUse = window;
69-
if (windowToUse == nil) {
70-
for (UIWindow *window in [UIApplication sharedApplication].windows) {
71-
if (window.isKeyWindow) {
72-
windowToUse = window;
73-
break;
74-
}
75-
}
76-
}
77-
78-
UIViewController *topController = windowToUse.rootViewController;
79-
while (topController.presentedViewController) {
80-
topController = topController.presentedViewController;
81-
}
82-
return topController;
83-
}
84-
8579
/// Returns the UIImagePickerControllerCameraDevice to use given [source].
8680
///
8781
/// @param source The source specification from Dart.
@@ -323,9 +317,9 @@ - (void)showCamera:(UIImagePickerControllerCameraDevice)device
323317
[UIImagePickerController isCameraDeviceAvailable:device]) {
324318
imagePickerController.sourceType = UIImagePickerControllerSourceTypeCamera;
325319
imagePickerController.cameraDevice = device;
326-
[[self viewControllerWithWindow:nil] presentViewController:imagePickerController
327-
animated:YES
328-
completion:nil];
320+
[self.viewProvider.viewController presentViewController:imagePickerController
321+
animated:YES
322+
completion:nil];
329323
} else {
330324
UIAlertController *cameraErrorAlert = [UIAlertController
331325
alertControllerWithTitle:NSLocalizedString(@"Error", @"Alert title when camera unavailable")
@@ -338,9 +332,9 @@ - (void)showCamera:(UIImagePickerControllerCameraDevice)device
338332
style:UIAlertActionStyleDefault
339333
handler:^(UIAlertAction *action){
340334
}]];
341-
[[self viewControllerWithWindow:nil] presentViewController:cameraErrorAlert
342-
animated:YES
343-
completion:nil];
335+
[self.viewProvider.viewController presentViewController:cameraErrorAlert
336+
animated:YES
337+
completion:nil];
344338
[self sendCallResultWithSavedPathList:nil];
345339
}
346340
}
@@ -438,16 +432,16 @@ - (void)errorNoPhotoAccess:(PHAuthorizationStatus)status {
438432

439433
- (void)showPhotoLibraryWithPHPicker:(PHPickerViewController *)pickerViewController
440434
API_AVAILABLE(ios(14)) {
441-
[[self viewControllerWithWindow:nil] presentViewController:pickerViewController
442-
animated:YES
443-
completion:nil];
435+
[self.viewProvider.viewController presentViewController:pickerViewController
436+
animated:YES
437+
completion:nil];
444438
}
445439

446440
- (void)showPhotoLibraryWithImagePicker:(UIImagePickerController *)imagePickerController {
447441
imagePickerController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;
448-
[[self viewControllerWithWindow:nil] presentViewController:imagePickerController
449-
animated:YES
450-
completion:nil];
442+
[self.viewProvider.viewController presentViewController:imagePickerController
443+
animated:YES
444+
completion:nil];
451445
}
452446

453447
- (NSNumber *)getDesiredImageQuality:(NSNumber *)imageQuality {

packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/include/ImagePickerPlugin.modulemap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ framework module image_picker_ios {
55
module * { export * }
66

77
explicit module Test {
8+
header "FIPViewProvider.h"
89
header "FLTImagePickerPlugin_Test.h"
910
header "FLTImagePickerImageUtil.h"
1011
header "FLTImagePickerMetaDataUtil.h"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2013 The Flutter Authors
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
@import Flutter;
6+
@import UIKit;
7+
8+
NS_ASSUME_NONNULL_BEGIN
9+
10+
/// Protocol for obtaining the view controller containing the Flutter content.
11+
@protocol FIPViewProvider <NSObject>
12+
@required
13+
/// The view controller containing the Flutter content.
14+
@property(nonatomic, readonly, nullable) UIViewController *viewController;
15+
@end
16+
17+
/// A default implementation of the FIPViewProvider protocol.
18+
@interface FIPDefaultViewProvider : NSObject <FIPViewProvider>
19+
/// Returns a provider backed by the given registrar.
20+
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar
21+
NS_DESIGNATED_INITIALIZER;
22+
- (instancetype)init NS_UNAVAILABLE;
23+
@end
24+
25+
NS_ASSUME_NONNULL_END

packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/include/image_picker_ios/FLTImagePickerPlugin.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
NS_ASSUME_NONNULL_BEGIN
99

1010
@interface FLTImagePickerPlugin : NSObject <FlutterPlugin>
11+
/// FLTImagePickerPlugin has no public initializers, as it should not be
12+
/// created directly by plugin clients.
13+
- (instancetype)init NS_UNAVAILABLE;
1114
@end
1215

1316
NS_ASSUME_NONNULL_END

packages/image_picker/image_picker_ios/ios/image_picker_ios/Sources/image_picker_ios/include/image_picker_ios/FLTImagePickerPlugin_Test.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
// This header is available in the Test module. Import via "@import image_picker_ios_ios.Test;"
66

7-
#import <image_picker_ios/FLTImagePickerPlugin.h>
8-
7+
#import "FIPViewProvider.h"
8+
#import "FLTImagePickerPlugin.h"
99
#import "messages.g.h"
1010

1111
NS_ASSUME_NONNULL_BEGIN
@@ -62,7 +62,7 @@ typedef void (^FlutterResultAdapter)(NSArray<NSString *> *_Nullable, FlutterErro
6262
/// The context of the Flutter method call that is currently being handled, if any.
6363
@property(strong, nonatomic, nullable) FLTImagePickerMethodCallContext *callContext;
6464

65-
- (UIViewController *)viewControllerWithWindow:(nullable UIWindow *)window;
65+
- (instancetype)initWithViewProvider:(NSObject<FIPViewProvider> *)viewProvider;
6666

6767
/// Validates the provided paths list, then sends it via `callContext.result` as the result of the
6868
/// original platform channel method call, clearing the in-progress call state.

packages/image_picker/image_picker_ios/pubspec.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ name: image_picker_ios
22
description: iOS implementation of the image_picker plugin.
33
repository: https://github.com/flutter/packages/tree/main/packages/image_picker/image_picker_ios
44
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+image_picker%22
5-
version: 0.8.13+3
5+
version: 0.8.13+4
66

77
environment:
8-
sdk: ^3.9.0
9-
flutter: ">=3.35.0"
8+
sdk: ^3.10.0
9+
flutter: ">=3.38.0"
1010

1111
flutter:
1212
plugin:

0 commit comments

Comments
 (0)