Skip to content

Commit ac8994f

Browse files
[macOS] Extract codecs from plugin API
Extracts method codecs into stand-alone objects, and allows plugins to indicate that they use a codec other than JSON. This is an incremental step toward aligning the plugin API with Flutter's MethodChannel API. For now no other codecs are actually implemented in the framework, and the default is still JSON (unlike in existing Flutter code) so that this is not a breaking change. A future change will full move to MethodChannel, which will be a breaking change. Partially addresses issue google#67 in that custom codecs are now supported, even though the Standard (binary) codec is not yet implemented.
1 parent d9393f7 commit ac8994f

File tree

8 files changed

+228
-106
lines changed

8 files changed

+228
-106
lines changed

macos/library/FLEChannels.h

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,9 @@
1616

1717
/**
1818
* An object encapsulating a method call from Flutter.
19-
*
20-
* TODO: Move serialization details into a method codec class, to match mobile Flutter plugin APIs.
2119
*/
2220
@interface FLEMethodCall : NSObject
2321

24-
/**
25-
* Returns a new FLEMethodCall created from a JSON message received from the Flutter engine.
26-
*/
27-
+ (nullable instancetype)methodCallFromMessage:(nonnull NSDictionary *)message;
28-
2922
/**
3023
* Initializes an FLEMethodCall. If |arguments| is provided, it must be serializable to JSON.
3124
*/
@@ -47,11 +40,6 @@
4740
*/
4841
@property(readonly, nonatomic, nullable) id arguments;
4942

50-
/**
51-
* Returns a version of the method call serialized in the format expected by the Flutter engine.
52-
*/
53-
- (nonnull NSDictionary *)asMessage;
54-
5543
@end
5644

5745
#pragma mark -
@@ -100,11 +88,3 @@ extern NSString const *_Nonnull FLEMethodNotImplemented;
10088
@property(readonly, nonatomic, nullable) id details;
10189

10290
@end
103-
104-
/**
105-
* Returns the serialized JSON data that should be sent to the engine for the given
106-
* FLEMethodResult argument.
107-
*
108-
* // TODO: Move this logic into method codecs.
109-
*/
110-
NSData *_Nullable EngineResponseForMethodResult(id _Nullable result);

macos/library/FLEChannels.m

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,8 @@
1616

1717
NSString const *FLEMethodNotImplemented = @"notimplemented";
1818

19-
static NSString *const kMessageMethodKey = @"method";
20-
static NSString *const kMessageArgumentsKey = @"args";
21-
2219
@implementation FLEMethodCall
2320

24-
+ (nullable instancetype)methodCallFromMessage:(nonnull NSDictionary *)message {
25-
NSString *method = message[kMessageMethodKey];
26-
if (!method) {
27-
return nil;
28-
}
29-
id arguments = message[kMessageArgumentsKey];
30-
if (arguments == [NSNull null]) {
31-
arguments = nil;
32-
}
33-
return [[self alloc] initWithMethodName:method arguments:arguments];
34-
}
35-
3621
- (nonnull instancetype)initWithMethodName:(nonnull NSString *)name
3722
arguments:(nullable id)arguments {
3823
self = [super init];
@@ -43,13 +28,6 @@ - (nonnull instancetype)initWithMethodName:(nonnull NSString *)name
4328
return self;
4429
}
4530

46-
- (nonnull NSDictionary *)asMessage {
47-
return @{
48-
kMessageMethodKey : _methodName,
49-
kMessageArgumentsKey : _arguments ?: [NSNull null],
50-
};
51-
}
52-
5331
@end
5432

5533
#pragma mark -
@@ -69,31 +47,3 @@ - (nonnull instancetype)initWithCode:(nonnull NSString *)code
6947
}
7048

7149
@end
72-
73-
#pragma mark -
74-
75-
NSData *EngineResponseForMethodResult(id result) {
76-
// The engine expects one of three responses to a platform message:
77-
// - No data, indicating that the method is not implemented.
78-
// - A one-element array indicating succes, with the response payload as the element.
79-
// - A three-element array indicating an error, consisting of:
80-
// * An error code string.
81-
// * An optional human-readable error string.
82-
// * An optional object with more error information.
83-
NSArray *responseObject = nil;
84-
if (result == FLEMethodNotImplemented) {
85-
// Note that the compare above deliberately uses pointer equality rather than string equality
86-
// since only the constant itself should be treated as 'not implemented'.
87-
return nil;
88-
} else if ([result isKindOfClass:[FLEMethodError class]]) {
89-
FLEMethodError *error = (FLEMethodError *)result;
90-
responseObject = @[
91-
error.code,
92-
error.message ?: [NSNull null],
93-
error.details ?: [NSNull null],
94-
];
95-
} else {
96-
responseObject = @[ result ?: [NSNull null] ];
97-
}
98-
return [NSJSONSerialization dataWithJSONObject:responseObject options:0 error:NULL];
99-
}

macos/library/FLECodecs.h

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
// Copyright 2018 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#import <Foundation/Foundation.h>
16+
17+
#import "FLEChannels.h"
18+
19+
/**
20+
* Translates between a binary message and higher-level method call and response/error objects.
21+
*/
22+
@protocol FLEMethodCodec
23+
24+
/**
25+
* Returns the shared instance of the codec.
26+
*/
27+
+ (nonnull instancetype)sharedInstance;
28+
29+
/**
30+
* Returns a binary encoding of the given |methodCall|, or nil if the method call cannot be
31+
* serialized by this codec.
32+
*/
33+
- (nullable NSData*)encodeMethodCall:(nonnull FLEMethodCall*)methodCall;
34+
35+
/**
36+
* Returns the FLEMethodCall encoded in |methodData|, or nil if it cannot be decoded.
37+
*/
38+
- (nullable FLEMethodCall*)decodeMethodCall:(nonnull NSData*)methodData;
39+
40+
/**
41+
* Returns a binary encoding of |result|. Returns nil if |result| is not a type supported by the
42+
* codec.
43+
*/
44+
- (nullable NSData*)encodeSuccessEnvelope:(nullable id)result;
45+
46+
/**
47+
* Returns a binary encoding of |error|. Returns nil if the |details| parameter of |erorr| is not
48+
* a type supported by the codec.
49+
*/
50+
- (nullable NSData*)encodeErrorEnvelope:(nonnull FLEMethodError*)error;
51+
52+
@end
53+
54+
/**
55+
* A codec that uses JSON as the encoding format. Method arguments and error details for plugins
56+
* using this codec must be serializable to JSON.
57+
*/
58+
@interface FLEJSONMethodCodec : NSObject <FLEMethodCodec>
59+
@end
60+
61+
// TODO: Implement the other core Flutter codecs. Issue #67.

macos/library/FLECodecs.m

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2018 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#import "FLECodecs.h"
16+
17+
// Keys for JSON-encoded method calls.
18+
static NSString *const kMessageMethodKey = @"method";
19+
static NSString *const kMessageArgumentsKey = @"args";
20+
21+
/**
22+
* Returns the JSON serialiazation of |object|. If |object| is not serializable to JSON, logs an
23+
* error and returns nil.
24+
*/
25+
static NSData *SerializeAsJSON(id object) {
26+
if (![NSJSONSerialization isValidJSONObject:object]) {
27+
NSLog(@"Error: Unable to construct a valid JSON object from %@", object);
28+
return nil;
29+
}
30+
31+
NSError *error = nil;
32+
NSData *data = [NSJSONSerialization dataWithJSONObject:object options:0 error:&error];
33+
if (error) {
34+
NSLog(@"Error: Failed to create JSON message for %@: %@", object, error.debugDescription);
35+
}
36+
return data;
37+
}
38+
39+
// See the documentation for the Dart JSONMethodCodec class for the JSON structures used by this
40+
// this class.
41+
@implementation FLEJSONMethodCodec
42+
43+
#pragma mark FLEMethodCodec
44+
45+
+ (instancetype)sharedInstance {
46+
static FLEJSONMethodCodec *sharedInstance;
47+
if (!sharedInstance) {
48+
sharedInstance = [[FLEJSONMethodCodec alloc] init];
49+
}
50+
return sharedInstance;
51+
}
52+
53+
- (NSData *)encodeMethodCall:(FLEMethodCall *)methodCall {
54+
return SerializeAsJSON(@{
55+
kMessageMethodKey : methodCall.methodName,
56+
kMessageArgumentsKey : methodCall.arguments ?: [NSNull null],
57+
});
58+
}
59+
60+
- (FLEMethodCall *)decodeMethodCall:(NSData *)methodData {
61+
NSDictionary *message = [NSJSONSerialization JSONObjectWithData:methodData options:0 error:NULL];
62+
NSString *method = message[kMessageMethodKey];
63+
if (!method) {
64+
return nil;
65+
}
66+
id arguments = message[kMessageArgumentsKey];
67+
if (arguments == [NSNull null]) {
68+
arguments = nil;
69+
}
70+
return [[FLEMethodCall alloc] initWithMethodName:method arguments:arguments];
71+
}
72+
73+
- (NSData *)encodeSuccessEnvelope:(id _Nullable)result {
74+
return SerializeAsJSON(@[ result ?: [NSNull null] ]);
75+
}
76+
77+
- (NSData *)encodeErrorEnvelope:(FLEMethodError *)error {
78+
return SerializeAsJSON(@[
79+
error.code,
80+
error.message ?: [NSNull null],
81+
error.details ?: [NSNull null],
82+
]);
83+
}
84+
85+
@end

macos/library/FLEPlugin.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,18 @@
1515
#import <Foundation/Foundation.h>
1616

1717
#import "FLEChannels.h"
18+
#import "FLECodecs.h"
1819

1920
@class FLEViewController;
2021

2122
/**
2223
* A plugin is an object that can respond appropriately to Flutter platform messages over a specific
2324
* named system channel. See https://flutter.io/platform-channels/.
25+
*
26+
* Note: This interface will be changing in the future to more closely match the iOS Flutter
27+
* platform message API. It will be a one-time breaking change.
2428
*/
25-
@protocol FLEPlugin
29+
@protocol FLEPlugin <NSObject>
2630

2731
/**
2832
* A weak reference to the owning controller. May be used to send messages to the Flutter
@@ -50,4 +54,15 @@
5054
*/
5155
- (void)handleMethodCall:(nonnull FLEMethodCall *)call result:(nonnull FLEMethodResult)result;
5256

57+
@optional
58+
59+
/**
60+
* If implemented, returns the codec to use for method calls to this plugin.
61+
*
62+
* If not implemented, the codec is assumed to be FLEJSONMethodCodec. Note that this is different
63+
* from existing Flutter platforms, which default to the standard codec; this is to preserve
64+
* backwards compatibility for FLEPlugin until the breaking change for the platform messages API.
65+
*/
66+
@property(nonnull, readonly) id<FLEMethodCodec> codec;
67+
5368
@end

macos/library/FLEViewController.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
#import <Cocoa/Cocoa.h>
1616

17+
#import "FLECodecs.h"
1718
#import "FLEOpenGLContextHandling.h"
1819
#import "FLEPlugin.h"
1920

@@ -71,10 +72,20 @@
7172
- (BOOL)addPlugin:(nonnull id<FLEPlugin>)plugin;
7273

7374
/**
74-
* Sends a platform message to the Flutter engine on the given channel.
75+
* Sends a platform message to the Flutter engine on |channel|, encoded using |codec|.
7576
*
77+
* // TODO: Move to an API that mirrors the FlutterMethodChannel API.
7678
* // TODO: Support responses.
7779
*/
80+
- (void)invokeMethod:(nonnull NSString *)method
81+
arguments:(nullable id)arguments
82+
onChannel:(nonnull NSString *)channel
83+
withCodec:(nonnull id<FLEMethodCodec>)codec;
84+
85+
/**
86+
* Calls invokeMethod:arguments:onChannel:withCodec: using FLEJSONCodec. See the note in
87+
* FLEPlugin.h for why JSON is the default.
88+
*/
7889
- (void)invokeMethod:(nonnull NSString *)method
7990
arguments:(nullable id)arguments
8091
onChannel:(nonnull NSString *)channel;

0 commit comments

Comments
 (0)