[ios_platform_images] Convert to Pigeon (#4945)
Replaces the manual platform method code with Pigeon.
Also adds some additional Dart unit tests. Since the needs are minimal, this uses a manual fake rather than introducing Mockito.
Fixes https://github.com/flutter/flutter/issues/117911
diff --git a/packages/ios_platform_images/CHANGELOG.md b/packages/ios_platform_images/CHANGELOG.md
index 69781d6..0025950 100644
--- a/packages/ios_platform_images/CHANGELOG.md
+++ b/packages/ios_platform_images/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.2.2+3
+
+* Converts platform communication to Pigeon.
+
## 0.2.2+2
* Adds pub topics to package metadata.
diff --git a/packages/ios_platform_images/example/ios/Runner.xcodeproj/project.pbxproj b/packages/ios_platform_images/example/ios/Runner.xcodeproj/project.pbxproj
index acea2f5..ba03518 100644
--- a/packages/ios_platform_images/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/packages/ios_platform_images/example/ios/Runner.xcodeproj/project.pbxproj
@@ -222,7 +222,7 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
- LastUpgradeCheck = 1300;
+ LastUpgradeCheck = 1430;
ORGANIZATIONNAME = "The Flutter Authors";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
diff --git a/packages/ios_platform_images/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/ios_platform_images/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index 7ae2cb4..51e700a 100644
--- a/packages/ios_platform_images/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/packages/ios_platform_images/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
- LastUpgradeVersion = "1300"
+ LastUpgradeVersion = "1430"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
diff --git a/packages/ios_platform_images/ios/Classes/IosPlatformImagesPlugin.h b/packages/ios_platform_images/ios/Classes/IosPlatformImagesPlugin.h
index f3c8efe..b5dcc7b 100644
--- a/packages/ios_platform_images/ios/Classes/IosPlatformImagesPlugin.h
+++ b/packages/ios_platform_images/ios/Classes/IosPlatformImagesPlugin.h
@@ -4,7 +4,9 @@
#import <Flutter/Flutter.h>
+#import "messages.g.h"
+
/// A plugin for Flutter that allows Flutter to load images in a platform
/// specific way on iOS.
-@interface IosPlatformImagesPlugin : NSObject <FlutterPlugin>
+@interface IosPlatformImagesPlugin : NSObject <FlutterPlugin, FPIPlatformImagesApi>
@end
diff --git a/packages/ios_platform_images/ios/Classes/IosPlatformImagesPlugin.m b/packages/ios_platform_images/ios/Classes/IosPlatformImagesPlugin.m
index 5f7debc..b41ff99 100644
--- a/packages/ios_platform_images/ios/Classes/IosPlatformImagesPlugin.m
+++ b/packages/ios_platform_images/ios/Classes/IosPlatformImagesPlugin.m
@@ -14,35 +14,27 @@
@implementation IosPlatformImagesPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
- FlutterMethodChannel *channel =
- [FlutterMethodChannel methodChannelWithName:@"plugins.flutter.io/ios_platform_images"
- binaryMessenger:[registrar messenger]];
+ FPIPlatformImagesApiSetup(registrar.messenger, [[IosPlatformImagesPlugin alloc] init]);
+}
- [channel setMethodCallHandler:^(FlutterMethodCall *call, FlutterResult result) {
- if ([@"loadImage" isEqualToString:call.method]) {
- NSString *name = call.arguments;
- UIImage *image = [UIImage imageNamed:name];
- NSData *data = UIImagePNGRepresentation(image);
- if (data) {
- result(@{
- @"scale" : @(image.scale),
- @"data" : [FlutterStandardTypedData typedDataWithBytes:data],
- });
- } else {
- result(nil);
- }
- return;
- } else if ([@"resolveURL" isEqualToString:call.method]) {
- NSArray *args = call.arguments;
- NSString *name = args[0];
- NSString *extension = (args[1] == (id)NSNull.null) ? nil : args[1];
+- (nullable FPIPlatformImageData *)
+ loadImageWithName:(nonnull NSString *)name
+ error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error {
+ UIImage *image = [UIImage imageNamed:name];
+ NSData *data = UIImagePNGRepresentation(image);
+ if (!data) {
+ return nil;
+ }
+ return [FPIPlatformImageData makeWithData:[FlutterStandardTypedData typedDataWithBytes:data]
+ scale:@(image.scale)];
+}
- NSURL *url = [[NSBundle mainBundle] URLForResource:name withExtension:extension];
- result(url.absoluteString);
- return;
- }
- result(FlutterMethodNotImplemented);
- }];
+- (nullable NSString *)resolveURLForResource:(nonnull NSString *)name
+ withExtension:(nullable NSString *)extension
+ error:(FlutterError *_Nullable __autoreleasing *_Nonnull)
+ error {
+ NSURL *url = [[NSBundle mainBundle] URLForResource:name withExtension:extension];
+ return url.absoluteString;
}
@end
diff --git a/packages/ios_platform_images/ios/Classes/messages.g.h b/packages/ios_platform_images/ios/Classes/messages.g.h
new file mode 100644
index 0000000..f4235cf
--- /dev/null
+++ b/packages/ios_platform_images/ios/Classes/messages.g.h
@@ -0,0 +1,47 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Autogenerated from Pigeon (v11.0.1), do not edit directly.
+// See also: https://pub.dev/packages/pigeon
+
+#import <Foundation/Foundation.h>
+
+@protocol FlutterBinaryMessenger;
+@protocol FlutterMessageCodec;
+@class FlutterError;
+@class FlutterStandardTypedData;
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class FPIPlatformImageData;
+
+/// A serialization of a platform image's data.
+@interface FPIPlatformImageData : NSObject
+/// `init` unavailable to enforce nonnull fields, see the `make` class method.
+- (instancetype)init NS_UNAVAILABLE;
++ (instancetype)makeWithData:(FlutterStandardTypedData *)data scale:(NSNumber *)scale;
+/// The image data.
+@property(nonatomic, strong) FlutterStandardTypedData *data;
+/// The image's scale factor.
+@property(nonatomic, strong) NSNumber *scale;
+@end
+
+/// The codec used by FPIPlatformImagesApi.
+NSObject<FlutterMessageCodec> *FPIPlatformImagesApiGetCodec(void);
+
+@protocol FPIPlatformImagesApi
+/// Returns the URL for the given resource, or null if no such resource is
+/// found.
+- (nullable NSString *)resolveURLForResource:(NSString *)resourceName
+ withExtension:(nullable NSString *)extension
+ error:(FlutterError *_Nullable *_Nonnull)error;
+/// Returns the data for the image resource with the given name, or null if
+/// no such resource is found.
+- (nullable FPIPlatformImageData *)loadImageWithName:(NSString *)name
+ error:(FlutterError *_Nullable *_Nonnull)error;
+@end
+
+extern void FPIPlatformImagesApiSetup(id<FlutterBinaryMessenger> binaryMessenger,
+ NSObject<FPIPlatformImagesApi> *_Nullable api);
+
+NS_ASSUME_NONNULL_END
diff --git a/packages/ios_platform_images/ios/Classes/messages.g.m b/packages/ios_platform_images/ios/Classes/messages.g.m
new file mode 100644
index 0000000..b96e65e
--- /dev/null
+++ b/packages/ios_platform_images/ios/Classes/messages.g.m
@@ -0,0 +1,163 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Autogenerated from Pigeon (v11.0.1), do not edit directly.
+// See also: https://pub.dev/packages/pigeon
+
+#import "messages.g.h"
+
+#if TARGET_OS_OSX
+#import <FlutterMacOS/FlutterMacOS.h>
+#else
+#import <Flutter/Flutter.h>
+#endif
+
+#if !__has_feature(objc_arc)
+#error File requires ARC to be enabled.
+#endif
+
+static NSArray *wrapResult(id result, FlutterError *error) {
+ if (error) {
+ return @[
+ error.code ?: [NSNull null], error.message ?: [NSNull null], error.details ?: [NSNull null]
+ ];
+ }
+ return @[ result ?: [NSNull null] ];
+}
+static id GetNullableObjectAtIndex(NSArray *array, NSInteger key) {
+ id result = array[key];
+ return (result == [NSNull null]) ? nil : result;
+}
+
+@interface FPIPlatformImageData ()
++ (FPIPlatformImageData *)fromList:(NSArray *)list;
++ (nullable FPIPlatformImageData *)nullableFromList:(NSArray *)list;
+- (NSArray *)toList;
+@end
+
+@implementation FPIPlatformImageData
++ (instancetype)makeWithData:(FlutterStandardTypedData *)data scale:(NSNumber *)scale {
+ FPIPlatformImageData *pigeonResult = [[FPIPlatformImageData alloc] init];
+ pigeonResult.data = data;
+ pigeonResult.scale = scale;
+ return pigeonResult;
+}
++ (FPIPlatformImageData *)fromList:(NSArray *)list {
+ FPIPlatformImageData *pigeonResult = [[FPIPlatformImageData alloc] init];
+ pigeonResult.data = GetNullableObjectAtIndex(list, 0);
+ NSAssert(pigeonResult.data != nil, @"");
+ pigeonResult.scale = GetNullableObjectAtIndex(list, 1);
+ NSAssert(pigeonResult.scale != nil, @"");
+ return pigeonResult;
+}
++ (nullable FPIPlatformImageData *)nullableFromList:(NSArray *)list {
+ return (list) ? [FPIPlatformImageData fromList:list] : nil;
+}
+- (NSArray *)toList {
+ return @[
+ (self.data ?: [NSNull null]),
+ (self.scale ?: [NSNull null]),
+ ];
+}
+@end
+
+@interface FPIPlatformImagesApiCodecReader : FlutterStandardReader
+@end
+@implementation FPIPlatformImagesApiCodecReader
+- (nullable id)readValueOfType:(UInt8)type {
+ switch (type) {
+ case 128:
+ return [FPIPlatformImageData fromList:[self readValue]];
+ default:
+ return [super readValueOfType:type];
+ }
+}
+@end
+
+@interface FPIPlatformImagesApiCodecWriter : FlutterStandardWriter
+@end
+@implementation FPIPlatformImagesApiCodecWriter
+- (void)writeValue:(id)value {
+ if ([value isKindOfClass:[FPIPlatformImageData class]]) {
+ [self writeByte:128];
+ [self writeValue:[value toList]];
+ } else {
+ [super writeValue:value];
+ }
+}
+@end
+
+@interface FPIPlatformImagesApiCodecReaderWriter : FlutterStandardReaderWriter
+@end
+@implementation FPIPlatformImagesApiCodecReaderWriter
+- (FlutterStandardWriter *)writerWithData:(NSMutableData *)data {
+ return [[FPIPlatformImagesApiCodecWriter alloc] initWithData:data];
+}
+- (FlutterStandardReader *)readerWithData:(NSData *)data {
+ return [[FPIPlatformImagesApiCodecReader alloc] initWithData:data];
+}
+@end
+
+NSObject<FlutterMessageCodec> *FPIPlatformImagesApiGetCodec(void) {
+ static FlutterStandardMessageCodec *sSharedObject = nil;
+ static dispatch_once_t sPred = 0;
+ dispatch_once(&sPred, ^{
+ FPIPlatformImagesApiCodecReaderWriter *readerWriter =
+ [[FPIPlatformImagesApiCodecReaderWriter alloc] init];
+ sSharedObject = [FlutterStandardMessageCodec codecWithReaderWriter:readerWriter];
+ });
+ return sSharedObject;
+}
+
+void FPIPlatformImagesApiSetup(id<FlutterBinaryMessenger> binaryMessenger,
+ NSObject<FPIPlatformImagesApi> *api) {
+ /// Returns the URL for the given resource, or null if no such resource is
+ /// found.
+ {
+ FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
+ initWithName:@"dev.flutter.pigeon.ios_platform_images.PlatformImagesApi.resolveUrl"
+ binaryMessenger:binaryMessenger
+ codec:FPIPlatformImagesApiGetCodec()];
+ if (api) {
+ NSCAssert([api respondsToSelector:@selector(resolveURLForResource:withExtension:error:)],
+ @"FPIPlatformImagesApi api (%@) doesn't respond to "
+ @"@selector(resolveURLForResource:withExtension:error:)",
+ api);
+ [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+ NSArray *args = message;
+ NSString *arg_resourceName = GetNullableObjectAtIndex(args, 0);
+ NSString *arg_extension = GetNullableObjectAtIndex(args, 1);
+ FlutterError *error;
+ NSString *output = [api resolveURLForResource:arg_resourceName
+ withExtension:arg_extension
+ error:&error];
+ callback(wrapResult(output, error));
+ }];
+ } else {
+ [channel setMessageHandler:nil];
+ }
+ }
+ /// Returns the data for the image resource with the given name, or null if
+ /// no such resource is found.
+ {
+ FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc]
+ initWithName:@"dev.flutter.pigeon.ios_platform_images.PlatformImagesApi.loadImage"
+ binaryMessenger:binaryMessenger
+ codec:FPIPlatformImagesApiGetCodec()];
+ if (api) {
+ NSCAssert(
+ [api respondsToSelector:@selector(loadImageWithName:error:)],
+ @"FPIPlatformImagesApi api (%@) doesn't respond to @selector(loadImageWithName:error:)",
+ api);
+ [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) {
+ NSArray *args = message;
+ NSString *arg_name = GetNullableObjectAtIndex(args, 0);
+ FlutterError *error;
+ FPIPlatformImageData *output = [api loadImageWithName:arg_name error:&error];
+ callback(wrapResult(output, error));
+ }];
+ } else {
+ [channel setMessageHandler:nil];
+ }
+ }
+}
diff --git a/packages/ios_platform_images/lib/ios_platform_images.dart b/packages/ios_platform_images/lib/ios_platform_images.dart
index b372d36..22e0b84 100644
--- a/packages/ios_platform_images/lib/ios_platform_images.dart
+++ b/packages/ios_platform_images/lib/ios_platform_images.dart
@@ -6,10 +6,17 @@
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart'
- show SynchronousFuture, describeIdentity, immutable, objectRuntimeType;
+ show
+ SynchronousFuture,
+ describeIdentity,
+ immutable,
+ objectRuntimeType,
+ visibleForTesting;
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
+import 'src/messages.g.dart';
+
class _FutureImageStreamCompleter extends ImageStreamCompleter {
_FutureImageStreamCompleter({
required Future<ui.Codec> codec,
@@ -101,14 +108,22 @@
'(${describeIdentity(_futureBytes)}, scale: $_futureScale)';
}
+PlatformImagesApi _hostApi = PlatformImagesApi();
+
+/// Sets the [PlatformImagesApi] instance used to implement the static methods
+/// of [IosPlatformImages].
+///
+/// This exists only for unit tests.
+@visibleForTesting
+void setPlatformImageHostApi(PlatformImagesApi api) {
+ _hostApi = api;
+}
+
// ignore: avoid_classes_with_only_static_members
/// Class to help loading of iOS platform images into Flutter.
///
/// For example, loading an image that is in `Assets.xcassts`.
class IosPlatformImages {
- static const MethodChannel _channel =
- MethodChannel('plugins.flutter.io/ios_platform_images');
-
/// Loads an image from asset catalogs. The equivalent would be:
/// `[UIImage imageNamed:name]`.
///
@@ -116,12 +131,11 @@
///
/// See [https://developer.apple.com/documentation/uikit/uiimage/1624146-imagenamed?language=objc]
static ImageProvider load(String name) {
- final Future<Map<String, dynamic>?> loadInfo =
- _channel.invokeMapMethod<String, dynamic>('loadImage', name);
+ final Future<PlatformImageData?> imageData = _hostApi.loadImage(name);
final Completer<Uint8List> bytesCompleter = Completer<Uint8List>();
final Completer<double> scaleCompleter = Completer<double>();
- loadInfo.then((Map<String, dynamic>? map) {
- if (map == null) {
+ imageData.then((PlatformImageData? image) {
+ if (image == null) {
scaleCompleter.completeError(
Exception("Image couldn't be found: $name"),
);
@@ -130,8 +144,8 @@
);
return;
}
- scaleCompleter.complete(map['scale']! as double);
- bytesCompleter.complete(map['data']! as Uint8List);
+ scaleCompleter.complete(image.scale);
+ bytesCompleter.complete(image.data);
});
return _FutureMemoryImage(bytesCompleter.future, scaleCompleter.future);
}
@@ -143,7 +157,6 @@
///
/// See [https://developer.apple.com/documentation/foundation/nsbundle/1411540-urlforresource?language=objc]
static Future<String?> resolveURL(String name, {String? extension}) {
- return _channel
- .invokeMethod<String>('resolveURL', <Object?>[name, extension]);
+ return _hostApi.resolveUrl(name, extension);
}
}
diff --git a/packages/ios_platform_images/lib/src/messages.g.dart b/packages/ios_platform_images/lib/src/messages.g.dart
new file mode 100644
index 0000000..a3a5ab4
--- /dev/null
+++ b/packages/ios_platform_images/lib/src/messages.g.dart
@@ -0,0 +1,126 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+// Autogenerated from Pigeon (v11.0.1), do not edit directly.
+// See also: https://pub.dev/packages/pigeon
+// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import
+
+import 'dart:async';
+import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
+
+import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
+import 'package:flutter/services.dart';
+
+/// A serialization of a platform image's data.
+class PlatformImageData {
+ PlatformImageData({
+ required this.data,
+ required this.scale,
+ });
+
+ /// The image data.
+ Uint8List data;
+
+ /// The image's scale factor.
+ double scale;
+
+ Object encode() {
+ return <Object?>[
+ data,
+ scale,
+ ];
+ }
+
+ static PlatformImageData decode(Object result) {
+ result as List<Object?>;
+ return PlatformImageData(
+ data: result[0]! as Uint8List,
+ scale: result[1]! as double,
+ );
+ }
+}
+
+class _PlatformImagesApiCodec extends StandardMessageCodec {
+ const _PlatformImagesApiCodec();
+ @override
+ void writeValue(WriteBuffer buffer, Object? value) {
+ if (value is PlatformImageData) {
+ buffer.putUint8(128);
+ writeValue(buffer, value.encode());
+ } else {
+ super.writeValue(buffer, value);
+ }
+ }
+
+ @override
+ Object? readValueOfType(int type, ReadBuffer buffer) {
+ switch (type) {
+ case 128:
+ return PlatformImageData.decode(readValue(buffer)!);
+ default:
+ return super.readValueOfType(type, buffer);
+ }
+ }
+}
+
+class PlatformImagesApi {
+ /// Constructor for [PlatformImagesApi]. The [binaryMessenger] named argument is
+ /// available for dependency injection. If it is left null, the default
+ /// BinaryMessenger will be used which routes to the host platform.
+ PlatformImagesApi({BinaryMessenger? binaryMessenger})
+ : _binaryMessenger = binaryMessenger;
+ final BinaryMessenger? _binaryMessenger;
+
+ static const MessageCodec<Object?> codec = _PlatformImagesApiCodec();
+
+ /// Returns the URL for the given resource, or null if no such resource is
+ /// found.
+ Future<String?> resolveUrl(
+ String arg_resourceName, String? arg_extension) async {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.ios_platform_images.PlatformImagesApi.resolveUrl',
+ codec,
+ binaryMessenger: _binaryMessenger);
+ final List<Object?>? replyList = await channel
+ .send(<Object?>[arg_resourceName, arg_extension]) as List<Object?>?;
+ if (replyList == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ );
+ } else if (replyList.length > 1) {
+ throw PlatformException(
+ code: replyList[0]! as String,
+ message: replyList[1] as String?,
+ details: replyList[2],
+ );
+ } else {
+ return (replyList[0] as String?);
+ }
+ }
+
+ /// Returns the data for the image resource with the given name, or null if
+ /// no such resource is found.
+ Future<PlatformImageData?> loadImage(String arg_name) async {
+ final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+ 'dev.flutter.pigeon.ios_platform_images.PlatformImagesApi.loadImage',
+ codec,
+ binaryMessenger: _binaryMessenger);
+ final List<Object?>? replyList =
+ await channel.send(<Object?>[arg_name]) as List<Object?>?;
+ if (replyList == null) {
+ throw PlatformException(
+ code: 'channel-error',
+ message: 'Unable to establish connection on channel.',
+ );
+ } else if (replyList.length > 1) {
+ throw PlatformException(
+ code: replyList[0]! as String,
+ message: replyList[1] as String?,
+ details: replyList[2],
+ );
+ } else {
+ return (replyList[0] as PlatformImageData?);
+ }
+ }
+}
diff --git a/packages/ios_platform_images/pigeons/copyright.txt b/packages/ios_platform_images/pigeons/copyright.txt
new file mode 100644
index 0000000..1236b63
--- /dev/null
+++ b/packages/ios_platform_images/pigeons/copyright.txt
@@ -0,0 +1,3 @@
+Copyright 2013 The Flutter Authors. All rights reserved.
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
diff --git a/packages/ios_platform_images/pigeons/messages.dart b/packages/ios_platform_images/pigeons/messages.dart
new file mode 100644
index 0000000..d0a8d34
--- /dev/null
+++ b/packages/ios_platform_images/pigeons/messages.dart
@@ -0,0 +1,37 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:pigeon/pigeon.dart';
+
+@ConfigurePigeon(PigeonOptions(
+ dartOut: 'lib/src/messages.g.dart',
+ objcOptions: ObjcOptions(prefix: 'FPI'),
+ objcHeaderOut: 'ios/Classes/messages.g.h',
+ objcSourceOut: 'ios/Classes/messages.g.m',
+ copyrightHeader: 'pigeons/copyright.txt',
+))
+
+/// A serialization of a platform image's data.
+class PlatformImageData {
+ PlatformImageData(this.data, this.scale);
+
+ /// The image data.
+ final Uint8List data;
+
+ /// The image's scale factor.
+ final double scale;
+}
+
+@HostApi()
+abstract class PlatformImagesApi {
+ /// Returns the URL for the given resource, or null if no such resource is
+ /// found.
+ @ObjCSelector('resolveURLForResource:withExtension:')
+ String? resolveUrl(String resourceName, String? extension);
+
+ /// Returns the data for the image resource with the given name, or null if
+ /// no such resource is found.
+ @ObjCSelector('loadImageWithName:')
+ PlatformImageData? loadImage(String name);
+}
diff --git a/packages/ios_platform_images/pubspec.yaml b/packages/ios_platform_images/pubspec.yaml
index 7a14d4a..c4a0eee 100644
--- a/packages/ios_platform_images/pubspec.yaml
+++ b/packages/ios_platform_images/pubspec.yaml
@@ -2,7 +2,7 @@
description: A plugin to share images between Flutter and iOS in add-to-app setups.
repository: https://github.com/flutter/packages/tree/main/packages/ios_platform_images
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+ios_platform_images%22
-version: 0.2.2+2
+version: 0.2.2+3
environment:
sdk: ">=2.19.0 <4.0.0"
@@ -21,6 +21,7 @@
dev_dependencies:
flutter_test:
sdk: flutter
+ pigeon: ^11.0.0
topics:
- image
diff --git a/packages/ios_platform_images/test/ios_platform_images_test.dart b/packages/ios_platform_images/test/ios_platform_images_test.dart
index f42b786..04605c1 100644
--- a/packages/ios_platform_images/test/ios_platform_images_test.dart
+++ b/packages/ios_platform_images/test/ios_platform_images_test.dart
@@ -5,34 +5,63 @@
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:ios_platform_images/ios_platform_images.dart';
+import 'package:ios_platform_images/src/messages.g.dart';
void main() {
- const MethodChannel channel =
- MethodChannel('plugins.flutter.io/ios_platform_images');
-
- TestWidgetsFlutterBinding.ensureInitialized();
+ late FakePlatformImagesApi fakeApi;
setUp(() {
- _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
- .defaultBinaryMessenger
- .setMockMethodCallHandler(channel, (MethodCall methodCall) async {
- return '42';
- });
+ fakeApi = FakePlatformImagesApi();
+ setPlatformImageHostApi(fakeApi);
});
- tearDown(() {
- _ambiguate(TestDefaultBinaryMessengerBinding.instance)!
- .defaultBinaryMessenger
- .setMockMethodCallHandler(channel, null);
+ test('resolveURL passes arguments', () async {
+ const String name = 'a name';
+ const String extension = '.extension';
+
+ await IosPlatformImages.resolveURL(name, extension: extension);
+
+ expect(fakeApi.passedName, name);
+ expect(fakeApi.passedExtension, extension);
});
- test('resolveURL', () async {
- expect(await IosPlatformImages.resolveURL('foobar'), '42');
+ test('resolveURL returns null', () async {
+ expect(await IosPlatformImages.resolveURL('foobar'), null);
+ });
+
+ test('resolveURL returns result', () async {
+ const String result = 'a result';
+ fakeApi.resolutionResult = result;
+
+ expect(await IosPlatformImages.resolveURL('foobar'), result);
+ });
+
+ test('loadImage passes argument', () async {
+ fakeApi.loadResult = PlatformImageData(data: Uint8List(1), scale: 1.0);
+ const String name = 'a name';
+
+ IosPlatformImages.load(name);
+
+ expect(fakeApi.passedName, name);
});
}
-/// This allows a value of type T or T? to be treated as a value of type T?.
-///
-/// We use this so that APIs that have become non-nullable can still be used
-/// with `!` and `?` on the stable branch.
-T? _ambiguate<T>(T? value) => value;
+class FakePlatformImagesApi implements PlatformImagesApi {
+ String? passedName;
+ String? passedExtension;
+ String? resolutionResult;
+ PlatformImageData? loadResult;
+
+ @override
+ Future<PlatformImageData?> loadImage(String name) async {
+ passedName = name;
+ return loadResult;
+ }
+
+ @override
+ Future<String?> resolveUrl(String name, String? extension) async {
+ passedName = name;
+ passedExtension = extension;
+ return resolutionResult;
+ }
+}