blob: 30b306a301f394622b4dac6a16d4dfd0435c409c [file] [log] [blame]
// 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 "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h"
#pragma mark - Basic message channel
static NSString* const kFlutterChannelBuffersChannel = @"dev.flutter/channel-buffers";
static void ResizeChannelBuffer(NSObject<FlutterBinaryMessenger>* binaryMessenger,
NSString* channel,
NSInteger newSize) {
NSString* messageString = [NSString stringWithFormat:@"resize\r%@\r%@", channel, @(newSize)];
NSData* message = [messageString dataUsingEncoding:NSUTF8StringEncoding];
[binaryMessenger sendOnChannel:kFlutterChannelBuffersChannel message:message];
}
static FlutterBinaryMessengerConnection SetMessageHandler(
NSObject<FlutterBinaryMessenger>* messenger,
NSString* name,
FlutterBinaryMessageHandler handler,
NSObject<FlutterTaskQueue>* taskQueue) {
if (taskQueue) {
NSCAssert([messenger respondsToSelector:@selector(setMessageHandlerOnChannel:
binaryMessageHandler:taskQueue:)],
@"");
return [messenger setMessageHandlerOnChannel:name
binaryMessageHandler:handler
taskQueue:taskQueue];
} else {
return [messenger setMessageHandlerOnChannel:name binaryMessageHandler:handler];
}
}
////////////////////////////////////////////////////////////////////////////////
@implementation FlutterBasicMessageChannel {
NSObject<FlutterBinaryMessenger>* _messenger;
NSString* _name;
NSObject<FlutterMessageCodec>* _codec;
FlutterBinaryMessengerConnection _connection;
NSObject<FlutterTaskQueue>* _taskQueue;
}
+ (instancetype)messageChannelWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
NSObject<FlutterMessageCodec>* codec = [FlutterStandardMessageCodec sharedInstance];
return [FlutterBasicMessageChannel messageChannelWithName:name
binaryMessenger:messenger
codec:codec];
}
+ (instancetype)messageChannelWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMessageCodec>*)codec {
return [[[FlutterBasicMessageChannel alloc] initWithName:name
binaryMessenger:messenger
codec:codec] autorelease];
}
- (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMessageCodec>*)codec {
self = [self initWithName:name binaryMessenger:messenger codec:codec taskQueue:nil];
return self;
}
- (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMessageCodec>*)codec
taskQueue:(NSObject<FlutterTaskQueue>*)taskQueue {
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_name = [name retain];
_messenger = [messenger retain];
_codec = [codec retain];
_taskQueue = [taskQueue retain];
return self;
}
- (void)dealloc {
[_name release];
[_messenger release];
[_codec release];
[_taskQueue release];
[super dealloc];
}
- (void)sendMessage:(id)message {
[_messenger sendOnChannel:_name message:[_codec encode:message]];
}
- (void)sendMessage:(id)message reply:(FlutterReply)callback {
FlutterBinaryReply reply = ^(NSData* data) {
if (callback) {
callback([_codec decode:data]);
}
};
[_messenger sendOnChannel:_name message:[_codec encode:message] binaryReply:reply];
}
- (void)setMessageHandler:(FlutterMessageHandler)handler {
if (!handler) {
if (_connection > 0) {
[_messenger cleanUpConnection:_connection];
_connection = 0;
} else {
[_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil];
}
return;
}
// Grab reference to avoid retain on self.
NSObject<FlutterMessageCodec>* codec = _codec;
FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
handler([codec decode:message], ^(id reply) {
callback([codec encode:reply]);
});
};
_connection = SetMessageHandler(_messenger, _name, messageHandler, _taskQueue);
}
- (void)resizeChannelBuffer:(NSInteger)newSize {
ResizeChannelBuffer(_messenger, _name, newSize);
}
@end
#pragma mark - Method channel
////////////////////////////////////////////////////////////////////////////////
@implementation FlutterError
+ (instancetype)errorWithCode:(NSString*)code message:(NSString*)message details:(id)details {
return [[[FlutterError alloc] initWithCode:code message:message details:details] autorelease];
}
- (instancetype)initWithCode:(NSString*)code message:(NSString*)message details:(id)details {
NSAssert(code, @"Code cannot be nil");
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_code = [code retain];
_message = [message retain];
_details = [details retain];
return self;
}
- (void)dealloc {
[_code release];
[_message release];
[_details release];
[super dealloc];
}
- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}
if (![object isKindOfClass:[FlutterError class]]) {
return NO;
}
FlutterError* other = (FlutterError*)object;
return [self.code isEqual:other.code] &&
((!self.message && !other.message) || [self.message isEqual:other.message]) &&
((!self.details && !other.details) || [self.details isEqual:other.details]);
}
- (NSUInteger)hash {
return [self.code hash] ^ [self.message hash] ^ [self.details hash];
}
@end
////////////////////////////////////////////////////////////////////////////////
@implementation FlutterMethodCall
+ (instancetype)methodCallWithMethodName:(NSString*)method arguments:(id)arguments {
return [[[FlutterMethodCall alloc] initWithMethodName:method arguments:arguments] autorelease];
}
- (instancetype)initWithMethodName:(NSString*)method arguments:(id)arguments {
NSAssert(method, @"Method name cannot be nil");
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_method = [method retain];
_arguments = [arguments retain];
return self;
}
- (void)dealloc {
[_method release];
[_arguments release];
[super dealloc];
}
- (BOOL)isEqual:(id)object {
if (self == object) {
return YES;
}
if (![object isKindOfClass:[FlutterMethodCall class]]) {
return NO;
}
FlutterMethodCall* other = (FlutterMethodCall*)object;
return [self.method isEqual:[other method]] &&
((!self.arguments && !other.arguments) || [self.arguments isEqual:other.arguments]);
}
- (NSUInteger)hash {
return [self.method hash] ^ [self.arguments hash];
}
@end
NSObject const* FlutterMethodNotImplemented = [[NSObject alloc] init];
////////////////////////////////////////////////////////////////////////////////
@implementation FlutterMethodChannel {
NSObject<FlutterBinaryMessenger>* _messenger;
NSString* _name;
NSObject<FlutterMethodCodec>* _codec;
FlutterBinaryMessengerConnection _connection;
NSObject<FlutterTaskQueue>* _taskQueue;
}
+ (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
codec:(NSObject<FlutterMethodCodec>*)codec {
return [[[FlutterMethodChannel alloc] initWithName:name binaryMessenger:messenger
codec:codec] autorelease];
}
- (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec {
self = [self initWithName:name binaryMessenger:messenger codec:codec taskQueue:nil];
return self;
}
- (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec
taskQueue:(NSObject<FlutterTaskQueue>*)taskQueue {
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_name = [name retain];
_messenger = [messenger retain];
_codec = [codec retain];
_taskQueue = [taskQueue retain];
return self;
}
- (void)dealloc {
[_name release];
[_messenger release];
[_codec release];
[_taskQueue release];
[super dealloc];
}
- (void)invokeMethod:(NSString*)method arguments:(id)arguments {
FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:method
arguments:arguments];
NSData* message = [_codec encodeMethodCall:methodCall];
[_messenger sendOnChannel:_name message:message];
}
- (void)invokeMethod:(NSString*)method arguments:(id)arguments result:(FlutterResult)callback {
FlutterMethodCall* methodCall = [FlutterMethodCall methodCallWithMethodName:method
arguments:arguments];
NSData* message = [_codec encodeMethodCall:methodCall];
FlutterBinaryReply reply = ^(NSData* data) {
if (callback) {
callback((data == nil) ? FlutterMethodNotImplemented : [_codec decodeEnvelope:data]);
}
};
[_messenger sendOnChannel:_name message:message binaryReply:reply];
}
- (void)setMethodCallHandler:(FlutterMethodCallHandler)handler {
if (!handler) {
if (_connection > 0) {
[_messenger cleanUpConnection:_connection];
_connection = 0;
} else {
[_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil];
}
return;
}
// Make sure the block captures the codec, not self.
NSObject<FlutterMethodCodec>* codec = _codec;
FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
FlutterMethodCall* call = [codec decodeMethodCall:message];
handler(call, ^(id result) {
if (result == FlutterMethodNotImplemented) {
callback(nil);
} else if ([result isKindOfClass:[FlutterError class]]) {
callback([codec encodeErrorEnvelope:(FlutterError*)result]);
} else {
callback([codec encodeSuccessEnvelope:result]);
}
});
};
_connection = SetMessageHandler(_messenger, _name, messageHandler, _taskQueue);
}
- (void)resizeChannelBuffer:(NSInteger)newSize {
ResizeChannelBuffer(_messenger, _name, newSize);
}
@end
#pragma mark - Event channel
NSObject const* FlutterEndOfEventStream = [[NSObject alloc] init];
////////////////////////////////////////////////////////////////////////////////
@implementation FlutterEventChannel {
NSObject<FlutterBinaryMessenger>* _messenger;
NSString* _name;
NSObject<FlutterMethodCodec>* _codec;
NSObject<FlutterTaskQueue>* _taskQueue;
FlutterBinaryMessengerConnection _connection;
}
+ (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 {
return [self initWithName:name binaryMessenger:messenger codec:codec taskQueue:nil];
}
- (instancetype)initWithName:(NSString*)name
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
codec:(NSObject<FlutterMethodCodec>*)codec
taskQueue:(NSObject<FlutterTaskQueue>* _Nullable)taskQueue {
self = [super init];
NSAssert(self, @"Super init cannot be nil");
_name = [name retain];
_messenger = [messenger retain];
_codec = [codec retain];
_taskQueue = [taskQueue retain];
return self;
}
- (void)dealloc {
[_name release];
[_codec release];
[_messenger release];
[_taskQueue release];
[super dealloc];
}
static FlutterBinaryMessengerConnection SetStreamHandlerMessageHandlerOnChannel(
NSObject<FlutterStreamHandler>* handler,
NSString* name,
NSObject<FlutterBinaryMessenger>* messenger,
NSObject<FlutterMethodCodec>* codec,
NSObject<FlutterTaskQueue>* taskQueue) {
__block FlutterEventSink currentSink = nil;
FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
FlutterMethodCall* call = [codec decodeMethodCall:message];
if ([call.method isEqual:@"listen"]) {
if (currentSink) {
FlutterError* error = [handler onCancelWithArguments:nil];
if (error) {
NSLog(@"Failed to cancel existing stream: %@. %@ (%@)", error.code, error.message,
error.details);
}
}
currentSink = ^(id event) {
if (event == FlutterEndOfEventStream) {
[messenger sendOnChannel:name message:nil];
} else if ([event isKindOfClass:[FlutterError class]]) {
[messenger sendOnChannel:name message:[codec encodeErrorEnvelope:(FlutterError*)event]];
} else {
[messenger sendOnChannel:name message:[codec encodeSuccessEnvelope:event]];
}
};
FlutterError* error = [handler onListenWithArguments:call.arguments eventSink:currentSink];
if (error) {
callback([codec encodeErrorEnvelope:error]);
} else {
callback([codec encodeSuccessEnvelope:nil]);
}
} else if ([call.method isEqual:@"cancel"]) {
if (!currentSink) {
callback(
[codec encodeErrorEnvelope:[FlutterError errorWithCode:@"error"
message:@"No active stream to cancel"
details:nil]]);
return;
}
currentSink = nil;
FlutterError* error = [handler onCancelWithArguments:call.arguments];
if (error) {
callback([codec encodeErrorEnvelope:error]);
} else {
callback([codec encodeSuccessEnvelope:nil]);
}
} else {
callback(nil);
}
};
return SetMessageHandler(messenger, name, messageHandler, taskQueue);
}
- (void)setStreamHandler:(NSObject<FlutterStreamHandler>*)handler {
if (!handler) {
[_messenger cleanUpConnection:_connection];
_connection = 0;
return;
}
_connection =
SetStreamHandlerMessageHandlerOnChannel(handler, _name, _messenger, _codec, _taskQueue);
}
@end