blob: f5527a438ed9e00b1ab11d6dc3ddc2411f9b1484 [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 FlutterChannelBuffersChannel = @"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:FlutterChannelBuffersChannel message:message];
}
@implementation FlutterBasicMessageChannel {
NSObject<FlutterBinaryMessenger>* _messenger;
NSString* _name;
NSObject<FlutterMessageCodec>* _codec;
FlutterBinaryMessengerConnection _connection;
}
+ (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 = [super init];
NSAssert(self, @"Super init cannot be nil");
_name = [name retain];
_messenger = [messenger retain];
_codec = [codec retain];
return self;
}
- (void)dealloc {
[_name release];
[_messenger release];
[_codec 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 = [_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:messageHandler];
}
- (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 new];
@implementation FlutterMethodChannel {
NSObject<FlutterBinaryMessenger>* _messenger;
NSString* _name;
NSObject<FlutterMethodCodec>* _codec;
FlutterBinaryMessengerConnection _connection;
}
+ (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 = [super init];
NSAssert(self, @"Super init cannot be nil");
_name = [name retain];
_messenger = [messenger retain];
_codec = [codec retain];
return self;
}
- (void)dealloc {
[_name release];
[_messenger release];
[_codec 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 = [_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:messageHandler];
}
- (void)resizeChannelBuffer:(NSInteger)newSize {
ResizeChannelBuffer(_messenger, _name, newSize);
}
@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)dealloc {
[_name release];
[_codec release];
[_messenger release];
[super dealloc];
}
static void SetStreamHandlerMessageHandlerOnChannel(NSObject<FlutterStreamHandler>* handler,
NSString* name,
NSObject<FlutterBinaryMessenger>* messenger,
NSObject<FlutterMethodCodec>* codec) {
__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);
}
};
[messenger setMessageHandlerOnChannel:name binaryMessageHandler:messageHandler];
}
- (void)setStreamHandler:(NSObject<FlutterStreamHandler>*)handler {
if (!handler) {
[_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil];
return;
}
SetStreamHandlerMessageHandlerOnChannel(handler, _name, _messenger, _codec);
}
@end