Skip to content

Commit 32460c7

Browse files
[image_picker] Fix exception when canceling pickMultipleMedia on iOS (#4761)
Makes the Pigeon interface for the iOS implementation consistent in returning non-nullable lists, in keeping with our strong preference in this repo for collections being non-nullable whenever reasonable (to avoid the issue of having two ways of expressing the same thing), and updates the native side never to return a `nil` list. Fixes flutter/flutter#133111
1 parent c0136f9 commit 32460c7

File tree

9 files changed

+49
-30
lines changed

9 files changed

+49
-30
lines changed

packages/image_picker/image_picker_ios/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
## NEXT
1+
## 0.8.8+1
22

3+
* Fixes exception when canceling pickMultipleMedia.
34
* Updates minimum supported SDK version to Flutter 3.7/Dart 2.19.
45

56
## 0.8.8

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,20 @@ - (void)testPluginMediaPathHasNoItem {
360360
[self waitForExpectationsWithTimeout:30 handler:nil];
361361
}
362362

363+
- (void)testPluginMediaPathConvertsNilToEmptyList {
364+
FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init];
365+
366+
XCTestExpectation *resultExpectation = [self expectationWithDescription:@"result"];
367+
plugin.callContext = [[FLTImagePickerMethodCallContext alloc]
368+
initWithResult:^(NSArray<NSString *> *_Nullable result, FlutterError *_Nullable error) {
369+
XCTAssertEqualObjects(result, @[]);
370+
[resultExpectation fulfill];
371+
}];
372+
[plugin sendCallResultWithSavedPathList:nil];
373+
374+
[self waitForExpectationsWithTimeout:30 handler:nil];
375+
}
376+
363377
- (void)testPluginMediaPathHasItem {
364378
FLTImagePickerPlugin *plugin = [[FLTImagePickerPlugin alloc] init];
365379
NSArray *pathList = @[ @"test" ];

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ - (void)pickImageWithSource:(nonnull FLTSourceSpecification *)source
173173
[self cancelInProgressCall];
174174
FLTImagePickerMethodCallContext *context = [[FLTImagePickerMethodCallContext alloc]
175175
initWithResult:^void(NSArray<NSString *> *paths, FlutterError *error) {
176-
if (paths && paths.count != 1) {
176+
if (paths.count > 1) {
177177
completion(nil, [FlutterError errorWithCode:@"invalid_result"
178178
message:@"Incorrect number of return paths provided"
179179
details:nil]);
@@ -246,7 +246,7 @@ - (void)pickVideoWithSource:(nonnull FLTSourceSpecification *)source
246246
(nonnull void (^)(NSString *_Nullable, FlutterError *_Nullable))completion {
247247
FLTImagePickerMethodCallContext *context = [[FLTImagePickerMethodCallContext alloc]
248248
initWithResult:^void(NSArray<NSString *> *paths, FlutterError *error) {
249-
if (paths && paths.count != 1) {
249+
if (paths.count > 1) {
250250
completion(nil, [FlutterError errorWithCode:@"invalid_result"
251251
message:@"Incorrect number of return paths provided"
252252
details:nil]);
@@ -687,7 +687,7 @@ - (void)sendCallResultWithSavedPathList:(nullable NSArray *)pathList {
687687
message:@"pathList's items should not be null"
688688
details:nil]);
689689
} else {
690-
self.callContext.result(pathList, nil);
690+
self.callContext.result(pathList ?: [NSArray array], nil);
691691
}
692692
self.callContext = nil;
693693
}

packages/image_picker/image_picker_ios/lib/image_picker_ios.dart

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class ImagePickerIOS extends ImagePickerPlatform {
8888
double? maxHeight,
8989
int? imageQuality,
9090
}) async {
91-
final List<dynamic>? paths = await _pickMultiImageAsPath(
91+
final List<dynamic> paths = await _pickMultiImageAsPath(
9292
options: MultiImagePickerOptions(
9393
imageOptions: ImageOptions(
9494
maxWidth: maxWidth,
@@ -97,7 +97,9 @@ class ImagePickerIOS extends ImagePickerPlatform {
9797
),
9898
),
9999
);
100-
if (paths == null) {
100+
// Convert an empty list to a null return since that was the legacy behavior
101+
// of this method.
102+
if (paths.isEmpty) {
101103
return null;
102104
}
103105

@@ -108,15 +110,11 @@ class ImagePickerIOS extends ImagePickerPlatform {
108110
Future<List<XFile>> getMultiImageWithOptions({
109111
MultiImagePickerOptions options = const MultiImagePickerOptions(),
110112
}) async {
111-
final List<String>? paths = await _pickMultiImageAsPath(options: options);
112-
if (paths == null) {
113-
return <XFile>[];
114-
}
115-
113+
final List<String> paths = await _pickMultiImageAsPath(options: options);
116114
return paths.map((String path) => XFile(path)).toList();
117115
}
118116

119-
Future<List<String>?> _pickMultiImageAsPath({
117+
Future<List<String>> _pickMultiImageAsPath({
120118
MultiImagePickerOptions options = const MultiImagePickerOptions(),
121119
}) async {
122120
final int? imageQuality = options.imageOptions.imageQuality;
@@ -141,7 +139,7 @@ class ImagePickerIOS extends ImagePickerPlatform {
141139
MaxSize(width: maxWidth, height: maxHeight),
142140
imageQuality,
143141
options.imageOptions.requestFullMetadata))
144-
?.cast<String>();
142+
.cast<String>();
145143
}
146144

147145
Future<String?> _pickImageAsPath({
@@ -272,7 +270,7 @@ class ImagePickerIOS extends ImagePickerPlatform {
272270
double? maxHeight,
273271
int? imageQuality,
274272
}) async {
275-
final List<String>? paths = await _pickMultiImageAsPath(
273+
final List<String> paths = await _pickMultiImageAsPath(
276274
options: MultiImagePickerOptions(
277275
imageOptions: ImageOptions(
278276
maxWidth: maxWidth,
@@ -281,7 +279,9 @@ class ImagePickerIOS extends ImagePickerPlatform {
281279
),
282280
),
283281
);
284-
if (paths == null) {
282+
// Convert an empty list to a null return since that was the legacy behavior
283+
// of this method.
284+
if (paths.isEmpty) {
285285
return null;
286286
}
287287

packages/image_picker/image_picker_ios/lib/src/messages.g.dart

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ class ImagePickerApi {
179179
}
180180
}
181181

182-
Future<List<String?>?> pickMultiImage(MaxSize arg_maxSize,
182+
Future<List<String?>> pickMultiImage(MaxSize arg_maxSize,
183183
int? arg_imageQuality, bool arg_requestFullMetadata) async {
184184
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
185185
'dev.flutter.pigeon.ImagePickerApi.pickMultiImage', codec,
@@ -198,8 +198,13 @@ class ImagePickerApi {
198198
message: replyList[1] as String?,
199199
details: replyList[2],
200200
);
201+
} else if (replyList[0] == null) {
202+
throw PlatformException(
203+
code: 'null-error',
204+
message: 'Host platform returned null value for non-null return value.',
205+
);
201206
} else {
202-
return (replyList[0] as List<Object?>?)?.cast<String?>();
207+
return (replyList[0] as List<Object?>?)!.cast<String?>();
203208
}
204209
}
205210

packages/image_picker/image_picker_ios/pigeons/messages.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ abstract class ImagePickerApi {
5454
int? imageQuality, bool requestFullMetadata);
5555
@async
5656
@ObjCSelector('pickMultiImageWithMaxSize:quality:fullMetadata:')
57-
List<String>? pickMultiImage(
57+
List<String?> pickMultiImage(
5858
MaxSize maxSize, int? imageQuality, bool requestFullMetadata);
5959
@async
6060
@ObjCSelector('pickVideoWithSource:maxDuration:')

packages/image_picker/image_picker_ios/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ 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.8
5+
version: 0.8.8+1
66

77
environment:
88
sdk: ">=2.19.0 <4.0.0"

packages/image_picker/image_picker_ios/test/image_picker_ios_test.dart

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class _ApiLogger implements TestHostImagePickerApi {
5757
}
5858

5959
@override
60-
Future<List<String?>?> pickMultiImage(
60+
Future<List<String?>> pickMultiImage(
6161
MaxSize maxSize,
6262
int? imageQuality,
6363
bool requestFullMetadata,
@@ -68,7 +68,7 @@ class _ApiLogger implements TestHostImagePickerApi {
6868
'imageQuality': imageQuality,
6969
'requestFullMetadata': requestFullMetadata,
7070
}));
71-
return returnValue as List<String?>?;
71+
return returnValue as List<String?>;
7272
}
7373

7474
@override
@@ -441,8 +441,8 @@ void main() {
441441
);
442442
});
443443

444-
test('handles a null image path response gracefully', () async {
445-
log.returnValue = null;
444+
test('returns null for an empty list', () async {
445+
log.returnValue = <String>[];
446446

447447
expect(await picker.pickMultiImage(), isNull);
448448
});
@@ -883,11 +883,10 @@ void main() {
883883
);
884884
});
885885

886-
test('handles a null image path response gracefully', () async {
887-
log.returnValue = null;
886+
test('returns null for an empty list', () async {
887+
log.returnValue = <String>[];
888888

889889
expect(await picker.getMultiImage(), isNull);
890-
expect(await picker.getMultiImage(), isNull);
891890
});
892891
});
893892

@@ -1643,8 +1642,8 @@ void main() {
16431642
);
16441643
});
16451644

1646-
test('handles a null image path response gracefully', () async {
1647-
log.returnValue = null;
1645+
test('handles an empty response', () async {
1646+
log.returnValue = <String>[];
16481647

16491648
expect(await picker.getMultiImageWithOptions(), isEmpty);
16501649
});

packages/image_picker/image_picker_ios/test/test_api.g.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ abstract class TestHostImagePickerApi {
5454
Future<String?> pickImage(SourceSpecification source, MaxSize maxSize,
5555
int? imageQuality, bool requestFullMetadata);
5656

57-
Future<List<String?>?> pickMultiImage(
57+
Future<List<String?>> pickMultiImage(
5858
MaxSize maxSize, int? imageQuality, bool requestFullMetadata);
5959

6060
Future<String?> pickVideo(
@@ -117,7 +117,7 @@ abstract class TestHostImagePickerApi {
117117
final bool? arg_requestFullMetadata = (args[2] as bool?);
118118
assert(arg_requestFullMetadata != null,
119119
'Argument for dev.flutter.pigeon.ImagePickerApi.pickMultiImage was null, expected non-null bool.');
120-
final List<String?>? output = await api.pickMultiImage(
120+
final List<String?> output = await api.pickMultiImage(
121121
arg_maxSize!, arg_imageQuality, arg_requestFullMetadata!);
122122
return <Object?>[output];
123123
});

0 commit comments

Comments
 (0)