提交 afb206d3 编写于 作者: M Mikkel Nygaard Ravn 提交者: GitHub

Flutter channel API cleanup (#3532)

上级 cd60a777
...@@ -81,6 +81,7 @@ java_library("flutter_shell_java") { ...@@ -81,6 +81,7 @@ java_library("flutter_shell_java") {
"io/flutter/plugin/common/ActivityLifecycleListener.java", "io/flutter/plugin/common/ActivityLifecycleListener.java",
"io/flutter/plugin/common/BinaryCodec.java", "io/flutter/plugin/common/BinaryCodec.java",
"io/flutter/plugin/common/FlutterException.java", "io/flutter/plugin/common/FlutterException.java",
"io/flutter/plugin/common/FlutterEventChannel.java",
"io/flutter/plugin/common/FlutterMessageChannel.java", "io/flutter/plugin/common/FlutterMessageChannel.java",
"io/flutter/plugin/common/FlutterMethodChannel.java", "io/flutter/plugin/common/FlutterMethodChannel.java",
"io/flutter/plugin/common/JSONMessageCodec.java", "io/flutter/plugin/common/JSONMessageCodec.java",
......
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package io.flutter.plugin.common;
import android.util.Log;
import io.flutter.view.FlutterView;
import io.flutter.view.FlutterView.BinaryMessageResponse;
import io.flutter.view.FlutterView.OnBinaryMessageListenerAsync;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicReference;
/**
* A named channel for communicating with the Flutter application using asynchronous
* event streams.
*
* Incoming requests for event stream setup are decoded from binary on receipt, and
* Java responses and events are encoded into binary before being transmitted back
* to Flutter. The {@link MethodCodec} used must be compatible with the one used by
* the Flutter application. This can be achieved by creating a PlatformEventChannel
* counterpart of this channel on the Flutter side. The Java type of responses and
* events is Object, but only values supported by the specified {@link MethodCodec}
* can be used.
*
* The identity of the channel is given by its name, so other uses of that name
* with may interfere with this channel's communication.
*/
public final class FlutterEventChannel {
private static final String TAG = "FlutterEventChannel#";
private final FlutterView view;
private final String name;
private final MethodCodec codec;
/**
* Creates a new channel associated with the specified {@link FlutterView} and with the
* specified name and the standard {@link MethodCodec}.
*
* @param view a {@link FlutterView}.
* @param name a channel name String.
*/
public FlutterEventChannel(FlutterView view, String name) {
this(view, name, StandardMethodCodec.INSTANCE);
}
/**
* Creates a new channel associated with the specified {@link FlutterView} and with the
* specified name and {@link MethodCodec}.
*
* @param view a {@link FlutterView}.
* @param name a channel name String.
* @param codec a {@link MessageCodec}.
*/
public FlutterEventChannel(FlutterView view, String name, MethodCodec codec) {
assert view != null;
assert name != null;
assert codec != null;
this.view = view;
this.name = name;
this.codec = codec;
}
/**
* Registers a stream handler on this channel.
*
* Overrides any existing handler registration.
*
* @param handler a {@link StreamHandler}, or null to deregister.
*/
public void setStreamHandler(final StreamHandler handler) {
view.addOnBinaryMessageListenerAsync(name,
handler == null ? null : new StreamListener(handler));
}
/**
* Strategy for handling event streams. Supports dual use:
* Producers of events to be sent to Flutter act as clients of this interface
* for sending events. Consumers of events sent from Flutter implement
* this interface for handling received events.
*/
public interface EventSink {
/**
* Consumes a successful event.
*
* @param event The event, possibly null.
*/
void success(Object event);
/**
* Consumes an error event.
*
* @param errorCode An error code String.
* @param errorMessage A human-readable error message String, possibly null.
* @param errorDetails Error details, possibly null
*/
void error(String errorCode, String errorMessage, Object errorDetails);
/**
* Consumes end of stream. No calls to {@link #success(Object)} or
* {@link #error(String, String, Object)} will be made following a call
* to this method.
*/
void endOfStream();
}
/**
* A call-back interface for handling stream setup and tear-down requests.
*/
public interface StreamHandler {
/**
* Handles a request to set up an event stream.
*
* @param arguments Stream configuration arguments, possibly null.
* @param eventSink An {@link EventSink} for sending events to the Flutter receiver.
*/
void onListen(Object arguments, EventSink eventSink);
/**
* Handles a request to tear down an event stream.
*
* @param arguments Stream configuration arguments, possibly null.
*/
void onCancel(Object arguments);
}
private final class StreamListener implements OnBinaryMessageListenerAsync {
private final StreamHandler handler;
private final AtomicReference<EventSink> activeSink = new AtomicReference<>(null);
StreamListener(StreamHandler handler) {
this.handler = handler;
}
@Override
public void onMessage(FlutterView view, ByteBuffer message,
final BinaryMessageResponse response) {
final MethodCall call = codec.decodeMethodCall(message);
if (call.method.equals("listen")) {
onListen(call.arguments, response);
} else if (call.method.equals("cancel")) {
onCancel(call.arguments, response);
} else {
response.send(null);
}
}
private void onListen(Object arguments, BinaryMessageResponse response) {
final EventSink eventSink = new EventSinkImplementation();
if (activeSink.compareAndSet(null, eventSink)) {
try {
handler.onListen(arguments, eventSink);
response.send(codec.encodeSuccessEnvelope(null));
} catch (Exception e) {
activeSink.set(null);
Log.e(TAG + name, "Failed to open event stream", e);
response.send(codec.encodeErrorEnvelope("error", e.getMessage(), null));
}
} else {
response.send(codec.encodeErrorEnvelope("error", "Stream already active", null));
}
}
private void onCancel(Object arguments, BinaryMessageResponse response) {
final EventSink oldSink = activeSink.getAndSet(null);
if (oldSink != null) {
try {
handler.onCancel(arguments);
response.send(codec.encodeSuccessEnvelope(null));
} catch (Exception e) {
Log.e(TAG + name, "Failed to close event stream", e);
response.send(codec.encodeErrorEnvelope("error", e.getMessage(), null));
}
} else {
response.send(codec.encodeErrorEnvelope("error", "No active stream to cancel", null));
}
}
private final class EventSinkImplementation implements EventSink {
@Override
public void success(Object event) {
if (activeSink.get() != this) {
return;
}
FlutterEventChannel.this.view.sendBinaryMessage(
name,
codec.encodeSuccessEnvelope(event),
null);
}
@Override
public void error(String errorCode, String errorMessage,
Object errorDetails) {
if (activeSink.get() != this) {
return;
}
FlutterEventChannel.this.view.sendBinaryMessage(
name,
codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails),
null);
}
@Override
public void endOfStream() {
if (activeSink.get() != this) {
return;
}
FlutterEventChannel.this.view.sendBinaryMessage(name, null, null);
}
}
}
}
...@@ -10,11 +10,10 @@ import io.flutter.view.FlutterView.BinaryMessageReplyCallback; ...@@ -10,11 +10,10 @@ import io.flutter.view.FlutterView.BinaryMessageReplyCallback;
import io.flutter.view.FlutterView.BinaryMessageResponse; import io.flutter.view.FlutterView.BinaryMessageResponse;
import io.flutter.view.FlutterView.OnBinaryMessageListenerAsync; import io.flutter.view.FlutterView.OnBinaryMessageListenerAsync;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
/** /**
* A named channel for communicating with the Flutter application using asynchronous * A named channel for communicating with the Flutter application using asynchronous
* method calls and event streams. * method calls.
* *
* Incoming method calls are decoded from binary on receipt, and Java results are encoded * Incoming method calls are decoded from binary on receipt, and Java results are encoded
* into binary before being transmitted back to Flutter. The {@link MethodCodec} used must be * into binary before being transmitted back to Flutter. The {@link MethodCodec} used must be
...@@ -86,7 +85,7 @@ public final class FlutterMethodChannel { ...@@ -86,7 +85,7 @@ public final class FlutterMethodChannel {
/** /**
* Registers a method call handler on this channel. * Registers a method call handler on this channel.
* *
* Overrides any existing handler registration (for messages, method calls, or streams). * Overrides any existing handler registration.
* *
* @param handler a {@link MethodCallHandler}, or null to deregister. * @param handler a {@link MethodCallHandler}, or null to deregister.
*/ */
...@@ -96,82 +95,45 @@ public final class FlutterMethodChannel { ...@@ -96,82 +95,45 @@ public final class FlutterMethodChannel {
} }
/** /**
* Registers a stream handler on this channel. * Strategy for handling the result of a method call. Supports dual use:
* * Implementations of methods to be invoked by Flutter act as clients of this interface
* Overrides any existing handler registration (for messages, method calls, or streams). * for sending results back to Flutter. Invokers of Flutter methods provide
* * implementations of this interface for handling results received from Flutter.
* @param handler a {@link StreamHandler}, or null to deregister.
*/
public void setStreamHandler(final StreamHandler handler) {
view.addOnBinaryMessageListenerAsync(name,
handler == null ? null : new StreamListener(handler));
}
/**
* A call-back interface for handling incoming method calls.
*/
public interface MethodCallHandler {
/**
* Handles the specified method call.
*
* @param call A {@link MethodCall}.
* @param response A {@link Response} for providing a single method call result.
*/
void onMethodCall(MethodCall call, Response response);
}
/**
* A call-back interface for handling stream setup and teardown requests.
*/
public interface StreamHandler {
/**
* Handles a stream setup request.
*
* @param arguments Stream configuration arguments, possibly null.
* @param eventSink An {@link EventSink} used to emit events once the stream has been set
* up.
*/
void listen(Object arguments, EventSink eventSink);
/**
* Handles a stream tear-down request.
*
* @param arguments Stream configuration arguments, possibly null.
*/
void cancel(Object arguments);
}
/**
* Response interface for sending results back to Flutter.
*/ */
public interface Response { public interface Response {
/** /**
* Submits a successful result. * Handles a successful result.
* *
* @param result The result, possibly null. * @param result The result, possibly null.
*/ */
void success(Object result); void success(Object result);
/** /**
* Submits an error during message handling, an error result of a method call, or an error * Handles an error result.
* event.
* *
* @param errorCode An error code String. * @param errorCode An error code String.
* @param errorMessage A human-readable error message String, possibly null. * @param errorMessage A human-readable error message String, possibly null.
* @param errorDetails Error details, possibly null * @param errorDetails Error details, possibly null
*/ */
void error(String errorCode, String errorMessage, Object errorDetails); void error(String errorCode, String errorMessage, Object errorDetails);
/**
* Handles a call to an unimplemented method.
*/
void notImplemented();
} }
/** /**
* A {@link Response} supporting multiple results and which can be terminated. * A call-back interface for handling incoming method calls.
*/ */
public interface EventSink extends Response { public interface MethodCallHandler {
/** /**
* Signals that no more events will be emitted. * Handles the specified method call.
*
* @param call A {@link MethodCall}.
* @param response A {@link Response} for providing a single method call result.
*/ */
void done(); void onMethodCall(MethodCall call, Response response);
} }
private final class MethodCallResultCallback implements BinaryMessageReplyCallback { private final class MethodCallResultCallback implements BinaryMessageReplyCallback {
...@@ -183,11 +145,15 @@ public final class FlutterMethodChannel { ...@@ -183,11 +145,15 @@ public final class FlutterMethodChannel {
@Override @Override
public void onReply(ByteBuffer reply) { public void onReply(ByteBuffer reply) {
try { if (reply == null) {
final Object result = codec.decodeEnvelope(reply); handler.notImplemented();
handler.success(result); } else {
} catch (FlutterException e) { try {
handler.error(e.code, e.getMessage(), e.details); final Object result = codec.decodeEnvelope(reply);
handler.success(result);
} catch (FlutterException e) {
handler.error(e.code, e.getMessage(), e.details);
}
} }
} }
} }
...@@ -222,6 +188,13 @@ public final class FlutterMethodChannel { ...@@ -222,6 +188,13 @@ public final class FlutterMethodChannel {
done = true; done = true;
} }
@Override
public void notImplemented() {
checkDone();
response.send(null);
done = true;
}
private void checkDone() { private void checkDone() {
if (done) { if (done) {
throw new IllegalStateException("Call result already provided"); throw new IllegalStateException("Call result already provided");
...@@ -234,68 +207,4 @@ public final class FlutterMethodChannel { ...@@ -234,68 +207,4 @@ public final class FlutterMethodChannel {
} }
} }
} }
private final class StreamListener implements OnBinaryMessageListenerAsync {
private final StreamHandler handler;
StreamListener(StreamHandler handler) {
this.handler = handler;
}
@Override
public void onMessage(FlutterView view, ByteBuffer message,
final BinaryMessageResponse response) {
final MethodCall call = codec.decodeMethodCall(message);
final AtomicBoolean cancelled = new AtomicBoolean(false);
if (call.method.equals("listen")) {
try {
handler.listen(call.arguments, new EventSink() {
@Override
public void success(Object event) {
if (cancelled.get()) {
return;
}
FlutterMethodChannel.this.view.sendBinaryMessage(
name,
codec.encodeSuccessEnvelope(event),
null);
}
@Override
public void error(String errorCode, String errorMessage,
Object errorDetails) {
if (cancelled.get()) {
return;
}
FlutterMethodChannel.this.view.sendBinaryMessage(
name,
codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails),
null);
}
@Override
public void done() {
if (cancelled.get()) {
return;
}
FlutterMethodChannel.this.view.sendBinaryMessage(name, null, null);
}
});
response.send(codec.encodeSuccessEnvelope(null));
} catch (Exception e) {
Log.e(TAG + name, "Failed to open event stream", e);
response.send(codec.encodeErrorEnvelope("error", e.getMessage(), null));
}
} else if (call.method.equals("cancel")) {
cancelled.set(true);
try {
handler.cancel(call.arguments);
response.send(codec.encodeSuccessEnvelope(null));
} catch (Exception e) {
Log.e(TAG + name, "Failed to close event stream", e);
response.send(codec.encodeErrorEnvelope("error", e.getMessage(), null));
}
}
}
}
} }
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
package io.flutter.plugin.common; package io.flutter.plugin.common;
/** /**
* Command object representing a method call on a {@link FlutterMessageChannel}. * Command object representing a method call on a {@link FlutterMethodChannel}.
*/ */
public final class MethodCall { public final class MethodCall {
/** /**
......
...@@ -65,7 +65,7 @@ public class TextInputPlugin implements MethodCallHandler { ...@@ -65,7 +65,7 @@ public class TextInputPlugin implements MethodCallHandler {
clearTextInputClient(); clearTextInputClient();
response.success(null); response.success(null);
} else { } else {
response.error("unknown", "Unknown method: " + call.method, null); response.notImplemented();
} }
} catch (JSONException e) { } catch (JSONException e) {
response.error("error", "JSON error: " + e.getMessage(), null); response.error("error", "JSON error: " + e.getMessage(), null);
......
...@@ -17,6 +17,7 @@ import android.view.HapticFeedbackConstants; ...@@ -17,6 +17,7 @@ import android.view.HapticFeedbackConstants;
import android.view.SoundEffectConstants; import android.view.SoundEffectConstants;
import android.view.View; import android.view.View;
import io.flutter.plugin.common.FlutterMethodChannel;
import io.flutter.util.PathUtils; import io.flutter.util.PathUtils;
import io.flutter.plugin.common.ActivityLifecycleListener; import io.flutter.plugin.common.ActivityLifecycleListener;
...@@ -81,7 +82,7 @@ public class PlatformPlugin implements MethodCallHandler, ActivityLifecycleListe ...@@ -81,7 +82,7 @@ public class PlatformPlugin implements MethodCallHandler, ActivityLifecycleListe
} else if (method.equals("PathProvider.getApplicationDocumentsDirectory")) { } else if (method.equals("PathProvider.getApplicationDocumentsDirectory")) {
response.success(getPathProviderApplicationDocumentsDirectory()); response.success(getPathProviderApplicationDocumentsDirectory());
} else { } else {
response.error("unknown", "Unknown method: " + method, null); response.notImplemented();
} }
} catch (JSONException e) { } catch (JSONException e) {
response.error("error", "JSON error: " + e.getMessage(), null); response.error("error", "JSON error: " + e.getMessage(), null);
......
...@@ -2,28 +2,75 @@ ...@@ -2,28 +2,75 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef FLUTTER_FLUTTERBINARYMESSAGES_H_ #ifndef FLUTTER_FLUTTERBINARYMESSENGER_H_
#define FLUTTER_FLUTTERBINARYMESSAGES_H_ #define FLUTTER_FLUTTERBINARYMESSENGER_H_
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#include "FlutterMacros.h" #include "FlutterMacros.h"
typedef void (^FlutterBinaryReplyHandler)(NSData* reply); NS_ASSUME_NONNULL_BEGIN
/**
A strategy for handling a binary message reply.
*/
typedef void (^FlutterBinaryReplyHandler)(NSData* _Nullable reply);
/**
A strategy for handling incoming binary messages and to send asynchronous
replies.
*/
typedef void (^FlutterBinaryMessageHandler)( typedef void (^FlutterBinaryMessageHandler)(
NSData* message, NSData* _Nullable message,
FlutterBinaryReplyHandler replyHandler); FlutterBinaryReplyHandler replyHandler);
/**
A facility for communicating with the Flutter side using asynchronous message
passing with binary messages.
- SeeAlso:
- `FlutterMessageChannel`, which supports communication using structured messages.
- `FlutterMethodChannel`, which supports communication using asynchronous method calls.
- `FlutterEventChannel`, which supports commuication using event streams.
*/
FLUTTER_EXPORT FLUTTER_EXPORT
@protocol FlutterBinaryMessenger<NSObject> @protocol FlutterBinaryMessenger<NSObject>
- (void)sendBinaryMessage:(NSData*)message channelName:(NSString*)channelName; /**
Sends a binary message to the Flutter side on the specified channel, expecting
no reply.
- Parameters:
- message: The message.
- channelName: The channel name.
*/
- (void)sendBinaryMessage:(NSData* _Nullable)message
channelName:(NSString*)channelName;
/**
Sends a binary message to the Flutter side on the specified channel, expecting
an asynchronous reply.
- (void)sendBinaryMessage:(NSData*)message - Parameters:
- message: The message.
- channelName: The channel name.
- handler: A reply handler.
*/
- (void)sendBinaryMessage:(NSData* _Nullable)message
channelName:(NSString*)channelName channelName:(NSString*)channelName
binaryReplyHandler:(FlutterBinaryReplyHandler)handler; binaryReplyHandler:(FlutterBinaryReplyHandler)handler;
/**
Registers a message handler for incoming binary messages from the Flutter side
on the specified channel.
Replaces any existing handler. Use a `nil` handler for unregistering the
existing handler.
- Parameters:
- channelName: The channel name.
- handler: The message handler.
*/
- (void)setBinaryMessageHandlerOnChannel:(NSString*)channelName - (void)setBinaryMessageHandlerOnChannel:(NSString*)channelName
binaryMessageHandler:(FlutterBinaryMessageHandler)handler; binaryMessageHandler:(FlutterBinaryMessageHandler _Nullable)handler;
@end @end
NS_ASSUME_NONNULL_END
#endif // FLUTTER_FLUTTERBINARYMESSAGES_H_ #endif // FLUTTER_FLUTTERBINARYMESSENGER_H_
...@@ -8,48 +8,372 @@ ...@@ -8,48 +8,372 @@
#include "FlutterBinaryMessenger.h" #include "FlutterBinaryMessenger.h"
#include "FlutterCodecs.h" #include "FlutterCodecs.h"
NS_ASSUME_NONNULL_BEGIN
/**
A strategy for handling a message reply.
- Parameter reply: The reply.
*/
typedef void (^FlutterReplyHandler)(id reply); typedef void (^FlutterReplyHandler)(id reply);
/**
A strategy for handling a message.
- Parameters:
- message: The incoming message.
- replyHandler: A call-back to asynchronously supply a reply to the message.
*/
typedef void (^FlutterMessageHandler)(id message, typedef void (^FlutterMessageHandler)(id message,
FlutterReplyHandler replyHandler); FlutterReplyHandler replyHandler);
/**
A channel for communicating with the Flutter side using asynchronous message
passing.
*/
FLUTTER_EXPORT FLUTTER_EXPORT
@interface FlutterMessageChannel : NSObject @interface FlutterMessageChannel : NSObject
+ (instancetype)messageChannelNamed:(NSString*)name /**
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger Creates a `FlutterMessageChannel` with the specified name and binary messenger.
codec:(NSObject<FlutterMessageCodec>*)codec;
The channel name logically identifies the channel; identically named channels
interfere with each other's communication.
The binary messenger is a facility for sending raw, binary messages to the
Flutter side. This protocol is implemented by `FlutterViewController`.
The channel uses `FlutterStandardMessageCodec` to encode and decode messages.
- Parameters:
- name: The channel name.
- messenger: The binary messenger.
*/
+ (instancetype)messageChannelWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;
/**
Creates a `FlutterMessageChannel` with the specified name, binary messenger,
and message codec.
The channel name logically identifies the channel; identically named channels
interfere with each other's communication.
The binary messenger is a facility for sending raw, binary messages to the
Flutter side. This protocol is implemented by `FlutterViewController`.
- Parameters:
- name: The channel name.
- messenger: The binary messenger.
- codec: The message codec.
*/
+ (instancetype)messageChannelWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMessageCodec>*)codec;
/**
Initializes a `FlutterMessageChannel` with the specified nane, binary messenger,
and message codec.
The channel name logically identifies the channel; identically named channels
interfere with each other's communication.
The binary messenger is a facility for sending raw, binary messages to the
Flutter side. This protocol is implemented by `FlutterViewController`.
- Parameters:
- name: The channel name.
- messenger: The binary messenger.
- codec: The message codec.
*/
- (instancetype)initWithName:(NSString*)name - (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMessageCodec>*)codec; codec:(NSObject<FlutterMessageCodec>*)codec;
- (void)sendMessage:(id)message;
- (void)sendMessage:(id)message replyHandler:(FlutterReplyHandler)handler; /**
- (void)setMessageHandler:(FlutterMessageHandler)handler; Sends the specified message to the Flutter side, ignoring any reply.
- Parameter message: The message. Must be supported by the codec of this channel.
*/
- (void)sendMessage:(id _Nullable)message;
/**
Sends the specified message to the Flutter side, expecting an asynchronous reply.
- Parameters:
- message: The message. Must be supported by the codec of this channel.
- handler: The reply handler.
*/
- (void)sendMessage:(id _Nullable)message replyHandler:(FlutterReplyHandler _Nullable)handler;
/**
Registers a message handler with this channel.
Replaces any existing handler. Use a `nil` handler for unregistering the
existing handler.
- Parameter handler: The message handler.
*/
- (void)setMessageHandler:(FlutterMessageHandler _Nullable)handler;
@end @end
typedef void (^FlutterResultReceiver)(id successResult, /**
FlutterError* errorResult); A receiver of the result of a method call.
typedef void (^FlutterEventReceiver)(id successEvent,
FlutterError* errorEvent, - Parameter result: The result. Will be a `FlutterError` instance, if the method
BOOL done); call resulted in an error on the Flutter side. Will be
`FlutterMethodNotImplemented`, if the method called was not implemented on
the Flutter side. All other values, including `nil` should be interpreted
as successful results.
*/
typedef void (^FlutterResultReceiver)(id _Nullable result);
/**
A strategy for handling method calls.
- Parameters:
- call: The incoming method call.
- resultReceiver: A call-back to asynchronously supply the result of the call.
Invoke the call-back with a `FlutterError` to indicate that the call failed.
Invoke the call-back with `FlutterMethodNotImplemented` to indicate that the
method was unknown. Any other values, including `nil` are interpreted as
successful results.
*/
typedef void (^FlutterMethodCallHandler)(FlutterMethodCall* call, typedef void (^FlutterMethodCallHandler)(FlutterMethodCall* call,
FlutterResultReceiver resultReceiver); FlutterResultReceiver resultReceiver);
typedef void (^FlutterStreamHandler)(FlutterMethodCall* call,
FlutterResultReceiver resultReceiver,
FlutterEventReceiver eventReceiver);
/**
A constant used with `FlutterMethodCallHandler` to respond to the call of an
unknown method.
*/
FLUTTER_EXPORT
extern NSObject const* FlutterMethodNotImplemented;
/**
A channel for communicating with the Flutter side using invocation of
asynchronous methods.
*/
FLUTTER_EXPORT FLUTTER_EXPORT
@interface FlutterMethodChannel : NSObject @interface FlutterMethodChannel : NSObject
+ (instancetype)methodChannelNamed:(NSString*)name /**
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger Creates a `FlutterMethodChannel` with the specified name and binary messenger.
The channel name logically identifies the channel; identically named channels
interfere with each other's communication.
The binary messenger is a facility for sending raw, binary messages to the
Flutter side. This protocol is implemented by `FlutterViewController`.
The channel uses `FlutterStandardMethodCodec` to encode and decode method calls
and result envelopes.
- Parameters:
- name: The channel name.
- messenger: The binary messenger.
*/
+ (instancetype)methodChannelWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;
/**
Creates a `FlutterMethodChannel` with the specified name, binary messenger, and
method codec.
The channel name logically identifies the channel; identically named channels
interfere with each other's communication.
The binary messenger is a facility for sending raw, binary messages to the
Flutter side. This protocol is implemented by `FlutterViewController`.
- Parameters:
- name: The channel name.
- messenger: The binary messenger.
- codec: The method codec.
*/
+ (instancetype)methodChannelWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec; codec:(NSObject<FlutterMethodCodec>*)codec;
/**
Initializes a `FlutterMethodChannel` with the specified name, binary messenger,
and method codec.
The channel name logically identifies the channel; identically named channels
interfere with each other's communication.
The binary messenger is a facility for sending raw, binary messages to the
Flutter side. This protocol is implemented by `FlutterViewController`.
- Parameters:
- name: The channel name.
- messenger: The binary messenger.
- codec: The method codec.
*/
- (instancetype)initWithName:(NSString*)name - (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec; codec:(NSObject<FlutterMethodCodec>*)codec;
- (void)invokeMethod:(NSString*)method arguments:(id)arguments;
/**
Invokes the specified Flutter method with the specified arguments, expecting
no results.
- Parameters:
- method: The name of the method to invoke.
- arguments: The arguments. Must be a value supported by the codec of this
channel.
*/
- (void)invokeMethod:(NSString*)method arguments:(id _Nullable)arguments;
/**
Invokes the specified Flutter method with the specified arguments, expecting
an asynchronous result.
- Parameters:
- method: The name of the method to invoke.
- arguments: The arguments. Must be a value supported by the codec of this
channel.
- resultReceiver: A call-back for receipt of an asynchronous result.
The result will be a `FlutterError` instance, if the method call resulted
in an error on the Flutter side. Will be `FlutterMethodNotImplemented`, if
the method called was not implemented on the Flutter side. Any other value,
including `nil` should be interpreted as successful results.
*/
- (void)invokeMethod:(NSString*)method - (void)invokeMethod:(NSString*)method
arguments:(id)arguments arguments:(id _Nullable)arguments
resultReceiver:(FlutterResultReceiver)resultReceiver; resultReceiver:(FlutterResultReceiver _Nullable)resultReceiver;
- (void)setMethodCallHandler:(FlutterMethodCallHandler)handler;
- (void)setStreamHandler:(FlutterStreamHandler)handler; /**
Registers a handler for method calls from the Flutter side.
Replaces any existing handler. Use a `nil` handler for unregistering the
existing handler.
- Parameter handler: The method call handler.
*/
- (void)setMethodCallHandler:(FlutterMethodCallHandler _Nullable)handler;
@end
/**
A strategy for consuming events.
- Parameter event: The event. Will be a `FlutterError` instance, if the
event represents an error. Will be `FlutterEndOfEventStream`, if no more
events will be emitted. All other values, including `nil` should be
interpreted as success events.
*/
typedef void (^FlutterEventReceiver)(id _Nullable event);
/**
A strategy for exposing an event stream to the Flutter side.
*/
FLUTTER_EXPORT
@protocol FlutterStreamHandler
/**
Sets up an event stream and begin emitting events.
Invoked when the first listener is registered with the Stream associated to
this channel on the Flutter side.
- Parameters:
- arguments: Arguments for the stream.
- eventReceiver: A call-back to asynchronously emit events. Invoke the
call-back with a `FlutterError` to emit an error event. Invoke the
call-back with `FlutterEndOfEventStream` to indicate that no more
events will be emitted. Any other value, including `nil` are emitted as
successful events.
- Returns: A FlutterError instance, if setup fails.
*/
- (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments
eventReceiver:(FlutterEventReceiver)eventReceiver;
/**
Tears down an event stream.
Invoked when the last listener is deregistered from the Stream associated to
this channel on the Flutter side.
- Parameter arguments: Arguments for the stream.
- Returns: A FlutterError instance, if teardown fails.
*/
- (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments;
@end
/**
A constant used with `FlutterEventChannel` to indicate end of stream.
*/
FLUTTER_EXPORT
extern NSObject const* FlutterEndOfEventStream;
/**
A channel for communicating with the Flutter side using event streams.
*/
FLUTTER_EXPORT
@interface FlutterEventChannel : NSObject
/**
Creates a `FlutterEventChannel` with the specified name and binary messenger.
The channel name logically identifies the channel; identically named channels
interfere with each other's communication.
The binary messenger is a facility for sending raw, binary messages to the
Flutter side. This protocol is implemented by `FlutterViewController`.
The channel uses `FlutterStandardMethodCodec` to decode stream setup and
teardown requests, and to encode event envelopes.
- Parameters:
- name: The channel name.
- messenger: The binary messenger.
- codec: The method codec.
*/
+ (instancetype)eventChannelWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;
/**
Creates a `FlutterEventChannel` with the specified name, binary messenger,
and method codec.
The channel name logically identifies the channel; identically named channels
interfere with each other's communication.
The binary messenger is a facility for sending raw, binary messages to the
Flutter side. This protocol is implemented by `FlutterViewController`.
- Parameters:
- name: The channel name.
- messenger: The binary messenger.
- codec: The method codec.
*/
+ (instancetype)eventChannelWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec;
/**
Initializes a `FlutterEventChannel` with the specified name, binary messenger,
and method codec.
The channel name logically identifies the channel; identically named channels
interfere with each other's communication.
The binary messenger is a facility for sending raw, binary messages to the
Flutter side. This protocol is implemented by `FlutterViewController`.
- Parameters:
- name: The channel name.
- messenger: The binary messenger.
- codec: The method codec.
*/
- (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec;
/**
Registers a handler for stream setup requests from the Flutter side.
Replaces any existing handler. Use a `nil` handler for unregistering the
existing handler.
- Parameter handler: The stream handler.
*/
- (void)setStreamHandler:(NSObject<FlutterStreamHandler>* _Nullable)streamHandler;
@end @end
NS_ASSUME_NONNULL_END
#endif // FLUTTER_FLUTTERCHANNELS_H_ #endif // FLUTTER_FLUTTERCHANNELS_H_
...@@ -8,47 +8,148 @@ ...@@ -8,47 +8,148 @@
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
#include "FlutterMacros.h" #include "FlutterMacros.h"
NS_ASSUME_NONNULL_BEGIN
/**
A message encoding/decoding mechanism.
*/
FLUTTER_EXPORT FLUTTER_EXPORT
@protocol FlutterMessageCodec @protocol FlutterMessageCodec
/**
Returns a shared instance of this `FlutterMessageCodec`.
*/
+ (instancetype)sharedInstance; + (instancetype)sharedInstance;
- (NSData*)encode:(id)message;
- (id)decode:(NSData*)message; /**
Encodes the specified message into binary.
- Parameter message: The message.
- Returns: The binary encoding, or `nil`, if `message` was `nil`.
*/
- (NSData* _Nullable)encode:(id _Nullable)message;
/**
Decodes the specified message from binary.
- Parameter message: The message.
- Returns: The decoded message, or `nil`, if `message` was `nil`.
*/
- (id _Nullable)decode:(NSData* _Nullable)message;
@end @end
/**
A `FlutterMessageCodec` using unencoded binary messages, represented as
`NSData` instances.
*/
FLUTTER_EXPORT FLUTTER_EXPORT
@interface FlutterBinaryCodec : NSObject<FlutterMessageCodec> @interface FlutterBinaryCodec : NSObject<FlutterMessageCodec>
@end @end
/**
A `FlutterMessageCodec` using UTF-8 encoded `NSString` messages.
*/
FLUTTER_EXPORT FLUTTER_EXPORT
@interface FlutterStringCodec : NSObject<FlutterMessageCodec> @interface FlutterStringCodec : NSObject<FlutterMessageCodec>
@end @end
/**
A `FlutterMessageCodec` using UTF-8 encoded JSON messages.
Supports the same values as `NSJSONSerialization`.
*/
FLUTTER_EXPORT FLUTTER_EXPORT
@interface FlutterJSONMessageCodec : NSObject<FlutterMessageCodec> @interface FlutterJSONMessageCodec : NSObject<FlutterMessageCodec>
@end @end
/**
A `FlutterMessageCodec` using the Flutter standard binary encoding.
The standard encoding is guaranteed to be compatible with the corresponding
standard codec for PlatformMessageChannels on the Flutter side. These parts
of the Flutter SDK are evolved synchronously.
Supported messages are acyclic values of these forms:
- `nil` or `NSNull`
- `NSNumber` (including their representation of Boolean values)
- `FlutterStandardBigInteger`
- `FlutterStandardTypedData`
- `NSString`
- `NSArray` of supported values
- `NSDictionary` with supported keys and values
*/
FLUTTER_EXPORT FLUTTER_EXPORT
@interface FlutterStandardMessageCodec : NSObject<FlutterMessageCodec> @interface FlutterStandardMessageCodec : NSObject<FlutterMessageCodec>
@end @end
/**
Command object representing a method call on a `FlutterMethodChannel`.
*/
FLUTTER_EXPORT FLUTTER_EXPORT
@interface FlutterMethodCall : NSObject @interface FlutterMethodCall : NSObject
/**
Creates a method call for invoking the specified named method with the
specified arguments.
- Parameters:
- method: the name of the method to call.
- arguments: the arguments value.
*/
+ (instancetype)methodCallWithMethodName:(NSString*)method + (instancetype)methodCallWithMethodName:(NSString*)method
arguments:(id)arguments; arguments:(id _Nullable)arguments;
/**
The method name.
*/
@property(readonly, nonatomic) NSString* method; @property(readonly, nonatomic) NSString* method;
@property(readonly, nonatomic) id arguments;
/**
The arguments.
*/
@property(readonly, nonatomic, nullable) id arguments;
@end @end
/**
Error object representing an unsuccessful outcome of invoking a method
on a `FlutterMethodChannel`, or an error event on a `FlutterEventChannel`.
*/
FLUTTER_EXPORT FLUTTER_EXPORT
@interface FlutterError : NSObject @interface FlutterError : NSObject
/**
Creates a `FlutterError` with the specified error code, message, and details.
- Parameters:
- code: An error code string for programmatic use.
- message: A human-readable error message.
- details: Custom error details.
*/
+ (instancetype)errorWithCode:(NSString*)code + (instancetype)errorWithCode:(NSString*)code
message:(NSString*)message message:(NSString* _Nullable)message
details:(id)details; details:(id _Nullable)details;
/**
The error code.
*/
@property(readonly, nonatomic) NSString* code; @property(readonly, nonatomic) NSString* code;
@property(readonly, nonatomic) NSString* message;
@property(readonly, nonatomic) id details; /**
The error message.
*/
@property(readonly, nonatomic, nullable) NSString* message;
/**
The error details.
*/
@property(readonly, nonatomic, nullable) id details;
@end @end
/**
Type of numeric data items encoded in a `FlutterStandardDataType`.
- FlutterStandardDataTypeUInt8: plain bytes
- FlutterStandardDataTypeInt32: 32-bit signed integers
- FlutterStandardDataTypeInt64: 64-bit signed integers
- FlutterStandardDataTypeFloat64: 64-bit floats
*/
typedef NS_ENUM(NSInteger, FlutterStandardDataType) { typedef NS_ENUM(NSInteger, FlutterStandardDataType) {
FlutterStandardDataTypeUInt8, FlutterStandardDataTypeUInt8,
FlutterStandardDataTypeInt32, FlutterStandardDataTypeInt32,
...@@ -56,40 +157,179 @@ typedef NS_ENUM(NSInteger, FlutterStandardDataType) { ...@@ -56,40 +157,179 @@ typedef NS_ENUM(NSInteger, FlutterStandardDataType) {
FlutterStandardDataTypeFloat64, FlutterStandardDataTypeFloat64,
}; };
/**
A byte buffer holding `UInt8`, `SInt32`, `SInt64`, or `Float64` values, used
with `FlutterStandardMessageCodec` and `FlutterStandardMethodCodec`.
Two's complement encoding is used for signed integers. IEEE754
double-precision representation is used for floats. The platform's native
endianness is assumed.
*/
FLUTTER_EXPORT FLUTTER_EXPORT
@interface FlutterStandardTypedData : NSObject @interface FlutterStandardTypedData : NSObject
/**
Creates a `FlutterStandardTypedData` which interprets the specified data
as plain bytes.
- Parameter data: the byte data.
*/
+ (instancetype)typedDataWithBytes:(NSData*)data; + (instancetype)typedDataWithBytes:(NSData*)data;
/**
Creates a `FlutterStandardTypedData` which interprets the specified data
as 32-bit signed integers.
- Parameter data: the byte data. The length must be divisible by 4.
*/
+ (instancetype)typedDataWithInt32:(NSData*)data; + (instancetype)typedDataWithInt32:(NSData*)data;
/**
Creates a `FlutterStandardTypedData` which interprets the specified data
as 64-bit signed integers.
- Parameter data: the byte data. The length must be divisible by 8.
*/
+ (instancetype)typedDataWithInt64:(NSData*)data; + (instancetype)typedDataWithInt64:(NSData*)data;
/**
Creates a `FlutterStandardTypedData` which interprets the specified data
as 64-bit floats.
- Parameter data: the byte data. The length must be divisible by 8.
*/
+ (instancetype)typedDataWithFloat64:(NSData*)data; + (instancetype)typedDataWithFloat64:(NSData*)data;
/**
The raw underlying data buffer.
*/
@property(readonly, nonatomic) NSData* data; @property(readonly, nonatomic) NSData* data;
/**
The type of the encoded values.
*/
@property(readonly, nonatomic) FlutterStandardDataType type; @property(readonly, nonatomic) FlutterStandardDataType type;
/**
The number of value items encoded.
*/
@property(readonly, nonatomic) UInt32 elementCount; @property(readonly, nonatomic) UInt32 elementCount;
/**
The number of bytes used by the encoding of a single value item.
*/
@property(readonly, nonatomic) UInt8 elementSize; @property(readonly, nonatomic) UInt8 elementSize;
@end @end
/**
An arbitrarily large integer value, used with `FlutterStandardMessageCodec`
and `FlutterStandardMethodCodec`.
*/
FLUTTER_EXPORT FLUTTER_EXPORT
@interface FlutterStandardBigInteger : NSObject @interface FlutterStandardBigInteger : NSObject
/**
Creates a `FlutterStandardBigInteger` from a hexadecimal representation.
- Parameter hex: a hexadecimal string.
*/
+ (instancetype)bigIntegerWithHex:(NSString*)hex; + (instancetype)bigIntegerWithHex:(NSString*)hex;
/**
The hexadecimal string representation of this integer.
*/
@property(readonly, nonatomic) NSString* hex; @property(readonly, nonatomic) NSString* hex;
@end @end
/**
A codec for method calls and enveloped results.
Method calls are encoded as binary messages with enough structure that the
codec can extract a method name `NSString` and an arguments `NSObject`,
possibly `nil`. These data items are used to populate a `FlutterMethodCall`.
Result envelopes are encoded as binary messages with enough structure that
the codec can determine whether the result was successful or an error. In
the former case, the codec can extract the result `NSObject`, possibly `nil`.
In the latter case, the codec can extract an error code `NSString`, a
human-readable `NSString` error message (possibly `nil`), and a custom
error details `NSObject`, possibly `nil`. These data items are used to
populate a `FlutterError`.
*/
FLUTTER_EXPORT FLUTTER_EXPORT
@protocol FlutterMethodCodec @protocol FlutterMethodCodec
/**
Provides access to a shared instance this codec.
- Returns: The shared instance.
*/
+ (instancetype)sharedInstance; + (instancetype)sharedInstance;
/**
Encodes the specified method call into binary.
- Parameter methodCall: The method call. The arguments value
must be supported by this codec.
- Returns: The binary encoding.
*/
- (NSData*)encodeMethodCall:(FlutterMethodCall*)methodCall; - (NSData*)encodeMethodCall:(FlutterMethodCall*)methodCall;
/**
Decodes the specified method call from binary.
- Parameter methodCall: The method call to decode.
- Returns: The decoded method call.
*/
- (FlutterMethodCall*)decodeMethodCall:(NSData*)methodCall; - (FlutterMethodCall*)decodeMethodCall:(NSData*)methodCall;
- (NSData*)encodeSuccessEnvelope:(id)result;
/**
Encodes the specified successful result into binary.
- Parameter result: The result. Must be a value supported by this codec.
- Returns: The binary encoding.
*/
- (NSData*)encodeSuccessEnvelope:(id _Nullable)result;
/**
Encodes the specified error result into binary.
- Parameter error: The error object. The error details value must be supported
by this codec.
- Returns: The binary encoding.
*/
- (NSData*)encodeErrorEnvelope:(FlutterError*)error; - (NSData*)encodeErrorEnvelope:(FlutterError*)error;
- (id)decodeEnvelope:(NSData*)envelope error:(FlutterError**)error;
/**
Deccodes the specified result envelope from binary.
- Parameter error: The error object.
- Returns: The result value, if the envelope represented a successful result,
or a `FlutterError` instance, if not.
*/
- (id _Nullable)decodeEnvelope:(NSData*)envelope;
@end @end
/**
A `FlutterMethodCodec` using UTF-8 encoded JSON method calls and result
envelopes. Values supported as methods arguments and result payloads are
those supported as top-level or leaf values by `FlutterJSONMessageCodec`.
*/
FLUTTER_EXPORT FLUTTER_EXPORT
@interface FlutterJSONMethodCodec : NSObject<FlutterMethodCodec> @interface FlutterJSONMethodCodec : NSObject<FlutterMethodCodec>
@end @end
/**
A `FlutterMethodCodec` using the Flutter standard binary encoding.
The standard codec is guaranteed to be compatible with the corresponding
standard codec for PlatformMethodChannels on the Flutter side. These parts of
the Flutter SDK are evolved synchronously.
Values supported as method arguments and result payloads are those supported by
`FlutterStandardMessageCodec`.
*/
FLUTTER_EXPORT FLUTTER_EXPORT
@interface FlutterStandardMethodCodec : NSObject<FlutterMethodCodec> @interface FlutterStandardMethodCodec : NSObject<FlutterMethodCodec>
@end @end
NS_ASSUME_NONNULL_END
#endif // FLUTTER_FLUTTERCODECS_H_ #endif // FLUTTER_FLUTTERCODECS_H_
...@@ -11,9 +11,16 @@ ...@@ -11,9 +11,16 @@
NSString* _name; NSString* _name;
NSObject<FlutterMessageCodec>* _codec; NSObject<FlutterMessageCodec>* _codec;
} }
+ (instancetype)messageChannelNamed:(NSString*)name + (instancetype)messageChannelWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
codec:(NSObject<FlutterMessageCodec>*)codec { NSObject<FlutterMessageCodec>* codec = [FlutterStandardMessageCodec sharedInstance];
return [FlutterMessageChannel messageChannelWithName:name
binaryMessenger:messenger
codec:codec];
}
+ (instancetype)messageChannelWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMessageCodec>*)codec {
return [[[FlutterMessageChannel alloc] initWithName:name return [[[FlutterMessageChannel alloc] initWithName:name
binaryMessenger:messenger binaryMessenger:messenger
codec:codec] autorelease]; codec:codec] autorelease];
...@@ -22,11 +29,11 @@ ...@@ -22,11 +29,11 @@
- (instancetype)initWithName:(NSString*)name - (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMessageCodec>*)codec { codec:(NSObject<FlutterMessageCodec>*)codec {
if (self = [super init]) { self = [super init];
_name = [name retain]; NSAssert(self, @"Super init cannot be nil");
_messenger = [messenger retain]; _name = [name retain];
_codec = [codec retain]; _messenger = [messenger retain];
} _codec = [codec retain];
return self; return self;
} }
...@@ -83,11 +90,11 @@ ...@@ -83,11 +90,11 @@
message:(NSString*)message message:(NSString*)message
details:(id)details { details:(id)details {
NSAssert(code, @"Code cannot be nil"); NSAssert(code, @"Code cannot be nil");
if (self = [super init]) { self = [super init];
_code = [code retain]; NSAssert(self, @"Super init cannot be nil");
_message = [message retain]; _code = [code retain];
_details = [details retain]; _message = [message retain];
} _details = [details retain];
return self; return self;
} }
...@@ -125,10 +132,10 @@ ...@@ -125,10 +132,10 @@
- (instancetype)initWithMethodName:(NSString*)method arguments:(id)arguments { - (instancetype)initWithMethodName:(NSString*)method arguments:(id)arguments {
NSAssert(method, @"Method name cannot be nil"); NSAssert(method, @"Method name cannot be nil");
if (self = [super init]) { self = [super init];
_method = [method retain]; NSAssert(self, @"Super init cannot be nil");
_arguments = [arguments retain]; _method = [method retain];
} _arguments = [arguments retain];
return self; return self;
} }
...@@ -154,13 +161,23 @@ ...@@ -154,13 +161,23 @@
} }
@end @end
NSObject const* FlutterMethodNotImplemented = [NSObject new];
@implementation FlutterMethodChannel { @implementation FlutterMethodChannel {
NSObject<FlutterBinaryMessenger>* _messenger; NSObject<FlutterBinaryMessenger>* _messenger;
NSString* _name; NSString* _name;
NSObject<FlutterMethodCodec>* _codec; NSObject<FlutterMethodCodec>* _codec;
} }
+ (instancetype)methodChannelNamed:(NSString*)name + (instancetype)methodChannelWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
NSObject<FlutterMethodCodec>* codec = [FlutterStandardMethodCodec sharedInstance];
return [FlutterMethodChannel methodChannelWithName:name
binaryMessenger:messenger
codec:codec];
}
+ (instancetype)methodChannelWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec { codec:(NSObject<FlutterMethodCodec>*)codec {
return [[[FlutterMethodChannel alloc] initWithName:name return [[[FlutterMethodChannel alloc] initWithName:name
...@@ -171,11 +188,11 @@ ...@@ -171,11 +188,11 @@
- (instancetype)initWithName:(NSString*)name - (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec { codec:(NSObject<FlutterMethodCodec>*)codec {
if (self = [super init]) { self = [super init];
_name = [name retain]; NSAssert(self, @"Super init cannot be nil");
_messenger = [messenger retain]; _name = [name retain];
_codec = [codec retain]; _messenger = [messenger retain];
} _codec = [codec retain];
return self; return self;
} }
...@@ -201,9 +218,7 @@ ...@@ -201,9 +218,7 @@
NSData* message = [_codec encodeMethodCall:methodCall]; NSData* message = [_codec encodeMethodCall:methodCall];
FlutterBinaryReplyHandler replyHandler = ^(NSData* reply) { FlutterBinaryReplyHandler replyHandler = ^(NSData* reply) {
if (resultReceiver) { if (resultReceiver) {
FlutterError* flutterError = nil; resultReceiver([_codec decodeEnvelope:reply]);
id result = [_codec decodeEnvelope:reply error:&flutterError];
resultReceiver(result, flutterError);
} }
}; };
[_messenger sendBinaryMessage:message [_messenger sendBinaryMessage:message
...@@ -220,9 +235,11 @@ ...@@ -220,9 +235,11 @@
FlutterBinaryMessageHandler messageHandler = FlutterBinaryMessageHandler messageHandler =
^(NSData* message, FlutterBinaryReplyHandler reply) { ^(NSData* message, FlutterBinaryReplyHandler reply) {
FlutterMethodCall* call = [_codec decodeMethodCall:message]; FlutterMethodCall* call = [_codec decodeMethodCall:message];
handler(call, ^(id result, FlutterError* error) { handler(call, ^(id result) {
if (error) if (result == FlutterMethodNotImplemented)
reply([_codec encodeErrorEnvelope:error]); reply(nil);
else if ([result isKindOfClass:[FlutterError class]])
reply([_codec encodeErrorEnvelope:(FlutterError*)result]);
else else
reply([_codec encodeSuccessEnvelope:result]); reply([_codec encodeSuccessEnvelope:result]);
}); });
...@@ -230,8 +247,45 @@ ...@@ -230,8 +247,45 @@
[_messenger setBinaryMessageHandlerOnChannel:_name [_messenger setBinaryMessageHandlerOnChannel:_name
binaryMessageHandler:messageHandler]; binaryMessageHandler:messageHandler];
} }
@end
#pragma mark - Event channel
NSObject const* FlutterEndOfEventStream = [NSObject new];
@implementation FlutterEventChannel {
NSObject<FlutterBinaryMessenger>* _messenger;
NSString* _name;
NSObject<FlutterMethodCodec>* _codec;
}
+ (instancetype)eventChannelWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
NSObject<FlutterMethodCodec>* codec = [FlutterStandardMethodCodec sharedInstance];
return [FlutterEventChannel eventChannelWithName:name
binaryMessenger:messenger
codec:codec];
}
+ (instancetype)eventChannelWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec {
return [[[FlutterEventChannel alloc] initWithName:name
binaryMessenger:messenger
codec:codec] autorelease];
}
- (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec {
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_name = [name retain];
_messenger = [messenger retain];
_codec = [codec retain];
return self;
}
- (void)setStreamHandler:(FlutterStreamHandler)handler { - (void)setStreamHandler:(NSObject<FlutterStreamHandler>*)handler {
if (!handler) { if (!handler) {
[_messenger setBinaryMessageHandlerOnChannel:_name [_messenger setBinaryMessageHandlerOnChannel:_name
binaryMessageHandler:nil]; binaryMessageHandler:nil];
...@@ -240,24 +294,31 @@ ...@@ -240,24 +294,31 @@
FlutterBinaryMessageHandler messageHandler = ^( FlutterBinaryMessageHandler messageHandler = ^(
NSData* message, FlutterBinaryReplyHandler reply) { NSData* message, FlutterBinaryReplyHandler reply) {
FlutterMethodCall* call = [_codec decodeMethodCall:message]; FlutterMethodCall* call = [_codec decodeMethodCall:message];
FlutterResultReceiver resultReceiver = ^(id result, FlutterError* error) { if ([call.method isEqual:@"listen"]) {
FlutterEventReceiver eventReceiver = ^(id event) {
if (event == FlutterEndOfEventStream)
[_messenger sendBinaryMessage:nil channelName:_name];
else if ([event isKindOfClass: [FlutterError class]])
[_messenger sendBinaryMessage:[_codec encodeErrorEnvelope:(FlutterError*)event]
channelName:_name];
else
[_messenger sendBinaryMessage:[_codec encodeSuccessEnvelope:event]
channelName:_name];
};
FlutterError* error = [handler onListenWithArguments:call.arguments eventReceiver:eventReceiver];
if (error) if (error)
reply([_codec encodeErrorEnvelope:error]); reply([_codec encodeErrorEnvelope:error]);
else else
reply([_codec encodeSuccessEnvelope:nil]); reply([_codec encodeSuccessEnvelope:nil]);
}; } else if ([call.method isEqual:@"cancel"]) {
FlutterEventReceiver eventReceiver = FlutterError* error = [handler onCancelWithArguments:call.arguments];
^(id event, FlutterError* error, BOOL done) { if (error)
if (error) reply([_codec encodeErrorEnvelope:error]);
[_messenger sendBinaryMessage:[_codec encodeErrorEnvelope:error] else
channelName:_name]; reply([_codec encodeSuccessEnvelope:nil]);
else if (done) } else {
[_messenger sendBinaryMessage:[NSData data] channelName:_name]; reply(nil);
else }
[_messenger sendBinaryMessage:[_codec encodeSuccessEnvelope:event]
channelName:_name];
};
handler(call, resultReceiver, eventReceiver);
}; };
[_messenger setBinaryMessageHandlerOnChannel:_name [_messenger setBinaryMessageHandlerOnChannel:_name
binaryMessageHandler:messageHandler]; binaryMessageHandler:messageHandler];
......
...@@ -14,10 +14,14 @@ ...@@ -14,10 +14,14 @@
} }
- (NSData*)encode:(NSData*)message { - (NSData*)encode:(NSData*)message {
if (!message.length)
return nil;
return message; return message;
} }
- (NSData*)decode:(NSData*)message { - (NSData*)decode:(NSData*)message {
if (!message.length)
return nil;
return message; return message;
} }
@end @end
...@@ -32,14 +36,15 @@ ...@@ -32,14 +36,15 @@
} }
- (NSData*)encode:(NSString*)message { - (NSData*)encode:(NSString*)message {
if (!message.length) { if (!message.length)
return [NSData data]; return nil;
}
const char* utf8 = message.UTF8String; const char* utf8 = message.UTF8String;
return [NSData dataWithBytes:utf8 length:strlen(utf8)]; return [NSData dataWithBytes:utf8 length:strlen(utf8)];
} }
- (NSString*)decode:(NSData*)message { - (NSString*)decode:(NSData*)message {
if (!message.length)
return nil;
return [[[NSString alloc] initWithData:message encoding:NSUTF8StringEncoding] return [[[NSString alloc] initWithData:message encoding:NSUTF8StringEncoding]
autorelease]; autorelease];
} }
...@@ -55,6 +60,8 @@ ...@@ -55,6 +60,8 @@
} }
- (NSData*)encode:(id)message { - (NSData*)encode:(id)message {
if (message == nil)
return nil;
NSData* encoding = NSData* encoding =
[NSJSONSerialization dataWithJSONObject:message options:0 error:nil]; [NSJSONSerialization dataWithJSONObject:message options:0 error:nil];
NSAssert(encoding, @"Invalid JSON message, encoding failed"); NSAssert(encoding, @"Invalid JSON message, encoding failed");
...@@ -62,6 +69,8 @@ ...@@ -62,6 +69,8 @@
} }
- (id)decode:(NSData*)message { - (id)decode:(NSData*)message {
if (!message.length)
return nil;
id decoded = id decoded =
[NSJSONSerialization JSONObjectWithData:message options:0 error:nil]; [NSJSONSerialization JSONObjectWithData:message options:0 error:nil];
NSAssert(decoded, @"Invalid JSON message, decoding failed"); NSAssert(decoded, @"Invalid JSON message, decoding failed");
...@@ -107,14 +116,13 @@ ...@@ -107,14 +116,13 @@
return [FlutterMethodCall methodCallWithMethodName:method arguments:arguments]; return [FlutterMethodCall methodCallWithMethodName:method arguments:arguments];
} }
- (id)decodeEnvelope:(NSData*)envelope error:(FlutterError**)error { - (id)decodeEnvelope:(NSData*)envelope {
NSArray* array = [[FlutterJSONMessageCodec sharedInstance] decode:envelope]; NSArray* array = [[FlutterJSONMessageCodec sharedInstance] decode:envelope];
if (array.count == 1) if (array.count == 1)
return array[0]; return array[0];
NSAssert(array.count == 3, @"Invalid JSON envelope"); NSAssert(array.count == 3, @"Invalid JSON envelope");
NSAssert([array[0] isKindOfClass:[NSString class]], @"Invalid JSON envelope"); NSAssert([array[0] isKindOfClass:[NSString class]], @"Invalid JSON envelope");
NSAssert(array[1] == nil || [array[1] isKindOfClass:[NSString class]], @"Invalid JSON envelope"); NSAssert(array[1] == nil || [array[1] isKindOfClass:[NSString class]], @"Invalid JSON envelope");
*error = [FlutterError errorWithCode:array[0] message:array[1] details:array[2]]; return [FlutterError errorWithCode:array[0] message:array[1] details:array[2]];
return nil;
} }
@end @end
...@@ -46,39 +46,39 @@ using namespace shell; ...@@ -46,39 +46,39 @@ using namespace shell;
id args = call.arguments; id args = call.arguments;
if ([method isEqualToString:@"SystemSound.play"]) { if ([method isEqualToString:@"SystemSound.play"]) {
[self playSystemSound:args]; [self playSystemSound:args];
resultReceiver(nil, nil); resultReceiver(nil);
} else if ([method isEqualToString:@"HapticFeedback.vibrate"]) { } else if ([method isEqualToString:@"HapticFeedback.vibrate"]) {
[self vibrateHapticFeedback]; [self vibrateHapticFeedback];
resultReceiver(nil, nil); resultReceiver(nil);
} else if ([method isEqualToString:@"UrlLauncher.launch"]) { } else if ([method isEqualToString:@"UrlLauncher.launch"]) {
[self launchURL:args]; [self launchURL:args];
resultReceiver(nil, nil); resultReceiver(nil);
} else if ([method isEqualToString:@"SystemChrome.setPreferredOrientations"]) { } else if ([method isEqualToString:@"SystemChrome.setPreferredOrientations"]) {
[self setSystemChromePreferredOrientations:args]; [self setSystemChromePreferredOrientations:args];
resultReceiver(nil, nil); resultReceiver(nil);
} else if ([method isEqualToString:@"SystemChrome.setApplicationSwitcherDescription"]) { } else if ([method isEqualToString:@"SystemChrome.setApplicationSwitcherDescription"]) {
[self setSystemChromeApplicationSwitcherDescription:args]; [self setSystemChromeApplicationSwitcherDescription:args];
resultReceiver(nil, nil); resultReceiver(nil);
} else if ([method isEqualToString:@"SystemChrome.setEnabledSystemUIOverlays"]) { } else if ([method isEqualToString:@"SystemChrome.setEnabledSystemUIOverlays"]) {
[self setSystemChromeEnabledSystemUIOverlays:args]; [self setSystemChromeEnabledSystemUIOverlays:args];
resultReceiver(nil, nil); resultReceiver(nil);
} else if ([method isEqualToString:@"SystemChrome.setSystemUIOverlayStyle"]) { } else if ([method isEqualToString:@"SystemChrome.setSystemUIOverlayStyle"]) {
[self setSystemChromeSystemUIOverlayStyle:args]; [self setSystemChromeSystemUIOverlayStyle:args];
resultReceiver(nil, nil); resultReceiver(nil);
} else if ([method isEqualToString:@"SystemNavigator.pop"]) { } else if ([method isEqualToString:@"SystemNavigator.pop"]) {
[self popSystemNavigator]; [self popSystemNavigator];
resultReceiver(nil, nil); resultReceiver(nil);
} else if ([method isEqualToString:@"Clipboard.getData"]) { } else if ([method isEqualToString:@"Clipboard.getData"]) {
resultReceiver([self getClipboardData:args], nil); resultReceiver([self getClipboardData:args]);
} else if ([method isEqualToString:@"Clipboard.setData"]) { } else if ([method isEqualToString:@"Clipboard.setData"]) {
[self setClipboardData:args]; [self setClipboardData:args];
resultReceiver(nil, nil); resultReceiver(nil);
} else if ([method isEqualToString:@"PathProvider.getTemporaryDirectory"]) { } else if ([method isEqualToString:@"PathProvider.getTemporaryDirectory"]) {
resultReceiver([self getPathProviderTemporaryDirectory], nil); resultReceiver([self getPathProviderTemporaryDirectory]);
} else if ([method isEqualToString:@"PathProvider.getApplicationDocumentsDirectory"]) { } else if ([method isEqualToString:@"PathProvider.getApplicationDocumentsDirectory"]) {
resultReceiver([self getPathProviderApplicationDocumentsDirectory], nil); resultReceiver([self getPathProviderApplicationDocumentsDirectory]);
} else { } else {
resultReceiver(nil, [FlutterError errorWithCode:@"UNKNOWN" message:@"Unknown method" details: nil]); resultReceiver(FlutterMethodNotImplemented);
} }
} }
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
} }
- (NSData*)encode:(id)message { - (NSData*)encode:(id)message {
if (message == nil)
return nil;
NSMutableData* data = [NSMutableData dataWithCapacity:32]; NSMutableData* data = [NSMutableData dataWithCapacity:32];
FlutterStandardWriter* writer = [FlutterStandardWriter writerWithData:data]; FlutterStandardWriter* writer = [FlutterStandardWriter writerWithData:data];
[writer writeValue:message]; [writer writeValue:message];
...@@ -23,6 +25,8 @@ ...@@ -23,6 +25,8 @@
} }
- (id)decode:(NSData*)message { - (id)decode:(NSData*)message {
if (!message.length)
return nil;
FlutterStandardReader* reader = FlutterStandardReader* reader =
[FlutterStandardReader readerWithData:message]; [FlutterStandardReader readerWithData:message];
id value = [reader readValue]; id value = [reader readValue];
...@@ -79,7 +83,7 @@ ...@@ -79,7 +83,7 @@
return [FlutterMethodCall methodCallWithMethodName:value1 arguments:value2]; return [FlutterMethodCall methodCallWithMethodName:value1 arguments:value2];
} }
- (id)decodeEnvelope:(NSData*)envelope error:(FlutterError**)error { - (id)decodeEnvelope:(NSData*)envelope {
FlutterStandardReader* reader = FlutterStandardReader* reader =
[FlutterStandardReader readerWithData:envelope]; [FlutterStandardReader readerWithData:envelope];
UInt8 flag = [reader readByte]; UInt8 flag = [reader readByte];
...@@ -99,9 +103,7 @@ ...@@ -99,9 +103,7 @@
@"Invalid standard envelope"); @"Invalid standard envelope");
NSAssert(message == nil || [message isKindOfClass:[NSString class]], NSAssert(message == nil || [message isKindOfClass:[NSString class]],
@"Invalid standard envelope"); @"Invalid standard envelope");
*error = result = [FlutterError errorWithCode:code message:message details:details];
[FlutterError errorWithCode:code message:message details:details];
result = nil;
} break; } break;
} }
return result; return result;
...@@ -148,12 +150,12 @@ using namespace shell; ...@@ -148,12 +150,12 @@ using namespace shell;
NSAssert(data, @"Data cannot be nil"); NSAssert(data, @"Data cannot be nil");
NSAssert(data.length % elementSize == 0, NSAssert(data.length % elementSize == 0,
@"Data must contain integral number of elements"); @"Data must contain integral number of elements");
if (self = [super init]) { self = [super init];
_data = [data retain]; NSAssert(self, @"Super init cannot be nil");
_type = type; _data = [data retain];
_elementSize = elementSize; _type = type;
_elementCount = data.length / elementSize; _elementSize = elementSize;
} _elementCount = data.length / elementSize;
return self; return self;
} }
...@@ -184,9 +186,9 @@ using namespace shell; ...@@ -184,9 +186,9 @@ using namespace shell;
- (instancetype)initWithHex:(NSString*)hex { - (instancetype)initWithHex:(NSString*)hex {
NSAssert(hex, @"Hex cannot be nil"); NSAssert(hex, @"Hex cannot be nil");
if (self = [super init]) { self = [super init];
_hex = [hex retain]; NSAssert(self, @"Super init cannot be nil");
} _hex = [hex retain];
return self; return self;
} }
...@@ -223,9 +225,9 @@ using namespace shell; ...@@ -223,9 +225,9 @@ using namespace shell;
} }
- (instancetype)initWithData:(NSMutableData*)data { - (instancetype)initWithData:(NSMutableData*)data {
if (self = [super init]) { self = [super init];
_data = [data retain]; NSAssert(self, @"Super init cannot be nil");
} _data = [data retain];
return self; return self;
} }
...@@ -359,10 +361,10 @@ using namespace shell; ...@@ -359,10 +361,10 @@ using namespace shell;
} }
- (instancetype)initWithData:(NSData*)data { - (instancetype)initWithData:(NSData*)data {
if (self = [super init]) { self = [super init];
_data = [data retain]; NSAssert(self, @"Super init cannot be nil");
_range = NSMakeRange(0, 0); _data = [data retain];
} _range = NSMakeRange(0, 0);
return self; return self;
} }
......
...@@ -160,23 +160,21 @@ static UIKeyboardType ToUIKeyboardType(NSString* inputType) { ...@@ -160,23 +160,21 @@ static UIKeyboardType ToUIKeyboardType(NSString* inputType) {
id args = call.arguments; id args = call.arguments;
if ([method isEqualToString:@"TextInput.show"]) { if ([method isEqualToString:@"TextInput.show"]) {
[self showTextInput]; [self showTextInput];
resultReceiver(nil, nil); resultReceiver(nil);
} else if ([method isEqualToString:@"TextInput.hide"]) { } else if ([method isEqualToString:@"TextInput.hide"]) {
[self hideTextInput]; [self hideTextInput];
resultReceiver(nil, nil); resultReceiver(nil);
} else if ([method isEqualToString:@"TextInput.setClient"]) { } else if ([method isEqualToString:@"TextInput.setClient"]) {
[self setTextInputClient:[args[0] intValue] withConfiguration:args[1]]; [self setTextInputClient:[args[0] intValue] withConfiguration:args[1]];
resultReceiver(nil, nil); resultReceiver(nil);
} else if ([method isEqualToString:@"TextInput.setEditingState"]) { } else if ([method isEqualToString:@"TextInput.setEditingState"]) {
[self setTextInputEditingState:args]; [self setTextInputEditingState:args];
resultReceiver(nil, nil); resultReceiver(nil);
} else if ([method isEqualToString:@"TextInput.clearClient"]) { } else if ([method isEqualToString:@"TextInput.clearClient"]) {
[self clearTextInputClient]; [self clearTextInputClient];
resultReceiver(nil, nil); resultReceiver(nil);
} else { } else {
resultReceiver(nil, [FlutterError errorWithCode:@"UNKNOWN" resultReceiver(FlutterMethodNotImplemented);
message:@"Unknown method"
details:nil]);
} }
} }
......
...@@ -585,8 +585,9 @@ constexpr CGFloat kStandardStatusBarHeight = 20.0; ...@@ -585,8 +585,9 @@ constexpr CGFloat kStandardStatusBarHeight = 20.0;
#pragma mark - Application Messages #pragma mark - Application Messages
- (void)sendBinaryMessage:(NSData*)message channelName:(NSString*)channel { - (void)sendBinaryMessage:(NSData*)message channelName:(NSString*)channel {
NSAssert(message, @"The message must not be null");
NSAssert(channel, @"The channel must not be null"); NSAssert(channel, @"The channel must not be null");
if (message == nil)
message = [NSData data];
_platformView->DispatchPlatformMessage( _platformView->DispatchPlatformMessage(
ftl::MakeRefCounted<blink::PlatformMessage>( ftl::MakeRefCounted<blink::PlatformMessage>(
channel.UTF8String, shell::GetVectorFromNSData(message), nil)); channel.UTF8String, shell::GetVectorFromNSData(message), nil));
...@@ -595,15 +596,15 @@ constexpr CGFloat kStandardStatusBarHeight = 20.0; ...@@ -595,15 +596,15 @@ constexpr CGFloat kStandardStatusBarHeight = 20.0;
- (void)sendBinaryMessage:(NSData*)message - (void)sendBinaryMessage:(NSData*)message
channelName:(NSString*)channel channelName:(NSString*)channel
binaryReplyHandler:(FlutterBinaryReplyHandler)callback { binaryReplyHandler:(FlutterBinaryReplyHandler)callback {
NSAssert(message, @"The message must not be null");
NSAssert(channel, @"The channel must not be null"); NSAssert(channel, @"The channel must not be null");
NSAssert(callback, @"The callback must not be null"); NSAssert(callback, @"The callback must not be null");
if (message == nil)
message = [NSData data];
_platformView->DispatchPlatformMessage( _platformView->DispatchPlatformMessage(
ftl::MakeRefCounted<blink::PlatformMessage>( ftl::MakeRefCounted<blink::PlatformMessage>(
channel.UTF8String, shell::GetVectorFromNSData(message), channel.UTF8String, shell::GetVectorFromNSData(message),
ftl::MakeRefCounted<PlatformMessageResponseDarwin>(^(NSData* reply) { ftl::MakeRefCounted<PlatformMessageResponseDarwin>(^(NSData* reply) {
if (callback) callback(reply);
callback(reply);
}))); })));
} }
......
...@@ -7,16 +7,14 @@ ...@@ -7,16 +7,14 @@
TEST(FlutterStringCodec, CanEncodeAndDecodeNil) { TEST(FlutterStringCodec, CanEncodeAndDecodeNil) {
FlutterStringCodec* codec = [FlutterStringCodec sharedInstance]; FlutterStringCodec* codec = [FlutterStringCodec sharedInstance];
ASSERT_TRUE([[codec encode:nil] isEqualTo:[NSData data]]); ASSERT_TRUE([codec encode:nil] == nil);
ASSERT_TRUE([[codec decode:nil] isEqualTo:@""]); ASSERT_TRUE([codec decode:nil] == nil);
} }
TEST(FlutterStringCodec, CanEncodeAndDecodeEmptyString) { TEST(FlutterStringCodec, CanEncodeAndDecodeEmptyString) {
NSString* value = @"";
FlutterStringCodec* codec = [FlutterStringCodec sharedInstance]; FlutterStringCodec* codec = [FlutterStringCodec sharedInstance];
NSData* encoded = [codec encode:value]; ASSERT_TRUE([codec encode:@""] == nil);
NSString* decoded = [codec decode:encoded]; ASSERT_TRUE([codec decode:[NSData data]] == nil);
ASSERT_TRUE([value isEqualTo:decoded]);
} }
TEST(FlutterStringCodec, CanEncodeAndDecodeAsciiString) { TEST(FlutterStringCodec, CanEncodeAndDecodeAsciiString) {
...@@ -43,6 +41,13 @@ TEST(FlutterStringCodec, CanEncodeAndDecodeNonBMPString) { ...@@ -43,6 +41,13 @@ TEST(FlutterStringCodec, CanEncodeAndDecodeNonBMPString) {
ASSERT_TRUE([value isEqualTo:decoded]); ASSERT_TRUE([value isEqualTo:decoded]);
} }
TEST(FlutterJSONCodec, CanEncodeAndDecodeNil) {
FlutterStringCodec* codec = [FlutterStringCodec sharedInstance];
ASSERT_TRUE([codec encode:nil] == nil);
ASSERT_TRUE([codec decode:nil] == nil);
ASSERT_TRUE([codec decode:[NSData data]] == nil);
}
TEST(FlutterJSONCodec, CanEncodeAndDecodeArray) { TEST(FlutterJSONCodec, CanEncodeAndDecodeArray) {
NSArray* value = NSArray* value =
@[ [NSNull null], @"hello", @3.14, @47, @[ [NSNull null], @"hello", @3.14, @47,
......
...@@ -9,7 +9,10 @@ void checkEncodeDecode(id value, NSData* expectedEncoding) { ...@@ -9,7 +9,10 @@ void checkEncodeDecode(id value, NSData* expectedEncoding) {
FlutterStandardMessageCodec* codec = FlutterStandardMessageCodec* codec =
[FlutterStandardMessageCodec sharedInstance]; [FlutterStandardMessageCodec sharedInstance];
NSData* encoded = [codec encode:value]; NSData* encoded = [codec encode:value];
ASSERT_TRUE([encoded isEqual:expectedEncoding]); if (expectedEncoding == nil)
ASSERT_TRUE(encoded == nil);
else
ASSERT_TRUE([encoded isEqual:expectedEncoding]);
id decoded = [codec decode:encoded]; id decoded = [codec decode:encoded];
if (value == nil || value == [NSNull null]) if (value == nil || value == [NSNull null])
ASSERT_TRUE(decoded == nil); ASSERT_TRUE(decoded == nil);
...@@ -29,8 +32,7 @@ void checkEncodeDecode(id value) { ...@@ -29,8 +32,7 @@ void checkEncodeDecode(id value) {
} }
TEST(FlutterStandardCodec, CanEncodeAndDecodeNil) { TEST(FlutterStandardCodec, CanEncodeAndDecodeNil) {
char bytes[1] = {0x00}; checkEncodeDecode(nil, nil);
checkEncodeDecode(nil, [NSData dataWithBytes:bytes length:1]);
} }
TEST(FlutterStandardCodec, CanEncodeAndDecodeNSNull) { TEST(FlutterStandardCodec, CanEncodeAndDecodeNSNull) {
...@@ -216,9 +218,7 @@ TEST(FlutterStandardCodec, HandlesSuccessEnvelopesWithNilResult) { ...@@ -216,9 +218,7 @@ TEST(FlutterStandardCodec, HandlesSuccessEnvelopesWithNilResult) {
FlutterStandardMethodCodec* codec = FlutterStandardMethodCodec* codec =
[FlutterStandardMethodCodec sharedInstance]; [FlutterStandardMethodCodec sharedInstance];
NSData* encoded = [codec encodeSuccessEnvelope:nil]; NSData* encoded = [codec encodeSuccessEnvelope:nil];
FlutterError* error = nil; id decoded = [codec decodeEnvelope:encoded];
id decoded = [codec decodeEnvelope:encoded error:&error];
ASSERT_TRUE(error == nil);
ASSERT_TRUE(decoded == nil); ASSERT_TRUE(decoded == nil);
} }
...@@ -226,10 +226,8 @@ TEST(FlutterStandardCodec, HandlesSuccessEnvelopesWithSingleResult) { ...@@ -226,10 +226,8 @@ TEST(FlutterStandardCodec, HandlesSuccessEnvelopesWithSingleResult) {
FlutterStandardMethodCodec* codec = FlutterStandardMethodCodec* codec =
[FlutterStandardMethodCodec sharedInstance]; [FlutterStandardMethodCodec sharedInstance];
NSData* encoded = [codec encodeSuccessEnvelope:@42]; NSData* encoded = [codec encodeSuccessEnvelope:@42];
FlutterError* decodedError = nil; id decoded = [codec decodeEnvelope:encoded];
id decodedResult = [codec decodeEnvelope:encoded error:&decodedError]; ASSERT_TRUE([decoded isEqual:@42]);
ASSERT_TRUE(decodedError == nil);
ASSERT_TRUE([decodedResult isEqual:@42]);
} }
TEST(FlutterStandardCodec, HandlesSuccessEnvelopesWithResultMap) { TEST(FlutterStandardCodec, HandlesSuccessEnvelopesWithResultMap) {
...@@ -237,9 +235,8 @@ TEST(FlutterStandardCodec, HandlesSuccessEnvelopesWithResultMap) { ...@@ -237,9 +235,8 @@ TEST(FlutterStandardCodec, HandlesSuccessEnvelopesWithResultMap) {
[FlutterStandardMethodCodec sharedInstance]; [FlutterStandardMethodCodec sharedInstance];
NSDictionary* result = @{ @"a" : @42, @42 : @"a" }; NSDictionary* result = @{ @"a" : @42, @42 : @"a" };
NSData* encoded = [codec encodeSuccessEnvelope:result]; NSData* encoded = [codec encodeSuccessEnvelope:result];
FlutterError* decodedError = nil; id decoded = [codec decodeEnvelope:encoded];
id decodedResult = [codec decodeEnvelope:encoded error:&decodedError]; ASSERT_TRUE([decoded isEqual:result]);
ASSERT_TRUE([decodedResult isEqual:result]);
} }
TEST(FlutterStandardCodec, HandlesErrorEnvelopes) { TEST(FlutterStandardCodec, HandlesErrorEnvelopes) {
...@@ -250,8 +247,6 @@ TEST(FlutterStandardCodec, HandlesErrorEnvelopes) { ...@@ -250,8 +247,6 @@ TEST(FlutterStandardCodec, HandlesErrorEnvelopes) {
message:@"something failed" message:@"something failed"
details:details]; details:details];
NSData* encoded = [codec encodeErrorEnvelope:error]; NSData* encoded = [codec encodeErrorEnvelope:error];
FlutterError* decodedError = nil; id decoded = [codec decodeEnvelope:encoded];
id decodedResult = [codec decodeEnvelope:encoded error:&decodedError]; ASSERT_TRUE([decoded isEqual:error]);
ASSERT_TRUE(decodedResult == nil);
ASSERT_TRUE([decodedError isEqual:error]);
} }
...@@ -1434,6 +1434,7 @@ FILE: ../../../flutter/lib/ui/painting/vertices.h ...@@ -1434,6 +1434,7 @@ FILE: ../../../flutter/lib/ui/painting/vertices.h
FILE: ../../../flutter/shell/gpu/gpu_surface_software.cc FILE: ../../../flutter/shell/gpu/gpu_surface_software.cc
FILE: ../../../flutter/shell/gpu/gpu_surface_software.h FILE: ../../../flutter/shell/gpu/gpu_surface_software.h
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/BinaryCodec.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/BinaryCodec.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/FlutterEventChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/FlutterException.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/FlutterException.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/FlutterMessageChannel.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/FlutterMessageChannel.java
FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/FlutterMethodChannel.java FILE: ../../../flutter/shell/platform/android/io/flutter/plugin/common/FlutterMethodChannel.java
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册