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/flutter/blob/230240c56880f2c19bf92d2c32203b064054f173/engine/src/flutter/lib/ui/channel_buffers.dart#L523
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(
57  [messenger
58  respondsToSelector:@selector(
59  setMessageHandlerOnChannel:binaryMessageHandler:taskQueue:)],
60  @"");
61  return [messenger setMessageHandlerOnChannel:name
62  binaryMessageHandler:handler
63  taskQueue:taskQueue];
64  } else {
65  return [messenger setMessageHandlerOnChannel:name binaryMessageHandler:handler];
66  }
67 }
68 
69 ////////////////////////////////////////////////////////////////////////////////
70 @implementation FlutterBasicMessageChannel {
71  NSObject<FlutterBinaryMessenger>* _messenger;
72  NSString* _name;
73  NSObject<FlutterMessageCodec>* _codec;
75  NSObject<FlutterTaskQueue>* _taskQueue;
76 }
77 + (instancetype)messageChannelWithName:(NSString*)name
78  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
79  NSObject<FlutterMessageCodec>* codec = [FlutterStandardMessageCodec sharedInstance];
81  binaryMessenger:messenger
82  codec:codec];
83 }
84 + (instancetype)messageChannelWithName:(NSString*)name
85  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
86  codec:(NSObject<FlutterMessageCodec>*)codec {
87  return [[FlutterBasicMessageChannel alloc] initWithName:name
88  binaryMessenger:messenger
89  codec:codec];
90 }
91 
92 - (instancetype)initWithName:(NSString*)name
93  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
94  codec:(NSObject<FlutterMessageCodec>*)codec {
95  self = [self initWithName:name binaryMessenger:messenger codec:codec taskQueue:nil];
96  return self;
97 }
98 
99 - (instancetype)initWithName:(NSString*)name
100  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
101  codec:(NSObject<FlutterMessageCodec>*)codec
102  taskQueue:(NSObject<FlutterTaskQueue>*)taskQueue {
103  self = [super init];
104  NSAssert(self, @"Super init cannot be nil");
105  _name = [name copy];
106  _messenger = messenger;
107  _codec = codec;
108  _taskQueue = taskQueue;
109  return self;
110 }
111 
112 - (void)sendMessage:(id)message {
113  [_messenger sendOnChannel:_name message:[_codec encode:message]];
114 }
115 
116 - (void)sendMessage:(id)message reply:(FlutterReply)callback {
117  FlutterBinaryReply reply = ^(NSData* data) {
118  if (callback) {
119  callback([_codec decode:data]);
120  }
121  };
122  [_messenger sendOnChannel:_name message:[_codec encode:message] binaryReply:reply];
123 }
124 
125 - (void)setMessageHandler:(FlutterMessageHandler)handler {
126  if (!handler) {
127  if (_connection > 0) {
128  [_messenger cleanUpConnection:_connection];
129  _connection = 0;
130  } else {
131  [_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil];
132  }
133  return;
134  }
135 
136  // Grab reference to avoid retain on self.
137  // `self` might be released before the block, so the block needs to retain the codec to
138  // make sure it is not released with `self`
139  NSObject<FlutterMessageCodec>* codec = _codec;
140  FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
141  handler([codec decode:message], ^(id reply) {
142  callback([codec encode:reply]);
143  });
144  };
145  _connection = SetMessageHandler(_messenger, _name, messageHandler, _taskQueue);
146 }
147 
148 + (void)resizeChannelWithName:(NSString*)name
149  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
150  size:(NSInteger)newSize {
151  ResizeChannelBuffer(messenger, name, newSize);
152 }
153 
154 - (void)resizeChannelBuffer:(NSInteger)newSize {
155  ResizeChannelBuffer(_messenger, _name, newSize);
156 }
157 
158 + (void)setWarnsOnOverflow:(BOOL)warns
159  forChannelWithName:(NSString*)name
160  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
161  SetWarnsOnOverflow(messenger, name, warns);
162 }
163 
164 - (void)setWarnsOnOverflow:(BOOL)warns {
165  SetWarnsOnOverflow(_messenger, _name, warns);
166 }
167 
168 @end
169 
170 #pragma mark - Method channel
171 
172 ////////////////////////////////////////////////////////////////////////////////
173 @implementation FlutterError
174 + (instancetype)errorWithCode:(NSString*)code message:(NSString*)message details:(id)details {
175  return [[FlutterError alloc] initWithCode:code message:message details:details];
176 }
177 
178 - (instancetype)initWithCode:(NSString*)code message:(NSString*)message details:(id)details {
179  NSAssert(code, @"Code cannot be nil");
180  self = [super init];
181  NSAssert(self, @"Super init cannot be nil");
182  _code = [code copy];
183  _message = [message copy];
184  _details = details;
185  return self;
186 }
187 
188 - (BOOL)isEqual:(id)object {
189  if (self == object) {
190  return YES;
191  }
192  if (![object isKindOfClass:[FlutterError class]]) {
193  return NO;
194  }
195  FlutterError* other = (FlutterError*)object;
196  return [self.code isEqual:other.code] &&
197  ((!self.message && !other.message) || [self.message isEqual:other.message]) &&
198  ((!self.details && !other.details) || [self.details isEqual:other.details]);
199 }
200 
201 - (NSUInteger)hash {
202  return [self.code hash] ^ [self.message hash] ^ [self.details hash];
203 }
204 @end
205 
206 ////////////////////////////////////////////////////////////////////////////////
207 @implementation FlutterMethodCall
208 + (instancetype)methodCallWithMethodName:(NSString*)method arguments:(id)arguments {
209  return [[FlutterMethodCall alloc] initWithMethodName:method arguments:arguments];
210 }
211 
212 - (instancetype)initWithMethodName:(NSString*)method arguments:(id)arguments {
213  NSAssert(method, @"Method name cannot be nil");
214  self = [super init];
215  NSAssert(self, @"Super init cannot be nil");
216  _method = [method copy];
217  _arguments = arguments;
218  return self;
219 }
220 
221 - (BOOL)isEqual:(id)object {
222  if (self == object) {
223  return YES;
224  }
225  if (![object isKindOfClass:[FlutterMethodCall class]]) {
226  return NO;
227  }
228  FlutterMethodCall* other = (FlutterMethodCall*)object;
229  return [self.method isEqual:[other method]] &&
230  ((!self.arguments && !other.arguments) || [self.arguments isEqual:other.arguments]);
231 }
232 
233 - (NSUInteger)hash {
234  return [self.method hash] ^ [self.arguments hash];
235 }
236 @end
237 
238 NSObject const* FlutterMethodNotImplemented = [[NSObject alloc] init];
239 
240 ////////////////////////////////////////////////////////////////////////////////
241 @implementation FlutterMethodChannel {
242  NSObject<FlutterBinaryMessenger>* _messenger;
243  NSString* _name;
244  NSObject<FlutterMethodCodec>* _codec;
246  NSObject<FlutterTaskQueue>* _taskQueue;
247 }
248 
249 + (instancetype)methodChannelWithName:(NSString*)name
250  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
251  NSObject<FlutterMethodCodec>* codec = [FlutterStandardMethodCodec sharedInstance];
252  return [FlutterMethodChannel methodChannelWithName:name binaryMessenger:messenger codec:codec];
253 }
254 
255 + (instancetype)methodChannelWithName:(NSString*)name
256  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
257  codec:(NSObject<FlutterMethodCodec>*)codec {
258  return [[FlutterMethodChannel alloc] initWithName:name binaryMessenger:messenger codec:codec];
259 }
260 
261 - (instancetype)initWithName:(NSString*)name
262  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
263  codec:(NSObject<FlutterMethodCodec>*)codec {
264  self = [self initWithName:name binaryMessenger:messenger codec:codec taskQueue:nil];
265  return self;
266 }
267 - (instancetype)initWithName:(NSString*)name
268  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
269  codec:(NSObject<FlutterMethodCodec>*)codec
270  taskQueue:(NSObject<FlutterTaskQueue>*)taskQueue {
271  self = [super init];
272  NSAssert(self, @"Super init cannot be nil");
273  _name = [name copy];
274  _messenger = messenger;
275  _codec = codec;
276  _taskQueue = taskQueue;
277  return self;
278 }
279 
280 - (void)invokeMethod:(NSString*)method arguments:(id)arguments {
282  arguments:arguments];
283  NSData* message = [_codec encodeMethodCall:methodCall];
284  [_messenger sendOnChannel:_name message:message];
285 }
286 
287 - (void)invokeMethod:(NSString*)method arguments:(id)arguments result:(FlutterResult)callback {
289  arguments:arguments];
290  NSData* message = [_codec encodeMethodCall:methodCall];
291  FlutterBinaryReply reply = ^(NSData* data) {
292  if (callback) {
293  callback((data == nil) ? FlutterMethodNotImplemented : [_codec decodeEnvelope:data]);
294  }
295  };
296  [_messenger sendOnChannel:_name message:message binaryReply:reply];
297 }
298 
299 - (void)setMethodCallHandler:(FlutterMethodCallHandler)handler {
300  if (!handler) {
301  if (_connection > 0) {
302  [_messenger cleanUpConnection:_connection];
303  _connection = 0;
304  } else {
305  [_messenger setMessageHandlerOnChannel:_name binaryMessageHandler:nil];
306  }
307  return;
308  }
309  // Make sure the block captures the codec, not self.
310  // `self` might be released before the block, so the block needs to retain the codec to
311  // make sure it is not released with `self`
312  NSObject<FlutterMethodCodec>* codec = _codec;
313  FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
314  FlutterMethodCall* call = [codec decodeMethodCall:message];
315  handler(call, ^(id result) {
316  if (result == FlutterMethodNotImplemented) {
317  callback(nil);
318  } else if ([result isKindOfClass:[FlutterError class]]) {
319  callback([codec encodeErrorEnvelope:(FlutterError*)result]);
320  } else {
321  callback([codec encodeSuccessEnvelope:result]);
322  }
323  });
324  };
325  _connection = SetMessageHandler(_messenger, _name, messageHandler, _taskQueue);
326 }
327 
328 - (void)resizeChannelBuffer:(NSInteger)newSize {
329  ResizeChannelBuffer(_messenger, _name, newSize);
330 }
331 
332 @end
333 
334 #pragma mark - Event channel
335 
336 NSObject const* FlutterEndOfEventStream = [[NSObject alloc] init];
337 
338 ////////////////////////////////////////////////////////////////////////////////
339 @implementation FlutterEventChannel {
340  NSObject<FlutterBinaryMessenger>* _messenger;
341  NSString* _name;
342  NSObject<FlutterMethodCodec>* _codec;
343  NSObject<FlutterTaskQueue>* _taskQueue;
345 }
346 + (instancetype)eventChannelWithName:(NSString*)name
347  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
348  NSObject<FlutterMethodCodec>* codec = [FlutterStandardMethodCodec sharedInstance];
349  return [FlutterEventChannel eventChannelWithName:name binaryMessenger:messenger codec:codec];
350 }
351 
352 + (instancetype)eventChannelWithName:(NSString*)name
353  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
354  codec:(NSObject<FlutterMethodCodec>*)codec {
355  return [[FlutterEventChannel alloc] initWithName:name binaryMessenger:messenger codec:codec];
356 }
357 
358 - (instancetype)initWithName:(NSString*)name
359  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
360  codec:(NSObject<FlutterMethodCodec>*)codec {
361  return [self initWithName:name binaryMessenger:messenger codec:codec taskQueue:nil];
362 }
363 
364 - (instancetype)initWithName:(NSString*)name
365  binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger
366  codec:(NSObject<FlutterMethodCodec>*)codec
367  taskQueue:(NSObject<FlutterTaskQueue>* _Nullable)taskQueue {
368  self = [super init];
369  NSAssert(self, @"Super init cannot be nil");
370  _name = [name copy];
371  _messenger = messenger;
372  _codec = codec;
373  _taskQueue = taskQueue;
374  return self;
375 }
376 
377 static FlutterBinaryMessengerConnection SetStreamHandlerMessageHandlerOnChannel(
378  NSObject<FlutterStreamHandler>* handler,
379  NSString* name,
380  NSObject<FlutterBinaryMessenger>* messenger,
381  NSObject<FlutterMethodCodec>* codec,
382  NSObject<FlutterTaskQueue>* taskQueue) {
383  __block FlutterEventSink currentSink = nil;
384  FlutterBinaryMessageHandler messageHandler = ^(NSData* message, FlutterBinaryReply callback) {
385  FlutterMethodCall* call = [codec decodeMethodCall:message];
386  if ([call.method isEqual:@"listen"]) {
387  if (currentSink) {
388  FlutterError* error = [handler onCancelWithArguments:nil];
389  if (error) {
390  NSLog(@"Failed to cancel existing stream: %@. %@ (%@)", error.code, error.message,
391  error.details);
392  }
393  }
394  currentSink = ^(id event) {
395  if (event == FlutterEndOfEventStream) {
396  [messenger sendOnChannel:name message:nil];
397  } else if ([event isKindOfClass:[FlutterError class]]) {
398  [messenger sendOnChannel:name message:[codec encodeErrorEnvelope:(FlutterError*)event]];
399  } else {
400  [messenger sendOnChannel:name message:[codec encodeSuccessEnvelope:event]];
401  }
402  };
403  FlutterError* error = [handler onListenWithArguments:call.arguments eventSink:currentSink];
404  if (error) {
405  callback([codec encodeErrorEnvelope:error]);
406  } else {
407  callback([codec encodeSuccessEnvelope:nil]);
408  }
409  } else if ([call.method isEqual:@"cancel"]) {
410  if (!currentSink) {
411  callback(
412  [codec encodeErrorEnvelope:[FlutterError errorWithCode:@"error"
413  message:@"No active stream to cancel"
414  details:nil]]);
415  return;
416  }
417  currentSink = nil;
418  FlutterError* error = [handler onCancelWithArguments:call.arguments];
419  if (error) {
420  callback([codec encodeErrorEnvelope:error]);
421  } else {
422  callback([codec encodeSuccessEnvelope:nil]);
423  }
424  } else {
425  callback(nil);
426  }
427  };
428  return SetMessageHandler(messenger, name, messageHandler, taskQueue);
429 }
430 
431 - (void)setStreamHandler:(NSObject<FlutterStreamHandler>*)handler {
432  if (!handler) {
433  [_messenger cleanUpConnection:_connection];
434  _connection = 0;
435  return;
436  }
437  _connection =
438  SetStreamHandlerMessageHandlerOnChannel(handler, _name, _messenger, _codec, _taskQueue);
439 }
440 @end
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterBinaryReply)(NSData *_Nullable reply)
void(^ FlutterBinaryMessageHandler)(NSData *_Nullable message, FlutterBinaryReply reply)
int64_t FlutterBinaryMessengerConnection
void(^ FlutterResult)(id _Nullable result)
void(^ FlutterMethodCallHandler)(FlutterMethodCall *call, FlutterResult result)
FLUTTER_DARWIN_EXPORT NSObject const * FlutterMethodNotImplemented
void(^ FlutterMessageHandler)(id _Nullable message, FlutterReply callback)
FLUTTER_DARWIN_EXPORT NSObject const * FlutterEndOfEventStream
NS_ASSUME_NONNULL_BEGIN typedef void(^ FlutterReply)(id _Nullable reply)
void(^ FlutterEventSink)(id _Nullable event)
static NSString *const kOverflowMethod
FlutterBinaryMessengerConnection _connection
NSString * _name
static void SetWarnsOnOverflow(NSObject< FlutterBinaryMessenger > *binaryMessenger, NSString *channel, BOOL warns)
static FlutterBinaryMessengerConnection SetMessageHandler(NSObject< FlutterBinaryMessenger > *messenger, NSString *name, FlutterBinaryMessageHandler handler, NSObject< FlutterTaskQueue > *taskQueue)
NSObject< FlutterMessageCodec > * _codec
NSObject< FlutterTaskQueue > * _taskQueue
static void ResizeChannelBuffer(NSObject< FlutterBinaryMessenger > *binaryMessenger, NSString *channel, NSInteger newSize)
static NSString *const kResizeMethod
static FLUTTER_ASSERT_ARC NSString *const kFlutterChannelBuffersChannel
#define FLUTTER_ASSERT_ARC
Definition: FlutterMacros.h:44
instancetype messageChannelWithName:binaryMessenger:codec:(NSString *name,[binaryMessenger] NSObject< FlutterBinaryMessenger > *messenger,[codec] NSObject< FlutterMessageCodec > *codec)
NSString * message
NSString * code
instancetype eventChannelWithName:binaryMessenger:codec:(NSString *name,[binaryMessenger] NSObject< FlutterBinaryMessenger > *messenger,[codec] NSObject< FlutterMethodCodec > *codec)
instancetype methodCallWithMethodName:arguments:(NSString *method,[arguments] id _Nullable arguments)
instancetype methodChannelWithName:binaryMessenger:codec:(NSString *name,[binaryMessenger] NSObject< FlutterBinaryMessenger > *messenger,[codec] NSObject< FlutterMethodCodec > *codec)
instancetype sharedInstance()