blob: 4439b03c1df8eb74de3d57b2aa8309611fb84685 [file] [log] [blame] [edit]
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:ffi';
import 'c_bindings_generated.dart' as c;
import 'internal.dart' show ObjCBlockBase;
import 'objective_c_bindings_generated.dart' as objc;
/// Helper class for building Objective C objects that implement protocols.
class ObjCProtocolBuilder {
final _builder = objc.DartProxyBuilder.new1();
/// Add a method implementation to the protocol.
///
/// It is not recommended to call this method directly. Instead, use the
/// implement methods on [ObjCProtocolMethod] and its subclasses.
void implementMethod(Pointer<c.ObjCSelector> sel,
objc.NSMethodSignature signature, ObjCBlockBase block) =>
_builder.implementMethod_withSignature_andBlock_(
sel, signature, block.pointer.cast());
/// Builds the object.
///
/// This can be called multiple times to construct multiple object instances
/// that all implement the same protocol methods using the same functions.
objc.DartProxy build() => objc.DartProxy.newFromBuilder_(_builder);
}
/// A method in an ObjC protocol.
///
/// Do not try to construct this class directly. The recommended way of getting
/// a method object is to use ffigen to generate bindings for the protocol you
/// want to implement. The generated bindings will include a
/// [ObjCProtocolMethod] for each method of the protocol.
class ObjCProtocolMethod<T extends Function> {
final Pointer<c.ObjCSelector> _sel;
final objc.NSMethodSignature _signature;
final ObjCBlockBase Function(T) _createBlock;
/// Only for use by ffigen bindings.
ObjCProtocolMethod(this._sel, this._signature, this._createBlock);
/// Implement this method on the protocol [builder] using a Dart [function].
///
/// The implemented method must be invoked by ObjC code running on the same
/// thread as the isolate that called implementMethod. Invoking the method on
/// the wrong thread will result in a crash.
void implement(ObjCProtocolBuilder builder, T? function) {
if (function != null) {
builder.implementMethod(_sel, _signature, _createBlock(function));
}
}
}
/// A method in an ObjC protocol that can be implemented as a listener.
///
/// Do not try to construct this class directly. The recommended way of getting
/// a method object is to use ffigen to generate bindings for the protocol you
/// want to implement. The generated bindings will include a
/// [ObjCProtocolMethod] for each method of the protocol.
class ObjCProtocolListenableMethod<T extends Function>
extends ObjCProtocolMethod<T> {
final ObjCBlockBase Function(T) _createListenerBlock;
/// Only for use by ffigen bindings.
ObjCProtocolListenableMethod(super._sel, super._signature, super._createBlock,
this._createListenerBlock);
/// Implement this method on the protocol [builder] as a listener using a Dart
/// [function].
///
/// This is based on FFI's NativeCallable.listener, and has the same
/// capabilities and limitations. This method can be invoked by ObjC from any
/// thread, but only supports void functions, and is not run synchronously.
/// See NativeCallable.listener for more details.
void implementAsListener(ObjCProtocolBuilder builder, T? function) {
if (function != null) {
builder.implementMethod(_sel, _signature, _createListenerBlock(function));
}
}
}