diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index d93dd1bb1a3a..64d13c71938d 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,3 +1,7 @@ +## 22.6.2 + +* Updates `README.md` with examples of using `@ProxyApi`. + ## 22.6.1 * [gobject] Moves class declarations to the header to work around a bug in some diff --git a/packages/pigeon/README.md b/packages/pigeon/README.md index cc375f5e2faa..fc6eeadb67b1 100644 --- a/packages/pigeon/README.md +++ b/packages/pigeon/README.md @@ -151,6 +151,22 @@ but reversed. For more information look at the annotation `@FlutterApi()` which denotes APIs that live in Flutter but are invoked from the host platform. [Example](./example/README.md#FlutterApi_Example). +### Wrapping a Native API with ProxyApis + +**Supported Languages: Kotlin, Swift** + +Pigeon supports wrapping a native API with the `@ProxyApi` annotation. For each native class/type +this annotation generates + +* A Dart class that acts as a proxy to the native class/type. +* A native language API that handles creating native instances, methods calls from Dart to host, + and method calls from host to Dart. +* A Dart and native language `InstanceManager` that automatically handles garbage collection of the + native instance. +* A native language registrar for setting up all the native language APIs. + +See this [example](./example/README.md#Simple_ProxyApi_Example) for example usage. + ## Feedback File an issue in [flutter/flutter](https://github.com/flutter/flutter) with diff --git a/packages/pigeon/example/README.md b/packages/pigeon/example/README.md index bd5e4bd116af..51ce6eeb09db 100644 --- a/packages/pigeon/example/README.md +++ b/packages/pigeon/example/README.md @@ -284,7 +284,7 @@ private class PigeonFlutterApi { aString aStringArg: String?, completion: @escaping (Result) -> Void ) { flutterAPI.flutterMethod(aString: aStringArg) { - completion(.success($0)) + completion(.success(try! $0.get())) } } } @@ -303,7 +303,7 @@ private class PigeonFlutterApi { } fun callFlutterMethod(aString: String, callback: (Result) -> Unit) { - flutterApi!!.flutterMethod(aString) { echo -> callback(Result.success(echo)) } + flutterApi!!.flutterMethod(aString) { echo -> callback(Result.success(echo.getOrNull()!!)) } } } ``` @@ -351,6 +351,264 @@ pigeon_example_package_message_flutter_api_flutter_method( self->flutter_api, "hello", nullptr, flutter_method_cb, self); ``` +## Simple ProxyApi Example + +This example provides a simple overview of how to use Pigeon to wrap a native class. + +### Example Class + +Below are example native classes that will be wrapped by this example. Note that other languages +can be wrapped, but the generated code will only be in the languages shown below. + +#### Kotlin + + +```kotlin +package dev.flutter.pigeon_example_app + +open class SimpleExampleNativeClass(val aField: String, private val aParameter: String) { + open fun flutterMethod(aParameter: String) {} + + fun hostMethod(aParameter: String): String { + return "aString" + } +} +``` + +#### Swift + + +```swift +class SimpleExampleNativeClass { + let aField: String + + init(aField: String, aParameter: String) { + self.aField = aField + } + + open func flutterMethod(aParameter: String) {} + + func hostMethod(aParamter: String) -> String { + return "some string" + } +} +``` + +### Dart input + +This class is declared in the pigeon file and defines the generated Dart class and the native +language API. See `@ProxyApi` for setting additional parameters such as importing native libraries, +setting minimum supported API versions, or setting supported platforms. + + +```dart +@ProxyApi( + kotlinOptions: KotlinProxyApiOptions( + fullClassName: 'dev.flutter.pigeon_example_app.SimpleExampleNativeClass', + ), + swiftOptions: SwiftProxyApiOptions( + name: 'SimpleExampleNativeClass', + ), +) +abstract class SimpleExampleNativeClass { + // ignore: avoid_unused_constructor_parameters + SimpleExampleNativeClass(String aParameter); + + late String aField; + + /// Makes a call from native language to Dart. + late void Function(String aParameter)? flutterMethod; + + /// Makes a call from Dart to native language. + String hostMethod(String aParameter); +} +``` + +### ProxyApi Implementation + +Below is an example of implementing the generated API in the host language. + +#### Kotlin + + +```kotlin +class SimpleExampleNativeClassProxyApi(override val pigeonRegistrar: ProxyApiRegistrar) : + PigeonApiSimpleExampleNativeClass(pigeonRegistrar) { + + internal class SimpleExampleNativeClassImpl( + val api: SimpleExampleNativeClassProxyApi, + aField: String, + aParameter: String + ) : SimpleExampleNativeClass(aField, aParameter) { + override fun flutterMethod(aParameter: String) { + api.flutterMethod(this, aParameter) {} + } + } + + override fun pigeon_defaultConstructor( + aField: String, + aParameter: String + ): SimpleExampleNativeClass { + return SimpleExampleNativeClassImpl(this, aParameter, aField) + } + + override fun aField(pigeon_instance: SimpleExampleNativeClass): String { + return pigeon_instance.aField + } + + override fun hostMethod(pigeon_instance: SimpleExampleNativeClass, aParameter: String): String { + return pigeon_instance.hostMethod(aParameter) + } +} +``` + +#### Swift + + +```swift +class SimpleExampleNativeClassImpl: SimpleExampleNativeClass { + let api: PigeonApiSimpleExampleNativeClass + + init(api: PigeonApiSimpleExampleNativeClass, aField: String, aParameter: String) { + self.api = api + super.init(aField: aField, aParameter: aField) + } + + override func flutterMethod(aParameter: String) { + api.flutterMethod(pigeonInstance: self, aParameter: aParameter) { _ in } + } +} + +class SimpleExampleNativeClassAPIDelegate: PigeonApiDelegateSimpleExampleNativeClass { + func pigeonDefaultConstructor( + pigeonApi: PigeonApiSimpleExampleNativeClass, aField: String, aParameter: String + ) throws -> SimpleExampleNativeClass { + return SimpleExampleNativeClassImpl(api: pigeonApi, aField: aField, aParameter: aParameter) + } + + func aField( + pigeonApi: PigeonApiSimpleExampleNativeClass, pigeonInstance: SimpleExampleNativeClass + ) throws -> String { + return pigeonInstance.aField + } + + func hostMethod( + pigeonApi: PigeonApiSimpleExampleNativeClass, pigeonInstance: SimpleExampleNativeClass, + aParameter: String + ) throws -> String { + return pigeonInstance.hostMethod(aParamter: aParameter) + } +} +``` + +### Registrar Implementation and Setup + +Below is an example of implementing the ProxyApi registrar or registrar delegate in the host +language. Followed by an example of setting up the registrar. + +#### Kotlin + + +```kotlin +open class ProxyApiRegistrar(binaryMessenger: BinaryMessenger) : + MessagesPigeonProxyApiRegistrar(binaryMessenger) { + override fun getPigeonApiSimpleExampleNativeClass(): PigeonApiSimpleExampleNativeClass { + return SimpleExampleNativeClassProxyApi(this) + } + // ··· +} +``` + + +```kotlin +val registrar = ProxyApiRegistrar(flutterEngine.dartExecutor.binaryMessenger) +registrar.setUp() +``` + +#### Swift + + +```swift +open class ProxyAPIDelegate: MessagesPigeonProxyApiDelegate { + func pigeonApiSimpleExampleNativeClass(_ registrar: MessagesPigeonProxyApiRegistrar) + -> PigeonApiSimpleExampleNativeClass + { + return PigeonApiSimpleExampleNativeClass( + pigeonRegistrar: registrar, delegate: SimpleExampleNativeClassAPIDelegate()) + } + // ··· +} +``` + + +```swift +proxyApiRegistrar = MessagesPigeonProxyApiRegistrar( + binaryMessenger: controller.binaryMessenger, apiDelegate: ProxyAPIDelegate()) +proxyApiRegistrar?.setUp() +``` + +### Generated Dart Class Usage + +Below is an example of using the generated Dart class to call a method and set a callback method. + + +```dart +final SimpleExampleNativeClass instance = SimpleExampleNativeClass( + aField: 'my field', + aParameter: 'my parameter', + flutterMethod: (SimpleExampleNativeClass instance, String aParameter) { + debugPrint(aParameter); + }, +); + +debugPrint(instance.aField); +debugPrint(await instance.hostMethod('my parameter')); +``` + +## Complex ProxyApi Example + +Example with inheritance, static methods, and attached fields. + +### Dart input + + +```dart +@ProxyApi( + kotlinOptions: KotlinProxyApiOptions( + fullClassName: 'dev.flutter.pigeon_example_app.ComplexExampleNativeClass', + ), +) +abstract class ComplexExampleNativeClass extends ExampleNativeSuperClass + implements ExampleNativeInterface { + @static + late ExampleNativeSuperClass aStaticField; + + @attached + late ExampleNativeSuperClass anAttachedField; + + @static + String staticHostMethod(); +} + +@ProxyApi( + kotlinOptions: KotlinProxyApiOptions( + fullClassName: 'dev.flutter.pigeon_example_app.ExampleNativeSuperClass', + ), +) +abstract class ExampleNativeSuperClass { + String inheritedSuperClassMethod(); +} + +@ProxyApi( + kotlinOptions: KotlinProxyApiOptions( + fullClassName: 'dev.flutter.pigeon_example_app.ExampleNativeInterface', + ), +) +abstract class ExampleNativeInterface { + late void Function()? inheritedInterfaceMethod; +} +``` + ## Swift / Kotlin Plugin Example A downloadable example of using Pigeon to create a Flutter Plugin with Swift and diff --git a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/ExampleLibrary.kt b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/ExampleLibrary.kt new file mode 100644 index 000000000000..8ba2846742a6 --- /dev/null +++ b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/ExampleLibrary.kt @@ -0,0 +1,41 @@ +// 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. + +@file:Suppress("unused") + +// #docregion simple-proxy-class +package dev.flutter.pigeon_example_app + +open class SimpleExampleNativeClass(val aField: String, private val aParameter: String) { + open fun flutterMethod(aParameter: String) {} + + fun hostMethod(aParameter: String): String { + return "aString" + } +} +// #enddocregion simple-proxy-class + +class ComplexExampleNativeClass : ExampleNativeSuperClass(), ExampleNativeInterface { + val anAttachedField = ExampleNativeSuperClass() + + companion object { + val aStaticField = ExampleNativeSuperClass() + + fun staticHostMethod(): String { + return "some string" + } + } + + override fun inheritedInterfaceMethod() {} +} + +open class ExampleNativeSuperClass { + fun inheritedSuperClassMethod(): String { + return "some string" + } +} + +interface ExampleNativeInterface { + fun inheritedInterfaceMethod() +} diff --git a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/MainActivity.kt b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/MainActivity.kt index 6882861350b8..91efaffe69ac 100644 --- a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/MainActivity.kt +++ b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/MainActivity.kt @@ -46,7 +46,7 @@ private class PigeonFlutterApi { } fun callFlutterMethod(aString: String, callback: (Result) -> Unit) { - flutterApi!!.flutterMethod(aString) { echo -> callback(Result.success(echo)) } + flutterApi!!.flutterMethod(aString) { echo -> callback(Result.success(echo.getOrNull()!!)) } } } // #enddocregion kotlin-class-flutter @@ -57,5 +57,10 @@ class MainActivity : FlutterActivity() { val api = PigeonApiImplementation() ExampleHostApi.setUp(flutterEngine.dartExecutor.binaryMessenger, api) + + // #docregion registrar-setup + val registrar = ProxyApiRegistrar(flutterEngine.dartExecutor.binaryMessenger) + registrar.setUp() + // #enddocregion registrar-setup } } diff --git a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt index c6b32713ebf4..68ae5b36569c 100644 --- a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt +++ b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/Messages.g.kt @@ -45,6 +45,419 @@ class FlutterError( override val message: String? = null, val details: Any? = null ) : Throwable() +/** + * Maintains instances used to communicate with the corresponding objects in Dart. + * + * Objects stored in this container are represented by an object in Dart that is also stored in an + * InstanceManager with the same identifier. + * + * When an instance is added with an identifier, either can be used to retrieve the other. + * + * Added instances are added as a weak reference and a strong reference. When the strong reference + * is removed with [remove] and the weak reference is deallocated, the + * `finalizationListener.onFinalize` is called with the instance's identifier. However, if the + * strong reference is removed and then the identifier is retrieved with the intention to pass the + * identifier to Dart (e.g. calling [getIdentifierForStrongReference]), the strong reference to the + * instance is recreated. The strong reference will then need to be removed manually again. + */ +@Suppress("UNCHECKED_CAST", "MemberVisibilityCanBePrivate") +class MessagesPigeonInstanceManager(private val finalizationListener: PigeonFinalizationListener) { + /** Interface for listening when a weak reference of an instance is removed from the manager. */ + interface PigeonFinalizationListener { + fun onFinalize(identifier: Long) + } + + private val identifiers = java.util.WeakHashMap() + private val weakInstances = HashMap>() + private val strongInstances = HashMap() + private val referenceQueue = java.lang.ref.ReferenceQueue() + private val weakReferencesToIdentifiers = HashMap, Long>() + private val handler = android.os.Handler(android.os.Looper.getMainLooper()) + private var nextIdentifier: Long = minHostCreatedIdentifier + private var hasFinalizationListenerStopped = false + + /** + * Modifies the time interval used to define how often this instance removes garbage collected + * weak references to native Android objects that this instance was managing. + */ + var clearFinalizedWeakReferencesInterval: Long = 3000 + set(value) { + handler.removeCallbacks { this.releaseAllFinalizedInstances() } + field = value + releaseAllFinalizedInstances() + } + + init { + handler.postDelayed({ releaseAllFinalizedInstances() }, clearFinalizedWeakReferencesInterval) + } + + companion object { + // Identifiers are locked to a specific range to avoid collisions with objects + // created simultaneously from Dart. + // Host uses identifiers >= 2^16 and Dart is expected to use values n where, + // 0 <= n < 2^16. + private const val minHostCreatedIdentifier: Long = 65536 + private const val tag = "PigeonInstanceManager" + + /** + * Instantiate a new manager with a listener for garbage collected weak references. + * + * When the manager is no longer needed, [stopFinalizationListener] must be called. + */ + fun create(finalizationListener: PigeonFinalizationListener): MessagesPigeonInstanceManager { + return MessagesPigeonInstanceManager(finalizationListener) + } + } + + /** + * Removes `identifier` and return its associated strongly referenced instance, if present, from + * the manager. + */ + fun remove(identifier: Long): T? { + logWarningIfFinalizationListenerHasStopped() + return strongInstances.remove(identifier) as T? + } + + /** + * Retrieves the identifier paired with an instance, if present, otherwise `null`. + * + * If the manager contains a strong reference to `instance`, it will return the identifier + * associated with `instance`. If the manager contains only a weak reference to `instance`, a new + * strong reference to `instance` will be added and will need to be removed again with [remove]. + * + * If this method returns a nonnull identifier, this method also expects the Dart + * `MessagesPigeonInstanceManager` to have, or recreate, a weak reference to the Dart instance the + * identifier is associated with. + */ + fun getIdentifierForStrongReference(instance: Any?): Long? { + logWarningIfFinalizationListenerHasStopped() + val identifier = identifiers[instance] + if (identifier != null) { + strongInstances[identifier] = instance!! + } + return identifier + } + + /** + * Adds a new instance that was instantiated from Dart. + * + * The same instance can be added multiple times, but each identifier must be unique. This allows + * two objects that are equivalent (e.g. the `equals` method returns true and their hashcodes are + * equal) to both be added. + * + * [identifier] must be >= 0 and unique. + */ + fun addDartCreatedInstance(instance: Any, identifier: Long) { + logWarningIfFinalizationListenerHasStopped() + addInstance(instance, identifier) + } + + /** + * Adds a new unique instance that was instantiated from the host platform. + * + * [identifier] must be >= 0 and unique. + */ + fun addHostCreatedInstance(instance: Any): Long { + logWarningIfFinalizationListenerHasStopped() + require(!containsInstance(instance)) { + "Instance of ${instance.javaClass} has already been added." + } + val identifier = nextIdentifier++ + addInstance(instance, identifier) + return identifier + } + + /** Retrieves the instance associated with identifier, if present, otherwise `null`. */ + fun getInstance(identifier: Long): T? { + logWarningIfFinalizationListenerHasStopped() + val instance = weakInstances[identifier] as java.lang.ref.WeakReference? + return instance?.get() + } + + /** Returns whether this manager contains the given `instance`. */ + fun containsInstance(instance: Any?): Boolean { + logWarningIfFinalizationListenerHasStopped() + return identifiers.containsKey(instance) + } + + /** + * Stops the periodic run of the [PigeonFinalizationListener] for instances that have been garbage + * collected. + * + * The InstanceManager can continue to be used, but the [PigeonFinalizationListener] will no + * longer be called and methods will log a warning. + */ + fun stopFinalizationListener() { + handler.removeCallbacks { this.releaseAllFinalizedInstances() } + hasFinalizationListenerStopped = true + } + + /** + * Removes all of the instances from this manager. + * + * The manager will be empty after this call returns. + */ + fun clear() { + identifiers.clear() + weakInstances.clear() + strongInstances.clear() + weakReferencesToIdentifiers.clear() + } + + /** + * Whether the [PigeonFinalizationListener] is still being called for instances that are garbage + * collected. + * + * See [stopFinalizationListener]. + */ + fun hasFinalizationListenerStopped(): Boolean { + return hasFinalizationListenerStopped + } + + private fun releaseAllFinalizedInstances() { + if (hasFinalizationListenerStopped()) { + return + } + var reference: java.lang.ref.WeakReference? + while ((referenceQueue.poll() as java.lang.ref.WeakReference?).also { reference = it } != + null) { + val identifier = weakReferencesToIdentifiers.remove(reference) + if (identifier != null) { + weakInstances.remove(identifier) + strongInstances.remove(identifier) + finalizationListener.onFinalize(identifier) + } + } + handler.postDelayed({ releaseAllFinalizedInstances() }, clearFinalizedWeakReferencesInterval) + } + + private fun addInstance(instance: Any, identifier: Long) { + require(identifier >= 0) { "Identifier must be >= 0: $identifier" } + require(!weakInstances.containsKey(identifier)) { + "Identifier has already been added: $identifier" + } + val weakReference = java.lang.ref.WeakReference(instance, referenceQueue) + identifiers[instance] = identifier + weakInstances[identifier] = weakReference + weakReferencesToIdentifiers[weakReference] = identifier + strongInstances[identifier] = instance + } + + private fun logWarningIfFinalizationListenerHasStopped() { + if (hasFinalizationListenerStopped()) { + Log.w( + tag, + "The manager was used after calls to the PigeonFinalizationListener has been stopped.") + } + } +} + +/** Generated API for managing the Dart and native `InstanceManager`s. */ +private class MessagesPigeonInstanceManagerApi(val binaryMessenger: BinaryMessenger) { + companion object { + /** The codec used by MessagesPigeonInstanceManagerApi. */ + val codec: MessageCodec by lazy { MessagesPigeonCodec() } + + /** + * Sets up an instance of `MessagesPigeonInstanceManagerApi` to handle messages from the + * `binaryMessenger`. + */ + fun setUpMessageHandlers( + binaryMessenger: BinaryMessenger, + instanceManager: MessagesPigeonInstanceManager? + ) { + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.pigeon_example_package.PigeonInternalInstanceManager.removeStrongReference", + codec) + if (instanceManager != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val identifierArg = args[0] as Long + val wrapped: List = + try { + instanceManager.remove(identifierArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.pigeon_example_package.PigeonInternalInstanceManager.clear", + codec) + if (instanceManager != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = + try { + instanceManager.clear() + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } + + fun removeStrongReference(identifierArg: Long, callback: (Result) -> Unit) { + val channelName = + "dev.flutter.pigeon.pigeon_example_package.PigeonInternalInstanceManager.removeStrongReference" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(identifierArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(createConnectionError(channelName))) + } + } + } +} +/** + * Provides implementations for each ProxyApi implementation and provides access to resources needed + * by any implementation. + */ +abstract class MessagesPigeonProxyApiRegistrar(val binaryMessenger: BinaryMessenger) { + /** Whether APIs should ignore calling to Dart. */ + public var ignoreCallsToDart = false + val instanceManager: MessagesPigeonInstanceManager + private var _codec: MessageCodec? = null + val codec: MessageCodec + get() { + if (_codec == null) { + _codec = MessagesPigeonProxyApiBaseCodec(this) + } + return _codec!! + } + + init { + val api = MessagesPigeonInstanceManagerApi(binaryMessenger) + instanceManager = + MessagesPigeonInstanceManager.create( + object : MessagesPigeonInstanceManager.PigeonFinalizationListener { + override fun onFinalize(identifier: Long) { + api.removeStrongReference(identifier) { + if (it.isFailure) { + Log.e( + "PigeonProxyApiRegistrar", + "Failed to remove Dart strong reference with identifier: $identifier") + } + } + } + }) + } + /** + * An implementation of [PigeonApiSimpleExampleNativeClass] used to add a new Dart instance of + * `SimpleExampleNativeClass` to the Dart `InstanceManager`. + */ + abstract fun getPigeonApiSimpleExampleNativeClass(): PigeonApiSimpleExampleNativeClass + + /** + * An implementation of [PigeonApiComplexExampleNativeClass] used to add a new Dart instance of + * `ComplexExampleNativeClass` to the Dart `InstanceManager`. + */ + abstract fun getPigeonApiComplexExampleNativeClass(): PigeonApiComplexExampleNativeClass + + /** + * An implementation of [PigeonApiExampleNativeSuperClass] used to add a new Dart instance of + * `ExampleNativeSuperClass` to the Dart `InstanceManager`. + */ + abstract fun getPigeonApiExampleNativeSuperClass(): PigeonApiExampleNativeSuperClass + + /** + * An implementation of [PigeonApiExampleNativeInterface] used to add a new Dart instance of + * `ExampleNativeInterface` to the Dart `InstanceManager`. + */ + open fun getPigeonApiExampleNativeInterface(): PigeonApiExampleNativeInterface { + return PigeonApiExampleNativeInterface(this) + } + + fun setUp() { + MessagesPigeonInstanceManagerApi.setUpMessageHandlers(binaryMessenger, instanceManager) + PigeonApiSimpleExampleNativeClass.setUpMessageHandlers( + binaryMessenger, getPigeonApiSimpleExampleNativeClass()) + PigeonApiComplexExampleNativeClass.setUpMessageHandlers( + binaryMessenger, getPigeonApiComplexExampleNativeClass()) + PigeonApiExampleNativeSuperClass.setUpMessageHandlers( + binaryMessenger, getPigeonApiExampleNativeSuperClass()) + } + + fun tearDown() { + MessagesPigeonInstanceManagerApi.setUpMessageHandlers(binaryMessenger, null) + PigeonApiSimpleExampleNativeClass.setUpMessageHandlers(binaryMessenger, null) + PigeonApiComplexExampleNativeClass.setUpMessageHandlers(binaryMessenger, null) + PigeonApiExampleNativeSuperClass.setUpMessageHandlers(binaryMessenger, null) + } +} + +private class MessagesPigeonProxyApiBaseCodec(val registrar: MessagesPigeonProxyApiRegistrar) : + MessagesPigeonCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return when (type) { + 128.toByte() -> { + return registrar.instanceManager.getInstance(readValue(buffer) as Long) + } + else -> super.readValueOfType(type, buffer) + } + } + + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + if (value is Boolean || + value is ByteArray || + value is Double || + value is DoubleArray || + value is FloatArray || + value is Int || + value is IntArray || + value is List<*> || + value is Long || + value is LongArray || + value is Map<*, *> || + value is String || + value is Code || + value == null) { + super.writeValue(stream, value) + return + } + + if (value is dev.flutter.pigeon_example_app.SimpleExampleNativeClass) { + registrar.getPigeonApiSimpleExampleNativeClass().pigeon_newInstance(value) {} + } else if (value is dev.flutter.pigeon_example_app.ComplexExampleNativeClass) { + registrar.getPigeonApiComplexExampleNativeClass().pigeon_newInstance(value) {} + } else if (value is dev.flutter.pigeon_example_app.ExampleNativeSuperClass) { + registrar.getPigeonApiExampleNativeSuperClass().pigeon_newInstance(value) {} + } else if (value is dev.flutter.pigeon_example_app.ExampleNativeInterface) { + registrar.getPigeonApiExampleNativeInterface().pigeon_newInstance(value) {} + } + + when { + registrar.instanceManager.containsInstance(value) -> { + stream.write(128) + writeValue(stream, registrar.instanceManager.getIdentifierForStrongReference(value)) + } + else -> + throw IllegalArgumentException( + "Unsupported value: '$value' of type '${value.javaClass.name}'") + } + } +} enum class Code(val raw: Int) { ONE(0), @@ -239,3 +652,442 @@ class MessageFlutterApi( } } } + +@Suppress("UNCHECKED_CAST") +abstract class PigeonApiSimpleExampleNativeClass( + open val pigeonRegistrar: MessagesPigeonProxyApiRegistrar +) { + abstract fun pigeon_defaultConstructor( + aField: String, + aParameter: String + ): dev.flutter.pigeon_example_app.SimpleExampleNativeClass + + abstract fun aField( + pigeon_instance: dev.flutter.pigeon_example_app.SimpleExampleNativeClass + ): String + + /** Makes a call from Dart to native language. */ + abstract fun hostMethod( + pigeon_instance: dev.flutter.pigeon_example_app.SimpleExampleNativeClass, + aParameter: String + ): String + + companion object { + @Suppress("LocalVariableName") + fun setUpMessageHandlers( + binaryMessenger: BinaryMessenger, + api: PigeonApiSimpleExampleNativeClass? + ) { + val codec = api?.pigeonRegistrar?.codec ?: MessagesPigeonCodec() + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.pigeon_example_package.SimpleExampleNativeClass.pigeon_defaultConstructor", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val pigeon_identifierArg = args[0] as Long + val aFieldArg = args[1] as String + val aParameterArg = args[2] as String + val wrapped: List = + try { + api.pigeonRegistrar.instanceManager.addDartCreatedInstance( + api.pigeon_defaultConstructor(aFieldArg, aParameterArg), pigeon_identifierArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.pigeon_example_package.SimpleExampleNativeClass.hostMethod", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val pigeon_instanceArg = + args[0] as dev.flutter.pigeon_example_app.SimpleExampleNativeClass + val aParameterArg = args[1] as String + val wrapped: List = + try { + listOf(api.hostMethod(pigeon_instanceArg, aParameterArg)) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } + + @Suppress("LocalVariableName", "FunctionName") + /** + * Creates a Dart instance of SimpleExampleNativeClass and attaches it to [pigeon_instanceArg]. + */ + fun pigeon_newInstance( + pigeon_instanceArg: dev.flutter.pigeon_example_app.SimpleExampleNativeClass, + callback: (Result) -> Unit + ) { + if (pigeonRegistrar.ignoreCallsToDart) { + callback( + Result.failure( + FlutterError("ignore-calls-error", "Calls to Dart are being ignored.", ""))) + return + } + if (pigeonRegistrar.instanceManager.containsInstance(pigeon_instanceArg)) { + Result.success(Unit) + return + } + val pigeon_identifierArg = + pigeonRegistrar.instanceManager.addHostCreatedInstance(pigeon_instanceArg) + val aFieldArg = aField(pigeon_instanceArg) + val binaryMessenger = pigeonRegistrar.binaryMessenger + val codec = pigeonRegistrar.codec + val channelName = + "dev.flutter.pigeon.pigeon_example_package.SimpleExampleNativeClass.pigeon_newInstance" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(pigeon_identifierArg, aFieldArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(createConnectionError(channelName))) + } + } + } + + /** Makes a call from native language to Dart. */ + fun flutterMethod( + pigeon_instanceArg: dev.flutter.pigeon_example_app.SimpleExampleNativeClass, + aParameterArg: String, + callback: (Result) -> Unit + ) { + if (pigeonRegistrar.ignoreCallsToDart) { + callback( + Result.failure( + FlutterError("ignore-calls-error", "Calls to Dart are being ignored.", ""))) + return + } + val binaryMessenger = pigeonRegistrar.binaryMessenger + val codec = pigeonRegistrar.codec + val channelName = + "dev.flutter.pigeon.pigeon_example_package.SimpleExampleNativeClass.flutterMethod" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(pigeon_instanceArg, aParameterArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(createConnectionError(channelName))) + } + } + } +} + +@Suppress("UNCHECKED_CAST") +abstract class PigeonApiComplexExampleNativeClass( + open val pigeonRegistrar: MessagesPigeonProxyApiRegistrar +) { + abstract fun aStaticField(): dev.flutter.pigeon_example_app.ExampleNativeSuperClass + + abstract fun anAttachedField( + pigeon_instance: dev.flutter.pigeon_example_app.ComplexExampleNativeClass + ): dev.flutter.pigeon_example_app.ExampleNativeSuperClass + + abstract fun staticHostMethod(): String + + companion object { + @Suppress("LocalVariableName") + fun setUpMessageHandlers( + binaryMessenger: BinaryMessenger, + api: PigeonApiComplexExampleNativeClass? + ) { + val codec = api?.pigeonRegistrar?.codec ?: MessagesPigeonCodec() + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.pigeon_example_package.ComplexExampleNativeClass.aStaticField", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val pigeon_identifierArg = args[0] as Long + val wrapped: List = + try { + api.pigeonRegistrar.instanceManager.addDartCreatedInstance( + api.aStaticField(), pigeon_identifierArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.pigeon_example_package.ComplexExampleNativeClass.anAttachedField", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val pigeon_instanceArg = + args[0] as dev.flutter.pigeon_example_app.ComplexExampleNativeClass + val pigeon_identifierArg = args[1] as Long + val wrapped: List = + try { + api.pigeonRegistrar.instanceManager.addDartCreatedInstance( + api.anAttachedField(pigeon_instanceArg), pigeon_identifierArg) + listOf(null) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.pigeon_example_package.ComplexExampleNativeClass.staticHostMethod", + codec) + if (api != null) { + channel.setMessageHandler { _, reply -> + val wrapped: List = + try { + listOf(api.staticHostMethod()) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } + + @Suppress("LocalVariableName", "FunctionName") + /** + * Creates a Dart instance of ComplexExampleNativeClass and attaches it to [pigeon_instanceArg]. + */ + fun pigeon_newInstance( + pigeon_instanceArg: dev.flutter.pigeon_example_app.ComplexExampleNativeClass, + callback: (Result) -> Unit + ) { + if (pigeonRegistrar.ignoreCallsToDart) { + callback( + Result.failure( + FlutterError("ignore-calls-error", "Calls to Dart are being ignored.", ""))) + return + } + if (pigeonRegistrar.instanceManager.containsInstance(pigeon_instanceArg)) { + Result.success(Unit) + return + } + val pigeon_identifierArg = + pigeonRegistrar.instanceManager.addHostCreatedInstance(pigeon_instanceArg) + val binaryMessenger = pigeonRegistrar.binaryMessenger + val codec = pigeonRegistrar.codec + val channelName = + "dev.flutter.pigeon.pigeon_example_package.ComplexExampleNativeClass.pigeon_newInstance" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(pigeon_identifierArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(createConnectionError(channelName))) + } + } + } + + @Suppress("FunctionName") + /** An implementation of [PigeonApiExampleNativeSuperClass] used to access callback methods */ + fun pigeon_getPigeonApiExampleNativeSuperClass(): PigeonApiExampleNativeSuperClass { + return pigeonRegistrar.getPigeonApiExampleNativeSuperClass() + } + + @Suppress("FunctionName") + /** An implementation of [PigeonApiExampleNativeInterface] used to access callback methods */ + fun pigeon_getPigeonApiExampleNativeInterface(): PigeonApiExampleNativeInterface { + return pigeonRegistrar.getPigeonApiExampleNativeInterface() + } +} + +@Suppress("UNCHECKED_CAST") +abstract class PigeonApiExampleNativeSuperClass( + open val pigeonRegistrar: MessagesPigeonProxyApiRegistrar +) { + abstract fun inheritedSuperClassMethod( + pigeon_instance: dev.flutter.pigeon_example_app.ExampleNativeSuperClass + ): String + + companion object { + @Suppress("LocalVariableName") + fun setUpMessageHandlers( + binaryMessenger: BinaryMessenger, + api: PigeonApiExampleNativeSuperClass? + ) { + val codec = api?.pigeonRegistrar?.codec ?: MessagesPigeonCodec() + run { + val channel = + BasicMessageChannel( + binaryMessenger, + "dev.flutter.pigeon.pigeon_example_package.ExampleNativeSuperClass.inheritedSuperClassMethod", + codec) + if (api != null) { + channel.setMessageHandler { message, reply -> + val args = message as List + val pigeon_instanceArg = + args[0] as dev.flutter.pigeon_example_app.ExampleNativeSuperClass + val wrapped: List = + try { + listOf(api.inheritedSuperClassMethod(pigeon_instanceArg)) + } catch (exception: Throwable) { + wrapError(exception) + } + reply.reply(wrapped) + } + } else { + channel.setMessageHandler(null) + } + } + } + } + + @Suppress("LocalVariableName", "FunctionName") + /** Creates a Dart instance of ExampleNativeSuperClass and attaches it to [pigeon_instanceArg]. */ + fun pigeon_newInstance( + pigeon_instanceArg: dev.flutter.pigeon_example_app.ExampleNativeSuperClass, + callback: (Result) -> Unit + ) { + if (pigeonRegistrar.ignoreCallsToDart) { + callback( + Result.failure( + FlutterError("ignore-calls-error", "Calls to Dart are being ignored.", ""))) + return + } + if (pigeonRegistrar.instanceManager.containsInstance(pigeon_instanceArg)) { + Result.success(Unit) + return + } + val pigeon_identifierArg = + pigeonRegistrar.instanceManager.addHostCreatedInstance(pigeon_instanceArg) + val binaryMessenger = pigeonRegistrar.binaryMessenger + val codec = pigeonRegistrar.codec + val channelName = + "dev.flutter.pigeon.pigeon_example_package.ExampleNativeSuperClass.pigeon_newInstance" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(pigeon_identifierArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(createConnectionError(channelName))) + } + } + } +} + +@Suppress("UNCHECKED_CAST") +open class PigeonApiExampleNativeInterface( + open val pigeonRegistrar: MessagesPigeonProxyApiRegistrar +) { + @Suppress("LocalVariableName", "FunctionName") + /** Creates a Dart instance of ExampleNativeInterface and attaches it to [pigeon_instanceArg]. */ + fun pigeon_newInstance( + pigeon_instanceArg: dev.flutter.pigeon_example_app.ExampleNativeInterface, + callback: (Result) -> Unit + ) { + if (pigeonRegistrar.ignoreCallsToDart) { + callback( + Result.failure( + FlutterError("ignore-calls-error", "Calls to Dart are being ignored.", ""))) + return + } + if (pigeonRegistrar.instanceManager.containsInstance(pigeon_instanceArg)) { + Result.success(Unit) + return + } + val pigeon_identifierArg = + pigeonRegistrar.instanceManager.addHostCreatedInstance(pigeon_instanceArg) + val binaryMessenger = pigeonRegistrar.binaryMessenger + val codec = pigeonRegistrar.codec + val channelName = + "dev.flutter.pigeon.pigeon_example_package.ExampleNativeInterface.pigeon_newInstance" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(pigeon_identifierArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(createConnectionError(channelName))) + } + } + } + + fun inheritedInterfaceMethod( + pigeon_instanceArg: dev.flutter.pigeon_example_app.ExampleNativeInterface, + callback: (Result) -> Unit + ) { + if (pigeonRegistrar.ignoreCallsToDart) { + callback( + Result.failure( + FlutterError("ignore-calls-error", "Calls to Dart are being ignored.", ""))) + return + } + val binaryMessenger = pigeonRegistrar.binaryMessenger + val codec = pigeonRegistrar.codec + val channelName = + "dev.flutter.pigeon.pigeon_example_package.ExampleNativeInterface.inheritedInterfaceMethod" + val channel = BasicMessageChannel(binaryMessenger, channelName, codec) + channel.send(listOf(pigeon_instanceArg)) { + if (it is List<*>) { + if (it.size > 1) { + callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?))) + } else { + callback(Result.success(Unit)) + } + } else { + callback(Result.failure(createConnectionError(channelName))) + } + } + } +} diff --git a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/ProxyApiImpl.kt b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/ProxyApiImpl.kt new file mode 100644 index 000000000000..811b4b475937 --- /dev/null +++ b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/ProxyApiImpl.kt @@ -0,0 +1,85 @@ +// 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. + +package dev.flutter.pigeon_example_app + +import MessagesPigeonProxyApiRegistrar +import PigeonApiComplexExampleNativeClass +import PigeonApiExampleNativeSuperClass +import PigeonApiSimpleExampleNativeClass +import io.flutter.plugin.common.BinaryMessenger + +// #docregion simple-proxy-api +class SimpleExampleNativeClassProxyApi(override val pigeonRegistrar: ProxyApiRegistrar) : + PigeonApiSimpleExampleNativeClass(pigeonRegistrar) { + + internal class SimpleExampleNativeClassImpl( + val api: SimpleExampleNativeClassProxyApi, + aField: String, + aParameter: String + ) : SimpleExampleNativeClass(aField, aParameter) { + override fun flutterMethod(aParameter: String) { + api.flutterMethod(this, aParameter) {} + } + } + + override fun pigeon_defaultConstructor( + aField: String, + aParameter: String + ): SimpleExampleNativeClass { + return SimpleExampleNativeClassImpl(this, aParameter, aField) + } + + override fun aField(pigeon_instance: SimpleExampleNativeClass): String { + return pigeon_instance.aField + } + + override fun hostMethod(pigeon_instance: SimpleExampleNativeClass, aParameter: String): String { + return pigeon_instance.hostMethod(aParameter) + } +} +// #enddocregion simple-proxy-api + +class ComplexExampleNativeClassProxyApi(override val pigeonRegistrar: ProxyApiRegistrar) : + PigeonApiComplexExampleNativeClass(pigeonRegistrar) { + override fun aStaticField(): ExampleNativeSuperClass { + return ComplexExampleNativeClass.aStaticField + } + + override fun anAttachedField( + pigeon_instance: ComplexExampleNativeClass + ): ExampleNativeSuperClass { + return pigeon_instance.anAttachedField + } + + override fun staticHostMethod(): String { + return ComplexExampleNativeClass.staticHostMethod() + } +} + +class ExampleNativeSuperClassProxyApi(override val pigeonRegistrar: ProxyApiRegistrar) : + PigeonApiExampleNativeSuperClass(pigeonRegistrar) { + override fun inheritedSuperClassMethod(pigeon_instance: ExampleNativeSuperClass): String { + return pigeon_instance.inheritedSuperClassMethod() + } +} + +// #docregion simple-proxy-registrar +open class ProxyApiRegistrar(binaryMessenger: BinaryMessenger) : + MessagesPigeonProxyApiRegistrar(binaryMessenger) { + override fun getPigeonApiSimpleExampleNativeClass(): PigeonApiSimpleExampleNativeClass { + return SimpleExampleNativeClassProxyApi(this) + } + // #enddocregion simple-proxy-registrar + + override fun getPigeonApiComplexExampleNativeClass(): PigeonApiComplexExampleNativeClass { + return ComplexExampleNativeClassProxyApi(this) + } + + override fun getPigeonApiExampleNativeSuperClass(): PigeonApiExampleNativeSuperClass { + return ExampleNativeSuperClassProxyApi(this) + } + // #docregion simple-proxy-registrar +} +// #enddocregion simple-proxy-registrar diff --git a/packages/pigeon/example/app/ios/Runner.xcodeproj/project.pbxproj b/packages/pigeon/example/app/ios/Runner.xcodeproj/project.pbxproj index 6541f88ab296..4b4475baf080 100644 --- a/packages/pigeon/example/app/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/pigeon/example/app/ios/Runner.xcodeproj/project.pbxproj @@ -11,9 +11,12 @@ 3368472729F02D040090029A /* Messages.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3368472629F02D040090029A /* Messages.g.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 8FDF48F52CE679E400CFC21A /* ExampleLibrary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FDF48F42CE679DD00CFC21A /* ExampleLibrary.swift */; }; + 8FDF48F72CE67E7500CFC21A /* ProxyAPIImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FDF48F62CE67E7200CFC21A /* ProxyAPIImpl.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + A91378310A4383ACF53270D9 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C371FCDAEFB05BDFB6C781C0 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -30,13 +33,18 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 032D0BEEC2F5B7541D49D842 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 3368472629F02D040090029A /* Messages.g.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Messages.g.swift; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; + 44A6E48C0D4AD15E28FFDA2E /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; + 8FDF48F42CE679DD00CFC21A /* ExampleLibrary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleLibrary.swift; sourceTree = ""; }; + 8FDF48F62CE67E7200CFC21A /* ProxyAPIImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyAPIImpl.swift; sourceTree = ""; }; + 9322F623923041F7DB1A5E1C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -44,6 +52,7 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + C371FCDAEFB05BDFB6C781C0 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -51,12 +60,23 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + A91378310A4383ACF53270D9 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 6F95680BA267CD049E7209B5 /* Pods */ = { + isa = PBXGroup; + children = ( + 032D0BEEC2F5B7541D49D842 /* Pods-Runner.debug.xcconfig */, + 44A6E48C0D4AD15E28FFDA2E /* Pods-Runner.release.xcconfig */, + 9322F623923041F7DB1A5E1C /* Pods-Runner.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -74,6 +94,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, + 6F95680BA267CD049E7209B5 /* Pods */, + FC48745DB2A31E8719B19551 /* Frameworks */, ); sourceTree = ""; }; @@ -88,6 +110,8 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + 8FDF48F62CE67E7200CFC21A /* ProxyAPIImpl.swift */, + 8FDF48F42CE679DD00CFC21A /* ExampleLibrary.swift */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, @@ -101,6 +125,14 @@ path = Runner; sourceTree = ""; }; + FC48745DB2A31E8719B19551 /* Frameworks */ = { + isa = PBXGroup; + children = ( + C371FCDAEFB05BDFB6C781C0 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -108,12 +140,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + B3E6BC2A0D90BB4C890327CC /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + CB7A696BDC28C5D1C670270F /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -130,7 +164,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -203,6 +237,45 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; + B3E6BC2A0D90BB4C890327CC /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + CB7A696BDC28C5D1C670270F /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -210,9 +283,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 8FDF48F52CE679E400CFC21A /* ExampleLibrary.swift in Sources */, 3368472729F02D040090029A /* Messages.g.swift in Sources */, 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + 8FDF48F72CE67E7500CFC21A /* ProxyAPIImpl.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -279,7 +354,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -356,7 +431,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -405,7 +480,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift index 51119e23fa38..57b4c73b296a 100644 --- a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift +++ b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift @@ -40,14 +40,16 @@ private class PigeonFlutterApi { aString aStringArg: String?, completion: @escaping (Result) -> Void ) { flutterAPI.flutterMethod(aString: aStringArg) { - completion(.success($0)) + completion(.success(try! $0.get())) } } } // #enddocregion swift-class-flutter -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { + var proxyApiRegistrar: MessagesPigeonProxyApiRegistrar? + override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? @@ -58,7 +60,12 @@ private class PigeonFlutterApi { let api = PigeonApiImplementation() ExampleHostApiSetup.setUp(binaryMessenger: controller.binaryMessenger, api: api) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) + // #docregion registrar-setup + proxyApiRegistrar = MessagesPigeonProxyApiRegistrar( + binaryMessenger: controller.binaryMessenger, apiDelegate: ProxyAPIDelegate()) + proxyApiRegistrar?.setUp() + // #enddocregion registrar-setup + return super.application(application, didFinishLaunchingWithOptions: launchOptions) } } diff --git a/packages/pigeon/example/app/ios/Runner/ExampleLibrary.swift b/packages/pigeon/example/app/ios/Runner/ExampleLibrary.swift new file mode 100644 index 000000000000..d2bda8089e4a --- /dev/null +++ b/packages/pigeon/example/app/ios/Runner/ExampleLibrary.swift @@ -0,0 +1,40 @@ +// 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. + +// #docregion simple-proxy-class +class SimpleExampleNativeClass { + let aField: String + + init(aField: String, aParameter: String) { + self.aField = aField + } + + open func flutterMethod(aParameter: String) {} + + func hostMethod(aParamter: String) -> String { + return "some string" + } +} +// #enddocregion simple-proxy-class + +class ComplexExampleNativeClass: ExampleNativeSuperClass, ExampleNativeInterface { + static let aStaticField = ExampleNativeSuperClass() + let anAttachedField = ExampleNativeSuperClass() + + static func staticHostMethod() -> String { + return "some string" + } + + func inheritedInterfaceMethod() {} +} + +class ExampleNativeSuperClass { + func inheritedSuperClassMethod() -> String { + return "some string" + } +} + +protocol ExampleNativeInterface { + func inheritedInterfaceMethod() +} diff --git a/packages/pigeon/example/app/ios/Runner/Messages.g.swift b/packages/pigeon/example/app/ios/Runner/Messages.g.swift index b42c8a41a1d5..dbcf2e9f6e46 100644 --- a/packages/pigeon/example/app/ios/Runner/Messages.g.swift +++ b/packages/pigeon/example/app/ios/Runner/Messages.g.swift @@ -72,6 +72,499 @@ private func nilOrValue(_ value: Any?) -> T? { if value is NSNull { return nil } return value as! T? } +/// Handles the callback when an object is deallocated. +protocol MessagesPigeonInternalFinalizerDelegate: AnyObject { + /// Invoked when the strong reference of an object is deallocated in an `InstanceManager`. + func onDeinit(identifier: Int64) +} + +// Attaches to an object to receive a callback when the object is deallocated. +internal final class MessagesPigeonInternalFinalizer { + private static let associatedObjectKey = malloc(1)! + + private let identifier: Int64 + // Reference to the delegate is weak because the callback should be ignored if the + // `InstanceManager` is deallocated. + private weak var delegate: MessagesPigeonInternalFinalizerDelegate? + + private init(identifier: Int64, delegate: MessagesPigeonInternalFinalizerDelegate) { + self.identifier = identifier + self.delegate = delegate + } + + internal static func attach( + to instance: AnyObject, identifier: Int64, delegate: MessagesPigeonInternalFinalizerDelegate + ) { + let finalizer = MessagesPigeonInternalFinalizer(identifier: identifier, delegate: delegate) + objc_setAssociatedObject(instance, associatedObjectKey, finalizer, .OBJC_ASSOCIATION_RETAIN) + } + + static func detach(from instance: AnyObject) { + objc_setAssociatedObject(instance, associatedObjectKey, nil, .OBJC_ASSOCIATION_ASSIGN) + } + + deinit { + delegate?.onDeinit(identifier: identifier) + } +} + +/// Maintains instances used to communicate with the corresponding objects in Dart. +/// +/// Objects stored in this container are represented by an object in Dart that is also stored in +/// an InstanceManager with the same identifier. +/// +/// When an instance is added with an identifier, either can be used to retrieve the other. +/// +/// Added instances are added as a weak reference and a strong reference. When the strong +/// reference is removed and the weak reference is deallocated,`MessagesPigeonInternalFinalizerDelegate.onDeinit` +/// is called with the instance's identifier. However, if the strong reference is removed and then the identifier is +/// retrieved with the intention to pass the identifier to Dart (e.g. by calling `identifierWithStrongReference`), +/// the strong reference to the instance is re-added. The strong reference will then need to be removed manually +/// again. +/// +/// Accessing and inserting to an InstanceManager is thread safe. +final class MessagesPigeonInstanceManager { + // Identifiers are locked to a specific range to avoid collisions with objects + // created simultaneously from Dart. + // Host uses identifiers >= 2^16 and Dart is expected to use values n where, + // 0 <= n < 2^16. + private static let minHostCreatedIdentifier: Int64 = 65536 + + private let lockQueue = DispatchQueue(label: "MessagesPigeonInstanceManager") + private let identifiers: NSMapTable = NSMapTable( + keyOptions: [.weakMemory, .objectPointerPersonality], valueOptions: .strongMemory) + private let weakInstances: NSMapTable = NSMapTable( + keyOptions: .strongMemory, valueOptions: [.weakMemory, .objectPointerPersonality]) + private let strongInstances: NSMapTable = NSMapTable( + keyOptions: .strongMemory, valueOptions: [.strongMemory, .objectPointerPersonality]) + private let finalizerDelegate: MessagesPigeonInternalFinalizerDelegate + private var nextIdentifier: Int64 = minHostCreatedIdentifier + + public init(finalizerDelegate: MessagesPigeonInternalFinalizerDelegate) { + self.finalizerDelegate = finalizerDelegate + } + + /// Adds a new instance that was instantiated from Dart. + /// + /// The same instance can be added multiple times, but each identifier must be unique. This allows + /// two objects that are equivalent (e.g. conforms to `Equatable`) to both be added. + /// + /// - Parameters: + /// - instance: the instance to be stored + /// - identifier: the identifier to be paired with instance. This value must be >= 0 and unique + func addDartCreatedInstance(_ instance: AnyObject, withIdentifier identifier: Int64) { + lockQueue.async { + self.addInstance(instance, withIdentifier: identifier) + } + } + + /// Adds a new instance that was instantiated from the host platform. + /// + /// - Parameters: + /// - instance: the instance to be stored. This must be unique to all other added instances. + /// - Returns: the unique identifier (>= 0) stored with instance + func addHostCreatedInstance(_ instance: AnyObject) -> Int64 { + assert(!containsInstance(instance), "Instance of \(instance) has already been added.") + var identifier: Int64 = -1 + lockQueue.sync { + identifier = nextIdentifier + nextIdentifier += 1 + self.addInstance(instance, withIdentifier: identifier) + } + return identifier + } + + /// Removes `instanceIdentifier` and its associated strongly referenced instance, if present, from the manager. + /// + /// - Parameters: + /// - instanceIdentifier: the identifier paired to an instance. + /// - Returns: removed instance if the manager contains the given identifier, otherwise `nil` if + /// the manager doesn't contain the value + func removeInstance(withIdentifier instanceIdentifier: Int64) throws -> T? { + var instance: AnyObject? = nil + lockQueue.sync { + instance = strongInstances.object(forKey: NSNumber(value: instanceIdentifier)) + strongInstances.removeObject(forKey: NSNumber(value: instanceIdentifier)) + } + return instance as? T + } + + /// Retrieves the instance associated with identifier. + /// + /// - Parameters: + /// - instanceIdentifier: the identifier associated with an instance + /// - Returns: the instance associated with `instanceIdentifier` if the manager contains the value, otherwise + /// `nil` if the manager doesn't contain the value + func instance(forIdentifier instanceIdentifier: Int64) -> T? { + var instance: AnyObject? = nil + lockQueue.sync { + instance = weakInstances.object(forKey: NSNumber(value: instanceIdentifier)) + } + return instance as? T + } + + private func addInstance(_ instance: AnyObject, withIdentifier identifier: Int64) { + assert(identifier >= 0) + assert( + weakInstances.object(forKey: identifier as NSNumber) == nil, + "Identifier has already been added: \(identifier)") + identifiers.setObject(NSNumber(value: identifier), forKey: instance) + weakInstances.setObject(instance, forKey: NSNumber(value: identifier)) + strongInstances.setObject(instance, forKey: NSNumber(value: identifier)) + MessagesPigeonInternalFinalizer.attach( + to: instance, identifier: identifier, delegate: finalizerDelegate) + } + + /// Retrieves the identifier paired with an instance. + /// + /// If the manager contains a strong reference to `instance`, it will return the identifier + /// associated with `instance`. If the manager contains only a weak reference to `instance`, a new + /// strong reference to `instance` will be added and will need to be removed again with `removeInstance`. + /// + /// If this method returns a nonnull identifier, this method also expects the Dart + /// `MessagesPigeonInstanceManager` to have, or recreate, a weak reference to the Dart instance the + /// identifier is associated with. + /// + /// - Parameters: + /// - instance: an instance that may be stored in the manager + /// - Returns: the identifier associated with `instance` if the manager contains the value, otherwise + /// `nil` if the manager doesn't contain the value + func identifierWithStrongReference(forInstance instance: AnyObject) -> Int64? { + var identifier: Int64? = nil + lockQueue.sync { + if let existingIdentifier = identifiers.object(forKey: instance)?.int64Value { + strongInstances.setObject(instance, forKey: NSNumber(value: existingIdentifier)) + identifier = existingIdentifier + } + } + return identifier + } + + /// Whether this manager contains the given `instance`. + /// + /// - Parameters: + /// - instance: the instance whose presence in this manager is to be tested + /// - Returns: whether this manager contains the given `instance` + func containsInstance(_ instance: AnyObject) -> Bool { + var containsInstance = false + lockQueue.sync { + containsInstance = identifiers.object(forKey: instance) != nil + } + return containsInstance + } + + /// Removes all of the instances from this manager. + /// + /// The manager will be empty after this call returns. + func removeAllObjects() throws { + lockQueue.sync { + identifiers.removeAllObjects() + weakInstances.removeAllObjects() + strongInstances.removeAllObjects() + nextIdentifier = MessagesPigeonInstanceManager.minHostCreatedIdentifier + } + } + + /// The number of instances stored as a strong reference. + /// + /// For debugging and testing purposes. + internal var strongInstanceCount: Int { + var count: Int = 0 + lockQueue.sync { + count = strongInstances.count + } + return count + } + + /// The number of instances stored as a weak reference. + /// + /// For debugging and testing purposes. NSMapTables that store keys or objects as weak + /// reference will be reclaimed non-deterministically. + internal var weakInstanceCount: Int { + var count: Int = 0 + lockQueue.sync { + count = weakInstances.count + } + return count + } +} + +private class MessagesPigeonInstanceManagerApi { + /// The codec used for serializing messages. + var codec: FlutterStandardMessageCodec { MessagesPigeonCodec.shared } + + /// Handles sending and receiving messages with Dart. + unowned let binaryMessenger: FlutterBinaryMessenger + + init(binaryMessenger: FlutterBinaryMessenger) { + self.binaryMessenger = binaryMessenger + } + + /// Sets up an instance of `MessagesPigeonInstanceManagerApi` to handle messages through the `binaryMessenger`. + static func setUpMessageHandlers( + binaryMessenger: FlutterBinaryMessenger, instanceManager: MessagesPigeonInstanceManager? + ) { + let codec = MessagesPigeonCodec.shared + let removeStrongReferenceChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.pigeon_example_package.PigeonInternalInstanceManager.removeStrongReference", + binaryMessenger: binaryMessenger, codec: codec) + if let instanceManager = instanceManager { + removeStrongReferenceChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let identifierArg = args[0] as! Int64 + do { + let _: AnyObject? = try instanceManager.removeInstance(withIdentifier: identifierArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + removeStrongReferenceChannel.setMessageHandler(nil) + } + let clearChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.pigeon_example_package.PigeonInternalInstanceManager.clear", + binaryMessenger: binaryMessenger, codec: codec) + if let instanceManager = instanceManager { + clearChannel.setMessageHandler { _, reply in + do { + try instanceManager.removeAllObjects() + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + clearChannel.setMessageHandler(nil) + } + } + + /// Sends a message to the Dart `InstanceManager` to remove the strong reference of the instance associated with `identifier`. + func removeStrongReference( + identifier identifierArg: Int64, completion: @escaping (Result) -> Void + ) { + let channelName: String = + "dev.flutter.pigeon.pigeon_example_package.PigeonInternalInstanceManager.removeStrongReference" + let channel = FlutterBasicMessageChannel( + name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([identifierArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(Void())) + } + } + } +} +protocol MessagesPigeonProxyApiDelegate { + /// An implementation of [PigeonApiSimpleExampleNativeClass] used to add a new Dart instance of + /// `SimpleExampleNativeClass` to the Dart `InstanceManager` and make calls to Dart. + func pigeonApiSimpleExampleNativeClass(_ registrar: MessagesPigeonProxyApiRegistrar) + -> PigeonApiSimpleExampleNativeClass + /// An implementation of [PigeonApiComplexExampleNativeClass] used to add a new Dart instance of + /// `ComplexExampleNativeClass` to the Dart `InstanceManager` and make calls to Dart. + func pigeonApiComplexExampleNativeClass(_ registrar: MessagesPigeonProxyApiRegistrar) + -> PigeonApiComplexExampleNativeClass + /// An implementation of [PigeonApiExampleNativeSuperClass] used to add a new Dart instance of + /// `ExampleNativeSuperClass` to the Dart `InstanceManager` and make calls to Dart. + func pigeonApiExampleNativeSuperClass(_ registrar: MessagesPigeonProxyApiRegistrar) + -> PigeonApiExampleNativeSuperClass + /// An implementation of [PigeonApiExampleNativeInterface] used to add a new Dart instance of + /// `ExampleNativeInterface` to the Dart `InstanceManager` and make calls to Dart. + func pigeonApiExampleNativeInterface(_ registrar: MessagesPigeonProxyApiRegistrar) + -> PigeonApiExampleNativeInterface +} + +extension MessagesPigeonProxyApiDelegate { + func pigeonApiExampleNativeInterface(_ registrar: MessagesPigeonProxyApiRegistrar) + -> PigeonApiExampleNativeInterface + { + return PigeonApiExampleNativeInterface( + pigeonRegistrar: registrar, delegate: PigeonApiDelegateExampleNativeInterface()) + } +} + +open class MessagesPigeonProxyApiRegistrar { + let binaryMessenger: FlutterBinaryMessenger + let apiDelegate: MessagesPigeonProxyApiDelegate + let instanceManager: MessagesPigeonInstanceManager + /// Whether APIs should ignore calling to Dart. + public var ignoreCallsToDart = false + private var _codec: FlutterStandardMessageCodec? + var codec: FlutterStandardMessageCodec { + if _codec == nil { + _codec = FlutterStandardMessageCodec( + readerWriter: MessagesPigeonInternalProxyApiCodecReaderWriter(pigeonRegistrar: self)) + } + return _codec! + } + + private class InstanceManagerApiFinalizerDelegate: MessagesPigeonInternalFinalizerDelegate { + let api: MessagesPigeonInstanceManagerApi + + init(_ api: MessagesPigeonInstanceManagerApi) { + self.api = api + } + + public func onDeinit(identifier: Int64) { + api.removeStrongReference(identifier: identifier) { + _ in + } + } + } + + init(binaryMessenger: FlutterBinaryMessenger, apiDelegate: MessagesPigeonProxyApiDelegate) { + self.binaryMessenger = binaryMessenger + self.apiDelegate = apiDelegate + self.instanceManager = MessagesPigeonInstanceManager( + finalizerDelegate: InstanceManagerApiFinalizerDelegate( + MessagesPigeonInstanceManagerApi(binaryMessenger: binaryMessenger))) + } + + func setUp() { + MessagesPigeonInstanceManagerApi.setUpMessageHandlers( + binaryMessenger: binaryMessenger, instanceManager: instanceManager) + PigeonApiSimpleExampleNativeClass.setUpMessageHandlers( + binaryMessenger: binaryMessenger, api: apiDelegate.pigeonApiSimpleExampleNativeClass(self)) + PigeonApiComplexExampleNativeClass.setUpMessageHandlers( + binaryMessenger: binaryMessenger, api: apiDelegate.pigeonApiComplexExampleNativeClass(self)) + PigeonApiExampleNativeSuperClass.setUpMessageHandlers( + binaryMessenger: binaryMessenger, api: apiDelegate.pigeonApiExampleNativeSuperClass(self)) + } + func tearDown() { + MessagesPigeonInstanceManagerApi.setUpMessageHandlers( + binaryMessenger: binaryMessenger, instanceManager: nil) + PigeonApiSimpleExampleNativeClass.setUpMessageHandlers( + binaryMessenger: binaryMessenger, api: nil) + PigeonApiComplexExampleNativeClass.setUpMessageHandlers( + binaryMessenger: binaryMessenger, api: nil) + PigeonApiExampleNativeSuperClass.setUpMessageHandlers( + binaryMessenger: binaryMessenger, api: nil) + } +} +private class MessagesPigeonInternalProxyApiCodecReaderWriter: FlutterStandardReaderWriter { + unowned let pigeonRegistrar: MessagesPigeonProxyApiRegistrar + + private class MessagesPigeonInternalProxyApiCodecReader: MessagesPigeonCodecReader { + unowned let pigeonRegistrar: MessagesPigeonProxyApiRegistrar + + init(data: Data, pigeonRegistrar: MessagesPigeonProxyApiRegistrar) { + self.pigeonRegistrar = pigeonRegistrar + super.init(data: data) + } + + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 128: + let identifier = self.readValue() + let instance: AnyObject? = pigeonRegistrar.instanceManager.instance( + forIdentifier: identifier is Int64 ? identifier as! Int64 : Int64(identifier as! Int32)) + return instance + default: + return super.readValue(ofType: type) + } + } + } + + private class MessagesPigeonInternalProxyApiCodecWriter: MessagesPigeonCodecWriter { + unowned let pigeonRegistrar: MessagesPigeonProxyApiRegistrar + + init(data: NSMutableData, pigeonRegistrar: MessagesPigeonProxyApiRegistrar) { + self.pigeonRegistrar = pigeonRegistrar + super.init(data: data) + } + + override func writeValue(_ value: Any) { + if value is [Any] || value is Bool || value is Data || value is [AnyHashable: Any] + || value is Double || value is FlutterStandardTypedData || value is Int64 || value is String + || value is Code + { + super.writeValue(value) + return + } + + if let instance = value as? SimpleExampleNativeClass { + pigeonRegistrar.apiDelegate.pigeonApiSimpleExampleNativeClass(pigeonRegistrar) + .pigeonNewInstance( + pigeonInstance: instance + ) { _ in } + super.writeByte(128) + super.writeValue( + pigeonRegistrar.instanceManager.identifierWithStrongReference( + forInstance: instance as AnyObject)!) + return + } + + if let instance = value as? ComplexExampleNativeClass { + pigeonRegistrar.apiDelegate.pigeonApiComplexExampleNativeClass(pigeonRegistrar) + .pigeonNewInstance( + pigeonInstance: instance + ) { _ in } + super.writeByte(128) + super.writeValue( + pigeonRegistrar.instanceManager.identifierWithStrongReference( + forInstance: instance as AnyObject)!) + return + } + + if let instance = value as? ExampleNativeSuperClass { + pigeonRegistrar.apiDelegate.pigeonApiExampleNativeSuperClass(pigeonRegistrar) + .pigeonNewInstance( + pigeonInstance: instance + ) { _ in } + super.writeByte(128) + super.writeValue( + pigeonRegistrar.instanceManager.identifierWithStrongReference( + forInstance: instance as AnyObject)!) + return + } + + if let instance = value as? ExampleNativeInterface { + pigeonRegistrar.apiDelegate.pigeonApiExampleNativeInterface(pigeonRegistrar) + .pigeonNewInstance( + pigeonInstance: instance + ) { _ in } + super.writeByte(128) + super.writeValue( + pigeonRegistrar.instanceManager.identifierWithStrongReference( + forInstance: instance as AnyObject)!) + return + } + + if let instance = value as AnyObject?, + pigeonRegistrar.instanceManager.containsInstance(instance) + { + super.writeByte(128) + super.writeValue( + pigeonRegistrar.instanceManager.identifierWithStrongReference(forInstance: instance)!) + } else { + print("Unsupported value: \(value) of \(type(of: value))") + assert(false, "Unsupported value for MessagesPigeonInternalProxyApiCodecWriter") + } + + } + } + + init(pigeonRegistrar: MessagesPigeonProxyApiRegistrar) { + self.pigeonRegistrar = pigeonRegistrar + } + + override func reader(with data: Data) -> FlutterStandardReader { + return MessagesPigeonInternalProxyApiCodecReader(data: data, pigeonRegistrar: pigeonRegistrar) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return MessagesPigeonInternalProxyApiCodecWriter(data: data, pigeonRegistrar: pigeonRegistrar) + } +} enum Code: Int { case one = 0 @@ -269,3 +762,486 @@ class MessageFlutterApi: MessageFlutterApiProtocol { } } } +protocol PigeonApiDelegateSimpleExampleNativeClass { + func pigeonDefaultConstructor( + pigeonApi: PigeonApiSimpleExampleNativeClass, aField: String, aParameter: String + ) throws -> SimpleExampleNativeClass + func aField( + pigeonApi: PigeonApiSimpleExampleNativeClass, pigeonInstance: SimpleExampleNativeClass + ) throws -> String + /// Makes a call from Dart to native language. + func hostMethod( + pigeonApi: PigeonApiSimpleExampleNativeClass, pigeonInstance: SimpleExampleNativeClass, + aParameter: String + ) throws -> String +} + +protocol PigeonApiProtocolSimpleExampleNativeClass { + /// Makes a call from native language to Dart. + func flutterMethod( + pigeonInstance pigeonInstanceArg: SimpleExampleNativeClass, aParameter aParameterArg: String, + completion: @escaping (Result) -> Void) +} + +final class PigeonApiSimpleExampleNativeClass: PigeonApiProtocolSimpleExampleNativeClass { + unowned let pigeonRegistrar: MessagesPigeonProxyApiRegistrar + let pigeonDelegate: PigeonApiDelegateSimpleExampleNativeClass + init( + pigeonRegistrar: MessagesPigeonProxyApiRegistrar, + delegate: PigeonApiDelegateSimpleExampleNativeClass + ) { + self.pigeonRegistrar = pigeonRegistrar + self.pigeonDelegate = delegate + } + static func setUpMessageHandlers( + binaryMessenger: FlutterBinaryMessenger, api: PigeonApiSimpleExampleNativeClass? + ) { + let codec: FlutterStandardMessageCodec = + api != nil + ? FlutterStandardMessageCodec( + readerWriter: MessagesPigeonInternalProxyApiCodecReaderWriter( + pigeonRegistrar: api!.pigeonRegistrar)) + : FlutterStandardMessageCodec.sharedInstance() + let pigeonDefaultConstructorChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.pigeon_example_package.SimpleExampleNativeClass.pigeon_defaultConstructor", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + pigeonDefaultConstructorChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let pigeonIdentifierArg = args[0] as! Int64 + let aFieldArg = args[1] as! String + let aParameterArg = args[2] as! String + do { + api.pigeonRegistrar.instanceManager.addDartCreatedInstance( + try api.pigeonDelegate.pigeonDefaultConstructor( + pigeonApi: api, aField: aFieldArg, aParameter: aParameterArg), + withIdentifier: pigeonIdentifierArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + pigeonDefaultConstructorChannel.setMessageHandler(nil) + } + let hostMethodChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.pigeon_example_package.SimpleExampleNativeClass.hostMethod", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + hostMethodChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let pigeonInstanceArg = args[0] as! SimpleExampleNativeClass + let aParameterArg = args[1] as! String + do { + let result = try api.pigeonDelegate.hostMethod( + pigeonApi: api, pigeonInstance: pigeonInstanceArg, aParameter: aParameterArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + hostMethodChannel.setMessageHandler(nil) + } + } + + ///Creates a Dart instance of SimpleExampleNativeClass and attaches it to [pigeonInstance]. + func pigeonNewInstance( + pigeonInstance: SimpleExampleNativeClass, + completion: @escaping (Result) -> Void + ) { + if pigeonRegistrar.ignoreCallsToDart { + completion( + .failure( + PigeonError( + code: "ignore-calls-error", + message: "Calls to Dart are being ignored.", details: ""))) + return + } + if pigeonRegistrar.instanceManager.containsInstance(pigeonInstance as AnyObject) { + completion(.success(Void())) + return + } + let pigeonIdentifierArg = pigeonRegistrar.instanceManager.addHostCreatedInstance( + pigeonInstance as AnyObject) + let aFieldArg = try! pigeonDelegate.aField(pigeonApi: self, pigeonInstance: pigeonInstance) + let binaryMessenger = pigeonRegistrar.binaryMessenger + let codec = pigeonRegistrar.codec + let channelName: String = + "dev.flutter.pigeon.pigeon_example_package.SimpleExampleNativeClass.pigeon_newInstance" + let channel = FlutterBasicMessageChannel( + name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([pigeonIdentifierArg, aFieldArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(Void())) + } + } + } + /// Makes a call from native language to Dart. + func flutterMethod( + pigeonInstance pigeonInstanceArg: SimpleExampleNativeClass, aParameter aParameterArg: String, + completion: @escaping (Result) -> Void + ) { + if pigeonRegistrar.ignoreCallsToDart { + completion( + .failure( + PigeonError( + code: "ignore-calls-error", + message: "Calls to Dart are being ignored.", details: ""))) + return + } + let binaryMessenger = pigeonRegistrar.binaryMessenger + let codec = pigeonRegistrar.codec + let channelName: String = + "dev.flutter.pigeon.pigeon_example_package.SimpleExampleNativeClass.flutterMethod" + let channel = FlutterBasicMessageChannel( + name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([pigeonInstanceArg, aParameterArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(Void())) + } + } + } + +} +protocol PigeonApiDelegateComplexExampleNativeClass { + func aStaticField(pigeonApi: PigeonApiComplexExampleNativeClass) throws -> ExampleNativeSuperClass + func anAttachedField( + pigeonApi: PigeonApiComplexExampleNativeClass, pigeonInstance: ComplexExampleNativeClass + ) throws -> ExampleNativeSuperClass + func staticHostMethod(pigeonApi: PigeonApiComplexExampleNativeClass) throws -> String +} + +protocol PigeonApiProtocolComplexExampleNativeClass { +} + +final class PigeonApiComplexExampleNativeClass: PigeonApiProtocolComplexExampleNativeClass { + unowned let pigeonRegistrar: MessagesPigeonProxyApiRegistrar + let pigeonDelegate: PigeonApiDelegateComplexExampleNativeClass + ///An implementation of [ExampleNativeSuperClass] used to access callback methods + var pigeonApiExampleNativeSuperClass: PigeonApiExampleNativeSuperClass { + return pigeonRegistrar.apiDelegate.pigeonApiExampleNativeSuperClass(pigeonRegistrar) + } + + ///An implementation of [ExampleNativeInterface] used to access callback methods + var pigeonApiExampleNativeInterface: PigeonApiExampleNativeInterface { + return pigeonRegistrar.apiDelegate.pigeonApiExampleNativeInterface(pigeonRegistrar) + } + + init( + pigeonRegistrar: MessagesPigeonProxyApiRegistrar, + delegate: PigeonApiDelegateComplexExampleNativeClass + ) { + self.pigeonRegistrar = pigeonRegistrar + self.pigeonDelegate = delegate + } + static func setUpMessageHandlers( + binaryMessenger: FlutterBinaryMessenger, api: PigeonApiComplexExampleNativeClass? + ) { + let codec: FlutterStandardMessageCodec = + api != nil + ? FlutterStandardMessageCodec( + readerWriter: MessagesPigeonInternalProxyApiCodecReaderWriter( + pigeonRegistrar: api!.pigeonRegistrar)) + : FlutterStandardMessageCodec.sharedInstance() + let aStaticFieldChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.pigeon_example_package.ComplexExampleNativeClass.aStaticField", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + aStaticFieldChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let pigeonIdentifierArg = args[0] as! Int64 + do { + api.pigeonRegistrar.instanceManager.addDartCreatedInstance( + try api.pigeonDelegate.aStaticField(pigeonApi: api), withIdentifier: pigeonIdentifierArg + ) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + aStaticFieldChannel.setMessageHandler(nil) + } + let anAttachedFieldChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.pigeon_example_package.ComplexExampleNativeClass.anAttachedField", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + anAttachedFieldChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let pigeonInstanceArg = args[0] as! ComplexExampleNativeClass + let pigeonIdentifierArg = args[1] as! Int64 + do { + api.pigeonRegistrar.instanceManager.addDartCreatedInstance( + try api.pigeonDelegate.anAttachedField( + pigeonApi: api, pigeonInstance: pigeonInstanceArg), + withIdentifier: pigeonIdentifierArg) + reply(wrapResult(nil)) + } catch { + reply(wrapError(error)) + } + } + } else { + anAttachedFieldChannel.setMessageHandler(nil) + } + let staticHostMethodChannel = FlutterBasicMessageChannel( + name: "dev.flutter.pigeon.pigeon_example_package.ComplexExampleNativeClass.staticHostMethod", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + staticHostMethodChannel.setMessageHandler { _, reply in + do { + let result = try api.pigeonDelegate.staticHostMethod(pigeonApi: api) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + staticHostMethodChannel.setMessageHandler(nil) + } + } + + ///Creates a Dart instance of ComplexExampleNativeClass and attaches it to [pigeonInstance]. + func pigeonNewInstance( + pigeonInstance: ComplexExampleNativeClass, + completion: @escaping (Result) -> Void + ) { + if pigeonRegistrar.ignoreCallsToDart { + completion( + .failure( + PigeonError( + code: "ignore-calls-error", + message: "Calls to Dart are being ignored.", details: ""))) + return + } + if pigeonRegistrar.instanceManager.containsInstance(pigeonInstance as AnyObject) { + completion(.success(Void())) + return + } + let pigeonIdentifierArg = pigeonRegistrar.instanceManager.addHostCreatedInstance( + pigeonInstance as AnyObject) + let binaryMessenger = pigeonRegistrar.binaryMessenger + let codec = pigeonRegistrar.codec + let channelName: String = + "dev.flutter.pigeon.pigeon_example_package.ComplexExampleNativeClass.pigeon_newInstance" + let channel = FlutterBasicMessageChannel( + name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([pigeonIdentifierArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(Void())) + } + } + } +} +protocol PigeonApiDelegateExampleNativeSuperClass { + func inheritedSuperClassMethod( + pigeonApi: PigeonApiExampleNativeSuperClass, pigeonInstance: ExampleNativeSuperClass + ) throws -> String +} + +protocol PigeonApiProtocolExampleNativeSuperClass { +} + +final class PigeonApiExampleNativeSuperClass: PigeonApiProtocolExampleNativeSuperClass { + unowned let pigeonRegistrar: MessagesPigeonProxyApiRegistrar + let pigeonDelegate: PigeonApiDelegateExampleNativeSuperClass + init( + pigeonRegistrar: MessagesPigeonProxyApiRegistrar, + delegate: PigeonApiDelegateExampleNativeSuperClass + ) { + self.pigeonRegistrar = pigeonRegistrar + self.pigeonDelegate = delegate + } + static func setUpMessageHandlers( + binaryMessenger: FlutterBinaryMessenger, api: PigeonApiExampleNativeSuperClass? + ) { + let codec: FlutterStandardMessageCodec = + api != nil + ? FlutterStandardMessageCodec( + readerWriter: MessagesPigeonInternalProxyApiCodecReaderWriter( + pigeonRegistrar: api!.pigeonRegistrar)) + : FlutterStandardMessageCodec.sharedInstance() + let inheritedSuperClassMethodChannel = FlutterBasicMessageChannel( + name: + "dev.flutter.pigeon.pigeon_example_package.ExampleNativeSuperClass.inheritedSuperClassMethod", + binaryMessenger: binaryMessenger, codec: codec) + if let api = api { + inheritedSuperClassMethodChannel.setMessageHandler { message, reply in + let args = message as! [Any?] + let pigeonInstanceArg = args[0] as! ExampleNativeSuperClass + do { + let result = try api.pigeonDelegate.inheritedSuperClassMethod( + pigeonApi: api, pigeonInstance: pigeonInstanceArg) + reply(wrapResult(result)) + } catch { + reply(wrapError(error)) + } + } + } else { + inheritedSuperClassMethodChannel.setMessageHandler(nil) + } + } + + ///Creates a Dart instance of ExampleNativeSuperClass and attaches it to [pigeonInstance]. + func pigeonNewInstance( + pigeonInstance: ExampleNativeSuperClass, + completion: @escaping (Result) -> Void + ) { + if pigeonRegistrar.ignoreCallsToDart { + completion( + .failure( + PigeonError( + code: "ignore-calls-error", + message: "Calls to Dart are being ignored.", details: ""))) + return + } + if pigeonRegistrar.instanceManager.containsInstance(pigeonInstance as AnyObject) { + completion(.success(Void())) + return + } + let pigeonIdentifierArg = pigeonRegistrar.instanceManager.addHostCreatedInstance( + pigeonInstance as AnyObject) + let binaryMessenger = pigeonRegistrar.binaryMessenger + let codec = pigeonRegistrar.codec + let channelName: String = + "dev.flutter.pigeon.pigeon_example_package.ExampleNativeSuperClass.pigeon_newInstance" + let channel = FlutterBasicMessageChannel( + name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([pigeonIdentifierArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(Void())) + } + } + } +} +open class PigeonApiDelegateExampleNativeInterface { +} + +protocol PigeonApiProtocolExampleNativeInterface { + func inheritedInterfaceMethod( + pigeonInstance pigeonInstanceArg: ExampleNativeInterface, + completion: @escaping (Result) -> Void) +} + +final class PigeonApiExampleNativeInterface: PigeonApiProtocolExampleNativeInterface { + unowned let pigeonRegistrar: MessagesPigeonProxyApiRegistrar + let pigeonDelegate: PigeonApiDelegateExampleNativeInterface + init( + pigeonRegistrar: MessagesPigeonProxyApiRegistrar, + delegate: PigeonApiDelegateExampleNativeInterface + ) { + self.pigeonRegistrar = pigeonRegistrar + self.pigeonDelegate = delegate + } + ///Creates a Dart instance of ExampleNativeInterface and attaches it to [pigeonInstance]. + func pigeonNewInstance( + pigeonInstance: ExampleNativeInterface, + completion: @escaping (Result) -> Void + ) { + if pigeonRegistrar.ignoreCallsToDart { + completion( + .failure( + PigeonError( + code: "ignore-calls-error", + message: "Calls to Dart are being ignored.", details: ""))) + return + } + if pigeonRegistrar.instanceManager.containsInstance(pigeonInstance as AnyObject) { + completion(.success(Void())) + return + } + let pigeonIdentifierArg = pigeonRegistrar.instanceManager.addHostCreatedInstance( + pigeonInstance as AnyObject) + let binaryMessenger = pigeonRegistrar.binaryMessenger + let codec = pigeonRegistrar.codec + let channelName: String = + "dev.flutter.pigeon.pigeon_example_package.ExampleNativeInterface.pigeon_newInstance" + let channel = FlutterBasicMessageChannel( + name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([pigeonIdentifierArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(Void())) + } + } + } + func inheritedInterfaceMethod( + pigeonInstance pigeonInstanceArg: ExampleNativeInterface, + completion: @escaping (Result) -> Void + ) { + if pigeonRegistrar.ignoreCallsToDart { + completion( + .failure( + PigeonError( + code: "ignore-calls-error", + message: "Calls to Dart are being ignored.", details: ""))) + return + } + let binaryMessenger = pigeonRegistrar.binaryMessenger + let codec = pigeonRegistrar.codec + let channelName: String = + "dev.flutter.pigeon.pigeon_example_package.ExampleNativeInterface.inheritedInterfaceMethod" + let channel = FlutterBasicMessageChannel( + name: channelName, binaryMessenger: binaryMessenger, codec: codec) + channel.sendMessage([pigeonInstanceArg] as [Any?]) { response in + guard let listResponse = response as? [Any?] else { + completion(.failure(createConnectionError(withChannelName: channelName))) + return + } + if listResponse.count > 1 { + let code: String = listResponse[0] as! String + let message: String? = nilOrValue(listResponse[1]) + let details: String? = nilOrValue(listResponse[2]) + completion(.failure(PigeonError(code: code, message: message, details: details))) + } else { + completion(.success(Void())) + } + } + } + +} diff --git a/packages/pigeon/example/app/ios/Runner/ProxyAPIImpl.swift b/packages/pigeon/example/app/ios/Runner/ProxyAPIImpl.swift new file mode 100644 index 000000000000..55d239864ba8 --- /dev/null +++ b/packages/pigeon/example/app/ios/Runner/ProxyAPIImpl.swift @@ -0,0 +1,91 @@ +// 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. + +// #docregion simple-proxy-api +class SimpleExampleNativeClassImpl: SimpleExampleNativeClass { + let api: PigeonApiSimpleExampleNativeClass + + init(api: PigeonApiSimpleExampleNativeClass, aField: String, aParameter: String) { + self.api = api + super.init(aField: aField, aParameter: aField) + } + + override func flutterMethod(aParameter: String) { + api.flutterMethod(pigeonInstance: self, aParameter: aParameter) { _ in } + } +} + +class SimpleExampleNativeClassAPIDelegate: PigeonApiDelegateSimpleExampleNativeClass { + func pigeonDefaultConstructor( + pigeonApi: PigeonApiSimpleExampleNativeClass, aField: String, aParameter: String + ) throws -> SimpleExampleNativeClass { + return SimpleExampleNativeClassImpl(api: pigeonApi, aField: aField, aParameter: aParameter) + } + + func aField( + pigeonApi: PigeonApiSimpleExampleNativeClass, pigeonInstance: SimpleExampleNativeClass + ) throws -> String { + return pigeonInstance.aField + } + + func hostMethod( + pigeonApi: PigeonApiSimpleExampleNativeClass, pigeonInstance: SimpleExampleNativeClass, + aParameter: String + ) throws -> String { + return pigeonInstance.hostMethod(aParamter: aParameter) + } +} +// #enddocregion simple-proxy-api + +class ComplexExampleNativeClassAPIDelegate: PigeonApiDelegateComplexExampleNativeClass { + func aStaticField(pigeonApi: PigeonApiComplexExampleNativeClass) throws -> ExampleNativeSuperClass + { + return ComplexExampleNativeClass.aStaticField + } + + func anAttachedField( + pigeonApi: PigeonApiComplexExampleNativeClass, pigeonInstance: ComplexExampleNativeClass + ) throws -> ExampleNativeSuperClass { + return pigeonInstance.anAttachedField + } + + func staticHostMethod(pigeonApi: PigeonApiComplexExampleNativeClass) throws -> String { + return ComplexExampleNativeClass.staticHostMethod() + } +} + +class ExampleNativeSuperClassAPIDelegate: PigeonApiDelegateExampleNativeSuperClass { + func inheritedSuperClassMethod( + pigeonApi: PigeonApiExampleNativeSuperClass, pigeonInstance: ExampleNativeSuperClass + ) throws -> String { + return pigeonInstance.inheritedSuperClassMethod() + } +} + +// #docregion simple-proxy-registrar +open class ProxyAPIDelegate: MessagesPigeonProxyApiDelegate { + func pigeonApiSimpleExampleNativeClass(_ registrar: MessagesPigeonProxyApiRegistrar) + -> PigeonApiSimpleExampleNativeClass + { + return PigeonApiSimpleExampleNativeClass( + pigeonRegistrar: registrar, delegate: SimpleExampleNativeClassAPIDelegate()) + } + // #enddocregion simple-proxy-registrar + + func pigeonApiComplexExampleNativeClass(_ registrar: MessagesPigeonProxyApiRegistrar) + -> PigeonApiComplexExampleNativeClass + { + return PigeonApiComplexExampleNativeClass( + pigeonRegistrar: registrar, delegate: ComplexExampleNativeClassAPIDelegate()) + } + + func pigeonApiExampleNativeSuperClass(_ registrar: MessagesPigeonProxyApiRegistrar) + -> PigeonApiExampleNativeSuperClass + { + return PigeonApiExampleNativeSuperClass( + pigeonRegistrar: registrar, delegate: ExampleNativeSuperClassAPIDelegate()) + } + // #docregion simple-proxy-registrar +} +// #enddocregion simple-proxy-registrar diff --git a/packages/pigeon/example/app/lib/main.dart b/packages/pigeon/example/app/lib/main.dart index cc158abe0c1f..c56794d6be1f 100644 --- a/packages/pigeon/example/app/lib/main.dart +++ b/packages/pigeon/example/app/lib/main.dart @@ -85,6 +85,21 @@ class _MyHomePageState extends State { } // #enddocregion main-dart + Future useProxyClass() async { + // #docregion simple-proxy-class + final SimpleExampleNativeClass instance = SimpleExampleNativeClass( + aField: 'my field', + aParameter: 'my parameter', + flutterMethod: (SimpleExampleNativeClass instance, String aParameter) { + debugPrint(aParameter); + }, + ); + + debugPrint(instance.aField); + debugPrint(await instance.hostMethod('my parameter')); + // #enddocregion simple-proxy-class + } + @override void initState() { super.initState(); diff --git a/packages/pigeon/example/app/lib/src/messages.g.dart b/packages/pigeon/example/app/lib/src/messages.g.dart index 330809525762..a12c5436bd4f 100644 --- a/packages/pigeon/example/app/lib/src/messages.g.dart +++ b/packages/pigeon/example/app/lib/src/messages.g.dart @@ -8,8 +8,10 @@ import 'dart:async'; import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; -import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/foundation.dart' + show ReadBuffer, WriteBuffer, immutable, protected; import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart' show WidgetsFlutterBinding; PlatformException _createConnectionError(String channelName) { return PlatformException( @@ -29,6 +31,382 @@ List wrapResponse( return [error.code, error.message, error.details]; } +/// An immutable object that serves as the base class for all ProxyApis and +/// can provide functional copies of itself. +/// +/// All implementers are expected to be [immutable] as defined by the annotation +/// and override [pigeon_copy] returning an instance of itself. +@immutable +abstract class PigeonInternalProxyApiBaseClass { + /// Construct a [PigeonInternalProxyApiBaseClass]. + PigeonInternalProxyApiBaseClass({ + this.pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + }) : pigeon_instanceManager = + pigeon_instanceManager ?? PigeonInstanceManager.instance; + + /// Sends and receives binary data across the Flutter platform barrier. + /// + /// If it is null, the default BinaryMessenger will be used, which routes to + /// the host platform. + @protected + final BinaryMessenger? pigeon_binaryMessenger; + + /// Maintains instances stored to communicate with native language objects. + @protected + final PigeonInstanceManager pigeon_instanceManager; + + /// Instantiates and returns a functionally identical object to oneself. + /// + /// Outside of tests, this method should only ever be called by + /// [PigeonInstanceManager]. + /// + /// Subclasses should always override their parent's implementation of this + /// method. + @protected + PigeonInternalProxyApiBaseClass pigeon_copy(); +} + +/// Maintains instances used to communicate with the native objects they +/// represent. +/// +/// Added instances are stored as weak references and their copies are stored +/// as strong references to maintain access to their variables and callback +/// methods. Both are stored with the same identifier. +/// +/// When a weak referenced instance becomes inaccessible, +/// [onWeakReferenceRemoved] is called with its associated identifier. +/// +/// If an instance is retrieved and has the possibility to be used, +/// (e.g. calling [getInstanceWithWeakReference]) a copy of the strong reference +/// is added as a weak reference with the same identifier. This prevents a +/// scenario where the weak referenced instance was released and then later +/// returned by the host platform. +class PigeonInstanceManager { + /// Constructs a [PigeonInstanceManager]. + PigeonInstanceManager({required void Function(int) onWeakReferenceRemoved}) { + this.onWeakReferenceRemoved = (int identifier) { + _weakInstances.remove(identifier); + onWeakReferenceRemoved(identifier); + }; + _finalizer = Finalizer(this.onWeakReferenceRemoved); + } + + // Identifiers are locked to a specific range to avoid collisions with objects + // created simultaneously by the host platform. + // Host uses identifiers >= 2^16 and Dart is expected to use values n where, + // 0 <= n < 2^16. + static const int _maxDartCreatedIdentifier = 65536; + + /// The default [PigeonInstanceManager] used by ProxyApis. + /// + /// On creation, this manager makes a call to clear the native + /// InstanceManager. This is to prevent identifier conflicts after a host + /// restart. + static final PigeonInstanceManager instance = _initInstance(); + + // Expando is used because it doesn't prevent its keys from becoming + // inaccessible. This allows the manager to efficiently retrieve an identifier + // of an instance without holding a strong reference to that instance. + // + // It also doesn't use `==` to search for identifiers, which would lead to an + // infinite loop when comparing an object to its copy. (i.e. which was caused + // by calling instanceManager.getIdentifier() inside of `==` while this was a + // HashMap). + final Expando _identifiers = Expando(); + final Map> + _weakInstances = >{}; + final Map _strongInstances = + {}; + late final Finalizer _finalizer; + int _nextIdentifier = 0; + + /// Called when a weak referenced instance is removed by [removeWeakReference] + /// or becomes inaccessible. + late final void Function(int) onWeakReferenceRemoved; + + static PigeonInstanceManager _initInstance() { + WidgetsFlutterBinding.ensureInitialized(); + final _PigeonInternalInstanceManagerApi api = + _PigeonInternalInstanceManagerApi(); + // Clears the native `PigeonInstanceManager` on the initial use of the Dart one. + api.clear(); + final PigeonInstanceManager instanceManager = PigeonInstanceManager( + onWeakReferenceRemoved: (int identifier) { + api.removeStrongReference(identifier); + }, + ); + _PigeonInternalInstanceManagerApi.setUpMessageHandlers( + instanceManager: instanceManager); + SimpleExampleNativeClass.pigeon_setUpMessageHandlers( + pigeon_instanceManager: instanceManager); + ComplexExampleNativeClass.pigeon_setUpMessageHandlers( + pigeon_instanceManager: instanceManager); + ExampleNativeSuperClass.pigeon_setUpMessageHandlers( + pigeon_instanceManager: instanceManager); + ExampleNativeInterface.pigeon_setUpMessageHandlers( + pigeon_instanceManager: instanceManager); + return instanceManager; + } + + /// Adds a new instance that was instantiated by Dart. + /// + /// In other words, Dart wants to add a new instance that will represent + /// an object that will be instantiated on the host platform. + /// + /// Throws assertion error if the instance has already been added. + /// + /// Returns the randomly generated id of the [instance] added. + int addDartCreatedInstance(PigeonInternalProxyApiBaseClass instance) { + final int identifier = _nextUniqueIdentifier(); + _addInstanceWithIdentifier(instance, identifier); + return identifier; + } + + /// Removes the instance, if present, and call [onWeakReferenceRemoved] with + /// its identifier. + /// + /// Returns the identifier associated with the removed instance. Otherwise, + /// `null` if the instance was not found in this manager. + /// + /// This does not remove the strong referenced instance associated with + /// [instance]. This can be done with [remove]. + int? removeWeakReference(PigeonInternalProxyApiBaseClass instance) { + final int? identifier = getIdentifier(instance); + if (identifier == null) { + return null; + } + + _identifiers[instance] = null; + _finalizer.detach(instance); + onWeakReferenceRemoved(identifier); + + return identifier; + } + + /// Removes [identifier] and its associated strongly referenced instance, if + /// present, from the manager. + /// + /// Returns the strong referenced instance associated with [identifier] before + /// it was removed. Returns `null` if [identifier] was not associated with + /// any strong reference. + /// + /// This does not remove the weak referenced instance associated with + /// [identifier]. This can be done with [removeWeakReference]. + T? remove(int identifier) { + return _strongInstances.remove(identifier) as T?; + } + + /// Retrieves the instance associated with identifier. + /// + /// The value returned is chosen from the following order: + /// + /// 1. A weakly referenced instance associated with identifier. + /// 2. If the only instance associated with identifier is a strongly + /// referenced instance, a copy of the instance is added as a weak reference + /// with the same identifier. Returning the newly created copy. + /// 3. If no instance is associated with identifier, returns null. + /// + /// This method also expects the host `InstanceManager` to have a strong + /// reference to the instance the identifier is associated with. + T? getInstanceWithWeakReference( + int identifier) { + final PigeonInternalProxyApiBaseClass? weakInstance = + _weakInstances[identifier]?.target; + + if (weakInstance == null) { + final PigeonInternalProxyApiBaseClass? strongInstance = + _strongInstances[identifier]; + if (strongInstance != null) { + final PigeonInternalProxyApiBaseClass copy = + strongInstance.pigeon_copy(); + _identifiers[copy] = identifier; + _weakInstances[identifier] = + WeakReference(copy); + _finalizer.attach(copy, identifier, detach: copy); + return copy as T; + } + return strongInstance as T?; + } + + return weakInstance as T; + } + + /// Retrieves the identifier associated with instance. + int? getIdentifier(PigeonInternalProxyApiBaseClass instance) { + return _identifiers[instance]; + } + + /// Adds a new instance that was instantiated by the host platform. + /// + /// In other words, the host platform wants to add a new instance that + /// represents an object on the host platform. Stored with [identifier]. + /// + /// Throws assertion error if the instance or its identifier has already been + /// added. + /// + /// Returns unique identifier of the [instance] added. + void addHostCreatedInstance( + PigeonInternalProxyApiBaseClass instance, int identifier) { + _addInstanceWithIdentifier(instance, identifier); + } + + void _addInstanceWithIdentifier( + PigeonInternalProxyApiBaseClass instance, int identifier) { + assert(!containsIdentifier(identifier)); + assert(getIdentifier(instance) == null); + assert(identifier >= 0); + + _identifiers[instance] = identifier; + _weakInstances[identifier] = + WeakReference(instance); + _finalizer.attach(instance, identifier, detach: instance); + + final PigeonInternalProxyApiBaseClass copy = instance.pigeon_copy(); + _identifiers[copy] = identifier; + _strongInstances[identifier] = copy; + } + + /// Whether this manager contains the given [identifier]. + bool containsIdentifier(int identifier) { + return _weakInstances.containsKey(identifier) || + _strongInstances.containsKey(identifier); + } + + int _nextUniqueIdentifier() { + late int identifier; + do { + identifier = _nextIdentifier; + _nextIdentifier = (_nextIdentifier + 1) % _maxDartCreatedIdentifier; + } while (containsIdentifier(identifier)); + return identifier; + } +} + +/// Generated API for managing the Dart and native `PigeonInstanceManager`s. +class _PigeonInternalInstanceManagerApi { + /// Constructor for [_PigeonInternalInstanceManagerApi]. + _PigeonInternalInstanceManagerApi({BinaryMessenger? binaryMessenger}) + : pigeonVar_binaryMessenger = binaryMessenger; + + final BinaryMessenger? pigeonVar_binaryMessenger; + + static const MessageCodec pigeonChannelCodec = _PigeonCodec(); + + static void setUpMessageHandlers({ + bool pigeon_clearHandlers = false, + BinaryMessenger? binaryMessenger, + PigeonInstanceManager? instanceManager, + }) { + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_example_package.PigeonInternalInstanceManager.removeStrongReference', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_example_package.PigeonInternalInstanceManager.removeStrongReference was null.'); + final List args = (message as List?)!; + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.pigeon_example_package.PigeonInternalInstanceManager.removeStrongReference was null, expected non-null int.'); + try { + (instanceManager ?? PigeonInstanceManager.instance) + .remove(arg_identifier!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } + + Future removeStrongReference(int identifier) async { + const String pigeonVar_channelName = + 'dev.flutter.pigeon.pigeon_example_package.PigeonInternalInstanceManager.removeStrongReference'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([identifier]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } + + /// Clear the native `PigeonInstanceManager`. + /// + /// This is typically called after a hot restart. + Future clear() async { + const String pigeonVar_channelName = + 'dev.flutter.pigeon.pigeon_example_package.PigeonInternalInstanceManager.clear'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send(null) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + } +} + +class _PigeonInternalProxyApiBaseCodec extends _PigeonCodec { + const _PigeonInternalProxyApiBaseCodec(this.instanceManager); + final PigeonInstanceManager instanceManager; + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is PigeonInternalProxyApiBaseClass) { + buffer.putUint8(128); + writeValue(buffer, instanceManager.getIdentifier(value)); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 128: + return instanceManager + .getInstanceWithWeakReference(readValue(buffer)! as int); + default: + return super.readValueOfType(type, buffer); + } + } +} + enum Code { one, two, @@ -246,3 +624,647 @@ abstract class MessageFlutterApi { } } } + +class SimpleExampleNativeClass extends PigeonInternalProxyApiBaseClass { + SimpleExampleNativeClass({ + super.pigeon_binaryMessenger, + super.pigeon_instanceManager, + required this.aField, + this.flutterMethod, + required String aParameter, + }) { + final int pigeonVar_instanceIdentifier = + pigeon_instanceManager.addDartCreatedInstance(this); + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _pigeonVar_codecSimpleExampleNativeClass; + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + () async { + const String pigeonVar_channelName = + 'dev.flutter.pigeon.pigeon_example_package.SimpleExampleNativeClass.pigeon_defaultConstructor'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([pigeonVar_instanceIdentifier, aField, aParameter]) + as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + }(); + } + + /// Constructs [SimpleExampleNativeClass] without creating the associated native object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies for an [PigeonInstanceManager]. + @protected + SimpleExampleNativeClass.pigeon_detached({ + super.pigeon_binaryMessenger, + super.pigeon_instanceManager, + required this.aField, + this.flutterMethod, + }); + + late final _PigeonInternalProxyApiBaseCodec + _pigeonVar_codecSimpleExampleNativeClass = + _PigeonInternalProxyApiBaseCodec(pigeon_instanceManager); + + final String aField; + + /// Makes a call from native language to Dart. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final SimpleExampleNativeClass instance = SimpleExampleNativeClass( + /// flutterMethod: (SimpleExampleNativeClass pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final void Function( + SimpleExampleNativeClass pigeon_instance, + String aParameter, + )? flutterMethod; + + static void pigeon_setUpMessageHandlers({ + bool pigeon_clearHandlers = false, + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + SimpleExampleNativeClass Function(String aField)? pigeon_newInstance, + void Function( + SimpleExampleNativeClass pigeon_instance, + String aParameter, + )? flutterMethod, + }) { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _PigeonInternalProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? binaryMessenger = pigeon_binaryMessenger; + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_example_package.SimpleExampleNativeClass.pigeon_newInstance', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_example_package.SimpleExampleNativeClass.pigeon_newInstance was null.'); + final List args = (message as List?)!; + final int? arg_pigeon_instanceIdentifier = (args[0] as int?); + assert(arg_pigeon_instanceIdentifier != null, + 'Argument for dev.flutter.pigeon.pigeon_example_package.SimpleExampleNativeClass.pigeon_newInstance was null, expected non-null int.'); + final String? arg_aField = (args[1] as String?); + assert(arg_aField != null, + 'Argument for dev.flutter.pigeon.pigeon_example_package.SimpleExampleNativeClass.pigeon_newInstance was null, expected non-null String.'); + try { + (pigeon_instanceManager ?? PigeonInstanceManager.instance) + .addHostCreatedInstance( + pigeon_newInstance?.call(arg_aField!) ?? + SimpleExampleNativeClass.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + aField: arg_aField!, + ), + arg_pigeon_instanceIdentifier!, + ); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_example_package.SimpleExampleNativeClass.flutterMethod', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_example_package.SimpleExampleNativeClass.flutterMethod was null.'); + final List args = (message as List?)!; + final SimpleExampleNativeClass? arg_pigeon_instance = + (args[0] as SimpleExampleNativeClass?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_example_package.SimpleExampleNativeClass.flutterMethod was null, expected non-null SimpleExampleNativeClass.'); + final String? arg_aParameter = (args[1] as String?); + assert(arg_aParameter != null, + 'Argument for dev.flutter.pigeon.pigeon_example_package.SimpleExampleNativeClass.flutterMethod was null, expected non-null String.'); + try { + (flutterMethod ?? arg_pigeon_instance!.flutterMethod) + ?.call(arg_pigeon_instance!, arg_aParameter!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } + + /// Makes a call from Dart to native language. + Future hostMethod(String aParameter) async { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _pigeonVar_codecSimpleExampleNativeClass; + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + const String pigeonVar_channelName = + 'dev.flutter.pigeon.pigeon_example_package.SimpleExampleNativeClass.hostMethod'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([this, aParameter]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as String?)!; + } + } + + @override + SimpleExampleNativeClass pigeon_copy() { + return SimpleExampleNativeClass.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + aField: aField, + flutterMethod: flutterMethod, + ); + } +} + +class ComplexExampleNativeClass extends ExampleNativeSuperClass + implements ExampleNativeInterface { + /// Constructs [ComplexExampleNativeClass] without creating the associated native object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies for an [PigeonInstanceManager]. + @protected + ComplexExampleNativeClass.pigeon_detached({ + super.pigeon_binaryMessenger, + super.pigeon_instanceManager, + this.inheritedInterfaceMethod, + }) : super.pigeon_detached(); + + late final _PigeonInternalProxyApiBaseCodec + _pigeonVar_codecComplexExampleNativeClass = + _PigeonInternalProxyApiBaseCodec(pigeon_instanceManager); + + @override + final void Function(ExampleNativeInterface pigeon_instance)? + inheritedInterfaceMethod; + + static final ExampleNativeSuperClass aStaticField = pigeonVar_aStaticField(); + + late final ExampleNativeSuperClass anAttachedField = + pigeonVar_anAttachedField(); + + static void pigeon_setUpMessageHandlers({ + bool pigeon_clearHandlers = false, + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + ComplexExampleNativeClass Function()? pigeon_newInstance, + }) { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _PigeonInternalProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? binaryMessenger = pigeon_binaryMessenger; + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_example_package.ComplexExampleNativeClass.pigeon_newInstance', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_example_package.ComplexExampleNativeClass.pigeon_newInstance was null.'); + final List args = (message as List?)!; + final int? arg_pigeon_instanceIdentifier = (args[0] as int?); + assert(arg_pigeon_instanceIdentifier != null, + 'Argument for dev.flutter.pigeon.pigeon_example_package.ComplexExampleNativeClass.pigeon_newInstance was null, expected non-null int.'); + try { + (pigeon_instanceManager ?? PigeonInstanceManager.instance) + .addHostCreatedInstance( + pigeon_newInstance?.call() ?? + ComplexExampleNativeClass.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + ), + arg_pigeon_instanceIdentifier!, + ); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } + + static ExampleNativeSuperClass pigeonVar_aStaticField() { + final ExampleNativeSuperClass pigeonVar_instance = + ExampleNativeSuperClass.pigeon_detached(); + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _PigeonInternalProxyApiBaseCodec(PigeonInstanceManager.instance); + final BinaryMessenger pigeonVar_binaryMessenger = + ServicesBinding.instance.defaultBinaryMessenger; + final int pigeonVar_instanceIdentifier = PigeonInstanceManager.instance + .addDartCreatedInstance(pigeonVar_instance); + () async { + const String pigeonVar_channelName = + 'dev.flutter.pigeon.pigeon_example_package.ComplexExampleNativeClass.aStaticField'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([pigeonVar_instanceIdentifier]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + }(); + return pigeonVar_instance; + } + + ExampleNativeSuperClass pigeonVar_anAttachedField() { + final ExampleNativeSuperClass pigeonVar_instance = + ExampleNativeSuperClass.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + ); + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _pigeonVar_codecComplexExampleNativeClass; + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + final int pigeonVar_instanceIdentifier = + pigeon_instanceManager.addDartCreatedInstance(pigeonVar_instance); + () async { + const String pigeonVar_channelName = + 'dev.flutter.pigeon.pigeon_example_package.ComplexExampleNativeClass.anAttachedField'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = await pigeonVar_channel + .send([this, pigeonVar_instanceIdentifier]) + as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else { + return; + } + }(); + return pigeonVar_instance; + } + + static Future staticHostMethod({ + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + }) async { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _PigeonInternalProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + const String pigeonVar_channelName = + 'dev.flutter.pigeon.pigeon_example_package.ComplexExampleNativeClass.staticHostMethod'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send(null) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as String?)!; + } + } + + @override + ComplexExampleNativeClass pigeon_copy() { + return ComplexExampleNativeClass.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + inheritedInterfaceMethod: inheritedInterfaceMethod, + ); + } +} + +class ExampleNativeSuperClass extends PigeonInternalProxyApiBaseClass { + /// Constructs [ExampleNativeSuperClass] without creating the associated native object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies for an [PigeonInstanceManager]. + @protected + ExampleNativeSuperClass.pigeon_detached({ + super.pigeon_binaryMessenger, + super.pigeon_instanceManager, + }); + + late final _PigeonInternalProxyApiBaseCodec + _pigeonVar_codecExampleNativeSuperClass = + _PigeonInternalProxyApiBaseCodec(pigeon_instanceManager); + + static void pigeon_setUpMessageHandlers({ + bool pigeon_clearHandlers = false, + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + ExampleNativeSuperClass Function()? pigeon_newInstance, + }) { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _PigeonInternalProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? binaryMessenger = pigeon_binaryMessenger; + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_example_package.ExampleNativeSuperClass.pigeon_newInstance', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_example_package.ExampleNativeSuperClass.pigeon_newInstance was null.'); + final List args = (message as List?)!; + final int? arg_pigeon_instanceIdentifier = (args[0] as int?); + assert(arg_pigeon_instanceIdentifier != null, + 'Argument for dev.flutter.pigeon.pigeon_example_package.ExampleNativeSuperClass.pigeon_newInstance was null, expected non-null int.'); + try { + (pigeon_instanceManager ?? PigeonInstanceManager.instance) + .addHostCreatedInstance( + pigeon_newInstance?.call() ?? + ExampleNativeSuperClass.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + ), + arg_pigeon_instanceIdentifier!, + ); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } + + Future inheritedSuperClassMethod() async { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _pigeonVar_codecExampleNativeSuperClass; + final BinaryMessenger? pigeonVar_binaryMessenger = pigeon_binaryMessenger; + const String pigeonVar_channelName = + 'dev.flutter.pigeon.pigeon_example_package.ExampleNativeSuperClass.inheritedSuperClassMethod'; + final BasicMessageChannel pigeonVar_channel = + BasicMessageChannel( + pigeonVar_channelName, + pigeonChannelCodec, + binaryMessenger: pigeonVar_binaryMessenger, + ); + final List? pigeonVar_replyList = + await pigeonVar_channel.send([this]) as List?; + if (pigeonVar_replyList == null) { + throw _createConnectionError(pigeonVar_channelName); + } else if (pigeonVar_replyList.length > 1) { + throw PlatformException( + code: pigeonVar_replyList[0]! as String, + message: pigeonVar_replyList[1] as String?, + details: pigeonVar_replyList[2], + ); + } else if (pigeonVar_replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (pigeonVar_replyList[0] as String?)!; + } + } + + @override + ExampleNativeSuperClass pigeon_copy() { + return ExampleNativeSuperClass.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + ); + } +} + +class ExampleNativeInterface extends PigeonInternalProxyApiBaseClass { + /// Constructs [ExampleNativeInterface] without creating the associated native object. + /// + /// This should only be used by subclasses created by this library or to + /// create copies for an [PigeonInstanceManager]. + @protected + ExampleNativeInterface.pigeon_detached({ + super.pigeon_binaryMessenger, + super.pigeon_instanceManager, + this.inheritedInterfaceMethod, + }); + + /// Callback method. + /// + /// For the associated Native object to be automatically garbage collected, + /// it is required that the implementation of this `Function` doesn't have a + /// strong reference to the encapsulating class instance. When this `Function` + /// references a non-local variable, it is strongly recommended to access it + /// with a `WeakReference`: + /// + /// ```dart + /// final WeakReference weakMyVariable = WeakReference(myVariable); + /// final ExampleNativeInterface instance = ExampleNativeInterface( + /// inheritedInterfaceMethod: (ExampleNativeInterface pigeon_instance, ...) { + /// print(weakMyVariable?.target); + /// }, + /// ); + /// ``` + /// + /// Alternatively, [PigeonInstanceManager.removeWeakReference] can be used to + /// release the associated Native object manually. + final void Function(ExampleNativeInterface pigeon_instance)? + inheritedInterfaceMethod; + + static void pigeon_setUpMessageHandlers({ + bool pigeon_clearHandlers = false, + BinaryMessenger? pigeon_binaryMessenger, + PigeonInstanceManager? pigeon_instanceManager, + ExampleNativeInterface Function()? pigeon_newInstance, + void Function(ExampleNativeInterface pigeon_instance)? + inheritedInterfaceMethod, + }) { + final _PigeonInternalProxyApiBaseCodec pigeonChannelCodec = + _PigeonInternalProxyApiBaseCodec( + pigeon_instanceManager ?? PigeonInstanceManager.instance); + final BinaryMessenger? binaryMessenger = pigeon_binaryMessenger; + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_example_package.ExampleNativeInterface.pigeon_newInstance', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_example_package.ExampleNativeInterface.pigeon_newInstance was null.'); + final List args = (message as List?)!; + final int? arg_pigeon_instanceIdentifier = (args[0] as int?); + assert(arg_pigeon_instanceIdentifier != null, + 'Argument for dev.flutter.pigeon.pigeon_example_package.ExampleNativeInterface.pigeon_newInstance was null, expected non-null int.'); + try { + (pigeon_instanceManager ?? PigeonInstanceManager.instance) + .addHostCreatedInstance( + pigeon_newInstance?.call() ?? + ExampleNativeInterface.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + ), + arg_pigeon_instanceIdentifier!, + ); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + + { + final BasicMessageChannel< + Object?> pigeonVar_channel = BasicMessageChannel< + Object?>( + 'dev.flutter.pigeon.pigeon_example_package.ExampleNativeInterface.inheritedInterfaceMethod', + pigeonChannelCodec, + binaryMessenger: binaryMessenger); + if (pigeon_clearHandlers) { + pigeonVar_channel.setMessageHandler(null); + } else { + pigeonVar_channel.setMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.pigeon_example_package.ExampleNativeInterface.inheritedInterfaceMethod was null.'); + final List args = (message as List?)!; + final ExampleNativeInterface? arg_pigeon_instance = + (args[0] as ExampleNativeInterface?); + assert(arg_pigeon_instance != null, + 'Argument for dev.flutter.pigeon.pigeon_example_package.ExampleNativeInterface.inheritedInterfaceMethod was null, expected non-null ExampleNativeInterface.'); + try { + (inheritedInterfaceMethod ?? + arg_pigeon_instance!.inheritedInterfaceMethod) + ?.call(arg_pigeon_instance!); + return wrapResponse(empty: true); + } on PlatformException catch (e) { + return wrapResponse(error: e); + } catch (e) { + return wrapResponse( + error: PlatformException(code: 'error', message: e.toString())); + } + }); + } + } + } + + @override + ExampleNativeInterface pigeon_copy() { + return ExampleNativeInterface.pigeon_detached( + pigeon_binaryMessenger: pigeon_binaryMessenger, + pigeon_instanceManager: pigeon_instanceManager, + inheritedInterfaceMethod: inheritedInterfaceMethod, + ); + } +} diff --git a/packages/pigeon/example/app/pigeons/messages.dart b/packages/pigeon/example/app/pigeons/messages.dart index 2112fed96371..f4185e68daae 100644 --- a/packages/pigeon/example/app/pigeons/messages.dart +++ b/packages/pigeon/example/app/pigeons/messages.dart @@ -63,3 +63,63 @@ abstract class MessageFlutterApi { String flutterMethod(String? aString); } // #enddocregion flutter-definitions + +// #docregion simple-proxy-definition +@ProxyApi( + kotlinOptions: KotlinProxyApiOptions( + fullClassName: 'dev.flutter.pigeon_example_app.SimpleExampleNativeClass', + ), + swiftOptions: SwiftProxyApiOptions( + name: 'SimpleExampleNativeClass', + ), +) +abstract class SimpleExampleNativeClass { + // ignore: avoid_unused_constructor_parameters + SimpleExampleNativeClass(String aParameter); + + late String aField; + + /// Makes a call from native language to Dart. + late void Function(String aParameter)? flutterMethod; + + /// Makes a call from Dart to native language. + String hostMethod(String aParameter); +} +// #enddocregion simple-proxy-definition + +// #docregion complex-proxy-definition +@ProxyApi( + kotlinOptions: KotlinProxyApiOptions( + fullClassName: 'dev.flutter.pigeon_example_app.ComplexExampleNativeClass', + ), +) +abstract class ComplexExampleNativeClass extends ExampleNativeSuperClass + implements ExampleNativeInterface { + @static + late ExampleNativeSuperClass aStaticField; + + @attached + late ExampleNativeSuperClass anAttachedField; + + @static + String staticHostMethod(); +} + +@ProxyApi( + kotlinOptions: KotlinProxyApiOptions( + fullClassName: 'dev.flutter.pigeon_example_app.ExampleNativeSuperClass', + ), +) +abstract class ExampleNativeSuperClass { + String inheritedSuperClassMethod(); +} + +@ProxyApi( + kotlinOptions: KotlinProxyApiOptions( + fullClassName: 'dev.flutter.pigeon_example_app.ExampleNativeInterface', + ), +) +abstract class ExampleNativeInterface { + late void Function()? inheritedInterfaceMethod; +} +// #enddocregion complex-proxy-definition diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart index 8349285f1021..117cf8d6d946 100644 --- a/packages/pigeon/lib/generator_tools.dart +++ b/packages/pigeon/lib/generator_tools.dart @@ -14,7 +14,7 @@ import 'ast.dart'; /// The current version of pigeon. /// /// This must match the version in pubspec.yaml. -const String pigeonVersion = '22.6.1'; +const String pigeonVersion = '22.6.2'; /// Read all the content from [stdin] to a String. String readStdin() { diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index 31b736278cee..76b8cecce380 100644 --- a/packages/pigeon/pubspec.yaml +++ b/packages/pigeon/pubspec.yaml @@ -2,7 +2,7 @@ name: pigeon description: Code generator tool to make communication between Flutter and the host platform type-safe and easier. repository: https://github.com/flutter/packages/tree/main/packages/pigeon issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+pigeon%22 -version: 22.6.1 # This must match the version in lib/generator_tools.dart +version: 22.6.2 # This must match the version in lib/generator_tools.dart environment: sdk: ^3.3.0 diff --git a/packages/pigeon/tool/shared/generation.dart b/packages/pigeon/tool/shared/generation.dart index 4cee0d4de568..9416eb788d94 100644 --- a/packages/pigeon/tool/shared/generation.dart +++ b/packages/pigeon/tool/shared/generation.dart @@ -118,7 +118,7 @@ Future generateTestPigeons( ? null : '$outputBase/ios/Classes/$pascalCaseName.gen.swift', swiftErrorClassName: swiftErrorClassName, - swiftIncludeErrorClass: input != 'core_tests', + swiftIncludeErrorClass: input != 'background_platform_channels', // Linux gobjectHeaderOut: skipLanguages.contains(GeneratorLanguage.gobject) ? null @@ -150,7 +150,7 @@ Future generateTestPigeons( ? null : '$outputBase/macos/Classes/$pascalCaseName.gen.swift', swiftErrorClassName: swiftErrorClassName, - swiftIncludeErrorClass: input != 'core_tests', + swiftIncludeErrorClass: input != 'background_platform_channels', suppressVersion: true, dartPackageName: 'pigeon_integration_tests', injectOverflowTypes: includeOverflow && input == 'core_tests',