Flutter macOS Embedder
FlutterChannels.mm
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
6 
8 
9 #pragma mark - Basic message channel
10 
11 static NSString* const kFlutterChannelBuffersChannel = @"dev.flutter/channel-buffers";
12 static NSString* const kResizeMethod = @"resize";
13 static NSString* const kOverflowMethod = @"overflow";
14 
15 static void ResizeChannelBuffer(NSObject<FlutterBinaryMessenger>* binaryMessenger,
16  NSString* channel,
17  NSInteger newSize) {
18  NSCAssert(newSize >= 0, @"Channel buffer size must be non-negative");
19  // Cast newSize to int because the deserialization logic handles only 32 bits values,
20  // see
21  // https://github.com/flutter/engine/blob/93e8901490e78c7ba7e319cce4470d9c6478c6dc/lib/ui/channel_buffers.dart#L495.
22  NSArray* args = @[ channel, @(static_cast<int>(newSize)) ];
23  FlutterMethodCall* resizeMethodCall = [FlutterMethodCall methodCallWithMethodName:kResizeMethod
24  arguments:args];
25  NSObject<FlutterMethodCodec>* codec = [FlutterStandardMethodCodec sharedInstance];
26  NSData* message = [codec encodeMethodCall:resizeMethodCall];
27  [binaryMessenger sendOnChannel:kFlutterChannelBuffersChannel message:message];
28 }
29 
30 /**
31  * Defines whether a channel should show warning messages when discarding messages
32  * due to overflow.
33  *
34  * @param binaryMessenger The binary messenger.
35  * @param channel The channel name.
36  * @param warns When false, the channel is expected to overflow and warning messages
37  * will not be shown.
38  */
39 static void SetWarnsOnOverflow(NSObject<FlutterBinaryMessenger>* binaryMessenger,
40  NSString* channel,
41  BOOL warns) {
42  FlutterMethodCall* overflowMethodCall =
43  [FlutterMethodCall methodCallWithMethodName:kOverflowMethod
44  arguments:@[ channel, @(!warns) ]];
45  NSObject<FlutterMethodCodec>* codec = [FlutterStandardMethodCodec sharedInstance];
46  NSData* message = [codec encodeMethodCall:overflowMethodCall];
47  [binaryMessenger sendOnChannel:kFlutterChannelBuffersChannel message:message];
48 }
49 
51  NSObject<FlutterBinaryMessenger>* messenger,
52  NSString* name,
54  NSObject<FlutterTaskQueue>* taskQueue) {
55  if (taskQueue) {
56  NSCAssert([messenger respondsToSelector:@selector(setMessageHandlerOnChannel:
57  binaryMessageHandler:taskQueue:)],
58  @"");
59  return [messenger setMessageHandlerOnChannel:name
60  binaryMessageHandler:handler
61  taskQueue:taskQueue];
62  } else {
63  return [messenger setMessageHandlerOnChannel:name binaryMessageHandler:handler];
64  }
65 }
66 
67 ////////////////////////////////////////////////////////////////////////////////
68 @implementation FlutterBasicMessageChannel {
69  NSObject<FlutterBinaryMessenger>* _messenger;
70  NSString* _name;
71  NSObject<FlutterMessageCodec>* _codec;
73  NSObject<FlutterTaskQueue>* _taskQueue;
74 }
75 + (instancetype)messageChannelWithName:(NSString*)name
76  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
77  NSObject<FlutterMessageCodec>* codec = [FlutterStandardMessageCodec sharedInstance];
79  binaryMessenger:messenger
80  codec:codec];
81 }
82 + (instancetype)messageChannelWithName:(NSString*)name
83  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
84  codec:(NSObject<FlutterMessageCodec>*)codec {
85  return [[FlutterBasicMessageChannel alloc] initWithName:name
86  binaryMessenger:messenger
87  codec:codec];
88 }
89 
90 - (instancetype)initWithName:(NSString*)name
91  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
92  codec:(NSObject<FlutterMessageCodec>*)codec {
93  self = [self initWithName:name binaryMessenger:messenger codec:codec taskQueue:nil];
94  return self;
95 }
96 
97 - (instancetype)initWithName:(NSString*)name
98  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
99  codec:(NSObject<FlutterMessageCodec>*)codec
100  taskQueue:(NSObject<FlutterTaskQueue>*)taskQueue {
101  self = [super init];
102  NSAssert(self, @"Super init cannot be nil");
103  _name = [name copy];
104  _messenger = messenger;
105  _codec = codec;
106  _taskQueue = taskQueue;
107  return self;
108 }
109 
110 - (void)sendMessage:(id)message {
111  [_messenger sendOnChannel:_name message:[_codec encode:message]];
112 }
113 
114 - (void)sendMessage:(id)message reply:(FlutterReply)callback {
115  FlutterBinaryReply reply = ^(NSData* data) {
116  if (callback) {
117  callback([_codec decode:data]);
118  }
119  };
120  [_messenger sendOnChannel:_name message:[_codec encode:message] binaryReply:reply];
121 }
122 
123 - (void)setMessageHandler:(FlutterMessageHandler)handler {
124  if (!handler) {
125  if (_connection > 0) {
126  [_messenger cleanUpConnection:_connection];
127  _connection = 0;
128  } else {
129  [_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil];
130  }
131  return;
132  }
133 
134  // Grab reference to avoid retain on self.
135  // `self` might be released before the block, so the block needs to retain the codec to
136  // make sure it is not released with `self`
137  NSObject<FlutterMessageCodec>* codec = _codec;
138  FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
139  handler([codec decode:message], ^(id reply) {
140  callback([codec encode:reply]);
141  });
142  };
143  _connection = SetMessageHandler(_messenger, _name, messageHandler, _taskQueue);
144 }
145 
146 + (void)resizeChannelWithName:(NSString*)name
147  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
148  size:(NSInteger)newSize {
149  ResizeChannelBuffer(messenger, name, newSize);
150 }
151 
152 - (void)resizeChannelBuffer:(NSInteger)newSize {
153  ResizeChannelBuffer(_messenger, _name, newSize);
154 }
155 
156 + (void)setWarnsOnOverflow:(BOOL)warns
157  forChannelWithName:(NSString*)name
158  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
159  SetWarnsOnOverflow(messenger, name, warns);
160 }
161 
162 - (void)setWarnsOnOverflow:(BOOL)warns {
163  SetWarnsOnOverflow(_messenger, _name, warns);
164 }
165 
166 @end
167 
168 #pragma mark - Method channel
169 
170 ////////////////////////////////////////////////////////////////////////////////
171 @implementation FlutterError
172 + (instancetype)errorWithCode:(NSString*)code message:(NSString*)message details:(id)details {
173  return [[FlutterError alloc] initWithCode:code message:message details:details];
174 }
175 
176 - (instancetype)initWithCode:(NSString*)code message:(NSString*)message details:(id)details {
177  NSAssert(code, @"Code cannot be nil");
178  self = [super init];
179  NSAssert(self, @"Super init cannot be nil");
180  _code = [code copy];
181  _message = [message copy];
182  _details = details;
183  return self;
184 }
185 
186 - (BOOL)isEqual:(id)object {
187  if (self == object) {
188  return YES;
189  }
190  if (![object isKindOfClass:[FlutterError class]]) {
191  return NO;
192  }
193  FlutterError* other = (FlutterError*)object;
194  return [self.code isEqual:other.code] &&
195  ((!self.message && !other.message) || [self.message isEqual:other.message]) &&
196  ((!self.details && !other.details) || [self.details isEqual:other.details]);
197 }
198 
199 - (NSUInteger)hash {
200  return [self.code hash] ^ [self.message hash] ^ [self.details hash];
201 }
202 @end
203 
204 ////////////////////////////////////////////////////////////////////////////////
205 @implementation FlutterMethodCall
206 + (instancetype)methodCallWithMethodName:(NSString*)method arguments:(id)arguments {
207  return [[FlutterMethodCall alloc] initWithMethodName:method arguments:arguments];
208 }
209 
210 - (instancetype)initWithMethodName:(NSString*)method arguments:(id)arguments {
211  NSAssert(method, @"Method name cannot be nil");
212  self = [super init];
213  NSAssert(self, @"Super init cannot be nil");
214  _method = [method copy];
215  _arguments = arguments;
216  return self;
217 }
218 
219 - (BOOL)isEqual:(id)object {
220  if (self == object) {
221  return YES;
222  }
223  if (![object isKindOfClass:[FlutterMethodCall class]]) {
224  return NO;
225  }
226  FlutterMethodCall* other = (FlutterMethodCall*)object;
227  return [self.method isEqual:[other method]] &&
228  ((!self.arguments && !other.arguments) || [self.arguments isEqual:other.arguments]);
229 }
230 
231 - (NSUInteger)hash {
232  return [self.method hash] ^ [self.arguments hash];
233 }
234 @end
235 
236 NSObject const* FlutterMethodNotImplemented = [[NSObject alloc] init];
237 
238 ////////////////////////////////////////////////////////////////////////////////
239 @implementation FlutterMethodChannel {
240  NSObject<FlutterBinaryMessenger>* _messenger;
241  NSString* _name;
242  NSObject<FlutterMethodCodec>* _codec;
244  NSObject<FlutterTaskQueue>* _taskQueue;
245 }
246 
247 + (instancetype)methodChannelWithName:(NSString*)name
248  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
249  NSObject<FlutterMethodCodec>* codec = [FlutterStandardMethodCodec sharedInstance];
250  return [FlutterMethodChannel methodChannelWithName:name binaryMessenger:messenger codec:codec];
251 }
252 
253 + (instancetype)methodChannelWithName:(NSString*)name
254  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
255  codec:(NSObject<FlutterMethodCodec>*)codec {
256  return [[FlutterMethodChannel alloc] initWithName:name binaryMessenger:messenger codec:codec];
257 }
258 
259 - (instancetype)initWithName:(NSString*)name
260  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
261  codec:(NSObject<FlutterMethodCodec>*)codec {
262  self = [self initWithName:name binaryMessenger:messenger codec:codec taskQueue:nil];
263  return self;
264 }
265 - (instancetype)initWithName:(NSString*)name
266  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
267  codec:(NSObject<FlutterMethodCodec>*)codec
268  taskQueue:(NSObject<FlutterTaskQueue>*)taskQueue {
269  self = [super init];
270  NSAssert(self, @"Super init cannot be nil");
271  _name = [name copy];
272  _messenger = messenger;
273  _codec = codec;
274  _taskQueue = taskQueue;
275  return self;
276 }
277 
278 - (void)invokeMethod:(NSString*)method arguments:(id)arguments {
280  arguments:arguments];
281  NSData* message = [_codec encodeMethodCall:methodCall];
282  [_messenger sendOnChannel:_name message:message];
283 }
284 
285 - (void)invokeMethod:(NSString*)method arguments:(id)arguments result:(FlutterResult)callback {
287  arguments:arguments];
288  NSData* message = [_codec encodeMethodCall:methodCall];
289  FlutterBinaryReply reply = ^(NSData* data) {
290  if (callback) {
291  callback((data == nil) ? FlutterMethodNotImplemented : [_codec decodeEnvelope:data]);
292  }
293  };
294  [_messenger sendOnChannel:_name message:message binaryReply:reply];
295 }
296 
297 - (void)setMethodCallHandler:(FlutterMethodCallHandler)handler {
298  if (!handler) {
299  if (_connection > 0) {
300  [_messenger cleanUpConnection:_connection];
301  _connection = 0;
302  } else {
303  [_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil];
304  }
305  return;
306  }
307  // Make sure the block captures the codec, not self.
308  // `self` might be released before the block, so the block needs to retain the codec to
309  // make sure it is not released with `self`
310  NSObject<FlutterMethodCodec>* codec = _codec;
311  FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
312  FlutterMethodCall* call = [codec decodeMethodCall:message];
313  handler(call, ^(id result) {
314  if (result == FlutterMethodNotImplemented) {
315  callback(nil);
316  } else if ([result isKindOfClass:[FlutterError class]]) {
317  callback([codec encodeErrorEnvelope:(FlutterError*)result]);
318  } else {
319  callback([codec encodeSuccessEnvelope:result]);
320  }
321  });
322  };
323  _connection = SetMessageHandler(_messenger, _name, messageHandler, _taskQueue);
324 }
325 
326 - (void)resizeChannelBuffer:(NSInteger)newSize {
327  ResizeChannelBuffer(_messenger, _name, newSize);
328 }
329 
330 @end
331 
332 #pragma mark - Event channel
333 
334 NSObject const* FlutterEndOfEventStream = [[NSObject alloc] init];
335 
336 ////////////////////////////////////////////////////////////////////////////////
337 @implementation FlutterEventChannel {
338  NSObject<FlutterBinaryMessenger>* _messenger;
339  NSString* _name;
340  NSObject<FlutterMethodCodec>* _codec;
341  NSObject<FlutterTaskQueue>* _taskQueue;
343 }
344 + (instancetype)eventChannelWithName:(NSString*)name
345  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
346  NSObject<FlutterMethodCodec>* codec = [FlutterStandardMethodCodec sharedInstance];
347  return [FlutterEventChannel eventChannelWithName:name binaryMessenger:messenger codec:codec];
348 }
349 
350 + (instancetype)eventChannelWithName:(NSString*)name
351  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
352  codec:(NSObject<FlutterMethodCodec>*)codec {
353  return [[FlutterEventChannel alloc] initWithName:name binaryMessenger:messenger codec:codec];
354 }
355 
356 - (instancetype)initWithName:(NSString*)name
357  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
358  codec:(NSObject<FlutterMethodCodec>*)codec {
359  return [self initWithName:name binaryMessenger:messenger codec:codec taskQueue:nil];
360 }
361 
362 - (instancetype)initWithName:(NSString*)name
363  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
364  codec:(NSObject<FlutterMethodCodec>*)codec
365  taskQueue:(NSObject<FlutterTaskQueue>* _Nullable)taskQueue {
366  self = [super init];
367  NSAssert(self, @"Super init cannot be nil");
368  _name = [name copy];
369  _messenger = messenger;
370  _codec = codec;
371  _taskQueue = taskQueue;
372  return self;
373 }
374 
375 static FlutterBinaryMessengerConnection SetStreamHandlerMessageHandlerOnChannel(
376  NSObject<FlutterStreamHandler>* handler,
377  NSString* name,
378  NSObject<FlutterBinaryMessenger>* messenger,
379  NSObject<FlutterMethodCodec>* codec,
380  NSObject<FlutterTaskQueue>* taskQueue) {
381  __block FlutterEventSink currentSink = nil;
382  FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
383  FlutterMethodCall* call = [codec decodeMethodCall:message];
384  if ([call.method isEqual:@"listen"]) {
385  if (currentSink) {
386  FlutterError* error = [handler onCancelWithArguments:nil];
387  if (error) {
388  NSLog(@"Failed to cancel existing stream: %@. %@ (%@)", error.code, error.message,
389  error.details);
390  }
391  }
392  currentSink = ^(id event) {
393  if (event == FlutterEndOfEventStream) {
394  [messenger sendOnChannel:name message:nil];
395  } else if ([event isKindOfClass:[FlutterError class]]) {
396  [messenger sendOnChannel:name message:[codec encodeErrorEnvelope:(FlutterError*)event]];
397  } else {
398  [messenger sendOnChannel:name message:[codec encodeSuccessEnvelope:event]];
399  }
400  };
401  FlutterError* error = [handler onListenWithArguments:call.arguments eventSink:currentSink];
402  if (error) {
403  callback([codec encodeErrorEnvelope:error]);
404  } else {
405  callback([codec encodeSuccessEnvelope:nil]);
406  }
407  } else if ([call.method isEqual:@"cancel"]) {
408  if (!currentSink) {
409  callback(
410  [codec encodeErrorEnvelope:[FlutterError errorWithCode:@"error"
411  message:@"No active stream to cancel"
412  details:nil]]);
413  return;
414  }
415  currentSink = nil;
416  FlutterError* error = [handler onCancelWithArguments:call.arguments];
417  if (error) {
418  callback([codec encodeErrorEnvelope:error]);
419  } else {
420  callback([codec encodeSuccessEnvelope:nil]);
421  }
422  } else {
423  callback(nil);
424  }
425  };
426  return SetMessageHandler(messenger, name, messageHandler, taskQueue);
427 }
428 
429 - (void)setStreamHandler:(NSObject<FlutterStreamHandler>*)handler {
430  if (!handler) {
431  [_messenger cleanUpConnection:_connection];
432  _connection = 0;
433  return;
434  }
435  _connection =
436  SetStreamHandlerMessageHandlerOnChannel(handler, _name, _messenger, _codec, _taskQueue);
437 }
438 @end
+[FlutterBasicMessageChannel messageChannelWithName:binaryMessenger:codec:]
instancetype messageChannelWithName:binaryMessenger:codec:(NSString *name,[binaryMessenger] NSObject< FlutterBinaryMessenger > *messenger,[codec] NSObject< FlutterMessageCodec > *codec)
Definition: FlutterChannels.mm:82
FLUTTER_ASSERT_ARC
#define FLUTTER_ASSERT_ARC
Definition: FlutterMacros.h:44
+[FlutterMethodCall methodCallWithMethodName:arguments:]
instancetype methodCallWithMethodName:arguments:(NSString *method,[arguments] id _Nullable arguments)
FlutterBasicMessageChannel
Definition: FlutterChannels.h:37
FlutterMethodChannel
Definition: FlutterChannels.h:220
FlutterMethodNotImplemented
FLUTTER_DARWIN_EXPORT NSObject const * FlutterMethodNotImplemented
kOverflowMethod
static NSString *const kOverflowMethod
Definition: FlutterChannels.mm:13
_codec
NSObject< FlutterMessageCodec > * _codec
Definition: FlutterChannels.mm:71
FlutterError
Definition: FlutterCodecs.h:246
FlutterChannels.h
FlutterMethodCall::method
NSString * method
Definition: FlutterCodecs.h:233
FlutterMethodCallHandler
void(^ FlutterMethodCallHandler)(FlutterMethodCall *call, FlutterResult result)
Definition: FlutterChannels.h:206
FlutterEventChannel
Definition: FlutterChannels.h:400
+[FlutterEventChannel eventChannelWithName:binaryMessenger:codec:]
instancetype eventChannelWithName:binaryMessenger:codec:(NSString *name,[binaryMessenger] NSObject< FlutterBinaryMessenger > *messenger,[codec] NSObject< FlutterMethodCodec > *codec)
FlutterError::details
id details
Definition: FlutterCodecs.h:270
kResizeMethod
static NSString *const kResizeMethod
Definition: FlutterChannels.mm:12
FlutterEndOfEventStream
FLUTTER_DARWIN_EXPORT NSObject const * FlutterEndOfEventStream
FlutterBinaryMessageHandler
void(^ FlutterBinaryMessageHandler)(NSData *_Nullable message, FlutterBinaryReply reply)
Definition: FlutterBinaryMessenger.h:30
FlutterStandardMessageCodec
Definition: FlutterCodecs.h:209
SetMessageHandler
static FlutterBinaryMessengerConnection SetMessageHandler(NSObject< FlutterBinaryMessenger > *messenger, NSString *name, FlutterBinaryMessageHandler handler, NSObject< FlutterTaskQueue > *taskQueue)
Definition: FlutterChannels.mm:50
FlutterMethodCall
Definition: FlutterCodecs.h:220
FlutterError::message
NSString * message
Definition: FlutterCodecs.h:265
FlutterTaskQueue-p
Definition: FlutterBinaryMessenger.h:34
FlutterResult
void(^ FlutterResult)(id _Nullable result)
Definition: FlutterChannels.h:194
+[FlutterMethodChannel methodChannelWithName:binaryMessenger:codec:]
instancetype methodChannelWithName:binaryMessenger:codec:(NSString *name,[binaryMessenger] NSObject< FlutterBinaryMessenger > *messenger,[codec] NSObject< FlutterMethodCodec > *codec)
FlutterError::code
NSString * code
Definition: FlutterCodecs.h:260
-[FlutterBasicMessageChannel initWithName:binaryMessenger:codec:taskQueue:]
instancetype initWithName:binaryMessenger:codec:taskQueue:(NSString *name,[binaryMessenger] NSObject< FlutterBinaryMessenger > *messenger,[codec] NSObject< FlutterMessageCodec > *codec,[taskQueue] NSObject< FlutterTaskQueue > *_Nullable taskQueue)
ResizeChannelBuffer
static void ResizeChannelBuffer(NSObject< FlutterBinaryMessenger > *binaryMessenger, NSString *channel, NSInteger newSize)
Definition: FlutterChannels.mm:15
FlutterReply
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterReply)(id _Nullable reply)
FlutterEventSink
void(^ FlutterEventSink)(id _Nullable event)
Definition: FlutterChannels.h:350
SetWarnsOnOverflow
static void SetWarnsOnOverflow(NSObject< FlutterBinaryMessenger > *binaryMessenger, NSString *channel, BOOL warns)
Definition: FlutterChannels.mm:39
_name
NSString * _name
Definition: FlutterChannels.mm:68
FlutterMessageCodec-p
Definition: FlutterCodecs.h:18
FlutterStreamHandler-p
Definition: FlutterChannels.h:356
FlutterMessageHandler
void(^ FlutterMessageHandler)(id _Nullable message, FlutterReply callback)
Definition: FlutterChannels.h:30
_taskQueue
NSObject< FlutterTaskQueue > * _taskQueue
Definition: FlutterChannels.mm:73
kFlutterChannelBuffersChannel
static FLUTTER_ASSERT_ARC NSString *const kFlutterChannelBuffersChannel
Definition: FlutterChannels.mm:11
FlutterBinaryMessenger-p
Definition: FlutterBinaryMessenger.h:49
_connection
FlutterBinaryMessengerConnection _connection
Definition: FlutterChannels.mm:72
FlutterStandardMethodCodec
Definition: FlutterCodecs.h:469
FlutterBinaryMessengerConnection
int64_t FlutterBinaryMessengerConnection
Definition: FlutterBinaryMessenger.h:32
FlutterBinaryReply
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterBinaryReply)(NSData *_Nullable reply)
FlutterMethodCall::arguments
id arguments
Definition: FlutterCodecs.h:238
+[FlutterMessageCodec-p sharedInstance]
instancetype sharedInstance()