From 959374ceb15fcf5777622bb04dcd8651a09ab65b Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Fri, 25 Oct 2024 12:13:10 -0700 Subject: [PATCH 01/20] add event channels and sealed inheritance --- packages/pigeon/CHANGELOG.md | 7 + packages/pigeon/README.md | 7 +- packages/pigeon/example/README.md | 87 +++ .../EventChannelMessages.g.kt | 133 ++++ .../pigeon_example_app/MainActivity.kt | 51 +- .../app/ios/Runner.xcodeproj/project.pbxproj | 4 + .../example/app/ios/Runner/AppDelegate.swift | 34 +- .../ios/Runner/EventChannelMessages.g.swift | 179 ++++++ packages/pigeon/example/app/lib/main.dart | 30 + .../app/lib/src/event_channel_messages.g.dart | 102 ++++ .../app/pigeons/event_channel_messages.dart | 43 ++ .../pigeon/example/app/pigeons/messages.dart | 2 - packages/pigeon/lib/ast.dart | 63 +- packages/pigeon/lib/cpp_generator.dart | 14 +- packages/pigeon/lib/dart_generator.dart | 147 +++-- packages/pigeon/lib/generator.dart | 17 + packages/pigeon/lib/generator_tools.dart | 40 +- packages/pigeon/lib/gobject_generator.dart | 5 +- packages/pigeon/lib/java_generator.dart | 13 +- packages/pigeon/lib/kotlin_generator.dart | 112 +++- packages/pigeon/lib/objc_generator.dart | 15 +- packages/pigeon/lib/pigeon_lib.dart | 174 +++++- packages/pigeon/lib/swift_generator.dart | 144 ++++- .../pigeon/pigeons/event_channel_tests.dart | 144 +++++ .../lib/example_app.dart | 45 +- .../lib/generated.dart | 1 + .../lib/integration_tests.dart | 51 ++ .../generated/event_channel_tests.gen.dart | 453 ++++++++++++++ .../kotlin/com/example/test_plugin/.gitignore | 1 + .../test_plugin/EventChannelTests.gen.kt | 472 ++++++++++++++ .../com/example/test_plugin/TestPlugin.kt | 59 ++ .../test_plugin/ios/Classes/.gitignore | 1 + .../ios/Classes/EventChannelTests.gen.swift | 576 ++++++++++++++++++ .../test_plugin/ios/Classes/TestPlugin.swift | 196 ++++-- .../test_plugin/macos/Classes/.gitignore | 3 +- .../macos/Classes/EventChannelTests.gen.swift | 576 ++++++++++++++++++ .../macos/Classes/TestPlugin.swift | 51 ++ packages/pigeon/pubspec.yaml | 2 +- packages/pigeon/test/cpp_generator_test.dart | 41 +- packages/pigeon/test/dart_generator_test.dart | 27 +- packages/pigeon/test/java_generator_test.dart | 83 +-- .../pigeon/test/kotlin_generator_test.dart | 2 + packages/pigeon/test/objc_generator_test.dart | 1 + packages/pigeon/test/pigeon_lib_test.dart | 98 +++ .../pigeon/test/swift_generator_test.dart | 1 + packages/pigeon/tool/shared/generation.dart | 97 +-- 46 files changed, 4093 insertions(+), 311 deletions(-) create mode 100644 packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt create mode 100644 packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift create mode 100644 packages/pigeon/example/app/lib/src/event_channel_messages.g.dart create mode 100644 packages/pigeon/example/app/pigeons/event_channel_messages.dart create mode 100644 packages/pigeon/pigeons/event_channel_tests.dart create mode 100644 packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_tests.gen.dart create mode 100644 packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt create mode 100644 packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift create mode 100644 packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index 85842f046117..ddf6271e8a3c 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,3 +1,10 @@ +## 22.7.0 + +* [swift] Adds Event Channel support. +* [swift] Adds `sealed` class inheritance support. +* [kotlin] Adds Event Channel support. +* [kotlin] Adds `sealed` class inheritance support. + ## 22.6.0 * [swift] Adds `includeErrorClass` to `SwiftOptions`. diff --git a/packages/pigeon/README.md b/packages/pigeon/README.md index cc375f5e2faa..0ec4c1948f39 100644 --- a/packages/pigeon/README.md +++ b/packages/pigeon/README.md @@ -26,6 +26,8 @@ Pigeon uses the `StandardMessageCodec` so it supports Custom classes, nested datatypes, and enums are also supported. +Basic inheritance with empty `sealed` parent classes is allowed only on swift and kotlin. + Nullable enums in Objective-C generated code will be wrapped in a class to allow for nullability. By default, custom classes in Swift are defined as structs. @@ -104,8 +106,9 @@ to the api to allow for multiple instances to be created and operate in parallel 1) Method declarations on the API classes should have arguments and a return value whose types are defined in the file, are supported datatypes, or are `void`. -1) Generics are supported, but can currently only be used with nullable types - (example: `List`). +1) Event Channels are supported only on swift and kotlin generators. +1) Event Channel methods should be wrapped in an `abstract class` with the metadata `@EventChannelApi`. +1) Event Channel definitions should not include the `Stream` return type, just the type that is being streamed. 1) Objc and Swift have special naming conventions that can be utilized with the `@ObjCSelector` and `@SwiftFunction` respectively. diff --git a/packages/pigeon/example/README.md b/packages/pigeon/example/README.md index bd5e4bd116af..76f1d5f82940 100644 --- a/packages/pigeon/example/README.md +++ b/packages/pigeon/example/README.md @@ -351,6 +351,93 @@ pigeon_example_package_message_flutter_api_flutter_method( self->flutter_api, "hello", nullptr, flutter_method_cb, self); ``` +## Event Channel Example + +This example gives a basic overview of how to use Pigeon to set up an Event Channel method. + +### Dart input + + +```dart +@EventChannelApi() +abstract class EventApi { + SealedBaseClass streamEvents(); +} +``` + +### Dart + +The generated dart code will include a method that returns a `Stream` when invoked. +This `Stream` can then be used as any other `Stream` would be in dart. + + +```dart + final Stream events = streamEvents(); +``` + +### Swift + + +```swift +class SendEvents: StreamEventsStreamHandler { + var timerActive = false + + override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) { + var count = 0 + if !timerActive { + timerActive = true + Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in + if count >= 10 { + sink.endOfStream() + } else { + if (count % 2) == 0 { + sink.success(IntEvent(data: Int64(count))) + } else { + sink.success(StringEvent(data: String(count))) + } + count += 1 + } + } + } + } +} +``` + +### Kotlin + + +```kotlin +object SendClass : StreamEventsStreamHandler() { + val handler = Handler(Looper.getMainLooper()) + + override fun onListen(p0: Any?, sink: PigeonEventSink) { + var count: Int = 0 + val r: Runnable = + object : Runnable { + override fun run() { + if (count >= 10) { + sink.endOfStream() + } else { + if (count % 2 == 0) { + handler.post { + sink.success(IntEvent(count.toLong())) + count++ + } + } else { + handler.post { + sink.success(StringEvent(count.toString())) + count++ + } + } + handler.postDelayed(this, 1000) + } + } + } + handler.postDelayed(r, 1000) + } +} +``` + ## 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/EventChannelMessages.g.kt b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt new file mode 100644 index 000000000000..c43333aa65b9 --- /dev/null +++ b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt @@ -0,0 +1,133 @@ +// 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. +// Autogenerated from Pigeon, do not edit directly. +// See also: https://pub.dev/packages/pigeon +@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") + +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.EventChannel +import io.flutter.plugin.common.StandardMessageCodec +import io.flutter.plugin.common.StandardMethodCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer + +/** + * Generated class from Pigeon that represents data sent in messages. This class should not be + * extended by any user class outside of the generated file. + */ +sealed class SealedBaseClass +/** Generated class from Pigeon that represents data sent in messages. */ +data class IntEvent(val data: Long) : SealedBaseClass() { + companion object { + fun fromList(pigeonVar_list: List): IntEvent { + val data = pigeonVar_list[0] as Long + return IntEvent(data) + } + } + + fun toList(): List { + return listOf( + data, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class StringEvent(val data: String) : SealedBaseClass() { + companion object { + fun fromList(pigeonVar_list: List): StringEvent { + val data = pigeonVar_list[0] as String + return StringEvent(data) + } + } + + fun toList(): List { + return listOf( + data, + ) + } +} + +private open class EventChannelMessagesPigeonCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return when (type) { + 129.toByte() -> { + return (readValue(buffer) as? List)?.let { IntEvent.fromList(it) } + } + 130.toByte() -> { + return (readValue(buffer) as? List)?.let { StringEvent.fromList(it) } + } + else -> super.readValueOfType(type, buffer) + } + } + + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + when (value) { + is IntEvent -> { + stream.write(129) + writeValue(stream, value.toList()) + } + is StringEvent -> { + stream.write(130) + writeValue(stream, value.toList()) + } + else -> super.writeValue(stream, value) + } + } +} + +val EventChannelMessagesPigeonMethodCodec = StandardMethodCodec(EventChannelMessagesPigeonCodec()) + +private class PigeonStreamHandler(val wrapper: PigeonEventChannelWrapper) : + EventChannel.StreamHandler { + var pigeonSink: PigeonEventSink? = null + + override fun onListen(p0: Any?, sink: EventChannel.EventSink) { + pigeonSink = PigeonEventSink(sink) + wrapper.onListen(p0, pigeonSink!!) + } + + override fun onCancel(p0: Any?) { + pigeonSink = null + wrapper.onCancel(p0) + } +} + +interface PigeonEventChannelWrapper { + open fun onListen(p0: Any?, sink: PigeonEventSink) {} + + open fun onCancel(p0: Any?) {} +} + +class PigeonEventSink(private val sink: EventChannel.EventSink) { + fun success(value: T) { + sink.success(value) + } + + fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { + sink.error(errorCode, errorMessage, errorDetails) + } + + fun endOfStream() { + sink.endOfStream() + } +} + +abstract class StreamEventsStreamHandler : PigeonEventChannelWrapper { + companion object { + fun register( + messenger: BinaryMessenger, + wrapper: StreamEventsStreamHandler, + instanceName: String = "" + ) { + var channelName: String = "dev.flutter.pigeon.pigeon_example_package.EventApi.streamEvents" + if (instanceName.isNotEmpty()) { + channelName += ".$instanceName" + } + val streamHandler = PigeonStreamHandler(wrapper) + EventChannel(messenger, channelName, EventChannelMessagesPigeonMethodCodec) + .setStreamHandler(streamHandler) + } + } +} 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..9f4e1078658a 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 @@ -6,9 +6,15 @@ package dev.flutter.pigeon_example_app import ExampleHostApi import FlutterError +import IntEvent import MessageData import MessageFlutterApi -import androidx.annotation.NonNull +import PigeonEventSink +import SealedBaseClass +import StreamEventsStreamHandler +import StringEvent +import android.os.Handler +import android.os.Looper import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.embedding.engine.plugins.FlutterPlugin @@ -37,25 +43,58 @@ private class PigeonApiImplementation : ExampleHostApi { // #enddocregion kotlin-class // #docregion kotlin-class-flutter -private class PigeonFlutterApi { +private class PigeonFlutterApi(binding: FlutterPlugin.FlutterPluginBinding) { var flutterApi: MessageFlutterApi? = null - constructor(binding: FlutterPlugin.FlutterPluginBinding) { - flutterApi = MessageFlutterApi(binding.getBinaryMessenger()) + init { + flutterApi = MessageFlutterApi(binding.binaryMessenger) } fun callFlutterMethod(aString: String, callback: (Result) -> Unit) { - flutterApi!!.flutterMethod(aString) { echo -> callback(Result.success(echo)) } + flutterApi!!.flutterMethod(aString) { echo -> callback(echo) } } } // #enddocregion kotlin-class-flutter +// #docregion kotlin-class-event +object SendClass : StreamEventsStreamHandler() { + val handler = Handler(Looper.getMainLooper()) + + override fun onListen(p0: Any?, sink: PigeonEventSink) { + var count: Int = 0 + val r: Runnable = + object : Runnable { + override fun run() { + if (count >= 10) { + sink.endOfStream() + } else { + if (count % 2 == 0) { + handler.post { + sink.success(IntEvent(count.toLong())) + count++ + } + } else { + handler.post { + sink.success(StringEvent(count.toString())) + count++ + } + } + handler.postDelayed(this, 1000) + } + } + } + handler.postDelayed(r, 1000) + } +} +// #enddocregion kotlin-class-event + class MainActivity : FlutterActivity() { - override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { + override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) val api = PigeonApiImplementation() ExampleHostApi.setUp(flutterEngine.dartExecutor.binaryMessenger, api) + StreamEventsStreamHandler.register(flutterEngine.dartExecutor.binaryMessenger, SendClass) } } diff --git a/packages/pigeon/example/app/ios/Runner.xcodeproj/project.pbxproj b/packages/pigeon/example/app/ios/Runner.xcodeproj/project.pbxproj index 6541f88ab296..e934bc3d3877 100644 --- a/packages/pigeon/example/app/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/pigeon/example/app/ios/Runner.xcodeproj/project.pbxproj @@ -10,6 +10,7 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 3368472729F02D040090029A /* Messages.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3368472629F02D040090029A /* Messages.g.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 477F5F842CCC1D8D006725C4 /* EventChannelMessages.g.swift in Sources */ = {isa = PBXBuildFile; fileRef = 477F5F832CCC1D8D006725C4 /* EventChannelMessages.g.swift */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; @@ -34,6 +35,7 @@ 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 = ""; }; + 477F5F832CCC1D8D006725C4 /* EventChannelMessages.g.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventChannelMessages.g.swift; 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 = ""; }; @@ -88,6 +90,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( + 477F5F832CCC1D8D006725C4 /* EventChannelMessages.g.swift */, 97C146FA1CF9000F007C117D /* Main.storyboard */, 97C146FD1CF9000F007C117D /* Assets.xcassets */, 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, @@ -213,6 +216,7 @@ 3368472729F02D040090029A /* Messages.g.swift in Sources */, 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, + 477F5F842CCC1D8D006725C4 /* EventChannelMessages.g.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift index 51119e23fa38..a6cab8a449d7 100644 --- a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift +++ b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift @@ -37,26 +37,52 @@ private class PigeonFlutterApi { } func callFlutterMethod( - aString aStringArg: String?, completion: @escaping (Result) -> Void + aString aStringArg: String?, completion: @escaping (Result) -> Void ) { flutterAPI.flutterMethod(aString: aStringArg) { - completion(.success($0)) + completion($0) } } } // #enddocregion swift-class-flutter -@UIApplicationMain +// #docregion swift-class-event +class SendEvents: StreamEventsStreamHandler { + var timerActive = false + + override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) { + var count = 0 + if !timerActive { + timerActive = true + Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in + if count >= 10 { + sink.endOfStream() + } else { + if (count % 2) == 0 { + sink.success(IntEvent(data: Int64(count))) + } else { + sink.success(StringEvent(data: String(count))) + } + count += 1 + } + } + } + } +} +// #enddocregion swift-class-event + +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { - GeneratedPluginRegistrant.register(with: self) + GeneratedPluginRegistrant.register(with: self) let controller = window?.rootViewController as! FlutterViewController let api = PigeonApiImplementation() ExampleHostApiSetup.setUp(binaryMessenger: controller.binaryMessenger, api: api) + StreamEventsStreamHandler.register(with: controller.binaryMessenger, wrapper: SendEvents()) return super.application(application, didFinishLaunchingWithOptions: launchOptions) diff --git a/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift b/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift new file mode 100644 index 000000000000..6058be9fd454 --- /dev/null +++ b/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift @@ -0,0 +1,179 @@ +// 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. +// Autogenerated from Pigeon, do not edit directly. +// See also: https://pub.dev/packages/pigeon + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +private func isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + +private func nilOrValue(_ value: Any?) -> T? { + if value is NSNull { return nil } + return value as! T? +} + +/// Generated class from Pigeon that represents data sent in messages. +/// This class should not be extended by any user class outside of the generated file. +protocol SealedBaseClass { + +} + +/// Generated class from Pigeon that represents data sent in messages. +struct IntEvent: SealedBaseClass { + var data: Int64 + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> IntEvent? { + let data = pigeonVar_list[0] as! Int64 + + return IntEvent( + data: data + ) + } + func toList() -> [Any?] { + return [ + data + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct StringEvent: SealedBaseClass { + var data: String + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> StringEvent? { + let data = pigeonVar_list[0] as! String + + return StringEvent( + data: data + ) + } + func toList() -> [Any?] { + return [ + data + ] + } +} + +private class EventChannelMessagesPigeonCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 129: + return IntEvent.fromList(self.readValue() as! [Any?]) + case 130: + return StringEvent.fromList(self.readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class EventChannelMessagesPigeonCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? IntEvent { + super.writeByte(129) + super.writeValue(value.toList()) + } else if let value = value as? StringEvent { + super.writeByte(130) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class EventChannelMessagesPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return EventChannelMessagesPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return EventChannelMessagesPigeonCodecWriter(data: data) + } +} + +class EventChannelMessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = EventChannelMessagesPigeonCodec( + readerWriter: EventChannelMessagesPigeonCodecReaderWriter()) +} + +var eventChannelMessagesPigeonMethodCodec = FlutterStandardMethodCodec( + readerWriter: EventChannelMessagesPigeonCodecReaderWriter()) + +private class PigeonStreamHandler: NSObject, FlutterStreamHandler { + private let wrapper: PigeonEventChannelWrapper + private var pigeonSink: PigeonEventSink? = nil + + init(wrapper: PigeonEventChannelWrapper) { + self.wrapper = wrapper + } + + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) + -> FlutterError? + { + pigeonSink = PigeonEventSink(events) + wrapper.onListen(withArguments: arguments, sink: pigeonSink!) + return nil + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + pigeonSink = nil + wrapper.onCancel(withArguments: arguments) + return nil + } +} + +class PigeonEventChannelWrapper { + func onListen(withArguments arguments: Any?, sink: PigeonEventSink) {} + func onCancel(withArguments arguments: Any?) {} +} + +class PigeonEventSink { + private let sink: FlutterEventSink + + init(_ sink: @escaping FlutterEventSink) { + self.sink = sink + } + + func success(_ value: ReturnType) { + sink(value) + } + + func error(code: String, message: String?, details: Any?) { + sink(FlutterError(code: code, message: message, details: details)) + } + + func endOfStream() { + sink(FlutterEndOfEventStream) + } + +} + +class StreamEventsStreamHandler: PigeonEventChannelWrapper { + static func register( + with messenger: FlutterBinaryMessenger, + instanceName: String = "", + wrapper: StreamEventsStreamHandler + ) { + var channelName = "dev.flutter.pigeon.pigeon_integration_tests.EventChannelCoreApi.streamEvents" + if !instanceName.isEmpty { + channelName += ".\(instanceName)" + } + let streamHandler = PigeonStreamHandler(wrapper: wrapper) + let channel = FlutterEventChannel( + name: channelName, binaryMessenger: messenger, codec: eventChannelMessagesPigeonMethodCodec) + channel.setStreamHandler(streamHandler) + } +} diff --git a/packages/pigeon/example/app/lib/main.dart b/packages/pigeon/example/app/lib/main.dart index cc158abe0c1f..57b415377972 100644 --- a/packages/pigeon/example/app/lib/main.dart +++ b/packages/pigeon/example/app/lib/main.dart @@ -7,6 +7,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'src/event_channel_messages.g.dart'; import 'src/messages.g.dart'; // #docregion main-dart-flutter @@ -85,6 +86,13 @@ class _MyHomePageState extends State { } // #enddocregion main-dart + Stream getEventStream() { + // #docregion main-dart_event + final Stream events = streamEvents(); + // #enddocregion main-dart_event + return events; + } + @override void initState() { super.initState(); @@ -101,6 +109,7 @@ class _MyHomePageState extends State { @override Widget build(BuildContext context) { + String numsSoFar = ''; return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, @@ -114,6 +123,27 @@ class _MyHomePageState extends State { _hostCallResult ?? 'Waiting for host language...', ), if (_hostCallResult == null) const CircularProgressIndicator(), + StreamBuilder( + stream: streamEvents(), + builder: (BuildContext context, + AsyncSnapshot snapshot) { + if (snapshot.hasData) { + String newString = ''; + switch (snapshot.data) { + case null: + newString = 'null, '; + case IntEvent(): + newString = '${(snapshot.data! as IntEvent).data}, '; + case StringEvent(): + newString = '${(snapshot.data! as StringEvent).data}, '; + } + numsSoFar += newString; + return Text(numsSoFar); + } else { + return const CircularProgressIndicator(); + } + }, + ) ], ), ), diff --git a/packages/pigeon/example/app/lib/src/event_channel_messages.g.dart b/packages/pigeon/example/app/lib/src/event_channel_messages.g.dart new file mode 100644 index 000000000000..3ebb0d46bf06 --- /dev/null +++ b/packages/pigeon/example/app/lib/src/event_channel_messages.g.dart @@ -0,0 +1,102 @@ +// 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. +// Autogenerated from Pigeon, do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +sealed class SealedBaseClass {} + +class IntEvent extends SealedBaseClass { + IntEvent({ + required this.data, + }); + + int data; + + Object encode() { + return [ + data, + ]; + } + + static IntEvent decode(Object result) { + result as List; + return IntEvent( + data: result[0]! as int, + ); + } +} + +class StringEvent extends SealedBaseClass { + StringEvent({ + required this.data, + }); + + String data; + + Object encode() { + return [ + data, + ]; + } + + static StringEvent decode(Object result) { + result as List; + return StringEvent( + data: result[0]! as String, + ); + } +} + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is IntEvent) { + buffer.putUint8(129); + writeValue(buffer, value.encode()); + } else if (value is StringEvent) { + buffer.putUint8(130); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 129: + return IntEvent.decode(readValue(buffer)!); + case 130: + return StringEvent.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +const StandardMethodCodec pigeonMethodCodec = + StandardMethodCodec(_PigeonCodec()); + +Stream streamEvents({String instanceName = ''}) { + if (instanceName.isNotEmpty) { + instanceName = '.$instanceName'; + } + const EventChannel streamEventsChannel = EventChannel( + 'dev.flutter.pigeon.pigeon_example_package.EventApi.streamEvents', + pigeonMethodCodec); + return streamEventsChannel.receiveBroadcastStream().map((dynamic event) { + return event as SealedBaseClass; + }); +} diff --git a/packages/pigeon/example/app/pigeons/event_channel_messages.dart b/packages/pigeon/example/app/pigeons/event_channel_messages.dart new file mode 100644 index 000000000000..322510432acb --- /dev/null +++ b/packages/pigeon/example/app/pigeons/event_channel_messages.dart @@ -0,0 +1,43 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon(PigeonOptions( + dartOut: 'lib/src/event_channel_messages.g.dart', + dartOptions: DartOptions(), + cppOptions: CppOptions(namespace: 'pigeon_example'), + kotlinOut: + 'android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt', + kotlinOptions: KotlinOptions( + includeErrorClass: false, + ), + swiftOut: 'ios/Runner/EventChannelMessages.g.swift', + swiftOptions: SwiftOptions( + includeErrorClass: false, + ), + copyrightHeader: 'pigeons/copyright.txt', + dartPackageName: 'pigeon_example_package', +)) + +// #docregion sealed-definitions +sealed class SealedBaseClass {} + +class IntEvent extends SealedBaseClass { + IntEvent(this.data); + int data; +} + +class StringEvent extends SealedBaseClass { + StringEvent(this.data); + String data; +} +// #enddocregion sealed-definitions + +// #docregion event-definitions +@EventChannelApi() +abstract class EventApi { + SealedBaseClass streamEvents(); +} +// #enddocregion event-definitions diff --git a/packages/pigeon/example/app/pigeons/messages.dart b/packages/pigeon/example/app/pigeons/messages.dart index 2112fed96371..421e6ce1c9f8 100644 --- a/packages/pigeon/example/app/pigeons/messages.dart +++ b/packages/pigeon/example/app/pigeons/messages.dart @@ -30,8 +30,6 @@ import 'package:pigeon/pigeon.dart'; )) // #enddocregion config -// This file and ./messages_test.dart must be identical below this line. - // #docregion host-definitions enum Code { one, two } diff --git a/packages/pigeon/lib/ast.dart b/packages/pigeon/lib/ast.dart index 1c93d4630e30..3dc4f63889bd 100644 --- a/packages/pigeon/lib/ast.dart +++ b/packages/pigeon/lib/ast.dart @@ -341,6 +341,21 @@ class AstProxyApi extends Api { } } +/// Represents a collection of [Method]s that are wrappers for Event +class AstEventChannelApi extends Api { + /// Parametric constructor for [AstEventChannelApi]. + AstEventChannelApi({ + required super.name, + required super.methods, + super.documentationComments = const [], + }); + + @override + String toString() { + return '(EventChannelApi name:$name methods:$methods documentationComments:$documentationComments)'; + } +} + /// Represents a constructor for an API. class Constructor extends Method { /// Parametric constructor for [Constructor]. @@ -680,6 +695,9 @@ class Class extends Node { Class({ required this.name, required this.fields, + this.superClassName, + this.superClass, + this.isSealed = false, this.isReferenced = true, this.isSwiftClass = false, this.documentationComments = const [], @@ -691,6 +709,20 @@ class Class extends Node { /// All the fields contained in the class. List fields; + /// Name of parent class, will be empty when there is no super class. + String? superClassName; + + /// The definition of the parent class. + Class? superClass; + + /// List of class definitions of children. + /// + /// This is only meant to be used by sealed classes used in Event Channel methods. + List children = []; + + /// Whether the class is sealed. + bool isSealed; + /// Whether the class is referenced in any API. bool isReferenced; @@ -709,7 +741,7 @@ class Class extends Node { @override String toString() { - return '(Class name:$name fields:$fields documentationComments:$documentationComments)'; + return '(Class name:$name fields:$fields superClass:$superClassName children:$children isSealed:$isSealed isReferenced:$isReferenced documentationComments:$documentationComments)'; } } @@ -772,6 +804,10 @@ class Root extends Node { required this.classes, required this.apis, required this.enums, + this.containsHostApi = false, + this.containsFlutterApi = false, + this.containsProxyApi = false, + this.containsEventChannel = false, }); /// Factory function for generating an empty root, usually used when early errors are encountered. @@ -788,10 +824,33 @@ class Root extends Node { /// All of the enums contained in the AST. List enums; + /// Whether the root has any Host API definitions. + bool containsHostApi; + + /// Whether the root has any Flutter API definitions. + bool containsFlutterApi; + + /// Whether the root has any Proxy API definitions. + bool containsProxyApi; + + /// Whether the root has any Event Channel definitions. + bool containsEventChannel; + /// Returns true if the number of custom types would exceed the available enumerations /// on the standard codec. bool get requiresOverflowClass => - classes.length + enums.length >= totalCustomCodecKeysAllowed; + classes.length - _numberOfSealedClasses() + enums.length >= + totalCustomCodecKeysAllowed; + + int _numberOfSealedClasses() { + int count = 0; + for (final Class klass in classes) { + if (klass.isSealed) { + count++; + } + } + return count; + } @override String toString() { diff --git a/packages/pigeon/lib/cpp_generator.dart b/packages/pigeon/lib/cpp_generator.dart index 96579cc23f9a..d911da474220 100644 --- a/packages/pigeon/lib/cpp_generator.dart +++ b/packages/pigeon/lib/cpp_generator.dart @@ -213,15 +213,8 @@ class CppHeaderGenerator extends StructuredGenerator { Indent indent, { required String dartPackageName, }) { - final bool hasHostApi = root.apis - .whereType() - .any((Api api) => api.methods.isNotEmpty); - final bool hasFlutterApi = root.apis - .whereType() - .any((Api api) => api.methods.isNotEmpty); - _writeFlutterError(indent); - if (hasHostApi) { + if (root.containsHostApi) { _writeErrorOr( indent, friends: root.apis @@ -229,9 +222,6 @@ class CppHeaderGenerator extends StructuredGenerator { .map((Api api) => api.name), ); } - if (hasFlutterApi) { - // Nothing yet. - } } @override @@ -1002,7 +992,7 @@ EncodableValue $_overflowClassName::FromEncodableList( required String dartPackageName, }) { final List enumeratedTypes = - getEnumeratedTypes(root).toList(); + getEnumeratedTypes(root, excludeSealedClasses: true).toList(); indent.newln(); if (root.requiresOverflowClass) { _writeCodecOverflowUtilities( diff --git a/packages/pigeon/lib/dart_generator.dart b/packages/pigeon/lib/dart_generator.dart index e8c7550cd708..11e2a7cd9025 100644 --- a/packages/pigeon/lib/dart_generator.dart +++ b/packages/pigeon/lib/dart_generator.dart @@ -30,7 +30,10 @@ const DocumentCommentSpecification _docCommentSpec = DocumentCommentSpecification(_docCommentPrefix); /// The custom codec used for all pigeon APIs. -const String _pigeonCodec = '_PigeonCodec'; +const String _pigeonMessageCodec = '_PigeonCodec'; + +/// Name of field used for host API codec. +const String _pigeonMethodChannelCodec = 'pigeonMethodCodec'; const String _overflowClassName = '_PigeonCodecOverflow'; @@ -118,11 +121,10 @@ class DartGenerator extends StructuredGenerator { ); indent.newln(); - final bool hasProxyApi = root.apis.any((Api api) => api is AstProxyApi); indent.writeln( - "import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer${hasProxyApi ? ', immutable, protected' : ''};"); + "import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer${root.containsProxyApi ? ', immutable, protected' : ''};"); indent.writeln("import 'package:flutter/services.dart';"); - if (hasProxyApi) { + if (root.containsProxyApi) { indent.writeln( "import 'package:flutter/widgets.dart' show WidgetsFlutterBinding;", ); @@ -161,9 +163,16 @@ class DartGenerator extends StructuredGenerator { indent.newln(); addDocumentationComments( indent, classDefinition.documentationComments, _docCommentSpec); + final String sealed = classDefinition.isSealed ? 'sealed ' : ''; + final String implements = classDefinition.superClassName != null + ? 'extends ${classDefinition.superClassName} ' + : ''; - indent.write('class ${classDefinition.name} '); + indent.write('${sealed}class ${classDefinition.name} $implements'); indent.addScoped('{', '}', () { + if (classDefinition.fields.isEmpty) { + return; + } _writeConstructor(indent, classDefinition); indent.newln(); for (final NamedType field @@ -285,10 +294,11 @@ class DartGenerator extends StructuredGenerator { Indent indent, { required String dartPackageName, }) { - void writeEncodeLogic(EnumeratedType customType) { + void writeEncodeLogic(EnumeratedType customType, int offset) { indent.writeScoped('else if (value is ${customType.name}) {', '}', () { - if (customType.enumeration < maximumCodecFieldKey) { - indent.writeln('buffer.putUint8(${customType.enumeration});'); + if (customType.enumeration - offset < maximumCodecFieldKey) { + indent + .writeln('buffer.putUint8(${customType.enumeration - offset});'); if (customType.type == CustomTypes.customClass) { indent.writeln('writeValue(buffer, value.encode());'); } else if (customType.type == CustomTypes.customEnum) { @@ -299,18 +309,18 @@ class DartGenerator extends StructuredGenerator { ? '.encode()' : '.index'; indent.writeln( - 'final $_overflowClassName wrap = $_overflowClassName(type: ${customType.enumeration - maximumCodecFieldKey}, wrapped: value$encodeString);'); + 'final $_overflowClassName wrap = $_overflowClassName(type: ${customType.enumeration - offset - maximumCodecFieldKey}, wrapped: value$encodeString);'); indent.writeln('buffer.putUint8($maximumCodecFieldKey);'); indent.writeln('writeValue(buffer, wrap.encode());'); } }, addTrailingNewline: false); } - void writeDecodeLogic(EnumeratedType customType) { - indent.writeln('case ${customType.enumeration}: '); + void writeDecodeLogic(EnumeratedType customType, int offset) { + indent.writeln('case ${customType.enumeration - offset}: '); indent.nest(1, () { if (customType.type == CustomTypes.customClass) { - if (customType.enumeration == maximumCodecFieldKey) { + if (customType.enumeration - offset == maximumCodecFieldKey) { indent.writeln( 'final ${customType.name} wrapper = ${customType.name}.decode(readValue(buffer)!);'); indent.writeln('return wrapper.unwrap();'); @@ -331,14 +341,14 @@ class DartGenerator extends StructuredGenerator { indent.newln(); final List enumeratedTypes = - getEnumeratedTypes(root).toList(); + getEnumeratedTypes(root, excludeSealedClasses: true).toList(); if (root.requiresOverflowClass) { _writeCodecOverflowUtilities(indent, enumeratedTypes); } indent.newln(); - indent.write('class $_pigeonCodec extends StandardMessageCodec'); + indent.write('class $_pigeonMessageCodec extends StandardMessageCodec'); indent.addScoped(' {', '}', () { - indent.writeln('const $_pigeonCodec();'); + indent.writeln('const $_pigeonMessageCodec();'); indent.writeln('@override'); indent.write('void writeValue(WriteBuffer buffer, Object? value) '); indent.addScoped('{', '}', () { @@ -346,10 +356,14 @@ class DartGenerator extends StructuredGenerator { indent.writeln('buffer.putUint8(4);'); indent.writeln('buffer.putInt64(value);'); }, addTrailingNewline: false); - + int offset = 0; enumerate(enumeratedTypes, (int index, final EnumeratedType customType) { - writeEncodeLogic(customType); + if (customType.associatedClass?.isSealed ?? false) { + offset += 1; + return; + } + writeEncodeLogic(customType, offset); }); indent.addScoped(' else {', '}', () { indent.writeln('super.writeValue(buffer, value);'); @@ -361,13 +375,16 @@ class DartGenerator extends StructuredGenerator { indent.addScoped('{', '}', () { indent.write('switch (type) '); indent.addScoped('{', '}', () { + int offset = 0; for (final EnumeratedType customType in enumeratedTypes) { - if (customType.enumeration < maximumCodecFieldKey) { - writeDecodeLogic(customType); + if (customType.associatedClass?.isSealed ?? false) { + offset++; + } else if (customType.enumeration - offset < maximumCodecFieldKey) { + writeDecodeLogic(customType, offset); } } if (root.requiresOverflowClass) { - writeDecodeLogic(overflowClass); + writeDecodeLogic(overflowClass, 0); } indent.writeln('default:'); indent.nest(1, () { @@ -376,6 +393,11 @@ class DartGenerator extends StructuredGenerator { }); }); }); + if (root.containsEventChannel) { + indent.newln(); + indent.writeln( + 'const StandardMethodCodec $_pigeonMethodChannelCodec = StandardMethodCodec($_pigeonMessageCodec());'); + } } /// Writes the code for host [Api], [api]. @@ -408,7 +430,7 @@ class DartGenerator extends StructuredGenerator { 'static TestDefaultBinaryMessengerBinding? get _testBinaryMessengerBinding => TestDefaultBinaryMessengerBinding.instance;'); } indent.writeln( - 'static const MessageCodec $_pigeonChannelCodec = $_pigeonCodec();'); + 'static const MessageCodec $_pigeonChannelCodec = $_pigeonMessageCodec();'); indent.newln(); for (final Method func in api.methods) { addDocumentationComments( @@ -489,7 +511,7 @@ final BinaryMessenger? ${varNamePrefix}binaryMessenger; '''); indent.writeln( - 'static const MessageCodec $_pigeonChannelCodec = $_pigeonCodec();'); + 'static const MessageCodec $_pigeonChannelCodec = $_pigeonMessageCodec();'); indent.newln(); indent.writeln('final String $_suffixVarName;'); indent.newln(); @@ -512,6 +534,33 @@ final BinaryMessenger? ${varNamePrefix}binaryMessenger; }); } + @override + void writeEventChannelApi( + DartOptions generatorOptions, + Root root, + Indent indent, + AstEventChannelApi api, { + required String dartPackageName, + }) { + indent.newln(); + addDocumentationComments( + indent, api.documentationComments, _docCommentSpec); + for (final Method func in api.methods) { + indent.format(''' + Stream<${func.returnType.baseName}> ${func.name}(${_getMethodParameterSignature(func.parameters, addTrailingComma: true)} {String instanceName = ''}) { + if (instanceName.isNotEmpty) { + instanceName = '.\$instanceName'; + } + const EventChannel ${func.name}Channel = + EventChannel('${makeChannelName(api, func, dartPackageName)}', $_pigeonMethodChannelCodec); + return ${func.name}Channel.receiveBroadcastStream().map((dynamic event) { + return event as ${func.returnType.baseName}; + }); + } + '''); + } + } + @override void writeInstanceManager( DartOptions generatorOptions, @@ -582,7 +631,7 @@ final BinaryMessenger? ${varNamePrefix}binaryMessenger; ..type = cb.refer('MessageCodec') ..static = true ..modifier = cb.FieldModifier.constant - ..assignment = const cb.Code('$_pigeonCodec()'); + ..assignment = const cb.Code('$_pigeonMessageCodec()'); }, ) ], @@ -938,22 +987,12 @@ final BinaryMessenger? ${varNamePrefix}binaryMessenger; Indent indent, { required String dartPackageName, }) { - final bool hasHostMethod = root.apis - .whereType() - .any((AstHostApi api) => api.methods.isNotEmpty) || - root.apis.whereType().any((AstProxyApi api) => - api.constructors.isNotEmpty || - api.attachedFields.isNotEmpty || - api.hostMethods.isNotEmpty); - final bool hasFlutterMethod = root.apis - .whereType() - .any((AstFlutterApi api) => api.methods.isNotEmpty) || - root.apis.any((Api api) => api is AstProxyApi); - - if (hasHostMethod) { + if (root.containsHostApi || root.containsProxyApi) { _writeCreateConnectionError(indent); } - if (hasFlutterMethod || generatorOptions.testOutPath != null) { + if (root.containsFlutterApi || + root.containsProxyApi || + generatorOptions.testOutPath != null) { _writeWrapResponse(generatorOptions, root, indent); } } @@ -1015,16 +1054,21 @@ if (wrapped == null) { } '''); indent.writeScoped('switch (type) {', '}', () { + int offset = 0; for (int i = totalCustomCodecKeysAllowed; i < types.length; i++) { - indent.writeScoped('case ${i - totalCustomCodecKeysAllowed}:', '', - () { - if (types[i].type == CustomTypes.customClass) { - indent.writeln('return ${types[i].name}.decode(wrapped!);'); - } else if (types[i].type == CustomTypes.customEnum) { - indent.writeln( - 'return ${types[i].name}.values[wrapped! as int];'); - } - }); + if (types[i].associatedClass?.isSealed ?? false) { + offset++; + } else { + indent.writeScoped( + 'case ${i - offset - totalCustomCodecKeysAllowed}:', '', () { + if (types[i].type == CustomTypes.customClass) { + indent.writeln('return ${types[i].name}.decode(wrapped!);'); + } else if (types[i].type == CustomTypes.customEnum) { + indent.writeln( + 'return ${types[i].name}.values[wrapped! as int];'); + } + }); + } } }); indent.writeln('return null;'); @@ -2095,7 +2139,10 @@ String _getParameterName(int count, NamedType field) => /// Generates the parameters code for [func] /// Example: (func, _getParameterName) -> 'String? foo, int bar' -String _getMethodParameterSignature(Iterable parameters) { +String _getMethodParameterSignature( + Iterable parameters, { + bool addTrailingComma = false, +}) { String signature = ''; if (parameters.isEmpty) { return signature; @@ -2145,8 +2192,10 @@ String _getMethodParameterSignature(Iterable parameters) { return '$baseParams[$optionalParameterString$trailingComma]'; } if (namedParams.isNotEmpty) { - final String trailingComma = - requiredPositionalParams.length + namedParams.length > 2 ? ',' : ''; + final String trailingComma = addTrailingComma || + requiredPositionalParams.length + namedParams.length > 2 + ? ', ' + : ''; return '$baseParams{$namedParameterString$trailingComma}'; } return signature; diff --git a/packages/pigeon/lib/generator.dart b/packages/pigeon/lib/generator.dart index 952b80e89872..fdfec7b1eb34 100644 --- a/packages/pigeon/lib/generator.dart +++ b/packages/pigeon/lib/generator.dart @@ -282,6 +282,14 @@ abstract class StructuredGenerator extends Generator { api, dartPackageName: dartPackageName, ); + case AstEventChannelApi(): + writeEventChannelApi( + generatorOptions, + root, + indent, + api, + dartPackageName: dartPackageName, + ); } } } @@ -345,4 +353,13 @@ abstract class StructuredGenerator extends Generator { AstProxyApi api, { required String dartPackageName, }) {} + + /// Writes a single Event Channel Api to [indent]. + void writeEventChannelApi( + T generatorOptions, + Root root, + Indent indent, + AstEventChannelApi api, { + required String dartPackageName, + }) {} } diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart index cbec3e8da6fc..ffc0311e40ca 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.0'; +const String pigeonVersion = '22.7.0'; /// Read all the content from [stdin] to a String. String readStdin() { @@ -605,7 +605,10 @@ enum CustomTypes { /// Return the enumerated types that must exist in the codec /// where the enumeration should be the key used in the buffer. -Iterable getEnumeratedTypes(Root root) sync* { +Iterable getEnumeratedTypes( + Root root, { + bool excludeSealedClasses = false, +}) sync* { int index = 0; for (final Enum customEnum in root.enums) { @@ -619,13 +622,15 @@ Iterable getEnumeratedTypes(Root root) sync* { } for (final Class customClass in root.classes) { - yield EnumeratedType( - customClass.name, - index + minimumCodecFieldKey, - CustomTypes.customClass, - associatedClass: customClass, - ); - index += 1; + if (!excludeSealedClasses || !customClass.isSealed) { + yield EnumeratedType( + customClass.name, + index + minimumCodecFieldKey, + CustomTypes.customClass, + associatedClass: customClass, + ); + index += 1; + } } } @@ -801,6 +806,23 @@ String toUpperCamelCase(String text) { }).join(); } +/// Converts strings to Upper Camel Case. +String toLowerCamelCase(String text) { + final RegExp separatorPattern = RegExp(r'[ _-]'); + bool firstWord = true; + return text.split(separatorPattern).map((String word) { + if (firstWord) { + firstWord = false; + return word.isEmpty + ? '' + : word.substring(0, 1).toLowerCase() + word.substring(1); + } + return word.isEmpty + ? '' + : word.substring(0, 1).toUpperCase() + word.substring(1); + }).join(); +} + /// Converts string to SCREAMING_SNAKE_CASE. String toScreamingSnakeCase(String string) { return string diff --git a/packages/pigeon/lib/gobject_generator.dart b/packages/pigeon/lib/gobject_generator.dart index bee03804f53b..2d25a0249807 100644 --- a/packages/pigeon/lib/gobject_generator.dart +++ b/packages/pigeon/lib/gobject_generator.dart @@ -948,7 +948,8 @@ class GObjectSourceGenerator extends StructuredGenerator { final String codecClassName = _getClassName(module, _codecBaseName); final String codecMethodPrefix = _getMethodPrefix(module, _codecBaseName); - final Iterable customTypes = getEnumeratedTypes(root); + final Iterable customTypes = + getEnumeratedTypes(root, excludeSealedClasses: true); indent.newln(); _writeDeclareFinalType(indent, module, _codecBaseName, @@ -2008,7 +2009,7 @@ String _referenceValue(String module, TypeDeclaration type, String variableName, } int _getTypeEnumeration(Root root, TypeDeclaration type) { - return getEnumeratedTypes(root) + return getEnumeratedTypes(root, excludeSealedClasses: true) .firstWhere((EnumeratedType t) => (type.isClass && t.associatedClass == type.associatedClass) || (type.isEnum && t.associatedEnum == type.associatedEnum)) diff --git a/packages/pigeon/lib/java_generator.dart b/packages/pigeon/lib/java_generator.dart index 27a1a5915e78..938b20c2a057 100644 --- a/packages/pigeon/lib/java_generator.dart +++ b/packages/pigeon/lib/java_generator.dart @@ -458,7 +458,7 @@ class JavaGenerator extends StructuredGenerator { required String dartPackageName, }) { final List enumeratedTypes = - getEnumeratedTypes(root).toList(); + getEnumeratedTypes(root, excludeSealedClasses: true).toList(); void writeEncodeLogic(EnumeratedType customType) { final String encodeString = @@ -1117,20 +1117,13 @@ protected static ArrayList wrapError(@NonNull Throwable exception) { Indent indent, { required String dartPackageName, }) { - final bool hasHostApi = root.apis - .whereType() - .any((Api api) => api.methods.isNotEmpty); - final bool hasFlutterApi = root.apis - .whereType() - .any((Api api) => api.methods.isNotEmpty); - indent.newln(); _writeErrorClass(indent); - if (hasHostApi) { + if (root.containsHostApi) { indent.newln(); _writeWrapError(indent); } - if (hasFlutterApi) { + if (root.containsFlutterApi) { indent.newln(); _writeCreateConnectionError(indent); } diff --git a/packages/pigeon/lib/kotlin_generator.dart b/packages/pigeon/lib/kotlin_generator.dart index 3b5ff62aa822..7d47df9f89b5 100644 --- a/packages/pigeon/lib/kotlin_generator.dart +++ b/packages/pigeon/lib/kotlin_generator.dart @@ -30,6 +30,9 @@ const DocumentCommentSpecification _docCommentSpec = String _codecName = 'PigeonCodec'; +/// Name of field used for host API codec. +const String _pigeonMethodChannelCodec = 'PigeonMethodCodec'; + const String _overflowClassName = '${classNamePrefix}CodecOverflow'; /// Options that control how Kotlin code will be generated. @@ -147,7 +150,9 @@ class KotlinGenerator extends StructuredGenerator { indent.writeln('import android.util.Log'); indent.writeln('import io.flutter.plugin.common.BasicMessageChannel'); indent.writeln('import io.flutter.plugin.common.BinaryMessenger'); + indent.writeln('import io.flutter.plugin.common.EventChannel'); indent.writeln('import io.flutter.plugin.common.MessageCodec'); + indent.writeln('import io.flutter.plugin.common.StandardMethodCodec'); indent.writeln('import io.flutter.plugin.common.StandardMessageCodec'); indent.writeln('import java.io.ByteArrayOutputStream'); indent.writeln('import java.nio.ByteBuffer'); @@ -197,14 +202,21 @@ class KotlinGenerator extends StructuredGenerator { Class classDefinition, { required String dartPackageName, }) { - const List generatedMessages = [ + final List generatedMessages = [ ' Generated class from Pigeon that represents data sent in messages.' ]; + if (classDefinition.isSealed) { + generatedMessages.add( + ' This class should not be extended by any user class outside of the generated file.'); + } indent.newln(); addDocumentationComments( indent, classDefinition.documentationComments, _docCommentSpec, generatorComments: generatedMessages); _writeDataClassSignature(indent, classDefinition); + if (classDefinition.isSealed) { + return; + } indent.addScoped(' {', '}', () { writeClassDecode( generatorOptions, @@ -228,9 +240,16 @@ class KotlinGenerator extends StructuredGenerator { Class classDefinition, { bool private = false, }) { - indent.write( - '${private ? 'private ' : ''}data class ${classDefinition.name} '); - indent.addScoped('(', ')', () { + final String privateString = private ? 'private ' : ''; + final String classType = classDefinition.isSealed ? 'sealed' : 'data'; + final String inheritance = classDefinition.superClass != null + ? ' : ${classDefinition.superClassName}()' + : ''; + indent.write('$privateString$classType class ${classDefinition.name} '); + if (classDefinition.isSealed) { + return; + } + indent.addScoped('(', ')$inheritance', () { for (final NamedType element in getFieldsInSerializationOrder(classDefinition)) { _writeClassField(indent, element); @@ -334,7 +353,7 @@ class KotlinGenerator extends StructuredGenerator { required String dartPackageName, }) { final List enumeratedTypes = - getEnumeratedTypes(root).toList(); + getEnumeratedTypes(root, excludeSealedClasses: true).toList(); void writeEncodeLogic(EnumeratedType customType) { final String encodeString = @@ -427,6 +446,11 @@ class KotlinGenerator extends StructuredGenerator { }); }); indent.newln(); + if (root.containsEventChannel) { + indent.writeln( + 'val ${generatorOptions.fileSpecificClassNameComponent}$_pigeonMethodChannelCodec = StandardMethodCodec(${generatorOptions.fileSpecificClassNameComponent}$_codecName());'); + indent.newln(); + } } void _writeCodecOverflowUtilities( @@ -984,6 +1008,72 @@ if (wrapped == null) { ); } + @override + void writeEventChannelApi( + KotlinOptions generatorOptions, + Root root, + Indent indent, + AstEventChannelApi api, { + required String dartPackageName, + }) { + indent.newln(); + indent.format(''' + private class PigeonStreamHandler( + val wrapper: PigeonEventChannelWrapper + ) : EventChannel.StreamHandler { + var pigeonSink: PigeonEventSink? = null + + override fun onListen(p0: Any?, sink: EventChannel.EventSink) { + pigeonSink = PigeonEventSink(sink) + wrapper.onListen(p0, pigeonSink!!) + } + + override fun onCancel(p0: Any?) { + pigeonSink = null + wrapper.onCancel(p0) + } + } + + interface PigeonEventChannelWrapper { + open fun onListen(p0: Any?, sink: PigeonEventSink) {} + + open fun onCancel(p0: Any?) {} + } + + class PigeonEventSink(private val sink: EventChannel.EventSink) { + fun success(value: T) { + sink.success(value) + } + + fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { + sink.error(errorCode, errorMessage, errorDetails) + } + + fun endOfStream() { + sink.endOfStream() + } + } + '''); + addDocumentationComments( + indent, api.documentationComments, _docCommentSpec); + for (final Method func in api.methods) { + indent.format(''' + abstract class ${toUpperCamelCase(func.name)}StreamHandler : PigeonEventChannelWrapper<${_kotlinTypeForDartType(func.returnType)}> { + companion object { + fun register(messenger: BinaryMessenger, wrapper: ${toUpperCamelCase(func.name)}StreamHandler, instanceName: String = "") { + var channelName: String = "${makeChannelName(api, func, dartPackageName)}" + if (instanceName.isNotEmpty()) { + channelName += ".\$instanceName" + } + val streamHandler = PigeonStreamHandler<${_kotlinTypeForDartType(func.returnType)}>(wrapper) + EventChannel(messenger, channelName, ${generatorOptions.fileSpecificClassNameComponent}$_pigeonMethodChannelCodec).setStreamHandler(streamHandler) + } + } + } + '''); + } + } + void _writeWrapResult(Indent indent) { indent.newln(); indent.write('private fun wrapResult(result: Any?): List '); @@ -1054,19 +1144,11 @@ if (wrapped == null) { Indent indent, { required String dartPackageName, }) { - final bool hasHostApi = root.apis - .whereType() - .any((Api api) => api.methods.isNotEmpty); - final bool hasFlutterApi = root.apis - .whereType() - .any((Api api) => api.methods.isNotEmpty); - final bool hasProxyApi = root.apis.any((Api api) => api is AstProxyApi); - - if (hasHostApi || hasProxyApi) { + if (root.containsHostApi || root.containsProxyApi) { _writeWrapResult(indent); _writeWrapError(generatorOptions, indent); } - if (hasFlutterApi || hasProxyApi) { + if (root.containsFlutterApi || root.containsProxyApi) { _writeCreateConnectionError(generatorOptions, indent); } if (generatorOptions.includeErrorClass) { diff --git a/packages/pigeon/lib/objc_generator.dart b/packages/pigeon/lib/objc_generator.dart index d0e0e99ee293..ed6bd37ac760 100644 --- a/packages/pigeon/lib/objc_generator.dart +++ b/packages/pigeon/lib/objc_generator.dart @@ -695,7 +695,7 @@ if (self.wrapped == nil) { }) { const String codecName = 'PigeonCodec'; final List enumeratedTypes = - getEnumeratedTypes(root).toList(); + getEnumeratedTypes(root, excludeSealedClasses: true).toList(); final String readerWriterName = '${generatorOptions.prefix}${toUpperCamelCase(generatorOptions.fileSpecificClassNameComponent ?? '')}${codecName}ReaderWriter'; final String readerName = @@ -901,23 +901,16 @@ if (self.wrapped == nil) { Indent indent, { required String dartPackageName, }) { - final bool hasHostApi = root.apis - .whereType() - .any((Api api) => api.methods.isNotEmpty); - final bool hasFlutterApi = root.apis - .whereType() - .any((Api api) => api.methods.isNotEmpty); - - if (hasHostApi) { + if (root.containsHostApi) { _writeWrapError(indent); indent.newln(); } - if (hasFlutterApi) { + if (root.containsFlutterApi) { _writeCreateConnectionError(indent); indent.newln(); } - if (hasHostApi || hasFlutterApi) { + if (root.containsHostApi || root.containsFlutterApi) { _writeGetNullableObjectAtIndex(indent); } diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart index 075049d98298..f388a9906950 100644 --- a/packages/pigeon/lib/pigeon_lib.dart +++ b/packages/pigeon/lib/pigeon_lib.dart @@ -160,6 +160,12 @@ class ProxyApi { final KotlinProxyApiOptions? kotlinOptions; } +/// Metadata to annotate a pigeon API that contains Event Channels. +class EventChannelApi { + /// Constructor. + const EventChannelApi(); +} + /// Metadata to annotation methods to control the selector used for objc output. /// The number of components in the provided selector must match the number of /// arguments in the annotated method. @@ -537,6 +543,29 @@ DartOptions _dartOptionsWithCopyrightHeader( )); } +void _errorOnEventChannelApi(List errors, String generator, Root root) { + if (root.containsEventChannel) { + errors.add(Error(message: '$generator does not support Event Channels')); + } +} + +void _errorOnSealedClass(List errors, String generator, Root root) { + if (root.classes.any( + (Class element) => element.isSealed, + )) { + errors.add(Error(message: '$generator does not support sealed classes')); + } +} + +void _errorOnInheritedClass(List errors, String generator, Root root) { + if (root.classes.any( + (Class element) => element.superClass != null, + )) { + errors.add( + Error(message: '$generator does not support inheritance in classes')); + } +} + /// A [GeneratorAdapter] that generates the AST. class AstGeneratorAdapter implements GeneratorAdapter { /// Constructor for [AstGeneratorAdapter]. @@ -564,6 +593,9 @@ class DartGeneratorAdapter implements GeneratorAdapter { /// Constructor for [DartGeneratorAdapter]. DartGeneratorAdapter(); + /// A string representing the name of the language being generated. + String languageString = 'dart'; + @override List fileTypeList = const [FileType.na]; @@ -644,6 +676,9 @@ class ObjcGeneratorAdapter implements GeneratorAdapter { ObjcGeneratorAdapter( {this.fileTypeList = const [FileType.header, FileType.source]}); + /// A string representing the name of the language being generated. + String languageString = 'objc'; + @override List fileTypeList; @@ -685,7 +720,13 @@ class ObjcGeneratorAdapter implements GeneratorAdapter { } @override - List validate(PigeonOptions options, Root root) => []; + List validate(PigeonOptions options, Root root) { + final List errors = []; + _errorOnEventChannelApi(errors, languageString, root); + _errorOnSealedClass(errors, languageString, root); + _errorOnInheritedClass(errors, languageString, root); + return errors; + } } /// A [GeneratorAdapter] that generates Java source code. @@ -693,6 +734,9 @@ class JavaGeneratorAdapter implements GeneratorAdapter { /// Constructor for [JavaGeneratorAdapter]. JavaGeneratorAdapter(); + /// A string representing the name of the language being generated. + String languageString = 'java'; + @override List fileTypeList = const [FileType.na]; @@ -722,7 +766,13 @@ class JavaGeneratorAdapter implements GeneratorAdapter { _openSink(options.javaOut, basePath: options.basePath ?? ''); @override - List validate(PigeonOptions options, Root root) => []; + List validate(PigeonOptions options, Root root) { + final List errors = []; + _errorOnEventChannelApi(errors, languageString, root); + _errorOnSealedClass(errors, languageString, root); + _errorOnInheritedClass(errors, languageString, root); + return errors; + } } /// A [GeneratorAdapter] that generates Swift source code. @@ -730,6 +780,9 @@ class SwiftGeneratorAdapter implements GeneratorAdapter { /// Constructor for [SwiftGeneratorAdapter]. SwiftGeneratorAdapter(); + /// A string representing the name of the language being generated. + String languageString = 'swift'; + @override List fileTypeList = const [FileType.na]; @@ -770,6 +823,9 @@ class CppGeneratorAdapter implements GeneratorAdapter { CppGeneratorAdapter( {this.fileTypeList = const [FileType.header, FileType.source]}); + /// A string representing the name of the language being generated. + String languageString = 'cpp'; + @override List fileTypeList; @@ -805,7 +861,13 @@ class CppGeneratorAdapter implements GeneratorAdapter { } @override - List validate(PigeonOptions options, Root root) => []; + List validate(PigeonOptions options, Root root) { + final List errors = []; + _errorOnEventChannelApi(errors, languageString, root); + _errorOnSealedClass(errors, languageString, root); + _errorOnInheritedClass(errors, languageString, root); + return errors; + } } /// A [GeneratorAdapter] that generates GObject source code. @@ -814,6 +876,9 @@ class GObjectGeneratorAdapter implements GeneratorAdapter { GObjectGeneratorAdapter( {this.fileTypeList = const [FileType.header, FileType.source]}); + /// A string representing the name of the language being generated. + String languageString = 'gobject'; + @override List fileTypeList; @@ -862,6 +927,10 @@ class GObjectGeneratorAdapter implements GeneratorAdapter { message: 'GObject generator does not yet support more than $totalCustomCodecKeysAllowed custom types.')); } + _errorOnEventChannelApi(errors, languageString, root); + _errorOnSealedClass(errors, languageString, root); + _errorOnInheritedClass(errors, languageString, root); + return errors; } } @@ -986,6 +1055,24 @@ List _validateAst(Root root, String source) { lineNumber: _calculateLineNumberNullable(source, field.offset), )); } + if (classDefinition.isSealed) { + if (classDefinition.fields.isNotEmpty) { + result.add(Error( + message: + 'Sealed class: "${classDefinition.name}" must not contain fields.', + lineNumber: _calculateLineNumberNullable(source, field.offset), + )); + } + } + if (classDefinition.superClass != null) { + if (!classDefinition.superClass!.isSealed) { + result.add(Error( + message: + 'Child class: "${classDefinition.name}" must extend a sealed class.', + lineNumber: _calculateLineNumberNullable(source, field.offset), + )); + } + } } } @@ -1020,6 +1107,13 @@ List _validateAst(Root root, String source) { lineNumber: _calculateLineNumberNullable(source, method.offset), )); } + if (api is AstEventChannelApi && method.parameters.isNotEmpty) { + result.add(Error( + message: + 'Event Channel methods must not be contain parameters, in method "${method.name}" in API: "${api.name}"', + lineNumber: _calculateLineNumberNullable(source, method.offset), + )); + } for (final Parameter param in method.parameters) { if (param.type.baseName.isEmpty) { result.add(Error( @@ -1387,20 +1481,43 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor { getReferencedTypes(_apis, _classes); final Set referencedTypeNames = referencedTypes.keys.map((TypeDeclaration e) => e.baseName).toSet(); - final List nonReferencedClasses = List.from(_classes); - nonReferencedClasses + final List nonReferencedTypes = List.from(_classes); + nonReferencedTypes .removeWhere((Class x) => referencedTypeNames.contains(x.name)); - for (final Class x in nonReferencedClasses) { + for (final Class x in nonReferencedTypes) { x.isReferenced = false; } final List referencedEnums = List.from(_enums); - final Root completeRoot = - Root(apis: _apis, classes: _classes, enums: referencedEnums); + bool containsHostApi = false; + bool containsFlutterApi = false; + bool containsProxyApi = false; + bool containsEventChannel = false; + + for (final Api api in _apis) { + switch (api) { + case AstHostApi(): + containsHostApi = true; + case AstFlutterApi(): + containsFlutterApi = true; + case AstProxyApi(): + containsProxyApi = true; + case AstEventChannelApi(): + containsEventChannel = true; + } + } + + final Root completeRoot = Root( + apis: _apis, + classes: _classes, + enums: referencedEnums, + containsHostApi: containsHostApi, + containsFlutterApi: containsFlutterApi, + containsProxyApi: containsProxyApi, + containsEventChannel: containsEventChannel, + ); - final List validateErrors = _validateAst(completeRoot, source); final List totalErrors = List.from(_errors); - totalErrors.addAll(validateErrors); for (final MapEntry> element in referencedTypes.entries) { @@ -1429,6 +1546,7 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor { classDefinition.fields = _attachAssociatedDefinitions( classDefinition.fields, ); + classDefinition.superClass = _attachSuperClass(classDefinition); } for (final Api api in _apis) { @@ -1456,6 +1574,8 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor { api.interfaces = newInterfaceSet; } } + final List validateErrors = _validateAst(completeRoot, source); + totalErrors.addAll(validateErrors); return ParseResults( root: totalErrors.isEmpty @@ -1503,6 +1623,20 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor { return result; } + Class? _attachSuperClass(Class childClass) { + if (childClass.superClassName == null) { + return null; + } + + for (final Class parentClass in _classes) { + if (parentClass.name == childClass.superClassName) { + parentClass.children.add(childClass); + return parentClass; + } + } + return null; + } + Object _expressionToMap(dart_ast.Expression expression) { if (expression is dart_ast.MethodInvocation) { final Map result = {}; @@ -1599,6 +1733,14 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor { _storeCurrentClass(); if (node.abstractKeyword != null) { + if (node.metadata.length > 2 || + (node.metadata.length > 1 && + !_hasMetadata(node.metadata, 'ConfigurePigeon'))) { + _errors.add(Error( + message: + 'API "${node.name.lexeme}" can only have one API annotation but contains: ${node.metadata}', + lineNumber: _calculateLineNumber(source, node.offset))); + } if (_hasMetadata(node.metadata, 'HostApi')) { final dart_ast.Annotation hostApi = node.metadata.firstWhere( (dart_ast.Annotation element) => element.name.name == 'HostApi'); @@ -1736,11 +1878,22 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor { documentationComments: _documentationCommentsParser(node.documentationComment?.tokens), ); + } else if (_hasMetadata(node.metadata, 'EventChannelApi')) { + _currentApi = AstEventChannelApi( + name: node.name.lexeme, + methods: [], + documentationComments: + _documentationCommentsParser(node.documentationComment?.tokens), + ); } } else { _currentClass = Class( name: node.name.lexeme, fields: [], + superClassName: + node.implementsClause?.interfaces.first.name2.toString() ?? + node.extendsClause?.superclass.name2.toString(), + isSealed: node.sealedKeyword != null, isSwiftClass: _hasMetadata(node.metadata, 'SwiftClass'), documentationComments: _documentationCommentsParser(node.documentationComment?.tokens), @@ -1889,6 +2042,7 @@ class _RootBuilder extends dart_ast_visitor.RecursiveAstVisitor { AstHostApi() => ApiLocation.host, AstProxyApi() => ApiLocation.host, AstFlutterApi() => ApiLocation.flutter, + AstEventChannelApi() => ApiLocation.host, }, isAsynchronous: isAsynchronous, objcSelector: objcSelector, diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index 289c50dbd887..0d0d17587d42 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -204,13 +204,13 @@ class SwiftGenerator extends StructuredGenerator { Indent indent, { required String dartPackageName, }) { - final String codecName = _getCodecName(generatorOptions); + final String codecName = _getMessageCodecName(generatorOptions); final String readerWriterName = '${codecName}ReaderWriter'; final String readerName = '${codecName}Reader'; final String writerName = '${codecName}Writer'; final List enumeratedTypes = - getEnumeratedTypes(root).toList(); + getEnumeratedTypes(root, excludeSealedClasses: true).toList(); void writeDecodeLogic(EnumeratedType customType) { indent.writeln('case ${customType.enumeration}:'); @@ -332,6 +332,11 @@ class SwiftGenerator extends StructuredGenerator { 'static let shared = $codecName(readerWriter: $readerWriterName())'); }); indent.newln(); + if (root.containsEventChannel) { + indent.writeln( + 'var ${_getMethodCodecVarName(generatorOptions)} = FlutterStandardMethodCodec(readerWriter: $readerWriterName());'); + indent.newln(); + } } void _writeDataClassSignature( @@ -340,10 +345,17 @@ class SwiftGenerator extends StructuredGenerator { bool private = false, }) { final String privateString = private ? 'private ' : ''; + final String extendsString = classDefinition.superClass != null + ? ': ${classDefinition.superClass!.name}' + : ''; if (classDefinition.isSwiftClass) { - indent.write('${privateString}class ${classDefinition.name} '); + indent.write( + '${privateString}class ${classDefinition.name}$extendsString '); + } else if (classDefinition.isSealed) { + indent.write('protocol ${classDefinition.name} '); } else { - indent.write('${privateString}struct ${classDefinition.name} '); + indent.write( + '${privateString}struct ${classDefinition.name}$extendsString '); } indent.addScoped('{', '', () { @@ -361,7 +373,7 @@ class SwiftGenerator extends StructuredGenerator { _writeClassField(indent, field, addNil: !classDefinition.isSwiftClass); indent.newln(); } - }); + }, addTrailingNewline: false); } void _writeCodecOverflowUtilities( @@ -444,15 +456,22 @@ if (wrapped == nil) { Class classDefinition, { required String dartPackageName, }) { - const List generatedComments = [ + final List generatedComments = [ ' Generated class from Pigeon that represents data sent in messages.' ]; + if (classDefinition.isSealed) { + generatedComments.add( + ' This class should not be extended by any user class outside of the generated file.'); + } indent.newln(); addDocumentationComments( indent, classDefinition.documentationComments, _docCommentSpec, generatorComments: generatedComments); _writeDataClassSignature(indent, classDefinition); indent.writeScoped('', '}', () { + if (classDefinition.isSealed) { + return; + } indent.newln(); writeClassDecode( generatorOptions, @@ -639,7 +658,7 @@ if (wrapped == nil) { indent.writeln( r'self.messageChannelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : ""'); }); - final String codecName = _getCodecName(generatorOptions); + final String codecName = _getMessageCodecName(generatorOptions); indent.write('var codec: $codecName '); indent.addScoped('{', '}', () { indent.writeln('return $codecName.shared'); @@ -705,7 +724,7 @@ if (wrapped == nil) { indent.write('class ${apiName}Setup '); indent.addScoped('{', '}', () { indent.writeln( - 'static var codec: FlutterStandardMessageCodec { ${_getCodecName(generatorOptions)}.shared }'); + 'static var codec: FlutterStandardMessageCodec { ${_getMessageCodecName(generatorOptions)}.shared }'); indent.writeln( '$_docCommentPrefix Sets up an instance of `$apiName` to handle messages through the `binaryMessenger`.'); indent.write( @@ -764,7 +783,7 @@ if (wrapped == nil) { _docCommentSpec, ); indent.writeln( - 'var codec: FlutterStandardMessageCodec { ${_getCodecName(generatorOptions)}.shared }', + 'var codec: FlutterStandardMessageCodec { ${_getMessageCodecName(generatorOptions)}.shared }', ); indent.newln(); @@ -797,7 +816,7 @@ if (wrapped == nil) { '}', () { indent.writeln( - 'let codec = ${_getCodecName(generatorOptions)}.shared', + 'let codec = ${_getMessageCodecName(generatorOptions)}.shared', ); const String setHandlerCondition = 'let instanceManager = instanceManager'; @@ -899,7 +918,7 @@ if (wrapped == nil) { indent.newln(); indent.writeScoped( - 'private class $filePrefix${classNamePrefix}ProxyApiCodecReader: ${_getCodecName(generatorOptions)}Reader {', + 'private class $filePrefix${classNamePrefix}ProxyApiCodecReader: ${_getMessageCodecName(generatorOptions)}Reader {', '}', () { indent.writeln('unowned let pigeonRegistrar: $registrarName'); @@ -938,7 +957,7 @@ if (wrapped == nil) { indent.newln(); indent.writeScoped( - 'private class $filePrefix${classNamePrefix}ProxyApiCodecWriter: ${_getCodecName(generatorOptions)}Writer {', + 'private class $filePrefix${classNamePrefix}ProxyApiCodecWriter: ${_getMessageCodecName(generatorOptions)}Writer {', '}', () { indent.writeln( @@ -1304,23 +1323,15 @@ private func nilOrValue(_ value: Any?) -> T? { Indent indent, { required String dartPackageName, }) { - final bool hasHostApi = root.apis - .whereType() - .any((Api api) => api.methods.isNotEmpty); - final bool hasFlutterApi = root.apis - .whereType() - .any((Api api) => api.methods.isNotEmpty); - final bool hasProxyApi = root.apis.any((Api api) => api is AstProxyApi); - if (generatorOptions.includeErrorClass) { _writePigeonError(generatorOptions, indent); } - if (hasHostApi || hasProxyApi) { + if (root.containsHostApi || root.containsProxyApi) { _writeWrapResult(indent); _writeWrapError(generatorOptions, indent); } - if (hasFlutterApi || hasProxyApi) { + if (root.containsFlutterApi || root.containsProxyApi) { _writeCreateConnectionError(generatorOptions, indent); } @@ -1328,6 +1339,86 @@ private func nilOrValue(_ value: Any?) -> T? { _writeNilOrValue(indent); } + @override + void writeEventChannelApi( + SwiftOptions generatorOptions, + Root root, + Indent indent, + AstEventChannelApi api, { + required String dartPackageName, + }) { + indent.newln(); + indent.format(''' + private class PigeonStreamHandler: NSObject, FlutterStreamHandler { + private let wrapper: PigeonEventChannelWrapper + private var pigeonSink: PigeonEventSink? = nil + + init(wrapper: PigeonEventChannelWrapper) { + self.wrapper = wrapper + } + + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) + -> FlutterError? + { + pigeonSink = PigeonEventSink(events) + wrapper.onListen(withArguments: arguments, sink: pigeonSink!) + return nil + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + pigeonSink = nil + wrapper.onCancel(withArguments: arguments) + return nil + } + } + + class PigeonEventChannelWrapper { + func onListen(withArguments arguments: Any?, sink: PigeonEventSink) {} + func onCancel(withArguments arguments: Any?) {} + } + + class PigeonEventSink { + private let sink: FlutterEventSink + + init(_ sink: @escaping FlutterEventSink) { + self.sink = sink + } + + func success(_ value: ReturnType) { + sink(value) + } + + func error(code: String, message: String?, details: Any?) { + sink(FlutterError(code: code, message: message, details: details)) + } + + func endOfStream() { + sink(FlutterEndOfEventStream) + } + + } + '''); + addDocumentationComments( + indent, api.documentationComments, _docCommentSpec); + for (final Method func in api.methods) { + indent.format(''' + class ${toUpperCamelCase(func.name)}StreamHandler: PigeonEventChannelWrapper<${_swiftTypeForDartType(func.returnType)}> { + static func register(with messenger: FlutterBinaryMessenger, + instanceName: String = "", + wrapper: ${toUpperCamelCase(func.name)}StreamHandler) { + var channelName = "dev.flutter.pigeon.pigeon_integration_tests.EventChannelCoreApi.${func.name}" + if !instanceName.isEmpty { + channelName += ".\\(instanceName)" + } + let streamHandler = PigeonStreamHandler<${_swiftTypeForDartType(func.returnType)}>(wrapper: wrapper) + let channel = FlutterEventChannel(name: channelName, binaryMessenger: messenger, codec: ${_getMethodCodecVarName(generatorOptions)}) + channel.setStreamHandler(streamHandler) + } + } + '''); + } + } + void _writeFlutterMethod( Indent indent, { required SwiftOptions generatorOptions, @@ -2491,8 +2582,13 @@ String? _tryGetUnsupportedPlatformsCondition(Iterable types) { } /// Calculates the name of the codec that will be generated for [api]. -String _getCodecName(SwiftOptions options) { - return '${options.fileSpecificClassNameComponent}PigeonCodec'; +String _getMessageCodecName(SwiftOptions options) { + return '${options.fileSpecificClassNameComponent ?? ''}PigeonCodec'; +} + +/// Calculates the name of the codec that will be generated for [api]. +String _getMethodCodecVarName(SwiftOptions options) { + return '${toLowerCamelCase(options.fileSpecificClassNameComponent ?? '')}PigeonMethodCodec'; } String _getErrorClassName(SwiftOptions generatorOptions) { diff --git a/packages/pigeon/pigeons/event_channel_tests.dart b/packages/pigeon/pigeons/event_channel_tests.dart new file mode 100644 index 000000000000..afa56cd7d9be --- /dev/null +++ b/packages/pigeon/pigeons/event_channel_tests.dart @@ -0,0 +1,144 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:pigeon/pigeon.dart'; + +enum EventEnum { + one, + two, + three, + fortyTwo, + fourHundredTwentyTwo, +} + +// Enums require special logic, having multiple ensures that the logic can be +// replicated without collision. +enum AnotherEventEnum { + justInCase, +} + +/// A class containing all supported nullable types. +@SwiftClass() +class EventAllNullableTypes { + EventAllNullableTypes( + this.aNullableBool, + this.aNullableInt, + this.aNullableInt64, + this.aNullableDouble, + this.aNullableByteArray, + this.aNullable4ByteArray, + this.aNullable8ByteArray, + this.aNullableFloatArray, + this.aNullableEnum, + this.anotherNullableEnum, + this.aNullableString, + this.aNullableObject, + this.allNullableTypes, + + // Lists + // This name is in a different format than the others to ensure that name + // collision with the word 'list' doesn't occur in the generated files. + this.list, + this.stringList, + this.intList, + this.doubleList, + this.boolList, + this.enumList, + this.objectList, + this.listList, + this.mapList, + this.recursiveClassList, + + // Maps + this.map, + this.stringMap, + this.intMap, + this.enumMap, + this.objectMap, + this.listMap, + this.mapMap, + this.recursiveClassMap, + ); + + bool? aNullableBool; + int? aNullableInt; + int? aNullableInt64; + double? aNullableDouble; + Uint8List? aNullableByteArray; + Int32List? aNullable4ByteArray; + Int64List? aNullable8ByteArray; + Float64List? aNullableFloatArray; + EventEnum? aNullableEnum; + AnotherEventEnum? anotherNullableEnum; + String? aNullableString; + Object? aNullableObject; + EventAllNullableTypes? allNullableTypes; + + // Lists + // ignore: strict_raw_type, always_specify_types + List? list; + List? stringList; + List? intList; + List? doubleList; + List? boolList; + List? enumList; + List? objectList; + List?>? listList; + List?>? mapList; + List? recursiveClassList; + + // Maps + // ignore: strict_raw_type, always_specify_types + Map? map; + Map? stringMap; + Map? intMap; + Map? enumMap; + Map? objectMap; + Map?>? listMap; + Map?>? mapMap; + Map? recursiveClassMap; +} + +sealed class EventChannelDataBase {} + +class IntEvent extends EventChannelDataBase { + IntEvent(this.value); + final int value; +} + +class StringEvent extends EventChannelDataBase { + StringEvent(this.value); + final String value; +} + +class BoolEvent extends EventChannelDataBase { + BoolEvent(this.value); + final bool value; +} + +class DoubleEvent extends EventChannelDataBase { + DoubleEvent(this.value); + final double value; +} + +class ObjectsEvent extends EventChannelDataBase { + ObjectsEvent(this.value); + final Object value; +} + +class EnumEvent extends EventChannelDataBase { + EnumEvent(this.value); + final EventEnum value; +} + +class ClassEvent extends EventChannelDataBase { + ClassEvent(this.value); + final EventAllNullableTypes value; +} + +@EventChannelApi() +abstract class EventChannelCoreApi { + int streamInts(); + EventChannelDataBase streamEvents(); +} diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/example_app.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/example_app.dart index 31075ae532f6..7280373b1e58 100644 --- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/example_app.dart +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/example_app.dart @@ -26,6 +26,7 @@ class ExampleApp extends StatefulWidget { class _ExampleAppState extends State { late final HostIntegrationCoreApi api; String status = 'Calling...'; + String numsSoFar = ''; @override void initState() { @@ -58,7 +59,49 @@ class _ExampleAppState extends State { title: const Text('Pigeon integration tests'), ), body: Center( - child: Text(status), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Text( + 'Counter :', + ), + StreamBuilder( + stream: streamEvents(), + builder: (BuildContext context, + AsyncSnapshot snapshot) { + if (snapshot.hasData) { + String newString = ''; + switch (snapshot.data) { + case null: + newString = 'null, '; + case IntEvent(): + newString = '${(snapshot.data! as IntEvent).value}, '; + case StringEvent(): + newString = + '${(snapshot.data! as StringEvent).value}, '; + case BoolEvent(): + newString = '${(snapshot.data! as BoolEvent).value}, '; + case DoubleEvent(): + newString = + '${(snapshot.data! as DoubleEvent).value}, '; + case ObjectsEvent(): + newString = + '${(snapshot.data! as ObjectsEvent).value}, '; + case EnumEvent(): + newString = '${(snapshot.data! as EnumEvent).value}, '; + case ClassEvent(): + newString = '${(snapshot.data! as ClassEvent).value}, '; + } + + numsSoFar += newString; + return Text(numsSoFar); + } else { + return const CircularProgressIndicator(); + } + }, + ), + ], + ), ), ), ); diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/generated.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/generated.dart index b7861ee4d574..76d63eb0fd86 100644 --- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/generated.dart +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/generated.dart @@ -3,5 +3,6 @@ // found in the LICENSE file. export 'src/generated/core_tests.gen.dart'; +export 'src/generated/event_channel_tests.gen.dart'; export 'src/generated/proxy_api_tests.gen.dart' show ProxyApiSuperClass, ProxyApiTestClass, ProxyApiTestEnum; diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart index 21d76637675c..01a64171d3cd 100644 --- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart @@ -2840,6 +2840,57 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { final UnusedClass unused = UnusedClass(); expect(unused, unused); }); + + /// Event Channels + + const List eventChannelSupported = [ + TargetGenerator.kotlin, + TargetGenerator.swift + ]; + testWidgets('event channel sends continuous ints', (_) async { + final Stream events = streamInts(); + final List listEvents = await events.toList(); + for (final int value in listEvents) { + expect(listEvents[value], value); + } + }, skip: !eventChannelSupported.contains(targetGenerator)); + + testWidgets('event channel handles extended sealed classes', (_) async { + int count = 0; + final Stream events = streamEvents(); + events.listen((EventChannelDataBase event) { + switch (event) { + case IntEvent(): + expect(event.value, 1); + expect(count, 0); + count++; + case StringEvent(): + expect(event.value, 'string'); + expect(count, 1); + count++; + case BoolEvent(): + expect(event.value, false); + expect(count, 2); + count++; + case DoubleEvent(): + expect(event.value, 3.14); + expect(count, 3); + count++; + case ObjectsEvent(): + expect(event.value, true); + expect(count, 4); + count++; + case EnumEvent(): + expect(event.value, EventEnum.fortyTwo); + expect(count, 5); + count++; + case ClassEvent(): + expect(event.value.aNullableInt, 0); + expect(count, 6); + count++; + } + }); + }, skip: !eventChannelSupported.contains(targetGenerator)); } class _FlutterApiTestImplementation implements FlutterIntegrationCoreApi { diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_tests.gen.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_tests.gen.dart new file mode 100644 index 000000000000..4e9f4fd75b56 --- /dev/null +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_tests.gen.dart @@ -0,0 +1,453 @@ +// 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. +// +// Autogenerated from Pigeon, do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import, no_leading_underscores_for_local_identifiers + +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + +enum EventEnum { + one, + two, + three, + fortyTwo, + fourHundredTwentyTwo, +} + +enum AnotherEventEnum { + justInCase, +} + +/// A class containing all supported nullable types. +class EventAllNullableTypes { + EventAllNullableTypes({ + this.aNullableBool, + this.aNullableInt, + this.aNullableInt64, + this.aNullableDouble, + this.aNullableByteArray, + this.aNullable4ByteArray, + this.aNullable8ByteArray, + this.aNullableFloatArray, + this.aNullableEnum, + this.anotherNullableEnum, + this.aNullableString, + this.aNullableObject, + this.allNullableTypes, + this.list, + this.stringList, + this.intList, + this.doubleList, + this.boolList, + this.enumList, + this.objectList, + this.listList, + this.mapList, + this.recursiveClassList, + this.map, + this.stringMap, + this.intMap, + this.enumMap, + this.objectMap, + this.listMap, + this.mapMap, + this.recursiveClassMap, + }); + + bool? aNullableBool; + + int? aNullableInt; + + int? aNullableInt64; + + double? aNullableDouble; + + Uint8List? aNullableByteArray; + + Int32List? aNullable4ByteArray; + + Int64List? aNullable8ByteArray; + + Float64List? aNullableFloatArray; + + EventEnum? aNullableEnum; + + AnotherEventEnum? anotherNullableEnum; + + String? aNullableString; + + Object? aNullableObject; + + EventAllNullableTypes? allNullableTypes; + + List? list; + + List? stringList; + + List? intList; + + List? doubleList; + + List? boolList; + + List? enumList; + + List? objectList; + + List?>? listList; + + List?>? mapList; + + List? recursiveClassList; + + Map? map; + + Map? stringMap; + + Map? intMap; + + Map? enumMap; + + Map? objectMap; + + Map?>? listMap; + + Map?>? mapMap; + + Map? recursiveClassMap; + + Object encode() { + return [ + aNullableBool, + aNullableInt, + aNullableInt64, + aNullableDouble, + aNullableByteArray, + aNullable4ByteArray, + aNullable8ByteArray, + aNullableFloatArray, + aNullableEnum, + anotherNullableEnum, + aNullableString, + aNullableObject, + allNullableTypes, + list, + stringList, + intList, + doubleList, + boolList, + enumList, + objectList, + listList, + mapList, + recursiveClassList, + map, + stringMap, + intMap, + enumMap, + objectMap, + listMap, + mapMap, + recursiveClassMap, + ]; + } + + static EventAllNullableTypes decode(Object result) { + result as List; + return EventAllNullableTypes( + aNullableBool: result[0] as bool?, + aNullableInt: result[1] as int?, + aNullableInt64: result[2] as int?, + aNullableDouble: result[3] as double?, + aNullableByteArray: result[4] as Uint8List?, + aNullable4ByteArray: result[5] as Int32List?, + aNullable8ByteArray: result[6] as Int64List?, + aNullableFloatArray: result[7] as Float64List?, + aNullableEnum: result[8] as EventEnum?, + anotherNullableEnum: result[9] as AnotherEventEnum?, + aNullableString: result[10] as String?, + aNullableObject: result[11], + allNullableTypes: result[12] as EventAllNullableTypes?, + list: result[13] as List?, + stringList: (result[14] as List?)?.cast(), + intList: (result[15] as List?)?.cast(), + doubleList: (result[16] as List?)?.cast(), + boolList: (result[17] as List?)?.cast(), + enumList: (result[18] as List?)?.cast(), + objectList: (result[19] as List?)?.cast(), + listList: (result[20] as List?)?.cast?>(), + mapList: (result[21] as List?)?.cast?>(), + recursiveClassList: + (result[22] as List?)?.cast(), + map: result[23] as Map?, + stringMap: + (result[24] as Map?)?.cast(), + intMap: (result[25] as Map?)?.cast(), + enumMap: (result[26] as Map?) + ?.cast(), + objectMap: + (result[27] as Map?)?.cast(), + listMap: + (result[28] as Map?)?.cast?>(), + mapMap: (result[29] as Map?) + ?.cast?>(), + recursiveClassMap: (result[30] as Map?) + ?.cast(), + ); + } +} + +sealed class EventChannelDataBase {} + +class IntEvent extends EventChannelDataBase { + IntEvent({ + required this.value, + }); + + int value; + + Object encode() { + return [ + value, + ]; + } + + static IntEvent decode(Object result) { + result as List; + return IntEvent( + value: result[0]! as int, + ); + } +} + +class StringEvent extends EventChannelDataBase { + StringEvent({ + required this.value, + }); + + String value; + + Object encode() { + return [ + value, + ]; + } + + static StringEvent decode(Object result) { + result as List; + return StringEvent( + value: result[0]! as String, + ); + } +} + +class BoolEvent extends EventChannelDataBase { + BoolEvent({ + required this.value, + }); + + bool value; + + Object encode() { + return [ + value, + ]; + } + + static BoolEvent decode(Object result) { + result as List; + return BoolEvent( + value: result[0]! as bool, + ); + } +} + +class DoubleEvent extends EventChannelDataBase { + DoubleEvent({ + required this.value, + }); + + double value; + + Object encode() { + return [ + value, + ]; + } + + static DoubleEvent decode(Object result) { + result as List; + return DoubleEvent( + value: result[0]! as double, + ); + } +} + +class ObjectsEvent extends EventChannelDataBase { + ObjectsEvent({ + required this.value, + }); + + Object value; + + Object encode() { + return [ + value, + ]; + } + + static ObjectsEvent decode(Object result) { + result as List; + return ObjectsEvent( + value: result[0]!, + ); + } +} + +class EnumEvent extends EventChannelDataBase { + EnumEvent({ + required this.value, + }); + + EventEnum value; + + Object encode() { + return [ + value, + ]; + } + + static EnumEvent decode(Object result) { + result as List; + return EnumEvent( + value: result[0]! as EventEnum, + ); + } +} + +class ClassEvent extends EventChannelDataBase { + ClassEvent({ + required this.value, + }); + + EventAllNullableTypes value; + + Object encode() { + return [ + value, + ]; + } + + static ClassEvent decode(Object result) { + result as List; + return ClassEvent( + value: result[0]! as EventAllNullableTypes, + ); + } +} + +class _PigeonCodec extends StandardMessageCodec { + const _PigeonCodec(); + @override + void writeValue(WriteBuffer buffer, Object? value) { + if (value is int) { + buffer.putUint8(4); + buffer.putInt64(value); + } else if (value is EventEnum) { + buffer.putUint8(129); + writeValue(buffer, value.index); + } else if (value is AnotherEventEnum) { + buffer.putUint8(130); + writeValue(buffer, value.index); + } else if (value is EventAllNullableTypes) { + buffer.putUint8(131); + writeValue(buffer, value.encode()); + } else if (value is IntEvent) { + buffer.putUint8(132); + writeValue(buffer, value.encode()); + } else if (value is StringEvent) { + buffer.putUint8(133); + writeValue(buffer, value.encode()); + } else if (value is BoolEvent) { + buffer.putUint8(134); + writeValue(buffer, value.encode()); + } else if (value is DoubleEvent) { + buffer.putUint8(135); + writeValue(buffer, value.encode()); + } else if (value is ObjectsEvent) { + buffer.putUint8(136); + writeValue(buffer, value.encode()); + } else if (value is EnumEvent) { + buffer.putUint8(137); + writeValue(buffer, value.encode()); + } else if (value is ClassEvent) { + buffer.putUint8(138); + writeValue(buffer, value.encode()); + } else { + super.writeValue(buffer, value); + } + } + + @override + Object? readValueOfType(int type, ReadBuffer buffer) { + switch (type) { + case 129: + final int? value = readValue(buffer) as int?; + return value == null ? null : EventEnum.values[value]; + case 130: + final int? value = readValue(buffer) as int?; + return value == null ? null : AnotherEventEnum.values[value]; + case 131: + return EventAllNullableTypes.decode(readValue(buffer)!); + case 132: + return IntEvent.decode(readValue(buffer)!); + case 133: + return StringEvent.decode(readValue(buffer)!); + case 134: + return BoolEvent.decode(readValue(buffer)!); + case 135: + return DoubleEvent.decode(readValue(buffer)!); + case 136: + return ObjectsEvent.decode(readValue(buffer)!); + case 137: + return EnumEvent.decode(readValue(buffer)!); + case 138: + return ClassEvent.decode(readValue(buffer)!); + default: + return super.readValueOfType(type, buffer); + } + } +} + +const StandardMethodCodec pigeonMethodCodec = + StandardMethodCodec(_PigeonCodec()); + +Stream streamInts({String instanceName = ''}) { + if (instanceName.isNotEmpty) { + instanceName = '.$instanceName'; + } + const EventChannel streamIntsChannel = EventChannel( + 'dev.flutter.pigeon.pigeon_integration_tests.EventChannelCoreApi.streamInts', + pigeonMethodCodec); + return streamIntsChannel.receiveBroadcastStream().map((dynamic event) { + return event as int; + }); +} + +Stream streamEvents({String instanceName = ''}) { + if (instanceName.isNotEmpty) { + instanceName = '.$instanceName'; + } + const EventChannel streamEventsChannel = EventChannel( + 'dev.flutter.pigeon.pigeon_integration_tests.EventChannelCoreApi.streamEvents', + pigeonMethodCodec); + return streamEventsChannel.receiveBroadcastStream().map((dynamic event) { + return event as EventChannelDataBase; + }); +} diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/.gitignore b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/.gitignore index 5f3fcaf31604..255032e2b82e 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/.gitignore +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/.gitignore @@ -9,3 +9,4 @@ !ProxyApiTestApiImpls.kt # Including this makes it easier to review code generation changes. !ProxyApiTests.gen.kt +!EventChannelTests.gen.kt diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt new file mode 100644 index 000000000000..0aa9876468ca --- /dev/null +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt @@ -0,0 +1,472 @@ +// 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. +// +// Autogenerated from Pigeon, do not edit directly. +// See also: https://pub.dev/packages/pigeon +@file:Suppress("UNCHECKED_CAST", "ArrayInDataClass") + +package com.example.test_plugin + +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.EventChannel +import io.flutter.plugin.common.StandardMessageCodec +import io.flutter.plugin.common.StandardMethodCodec +import java.io.ByteArrayOutputStream +import java.nio.ByteBuffer + +/** + * Error class for passing custom error details to Flutter via a thrown PlatformException. + * + * @property code The error code. + * @property message The error message. + * @property details The error details. Must be a datatype supported by the api codec. + */ +class EventChannelTestsError( + val code: String, + override val message: String? = null, + val details: Any? = null +) : Throwable() + +enum class EventEnum(val raw: Int) { + ONE(0), + TWO(1), + THREE(2), + FORTY_TWO(3), + FOUR_HUNDRED_TWENTY_TWO(4); + + companion object { + fun ofRaw(raw: Int): EventEnum? { + return values().firstOrNull { it.raw == raw } + } + } +} + +enum class AnotherEventEnum(val raw: Int) { + JUST_IN_CASE(0); + + companion object { + fun ofRaw(raw: Int): AnotherEventEnum? { + return values().firstOrNull { it.raw == raw } + } + } +} + +/** + * A class containing all supported nullable types. + * + * Generated class from Pigeon that represents data sent in messages. + */ +data class EventAllNullableTypes( + val aNullableBool: Boolean? = null, + val aNullableInt: Long? = null, + val aNullableInt64: Long? = null, + val aNullableDouble: Double? = null, + val aNullableByteArray: ByteArray? = null, + val aNullable4ByteArray: IntArray? = null, + val aNullable8ByteArray: LongArray? = null, + val aNullableFloatArray: DoubleArray? = null, + val aNullableEnum: EventEnum? = null, + val anotherNullableEnum: AnotherEventEnum? = null, + val aNullableString: String? = null, + val aNullableObject: Any? = null, + val allNullableTypes: EventAllNullableTypes? = null, + val list: List? = null, + val stringList: List? = null, + val intList: List? = null, + val doubleList: List? = null, + val boolList: List? = null, + val enumList: List? = null, + val objectList: List? = null, + val listList: List?>? = null, + val mapList: List?>? = null, + val recursiveClassList: List? = null, + val map: Map? = null, + val stringMap: Map? = null, + val intMap: Map? = null, + val enumMap: Map? = null, + val objectMap: Map? = null, + val listMap: Map?>? = null, + val mapMap: Map?>? = null, + val recursiveClassMap: Map? = null +) { + companion object { + fun fromList(pigeonVar_list: List): EventAllNullableTypes { + val aNullableBool = pigeonVar_list[0] as Boolean? + val aNullableInt = pigeonVar_list[1] as Long? + val aNullableInt64 = pigeonVar_list[2] as Long? + val aNullableDouble = pigeonVar_list[3] as Double? + val aNullableByteArray = pigeonVar_list[4] as ByteArray? + val aNullable4ByteArray = pigeonVar_list[5] as IntArray? + val aNullable8ByteArray = pigeonVar_list[6] as LongArray? + val aNullableFloatArray = pigeonVar_list[7] as DoubleArray? + val aNullableEnum = pigeonVar_list[8] as EventEnum? + val anotherNullableEnum = pigeonVar_list[9] as AnotherEventEnum? + val aNullableString = pigeonVar_list[10] as String? + val aNullableObject = pigeonVar_list[11] + val allNullableTypes = pigeonVar_list[12] as EventAllNullableTypes? + val list = pigeonVar_list[13] as List? + val stringList = pigeonVar_list[14] as List? + val intList = pigeonVar_list[15] as List? + val doubleList = pigeonVar_list[16] as List? + val boolList = pigeonVar_list[17] as List? + val enumList = pigeonVar_list[18] as List? + val objectList = pigeonVar_list[19] as List? + val listList = pigeonVar_list[20] as List?>? + val mapList = pigeonVar_list[21] as List?>? + val recursiveClassList = pigeonVar_list[22] as List? + val map = pigeonVar_list[23] as Map? + val stringMap = pigeonVar_list[24] as Map? + val intMap = pigeonVar_list[25] as Map? + val enumMap = pigeonVar_list[26] as Map? + val objectMap = pigeonVar_list[27] as Map? + val listMap = pigeonVar_list[28] as Map?>? + val mapMap = pigeonVar_list[29] as Map?>? + val recursiveClassMap = pigeonVar_list[30] as Map? + return EventAllNullableTypes( + aNullableBool, + aNullableInt, + aNullableInt64, + aNullableDouble, + aNullableByteArray, + aNullable4ByteArray, + aNullable8ByteArray, + aNullableFloatArray, + aNullableEnum, + anotherNullableEnum, + aNullableString, + aNullableObject, + allNullableTypes, + list, + stringList, + intList, + doubleList, + boolList, + enumList, + objectList, + listList, + mapList, + recursiveClassList, + map, + stringMap, + intMap, + enumMap, + objectMap, + listMap, + mapMap, + recursiveClassMap) + } + } + + fun toList(): List { + return listOf( + aNullableBool, + aNullableInt, + aNullableInt64, + aNullableDouble, + aNullableByteArray, + aNullable4ByteArray, + aNullable8ByteArray, + aNullableFloatArray, + aNullableEnum, + anotherNullableEnum, + aNullableString, + aNullableObject, + allNullableTypes, + list, + stringList, + intList, + doubleList, + boolList, + enumList, + objectList, + listList, + mapList, + recursiveClassList, + map, + stringMap, + intMap, + enumMap, + objectMap, + listMap, + mapMap, + recursiveClassMap, + ) + } +} + +/** + * Generated class from Pigeon that represents data sent in messages. This class should not be + * extended by any user class outside of the generated file. + */ +sealed class EventChannelDataBase +/** Generated class from Pigeon that represents data sent in messages. */ +data class IntEvent(val value: Long) : EventChannelDataBase() { + companion object { + fun fromList(pigeonVar_list: List): IntEvent { + val value = pigeonVar_list[0] as Long + return IntEvent(value) + } + } + + fun toList(): List { + return listOf( + value, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class StringEvent(val value: String) : EventChannelDataBase() { + companion object { + fun fromList(pigeonVar_list: List): StringEvent { + val value = pigeonVar_list[0] as String + return StringEvent(value) + } + } + + fun toList(): List { + return listOf( + value, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class BoolEvent(val value: Boolean) : EventChannelDataBase() { + companion object { + fun fromList(pigeonVar_list: List): BoolEvent { + val value = pigeonVar_list[0] as Boolean + return BoolEvent(value) + } + } + + fun toList(): List { + return listOf( + value, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class DoubleEvent(val value: Double) : EventChannelDataBase() { + companion object { + fun fromList(pigeonVar_list: List): DoubleEvent { + val value = pigeonVar_list[0] as Double + return DoubleEvent(value) + } + } + + fun toList(): List { + return listOf( + value, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class ObjectsEvent(val value: Any) : EventChannelDataBase() { + companion object { + fun fromList(pigeonVar_list: List): ObjectsEvent { + val value = pigeonVar_list[0] as Any + return ObjectsEvent(value) + } + } + + fun toList(): List { + return listOf( + value, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class EnumEvent(val value: EventEnum) : EventChannelDataBase() { + companion object { + fun fromList(pigeonVar_list: List): EnumEvent { + val value = pigeonVar_list[0] as EventEnum + return EnumEvent(value) + } + } + + fun toList(): List { + return listOf( + value, + ) + } +} + +/** Generated class from Pigeon that represents data sent in messages. */ +data class ClassEvent(val value: EventAllNullableTypes) : EventChannelDataBase() { + companion object { + fun fromList(pigeonVar_list: List): ClassEvent { + val value = pigeonVar_list[0] as EventAllNullableTypes + return ClassEvent(value) + } + } + + fun toList(): List { + return listOf( + value, + ) + } +} + +private open class EventChannelTestsPigeonCodec : StandardMessageCodec() { + override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? { + return when (type) { + 129.toByte() -> { + return (readValue(buffer) as Long?)?.let { EventEnum.ofRaw(it.toInt()) } + } + 130.toByte() -> { + return (readValue(buffer) as Long?)?.let { AnotherEventEnum.ofRaw(it.toInt()) } + } + 131.toByte() -> { + return (readValue(buffer) as? List)?.let { EventAllNullableTypes.fromList(it) } + } + 132.toByte() -> { + return (readValue(buffer) as? List)?.let { IntEvent.fromList(it) } + } + 133.toByte() -> { + return (readValue(buffer) as? List)?.let { StringEvent.fromList(it) } + } + 134.toByte() -> { + return (readValue(buffer) as? List)?.let { BoolEvent.fromList(it) } + } + 135.toByte() -> { + return (readValue(buffer) as? List)?.let { DoubleEvent.fromList(it) } + } + 136.toByte() -> { + return (readValue(buffer) as? List)?.let { ObjectsEvent.fromList(it) } + } + 137.toByte() -> { + return (readValue(buffer) as? List)?.let { EnumEvent.fromList(it) } + } + 138.toByte() -> { + return (readValue(buffer) as? List)?.let { ClassEvent.fromList(it) } + } + else -> super.readValueOfType(type, buffer) + } + } + + override fun writeValue(stream: ByteArrayOutputStream, value: Any?) { + when (value) { + is EventEnum -> { + stream.write(129) + writeValue(stream, value.raw) + } + is AnotherEventEnum -> { + stream.write(130) + writeValue(stream, value.raw) + } + is EventAllNullableTypes -> { + stream.write(131) + writeValue(stream, value.toList()) + } + is IntEvent -> { + stream.write(132) + writeValue(stream, value.toList()) + } + is StringEvent -> { + stream.write(133) + writeValue(stream, value.toList()) + } + is BoolEvent -> { + stream.write(134) + writeValue(stream, value.toList()) + } + is DoubleEvent -> { + stream.write(135) + writeValue(stream, value.toList()) + } + is ObjectsEvent -> { + stream.write(136) + writeValue(stream, value.toList()) + } + is EnumEvent -> { + stream.write(137) + writeValue(stream, value.toList()) + } + is ClassEvent -> { + stream.write(138) + writeValue(stream, value.toList()) + } + else -> super.writeValue(stream, value) + } + } +} + +val EventChannelTestsPigeonMethodCodec = StandardMethodCodec(EventChannelTestsPigeonCodec()) + +private class PigeonStreamHandler(val wrapper: PigeonEventChannelWrapper) : + EventChannel.StreamHandler { + var pigeonSink: PigeonEventSink? = null + + override fun onListen(p0: Any?, sink: EventChannel.EventSink) { + pigeonSink = PigeonEventSink(sink) + wrapper.onListen(p0, pigeonSink!!) + } + + override fun onCancel(p0: Any?) { + pigeonSink = null + wrapper.onCancel(p0) + } +} + +interface PigeonEventChannelWrapper { + open fun onListen(p0: Any?, sink: PigeonEventSink) {} + + open fun onCancel(p0: Any?) {} +} + +class PigeonEventSink(private val sink: EventChannel.EventSink) { + fun success(value: T) { + sink.success(value) + } + + fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { + sink.error(errorCode, errorMessage, errorDetails) + } + + fun endOfStream() { + sink.endOfStream() + } +} + +abstract class StreamIntsStreamHandler : PigeonEventChannelWrapper { + companion object { + fun register( + messenger: BinaryMessenger, + wrapper: StreamIntsStreamHandler, + instanceName: String = "" + ) { + var channelName: String = + "dev.flutter.pigeon.pigeon_integration_tests.EventChannelCoreApi.streamInts" + if (instanceName.isNotEmpty()) { + channelName += ".$instanceName" + } + val streamHandler = PigeonStreamHandler(wrapper) + EventChannel(messenger, channelName, EventChannelTestsPigeonMethodCodec) + .setStreamHandler(streamHandler) + } + } +} + +abstract class StreamEventsStreamHandler : PigeonEventChannelWrapper { + companion object { + fun register( + messenger: BinaryMessenger, + wrapper: StreamEventsStreamHandler, + instanceName: String = "" + ) { + var channelName: String = + "dev.flutter.pigeon.pigeon_integration_tests.EventChannelCoreApi.streamEvents" + if (instanceName.isNotEmpty()) { + channelName += ".$instanceName" + } + val streamHandler = PigeonStreamHandler(wrapper) + EventChannel(messenger, channelName, EventChannelTestsPigeonMethodCodec) + .setStreamHandler(streamHandler) + } + } +} diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt index 9563941624d7..ca183b9c5753 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt @@ -4,6 +4,8 @@ package com.example.test_plugin +import android.os.Handler +import android.os.Looper import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding @@ -26,6 +28,9 @@ class TestPlugin : FlutterPlugin, HostIntegrationCoreApi { proxyApiRegistrar = ProxyApiRegistrar(binding.binaryMessenger) proxyApiRegistrar!!.setUp() + + StreamEventsStreamHandler.register(binding.binaryMessenger, SendClass) + StreamIntsStreamHandler.register(binding.binaryMessenger, SendInts) } override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) { @@ -867,3 +872,57 @@ class TestPluginWithSuffix : HostSmallApi { callback(Result.success(Unit)) } } + +object SendInts : StreamIntsStreamHandler() { + val handler = Handler(Looper.getMainLooper()) + + override fun onListen(p0: Any?, sink: PigeonEventSink) { + var count: Long = 0 + val r: Runnable = + object : Runnable { + override fun run() { + handler.post { + sink.success(count) + count++ + } + handler.postDelayed(this, 1000) + if (count >= 5) { + sink.endOfStream() + } + } + } + handler.postDelayed(r, 1000) + } +} + +object SendClass : StreamEventsStreamHandler() { + val handler = Handler(Looper.getMainLooper()) + val eventList = + listOf( + IntEvent(1), + StringEvent("string"), + BoolEvent(false), + DoubleEvent(3.14), + ObjectsEvent(true), + EnumEvent(EventEnum.FORTY_TWO), + ClassEvent(EventAllNullableTypes(aNullableInt = 0))) + + override fun onListen(p0: Any?, sink: PigeonEventSink) { + var count: Int = 0 + val r: Runnable = + object : Runnable { + override fun run() { + if (count >= eventList.size) { + sink.endOfStream() + } else { + handler.post { + sink.success(eventList[count]) + count++ + } + handler.postDelayed(this, 10) + } + } + } + handler.postDelayed(r, 10) + } +} diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/.gitignore b/packages/pigeon/platform_tests/test_plugin/ios/Classes/.gitignore index 18ad850358d9..7f6cf31d4946 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/.gitignore +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/.gitignore @@ -7,3 +7,4 @@ !ProxyApiTests.gen.swift # Contains the class declartions for testing ProxyApis. !ProxyApiTestClass.swift +!EventChannelTests.gen.swift diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift new file mode 100644 index 000000000000..c2f8344c2044 --- /dev/null +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift @@ -0,0 +1,576 @@ +// 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. +// +// Autogenerated from Pigeon, do not edit directly. +// See also: https://pub.dev/packages/pigeon + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +/// Error class for passing custom error details to Dart side. +final class EventChannelTestsError: Error { + let code: String + let message: String? + let details: Any? + + init(code: String, message: String?, details: Any?) { + self.code = code + self.message = message + self.details = details + } + + var localizedDescription: String { + return + "EventChannelTestsError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } +} + +private func isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + +private func nilOrValue(_ value: Any?) -> T? { + if value is NSNull { return nil } + return value as! T? +} + +enum EventEnum: Int { + case one = 0 + case two = 1 + case three = 2 + case fortyTwo = 3 + case fourHundredTwentyTwo = 4 +} + +enum AnotherEventEnum: Int { + case justInCase = 0 +} + +/// A class containing all supported nullable types. +/// +/// Generated class from Pigeon that represents data sent in messages. +class EventAllNullableTypes { + init( + aNullableBool: Bool? = nil, + aNullableInt: Int64? = nil, + aNullableInt64: Int64? = nil, + aNullableDouble: Double? = nil, + aNullableByteArray: FlutterStandardTypedData? = nil, + aNullable4ByteArray: FlutterStandardTypedData? = nil, + aNullable8ByteArray: FlutterStandardTypedData? = nil, + aNullableFloatArray: FlutterStandardTypedData? = nil, + aNullableEnum: EventEnum? = nil, + anotherNullableEnum: AnotherEventEnum? = nil, + aNullableString: String? = nil, + aNullableObject: Any? = nil, + allNullableTypes: EventAllNullableTypes? = nil, + list: [Any?]? = nil, + stringList: [String?]? = nil, + intList: [Int64?]? = nil, + doubleList: [Double?]? = nil, + boolList: [Bool?]? = nil, + enumList: [EventEnum?]? = nil, + objectList: [Any?]? = nil, + listList: [[Any?]?]? = nil, + mapList: [[AnyHashable?: Any?]?]? = nil, + recursiveClassList: [EventAllNullableTypes?]? = nil, + map: [AnyHashable?: Any?]? = nil, + stringMap: [String?: String?]? = nil, + intMap: [Int64?: Int64?]? = nil, + enumMap: [EventEnum?: EventEnum?]? = nil, + objectMap: [AnyHashable?: Any?]? = nil, + listMap: [Int64?: [Any?]?]? = nil, + mapMap: [Int64?: [AnyHashable?: Any?]?]? = nil, + recursiveClassMap: [Int64?: EventAllNullableTypes?]? = nil + ) { + self.aNullableBool = aNullableBool + self.aNullableInt = aNullableInt + self.aNullableInt64 = aNullableInt64 + self.aNullableDouble = aNullableDouble + self.aNullableByteArray = aNullableByteArray + self.aNullable4ByteArray = aNullable4ByteArray + self.aNullable8ByteArray = aNullable8ByteArray + self.aNullableFloatArray = aNullableFloatArray + self.aNullableEnum = aNullableEnum + self.anotherNullableEnum = anotherNullableEnum + self.aNullableString = aNullableString + self.aNullableObject = aNullableObject + self.allNullableTypes = allNullableTypes + self.list = list + self.stringList = stringList + self.intList = intList + self.doubleList = doubleList + self.boolList = boolList + self.enumList = enumList + self.objectList = objectList + self.listList = listList + self.mapList = mapList + self.recursiveClassList = recursiveClassList + self.map = map + self.stringMap = stringMap + self.intMap = intMap + self.enumMap = enumMap + self.objectMap = objectMap + self.listMap = listMap + self.mapMap = mapMap + self.recursiveClassMap = recursiveClassMap + } + var aNullableBool: Bool? + var aNullableInt: Int64? + var aNullableInt64: Int64? + var aNullableDouble: Double? + var aNullableByteArray: FlutterStandardTypedData? + var aNullable4ByteArray: FlutterStandardTypedData? + var aNullable8ByteArray: FlutterStandardTypedData? + var aNullableFloatArray: FlutterStandardTypedData? + var aNullableEnum: EventEnum? + var anotherNullableEnum: AnotherEventEnum? + var aNullableString: String? + var aNullableObject: Any? + var allNullableTypes: EventAllNullableTypes? + var list: [Any?]? + var stringList: [String?]? + var intList: [Int64?]? + var doubleList: [Double?]? + var boolList: [Bool?]? + var enumList: [EventEnum?]? + var objectList: [Any?]? + var listList: [[Any?]?]? + var mapList: [[AnyHashable?: Any?]?]? + var recursiveClassList: [EventAllNullableTypes?]? + var map: [AnyHashable?: Any?]? + var stringMap: [String?: String?]? + var intMap: [Int64?: Int64?]? + var enumMap: [EventEnum?: EventEnum?]? + var objectMap: [AnyHashable?: Any?]? + var listMap: [Int64?: [Any?]?]? + var mapMap: [Int64?: [AnyHashable?: Any?]?]? + var recursiveClassMap: [Int64?: EventAllNullableTypes?]? + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> EventAllNullableTypes? { + let aNullableBool: Bool? = nilOrValue(pigeonVar_list[0]) + let aNullableInt: Int64? = nilOrValue(pigeonVar_list[1]) + let aNullableInt64: Int64? = nilOrValue(pigeonVar_list[2]) + let aNullableDouble: Double? = nilOrValue(pigeonVar_list[3]) + let aNullableByteArray: FlutterStandardTypedData? = nilOrValue(pigeonVar_list[4]) + let aNullable4ByteArray: FlutterStandardTypedData? = nilOrValue(pigeonVar_list[5]) + let aNullable8ByteArray: FlutterStandardTypedData? = nilOrValue(pigeonVar_list[6]) + let aNullableFloatArray: FlutterStandardTypedData? = nilOrValue(pigeonVar_list[7]) + let aNullableEnum: EventEnum? = nilOrValue(pigeonVar_list[8]) + let anotherNullableEnum: AnotherEventEnum? = nilOrValue(pigeonVar_list[9]) + let aNullableString: String? = nilOrValue(pigeonVar_list[10]) + let aNullableObject: Any? = pigeonVar_list[11] + let allNullableTypes: EventAllNullableTypes? = nilOrValue(pigeonVar_list[12]) + let list: [Any?]? = nilOrValue(pigeonVar_list[13]) + let stringList: [String?]? = nilOrValue(pigeonVar_list[14]) + let intList: [Int64?]? = nilOrValue(pigeonVar_list[15]) + let doubleList: [Double?]? = nilOrValue(pigeonVar_list[16]) + let boolList: [Bool?]? = nilOrValue(pigeonVar_list[17]) + let enumList: [EventEnum?]? = nilOrValue(pigeonVar_list[18]) + let objectList: [Any?]? = nilOrValue(pigeonVar_list[19]) + let listList: [[Any?]?]? = nilOrValue(pigeonVar_list[20]) + let mapList: [[AnyHashable?: Any?]?]? = nilOrValue(pigeonVar_list[21]) + let recursiveClassList: [EventAllNullableTypes?]? = nilOrValue(pigeonVar_list[22]) + let map: [AnyHashable?: Any?]? = nilOrValue(pigeonVar_list[23]) + let stringMap: [String?: String?]? = nilOrValue(pigeonVar_list[24]) + let intMap: [Int64?: Int64?]? = nilOrValue(pigeonVar_list[25]) + let enumMap: [EventEnum?: EventEnum?]? = pigeonVar_list[26] as? [EventEnum?: EventEnum?] + let objectMap: [AnyHashable?: Any?]? = nilOrValue(pigeonVar_list[27]) + let listMap: [Int64?: [Any?]?]? = nilOrValue(pigeonVar_list[28]) + let mapMap: [Int64?: [AnyHashable?: Any?]?]? = nilOrValue(pigeonVar_list[29]) + let recursiveClassMap: [Int64?: EventAllNullableTypes?]? = nilOrValue(pigeonVar_list[30]) + + return EventAllNullableTypes( + aNullableBool: aNullableBool, + aNullableInt: aNullableInt, + aNullableInt64: aNullableInt64, + aNullableDouble: aNullableDouble, + aNullableByteArray: aNullableByteArray, + aNullable4ByteArray: aNullable4ByteArray, + aNullable8ByteArray: aNullable8ByteArray, + aNullableFloatArray: aNullableFloatArray, + aNullableEnum: aNullableEnum, + anotherNullableEnum: anotherNullableEnum, + aNullableString: aNullableString, + aNullableObject: aNullableObject, + allNullableTypes: allNullableTypes, + list: list, + stringList: stringList, + intList: intList, + doubleList: doubleList, + boolList: boolList, + enumList: enumList, + objectList: objectList, + listList: listList, + mapList: mapList, + recursiveClassList: recursiveClassList, + map: map, + stringMap: stringMap, + intMap: intMap, + enumMap: enumMap, + objectMap: objectMap, + listMap: listMap, + mapMap: mapMap, + recursiveClassMap: recursiveClassMap + ) + } + func toList() -> [Any?] { + return [ + aNullableBool, + aNullableInt, + aNullableInt64, + aNullableDouble, + aNullableByteArray, + aNullable4ByteArray, + aNullable8ByteArray, + aNullableFloatArray, + aNullableEnum, + anotherNullableEnum, + aNullableString, + aNullableObject, + allNullableTypes, + list, + stringList, + intList, + doubleList, + boolList, + enumList, + objectList, + listList, + mapList, + recursiveClassList, + map, + stringMap, + intMap, + enumMap, + objectMap, + listMap, + mapMap, + recursiveClassMap, + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +/// This class should not be extended by any user class outside of the generated file. +protocol EventChannelDataBase { + +} + +/// Generated class from Pigeon that represents data sent in messages. +struct IntEvent: EventChannelDataBase { + var value: Int64 + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> IntEvent? { + let value = pigeonVar_list[0] as! Int64 + + return IntEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct StringEvent: EventChannelDataBase { + var value: String + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> StringEvent? { + let value = pigeonVar_list[0] as! String + + return StringEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct BoolEvent: EventChannelDataBase { + var value: Bool + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> BoolEvent? { + let value = pigeonVar_list[0] as! Bool + + return BoolEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct DoubleEvent: EventChannelDataBase { + var value: Double + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> DoubleEvent? { + let value = pigeonVar_list[0] as! Double + + return DoubleEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct ObjectsEvent: EventChannelDataBase { + var value: Any + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> ObjectsEvent? { + let value = pigeonVar_list[0]! + + return ObjectsEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct EnumEvent: EventChannelDataBase { + var value: EventEnum + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> EnumEvent? { + let value = pigeonVar_list[0] as! EventEnum + + return EnumEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct ClassEvent: EventChannelDataBase { + var value: EventAllNullableTypes + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> ClassEvent? { + let value = pigeonVar_list[0] as! EventAllNullableTypes + + return ClassEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +private class EventChannelTestsPigeonCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 129: + let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) + if let enumResultAsInt = enumResultAsInt { + return EventEnum(rawValue: enumResultAsInt) + } + return nil + case 130: + let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) + if let enumResultAsInt = enumResultAsInt { + return AnotherEventEnum(rawValue: enumResultAsInt) + } + return nil + case 131: + return EventAllNullableTypes.fromList(self.readValue() as! [Any?]) + case 132: + return IntEvent.fromList(self.readValue() as! [Any?]) + case 133: + return StringEvent.fromList(self.readValue() as! [Any?]) + case 134: + return BoolEvent.fromList(self.readValue() as! [Any?]) + case 135: + return DoubleEvent.fromList(self.readValue() as! [Any?]) + case 136: + return ObjectsEvent.fromList(self.readValue() as! [Any?]) + case 137: + return EnumEvent.fromList(self.readValue() as! [Any?]) + case 138: + return ClassEvent.fromList(self.readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class EventChannelTestsPigeonCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? EventEnum { + super.writeByte(129) + super.writeValue(value.rawValue) + } else if let value = value as? AnotherEventEnum { + super.writeByte(130) + super.writeValue(value.rawValue) + } else if let value = value as? EventAllNullableTypes { + super.writeByte(131) + super.writeValue(value.toList()) + } else if let value = value as? IntEvent { + super.writeByte(132) + super.writeValue(value.toList()) + } else if let value = value as? StringEvent { + super.writeByte(133) + super.writeValue(value.toList()) + } else if let value = value as? BoolEvent { + super.writeByte(134) + super.writeValue(value.toList()) + } else if let value = value as? DoubleEvent { + super.writeByte(135) + super.writeValue(value.toList()) + } else if let value = value as? ObjectsEvent { + super.writeByte(136) + super.writeValue(value.toList()) + } else if let value = value as? EnumEvent { + super.writeByte(137) + super.writeValue(value.toList()) + } else if let value = value as? ClassEvent { + super.writeByte(138) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class EventChannelTestsPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return EventChannelTestsPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return EventChannelTestsPigeonCodecWriter(data: data) + } +} + +class EventChannelTestsPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = EventChannelTestsPigeonCodec( + readerWriter: EventChannelTestsPigeonCodecReaderWriter()) +} + +var eventChannelTestsPigeonMethodCodec = FlutterStandardMethodCodec( + readerWriter: EventChannelTestsPigeonCodecReaderWriter()) + +private class PigeonStreamHandler: NSObject, FlutterStreamHandler { + private let wrapper: PigeonEventChannelWrapper + private var pigeonSink: PigeonEventSink? = nil + + init(wrapper: PigeonEventChannelWrapper) { + self.wrapper = wrapper + } + + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) + -> FlutterError? + { + pigeonSink = PigeonEventSink(events) + wrapper.onListen(withArguments: arguments, sink: pigeonSink!) + return nil + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + pigeonSink = nil + wrapper.onCancel(withArguments: arguments) + return nil + } +} + +class PigeonEventChannelWrapper { + func onListen(withArguments arguments: Any?, sink: PigeonEventSink) {} + func onCancel(withArguments arguments: Any?) {} +} + +class PigeonEventSink { + private let sink: FlutterEventSink + + init(_ sink: @escaping FlutterEventSink) { + self.sink = sink + } + + func success(_ value: ReturnType) { + sink(value) + } + + func error(code: String, message: String?, details: Any?) { + sink(FlutterError(code: code, message: message, details: details)) + } + + func endOfStream() { + sink(FlutterEndOfEventStream) + } + +} + +class StreamIntsStreamHandler: PigeonEventChannelWrapper { + static func register( + with messenger: FlutterBinaryMessenger, + instanceName: String = "", + wrapper: StreamIntsStreamHandler + ) { + var channelName = "dev.flutter.pigeon.pigeon_integration_tests.EventChannelCoreApi.streamInts" + if !instanceName.isEmpty { + channelName += ".\(instanceName)" + } + let streamHandler = PigeonStreamHandler(wrapper: wrapper) + let channel = FlutterEventChannel( + name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) + channel.setStreamHandler(streamHandler) + } +} + +class StreamEventsStreamHandler: PigeonEventChannelWrapper { + static func register( + with messenger: FlutterBinaryMessenger, + instanceName: String = "", + wrapper: StreamEventsStreamHandler + ) { + var channelName = "dev.flutter.pigeon.pigeon_integration_tests.EventChannelCoreApi.streamEvents" + if !instanceName.isEmpty { + channelName += ".\(instanceName)" + } + let streamHandler = PigeonStreamHandler(wrapper: wrapper) + let channel = FlutterEventChannel( + name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) + channel.setStreamHandler(streamHandler) + } +} diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift index b4238de3be91..a57e8e983238 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift @@ -28,6 +28,9 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { binaryMessenger: binaryMessenger, messageChannelSuffix: "suffixOne") flutterSmallApiTwo = FlutterSmallApi( binaryMessenger: binaryMessenger, messageChannelSuffix: "suffixTwo") + + StreamIntsStreamHandler.register(with: binaryMessenger, wrapper: SendInts()) + StreamEventsStreamHandler.register(with: binaryMessenger, wrapper: SendEvents()) proxyApiRegistrar = ProxyApiTestsPigeonProxyApiRegistrar( binaryMessenger: binaryMessenger, apiDelegate: ProxyApiDelegate()) proxyApiRegistrar!.setUp() @@ -1214,6 +1217,54 @@ public class TestPluginWithSuffix: HostSmallApi { } +class SendInts: StreamIntsStreamHandler { + var timerActive = false + + override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) { + var count: Int64 = 0 + if !timerActive { + timerActive = true + Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { _ in + sink.success(count) + count += 1 + if count >= 5 { + sink.endOfStream() + } + } + } + } +} + +class SendEvents: StreamEventsStreamHandler { + var timerActive = false + var eventList: [EventChannelDataBase] = + [ + IntEvent(value: 1), + StringEvent(value: "string"), + BoolEvent(value: false), + DoubleEvent(value: 3.14), + ObjectsEvent(value: true), + EnumEvent(value: EventEnum.fortyTwo), + ClassEvent(value: EventAllNullableTypes(aNullableInt: 0)), + ] + + override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) + { + var count = 0 + if !timerActive { + timerActive = true + Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { _ in + if count >= self.eventList.count { + sink.endOfStream() + } else { + sink.success(self.eventList[count]) + count += 1 + } + } + } + } +} + class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { func pigeonApiProxyApiTestClass(_ registrar: ProxyApiTestsPigeonProxyApiRegistrar) -> PigeonApiProxyApiTestClass @@ -1221,7 +1272,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { class ProxyApiTestClassDelegate: PigeonApiDelegateProxyApiTestClass { func pigeonDefaultConstructor( pigeonApi: PigeonApiProxyApiTestClass, aBool: Bool, anInt: Int64, aDouble: Double, - aString: String, aUint8List: FlutterStandardTypedData, aList: [Any?], aMap: [String?: Any?], + aString: String, aUint8List: FlutterStandardTypedData, aList: [Any?], + aMap: [String?: Any?], anEnum: ProxyApiTestEnum, aProxyApi: ProxyApiSuperClass, aNullableBool: Bool?, aNullableInt: Int64?, aNullableDouble: Double?, aNullableString: String?, aNullableUint8List: FlutterStandardTypedData?, aNullableList: [Any?]?, @@ -1238,35 +1290,43 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { return ProxyApiTestClass() } - func attachedField(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + func attachedField( + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass + ) throws -> ProxyApiSuperClass { return ProxyApiSuperClass() } - func staticAttachedField(pigeonApi: PigeonApiProxyApiTestClass) throws -> ProxyApiSuperClass { + func staticAttachedField(pigeonApi: PigeonApiProxyApiTestClass) throws + -> ProxyApiSuperClass + { return ProxyApiSuperClass() } - func aBool(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) throws + func aBool(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + throws -> Bool { return true } - func anInt(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) throws + func anInt(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + throws -> Int64 { return 0 } - func aDouble(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) throws + func aDouble(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + throws -> Double { return 0.0 } - func aString(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) throws + func aString(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + throws -> String { return "" @@ -1278,7 +1338,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { return FlutterStandardTypedData(bytes: Data()) } - func aList(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) throws + func aList(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + throws -> [Any?] { return [] @@ -1290,7 +1351,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { return [:] } - func anEnum(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) throws + func anEnum(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + throws -> ProxyApiTestEnum { return ProxyApiTestEnum.one @@ -1302,25 +1364,33 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { return ProxyApiSuperClass() } - func aNullableBool(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + func aNullableBool( + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass + ) throws -> Bool? { return nil } - func aNullableInt(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + func aNullableInt( + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass + ) throws -> Int64? { return nil } - func aNullableDouble(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + func aNullableDouble( + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass + ) throws -> Double? { return nil } - func aNullableString(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + func aNullableString( + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass + ) throws -> String? { return nil @@ -1332,19 +1402,25 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { return nil } - func aNullableList(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + func aNullableList( + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass + ) throws -> [Any?]? { return nil } - func aNullableMap(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + func aNullableMap( + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass + ) throws -> [String?: Any?]? { return nil } - func aNullableEnum(pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass) + func aNullableEnum( + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass + ) throws -> ProxyApiTestEnum? { return nil @@ -1384,7 +1460,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func echoDouble( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aDouble: Double + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aDouble: Double ) throws -> Double { return aDouble } @@ -1396,7 +1473,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func echoString( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aString: String + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aString: String ) throws -> String { return aString } @@ -1540,7 +1618,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func echoAsyncDouble( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aDouble: Double, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aDouble: Double, completion: @escaping (Result) -> Void ) { completion(.success(aDouble)) @@ -1554,7 +1633,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func echoAsyncString( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aString: String, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aString: String, completion: @escaping (Result) -> Void ) { completion(.success(aString)) @@ -1591,7 +1671,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { func echoAsyncEnum( pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, - anEnum: ProxyApiTestEnum, completion: @escaping (Result) -> Void + anEnum: ProxyApiTestEnum, + completion: @escaping (Result) -> Void ) { completion(.success(anEnum)) } @@ -1628,7 +1709,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func echoAsyncNullableDouble( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aDouble: Double?, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aDouble: Double?, completion: @escaping (Result) -> Void ) { completion(.success(aDouble)) @@ -1642,7 +1724,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func echoAsyncNullableString( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aString: String?, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aString: String?, completion: @escaping (Result) -> Void ) { completion(.success(aString)) @@ -1657,14 +1740,16 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func echoAsyncNullableObject( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, anObject: Any?, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + anObject: Any?, completion: @escaping (Result) -> Void ) { completion(.success(anObject)) } func echoAsyncNullableList( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aList: [Any?]?, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aList: [Any?]?, completion: @escaping (Result<[Any?]?, Error>) -> Void ) { completion(.success(aList)) @@ -1679,7 +1764,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { func echoAsyncNullableEnum( pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, - anEnum: ProxyApiTestEnum?, completion: @escaping (Result) -> Void + anEnum: ProxyApiTestEnum?, + completion: @escaping (Result) -> Void ) { completion(.success(anEnum)) } @@ -1688,13 +1774,15 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } - func echoStaticString(pigeonApi: PigeonApiProxyApiTestClass, aString: String) throws -> String + func echoStaticString(pigeonApi: PigeonApiProxyApiTestClass, aString: String) throws + -> String { return aString } func staticAsyncNoop( - pigeonApi: PigeonApiProxyApiTestClass, completion: @escaping (Result) -> Void + pigeonApi: PigeonApiProxyApiTestClass, + completion: @escaping (Result) -> Void ) { completion(.success(Void())) } @@ -1770,10 +1858,12 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func callFlutterEchoDouble( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aDouble: Double, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aDouble: Double, completion: @escaping (Result) -> Void ) { - pigeonApi.flutterEchoDouble(pigeonInstance: pigeonInstance, aDouble: aDouble) { response in + pigeonApi.flutterEchoDouble(pigeonInstance: pigeonInstance, aDouble: aDouble) { + response in switch response { case .success(let res): completion(.success(res)) @@ -1784,10 +1874,12 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func callFlutterEchoString( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aString: String, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aString: String, completion: @escaping (Result) -> Void ) { - pigeonApi.flutterEchoString(pigeonInstance: pigeonInstance, aString: aString) { response in + pigeonApi.flutterEchoString(pigeonInstance: pigeonInstance, aString: aString) { + response in switch response { case .success(let res): completion(.success(res)) @@ -1862,7 +1954,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { aMap: [String?: ProxyApiTestClass?], completion: @escaping (Result<[String?: ProxyApiTestClass?], Error>) -> Void ) { - pigeonApi.flutterEchoProxyApiMap(pigeonInstance: pigeonInstance, aMap: aMap) { response in + pigeonApi.flutterEchoProxyApiMap(pigeonInstance: pigeonInstance, aMap: aMap) { + response in switch response { case .success(let res): completion(.success(res)) @@ -1874,7 +1967,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { func callFlutterEchoEnum( pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, - anEnum: ProxyApiTestEnum, completion: @escaping (Result) -> Void + anEnum: ProxyApiTestEnum, + completion: @escaping (Result) -> Void ) { pigeonApi.flutterEchoEnum(pigeonInstance: pigeonInstance, anEnum: anEnum) { response in switch response { @@ -1921,7 +2015,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, anInt: Int64?, completion: @escaping (Result) -> Void ) { - pigeonApi.flutterEchoNullableInt(pigeonInstance: pigeonInstance, anInt: anInt) { response in + pigeonApi.flutterEchoNullableInt(pigeonInstance: pigeonInstance, anInt: anInt) { + response in switch response { case .success(let res): completion(.success(res)) @@ -1932,7 +2027,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func callFlutterEchoNullableDouble( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aDouble: Double?, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aDouble: Double?, completion: @escaping (Result) -> Void ) { pigeonApi.flutterEchoNullableDouble(pigeonInstance: pigeonInstance, aDouble: aDouble) { @@ -1947,7 +2043,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func callFlutterEchoNullableString( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aString: String?, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aString: String?, completion: @escaping (Result) -> Void ) { pigeonApi.flutterEchoNullableString(pigeonInstance: pigeonInstance, aString: aString) { @@ -1966,7 +2063,9 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { aUint8List: FlutterStandardTypedData?, completion: @escaping (Result) -> Void ) { - pigeonApi.flutterEchoNullableUint8List(pigeonInstance: pigeonInstance, aList: aUint8List) { + pigeonApi.flutterEchoNullableUint8List( + pigeonInstance: pigeonInstance, aList: aUint8List + ) { response in switch response { case .success(let res): @@ -1978,7 +2077,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func callFlutterEchoNullableList( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aList: [Any?]?, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aList: [Any?]?, completion: @escaping (Result<[Any?]?, Error>) -> Void ) { pigeonApi.flutterEchoNullableList(pigeonInstance: pigeonInstance, aList: aList) { @@ -1996,7 +2096,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aMap: [String?: Any?]?, completion: @escaping (Result<[String?: Any?]?, Error>) -> Void ) { - pigeonApi.flutterEchoNullableMap(pigeonInstance: pigeonInstance, aMap: aMap) { response in + pigeonApi.flutterEchoNullableMap(pigeonInstance: pigeonInstance, aMap: aMap) { + response in switch response { case .success(let res): completion(.success(res)) @@ -2008,7 +2109,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { func callFlutterEchoNullableEnum( pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, - anEnum: ProxyApiTestEnum?, completion: @escaping (Result) -> Void + anEnum: ProxyApiTestEnum?, + completion: @escaping (Result) -> Void ) { pigeonApi.flutterEchoNullableEnum(pigeonInstance: pigeonInstance, anEnum: anEnum) { response in @@ -2026,8 +2128,9 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { aProxyApi: ProxyApiSuperClass?, completion: @escaping (Result) -> Void ) { - pigeonApi.flutterEchoNullableProxyApi(pigeonInstance: pigeonInstance, aProxyApi: aProxyApi) - { response in + pigeonApi.flutterEchoNullableProxyApi( + pigeonInstance: pigeonInstance, aProxyApi: aProxyApi + ) { response in switch response { case .success(let res): completion(.success(res)) @@ -2052,7 +2155,8 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { } func callFlutterEchoAsyncString( - pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, aString: String, + pigeonApi: PigeonApiProxyApiTestClass, pigeonInstance: ProxyApiTestClass, + aString: String, completion: @escaping (Result) -> Void ) { pigeonApi.flutterEchoAsyncString(pigeonInstance: pigeonInstance, aString: aString) { @@ -2081,7 +2185,9 @@ class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { return ProxyApiSuperClass() } - func aSuperMethod(pigeonApi: PigeonApiProxyApiSuperClass, pigeonInstance: ProxyApiSuperClass) + func aSuperMethod( + pigeonApi: PigeonApiProxyApiSuperClass, pigeonInstance: ProxyApiSuperClass + ) throws {} } diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/.gitignore b/packages/pigeon/platform_tests/test_plugin/macos/Classes/.gitignore index 7a58c79195bb..68f71f9d95f6 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/.gitignore +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/.gitignore @@ -6,4 +6,5 @@ # Keeping this makes it easier to review changes to ProxyApi generation. !ProxyApiTests.gen.swift # Contains the class declartions for testing ProxyApis. -!ProxyApiTestClass.swift \ No newline at end of file +!ProxyApiTestClass.swift +!EventChannelTests.gen.swift \ No newline at end of file diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift new file mode 100644 index 000000000000..c2f8344c2044 --- /dev/null +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift @@ -0,0 +1,576 @@ +// 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. +// +// Autogenerated from Pigeon, do not edit directly. +// See also: https://pub.dev/packages/pigeon + +import Foundation + +#if os(iOS) + import Flutter +#elseif os(macOS) + import FlutterMacOS +#else + #error("Unsupported platform.") +#endif + +/// Error class for passing custom error details to Dart side. +final class EventChannelTestsError: Error { + let code: String + let message: String? + let details: Any? + + init(code: String, message: String?, details: Any?) { + self.code = code + self.message = message + self.details = details + } + + var localizedDescription: String { + return + "EventChannelTestsError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } +} + +private func isNullish(_ value: Any?) -> Bool { + return value is NSNull || value == nil +} + +private func nilOrValue(_ value: Any?) -> T? { + if value is NSNull { return nil } + return value as! T? +} + +enum EventEnum: Int { + case one = 0 + case two = 1 + case three = 2 + case fortyTwo = 3 + case fourHundredTwentyTwo = 4 +} + +enum AnotherEventEnum: Int { + case justInCase = 0 +} + +/// A class containing all supported nullable types. +/// +/// Generated class from Pigeon that represents data sent in messages. +class EventAllNullableTypes { + init( + aNullableBool: Bool? = nil, + aNullableInt: Int64? = nil, + aNullableInt64: Int64? = nil, + aNullableDouble: Double? = nil, + aNullableByteArray: FlutterStandardTypedData? = nil, + aNullable4ByteArray: FlutterStandardTypedData? = nil, + aNullable8ByteArray: FlutterStandardTypedData? = nil, + aNullableFloatArray: FlutterStandardTypedData? = nil, + aNullableEnum: EventEnum? = nil, + anotherNullableEnum: AnotherEventEnum? = nil, + aNullableString: String? = nil, + aNullableObject: Any? = nil, + allNullableTypes: EventAllNullableTypes? = nil, + list: [Any?]? = nil, + stringList: [String?]? = nil, + intList: [Int64?]? = nil, + doubleList: [Double?]? = nil, + boolList: [Bool?]? = nil, + enumList: [EventEnum?]? = nil, + objectList: [Any?]? = nil, + listList: [[Any?]?]? = nil, + mapList: [[AnyHashable?: Any?]?]? = nil, + recursiveClassList: [EventAllNullableTypes?]? = nil, + map: [AnyHashable?: Any?]? = nil, + stringMap: [String?: String?]? = nil, + intMap: [Int64?: Int64?]? = nil, + enumMap: [EventEnum?: EventEnum?]? = nil, + objectMap: [AnyHashable?: Any?]? = nil, + listMap: [Int64?: [Any?]?]? = nil, + mapMap: [Int64?: [AnyHashable?: Any?]?]? = nil, + recursiveClassMap: [Int64?: EventAllNullableTypes?]? = nil + ) { + self.aNullableBool = aNullableBool + self.aNullableInt = aNullableInt + self.aNullableInt64 = aNullableInt64 + self.aNullableDouble = aNullableDouble + self.aNullableByteArray = aNullableByteArray + self.aNullable4ByteArray = aNullable4ByteArray + self.aNullable8ByteArray = aNullable8ByteArray + self.aNullableFloatArray = aNullableFloatArray + self.aNullableEnum = aNullableEnum + self.anotherNullableEnum = anotherNullableEnum + self.aNullableString = aNullableString + self.aNullableObject = aNullableObject + self.allNullableTypes = allNullableTypes + self.list = list + self.stringList = stringList + self.intList = intList + self.doubleList = doubleList + self.boolList = boolList + self.enumList = enumList + self.objectList = objectList + self.listList = listList + self.mapList = mapList + self.recursiveClassList = recursiveClassList + self.map = map + self.stringMap = stringMap + self.intMap = intMap + self.enumMap = enumMap + self.objectMap = objectMap + self.listMap = listMap + self.mapMap = mapMap + self.recursiveClassMap = recursiveClassMap + } + var aNullableBool: Bool? + var aNullableInt: Int64? + var aNullableInt64: Int64? + var aNullableDouble: Double? + var aNullableByteArray: FlutterStandardTypedData? + var aNullable4ByteArray: FlutterStandardTypedData? + var aNullable8ByteArray: FlutterStandardTypedData? + var aNullableFloatArray: FlutterStandardTypedData? + var aNullableEnum: EventEnum? + var anotherNullableEnum: AnotherEventEnum? + var aNullableString: String? + var aNullableObject: Any? + var allNullableTypes: EventAllNullableTypes? + var list: [Any?]? + var stringList: [String?]? + var intList: [Int64?]? + var doubleList: [Double?]? + var boolList: [Bool?]? + var enumList: [EventEnum?]? + var objectList: [Any?]? + var listList: [[Any?]?]? + var mapList: [[AnyHashable?: Any?]?]? + var recursiveClassList: [EventAllNullableTypes?]? + var map: [AnyHashable?: Any?]? + var stringMap: [String?: String?]? + var intMap: [Int64?: Int64?]? + var enumMap: [EventEnum?: EventEnum?]? + var objectMap: [AnyHashable?: Any?]? + var listMap: [Int64?: [Any?]?]? + var mapMap: [Int64?: [AnyHashable?: Any?]?]? + var recursiveClassMap: [Int64?: EventAllNullableTypes?]? + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> EventAllNullableTypes? { + let aNullableBool: Bool? = nilOrValue(pigeonVar_list[0]) + let aNullableInt: Int64? = nilOrValue(pigeonVar_list[1]) + let aNullableInt64: Int64? = nilOrValue(pigeonVar_list[2]) + let aNullableDouble: Double? = nilOrValue(pigeonVar_list[3]) + let aNullableByteArray: FlutterStandardTypedData? = nilOrValue(pigeonVar_list[4]) + let aNullable4ByteArray: FlutterStandardTypedData? = nilOrValue(pigeonVar_list[5]) + let aNullable8ByteArray: FlutterStandardTypedData? = nilOrValue(pigeonVar_list[6]) + let aNullableFloatArray: FlutterStandardTypedData? = nilOrValue(pigeonVar_list[7]) + let aNullableEnum: EventEnum? = nilOrValue(pigeonVar_list[8]) + let anotherNullableEnum: AnotherEventEnum? = nilOrValue(pigeonVar_list[9]) + let aNullableString: String? = nilOrValue(pigeonVar_list[10]) + let aNullableObject: Any? = pigeonVar_list[11] + let allNullableTypes: EventAllNullableTypes? = nilOrValue(pigeonVar_list[12]) + let list: [Any?]? = nilOrValue(pigeonVar_list[13]) + let stringList: [String?]? = nilOrValue(pigeonVar_list[14]) + let intList: [Int64?]? = nilOrValue(pigeonVar_list[15]) + let doubleList: [Double?]? = nilOrValue(pigeonVar_list[16]) + let boolList: [Bool?]? = nilOrValue(pigeonVar_list[17]) + let enumList: [EventEnum?]? = nilOrValue(pigeonVar_list[18]) + let objectList: [Any?]? = nilOrValue(pigeonVar_list[19]) + let listList: [[Any?]?]? = nilOrValue(pigeonVar_list[20]) + let mapList: [[AnyHashable?: Any?]?]? = nilOrValue(pigeonVar_list[21]) + let recursiveClassList: [EventAllNullableTypes?]? = nilOrValue(pigeonVar_list[22]) + let map: [AnyHashable?: Any?]? = nilOrValue(pigeonVar_list[23]) + let stringMap: [String?: String?]? = nilOrValue(pigeonVar_list[24]) + let intMap: [Int64?: Int64?]? = nilOrValue(pigeonVar_list[25]) + let enumMap: [EventEnum?: EventEnum?]? = pigeonVar_list[26] as? [EventEnum?: EventEnum?] + let objectMap: [AnyHashable?: Any?]? = nilOrValue(pigeonVar_list[27]) + let listMap: [Int64?: [Any?]?]? = nilOrValue(pigeonVar_list[28]) + let mapMap: [Int64?: [AnyHashable?: Any?]?]? = nilOrValue(pigeonVar_list[29]) + let recursiveClassMap: [Int64?: EventAllNullableTypes?]? = nilOrValue(pigeonVar_list[30]) + + return EventAllNullableTypes( + aNullableBool: aNullableBool, + aNullableInt: aNullableInt, + aNullableInt64: aNullableInt64, + aNullableDouble: aNullableDouble, + aNullableByteArray: aNullableByteArray, + aNullable4ByteArray: aNullable4ByteArray, + aNullable8ByteArray: aNullable8ByteArray, + aNullableFloatArray: aNullableFloatArray, + aNullableEnum: aNullableEnum, + anotherNullableEnum: anotherNullableEnum, + aNullableString: aNullableString, + aNullableObject: aNullableObject, + allNullableTypes: allNullableTypes, + list: list, + stringList: stringList, + intList: intList, + doubleList: doubleList, + boolList: boolList, + enumList: enumList, + objectList: objectList, + listList: listList, + mapList: mapList, + recursiveClassList: recursiveClassList, + map: map, + stringMap: stringMap, + intMap: intMap, + enumMap: enumMap, + objectMap: objectMap, + listMap: listMap, + mapMap: mapMap, + recursiveClassMap: recursiveClassMap + ) + } + func toList() -> [Any?] { + return [ + aNullableBool, + aNullableInt, + aNullableInt64, + aNullableDouble, + aNullableByteArray, + aNullable4ByteArray, + aNullable8ByteArray, + aNullableFloatArray, + aNullableEnum, + anotherNullableEnum, + aNullableString, + aNullableObject, + allNullableTypes, + list, + stringList, + intList, + doubleList, + boolList, + enumList, + objectList, + listList, + mapList, + recursiveClassList, + map, + stringMap, + intMap, + enumMap, + objectMap, + listMap, + mapMap, + recursiveClassMap, + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +/// This class should not be extended by any user class outside of the generated file. +protocol EventChannelDataBase { + +} + +/// Generated class from Pigeon that represents data sent in messages. +struct IntEvent: EventChannelDataBase { + var value: Int64 + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> IntEvent? { + let value = pigeonVar_list[0] as! Int64 + + return IntEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct StringEvent: EventChannelDataBase { + var value: String + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> StringEvent? { + let value = pigeonVar_list[0] as! String + + return StringEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct BoolEvent: EventChannelDataBase { + var value: Bool + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> BoolEvent? { + let value = pigeonVar_list[0] as! Bool + + return BoolEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct DoubleEvent: EventChannelDataBase { + var value: Double + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> DoubleEvent? { + let value = pigeonVar_list[0] as! Double + + return DoubleEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct ObjectsEvent: EventChannelDataBase { + var value: Any + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> ObjectsEvent? { + let value = pigeonVar_list[0]! + + return ObjectsEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct EnumEvent: EventChannelDataBase { + var value: EventEnum + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> EnumEvent? { + let value = pigeonVar_list[0] as! EventEnum + + return EnumEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +/// Generated class from Pigeon that represents data sent in messages. +struct ClassEvent: EventChannelDataBase { + var value: EventAllNullableTypes + + // swift-format-ignore: AlwaysUseLowerCamelCase + static func fromList(_ pigeonVar_list: [Any?]) -> ClassEvent? { + let value = pigeonVar_list[0] as! EventAllNullableTypes + + return ClassEvent( + value: value + ) + } + func toList() -> [Any?] { + return [ + value + ] + } +} + +private class EventChannelTestsPigeonCodecReader: FlutterStandardReader { + override func readValue(ofType type: UInt8) -> Any? { + switch type { + case 129: + let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) + if let enumResultAsInt = enumResultAsInt { + return EventEnum(rawValue: enumResultAsInt) + } + return nil + case 130: + let enumResultAsInt: Int? = nilOrValue(self.readValue() as! Int?) + if let enumResultAsInt = enumResultAsInt { + return AnotherEventEnum(rawValue: enumResultAsInt) + } + return nil + case 131: + return EventAllNullableTypes.fromList(self.readValue() as! [Any?]) + case 132: + return IntEvent.fromList(self.readValue() as! [Any?]) + case 133: + return StringEvent.fromList(self.readValue() as! [Any?]) + case 134: + return BoolEvent.fromList(self.readValue() as! [Any?]) + case 135: + return DoubleEvent.fromList(self.readValue() as! [Any?]) + case 136: + return ObjectsEvent.fromList(self.readValue() as! [Any?]) + case 137: + return EnumEvent.fromList(self.readValue() as! [Any?]) + case 138: + return ClassEvent.fromList(self.readValue() as! [Any?]) + default: + return super.readValue(ofType: type) + } + } +} + +private class EventChannelTestsPigeonCodecWriter: FlutterStandardWriter { + override func writeValue(_ value: Any) { + if let value = value as? EventEnum { + super.writeByte(129) + super.writeValue(value.rawValue) + } else if let value = value as? AnotherEventEnum { + super.writeByte(130) + super.writeValue(value.rawValue) + } else if let value = value as? EventAllNullableTypes { + super.writeByte(131) + super.writeValue(value.toList()) + } else if let value = value as? IntEvent { + super.writeByte(132) + super.writeValue(value.toList()) + } else if let value = value as? StringEvent { + super.writeByte(133) + super.writeValue(value.toList()) + } else if let value = value as? BoolEvent { + super.writeByte(134) + super.writeValue(value.toList()) + } else if let value = value as? DoubleEvent { + super.writeByte(135) + super.writeValue(value.toList()) + } else if let value = value as? ObjectsEvent { + super.writeByte(136) + super.writeValue(value.toList()) + } else if let value = value as? EnumEvent { + super.writeByte(137) + super.writeValue(value.toList()) + } else if let value = value as? ClassEvent { + super.writeByte(138) + super.writeValue(value.toList()) + } else { + super.writeValue(value) + } + } +} + +private class EventChannelTestsPigeonCodecReaderWriter: FlutterStandardReaderWriter { + override func reader(with data: Data) -> FlutterStandardReader { + return EventChannelTestsPigeonCodecReader(data: data) + } + + override func writer(with data: NSMutableData) -> FlutterStandardWriter { + return EventChannelTestsPigeonCodecWriter(data: data) + } +} + +class EventChannelTestsPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable { + static let shared = EventChannelTestsPigeonCodec( + readerWriter: EventChannelTestsPigeonCodecReaderWriter()) +} + +var eventChannelTestsPigeonMethodCodec = FlutterStandardMethodCodec( + readerWriter: EventChannelTestsPigeonCodecReaderWriter()) + +private class PigeonStreamHandler: NSObject, FlutterStreamHandler { + private let wrapper: PigeonEventChannelWrapper + private var pigeonSink: PigeonEventSink? = nil + + init(wrapper: PigeonEventChannelWrapper) { + self.wrapper = wrapper + } + + func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) + -> FlutterError? + { + pigeonSink = PigeonEventSink(events) + wrapper.onListen(withArguments: arguments, sink: pigeonSink!) + return nil + } + + func onCancel(withArguments arguments: Any?) -> FlutterError? { + pigeonSink = nil + wrapper.onCancel(withArguments: arguments) + return nil + } +} + +class PigeonEventChannelWrapper { + func onListen(withArguments arguments: Any?, sink: PigeonEventSink) {} + func onCancel(withArguments arguments: Any?) {} +} + +class PigeonEventSink { + private let sink: FlutterEventSink + + init(_ sink: @escaping FlutterEventSink) { + self.sink = sink + } + + func success(_ value: ReturnType) { + sink(value) + } + + func error(code: String, message: String?, details: Any?) { + sink(FlutterError(code: code, message: message, details: details)) + } + + func endOfStream() { + sink(FlutterEndOfEventStream) + } + +} + +class StreamIntsStreamHandler: PigeonEventChannelWrapper { + static func register( + with messenger: FlutterBinaryMessenger, + instanceName: String = "", + wrapper: StreamIntsStreamHandler + ) { + var channelName = "dev.flutter.pigeon.pigeon_integration_tests.EventChannelCoreApi.streamInts" + if !instanceName.isEmpty { + channelName += ".\(instanceName)" + } + let streamHandler = PigeonStreamHandler(wrapper: wrapper) + let channel = FlutterEventChannel( + name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) + channel.setStreamHandler(streamHandler) + } +} + +class StreamEventsStreamHandler: PigeonEventChannelWrapper { + static func register( + with messenger: FlutterBinaryMessenger, + instanceName: String = "", + wrapper: StreamEventsStreamHandler + ) { + var channelName = "dev.flutter.pigeon.pigeon_integration_tests.EventChannelCoreApi.streamEvents" + if !instanceName.isEmpty { + channelName += ".\(instanceName)" + } + let streamHandler = PigeonStreamHandler(wrapper: wrapper) + let channel = FlutterEventChannel( + name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) + channel.setStreamHandler(streamHandler) + } +} diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift index fb5c21dabf18..13b81ca1dd04 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift @@ -30,6 +30,9 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { proxyApiRegistrar = ProxyApiTestsPigeonProxyApiRegistrar( binaryMessenger: binaryMessenger, apiDelegate: ProxyApiDelegate()) proxyApiRegistrar!.setUp() + + StreamIntsStreamHandler.register(with: binaryMessenger, wrapper: SendInts()) + StreamEventsStreamHandler.register(with: binaryMessenger, wrapper: SendEvents()) } public func detachFromEngine(for registrar: FlutterPluginRegistrar) { @@ -1213,6 +1216,54 @@ public class TestPluginWithSuffix: HostSmallApi { } +class SendInts: StreamIntsStreamHandler { + var timerActive = false + + override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) { + var count: Int64 = 0 + if !timerActive { + timerActive = true + Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { _ in + sink.success(count) + count += 1 + if count >= 5 { + sink.endOfStream() + } + } + } + } +} + +class SendEvents: StreamEventsStreamHandler { + var timerActive = false + var eventList: [EventChannelDataBase] = + [ + IntEvent(value: 1), + StringEvent(value: "string"), + BoolEvent(value: false), + DoubleEvent(value: 3.14), + ObjectsEvent(value: true), + EnumEvent(value: EventEnum.fortyTwo), + ClassEvent(value: EventAllNullableTypes(aNullableInt: 0)), + ] + + override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) + { + var count = 0 + if !timerActive { + timerActive = true + Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { _ in + if count >= self.eventList.count { + sink.endOfStream() + } else { + sink.success(self.eventList[count]) + count += 1 + } + } + } + } +} + class ProxyApiDelegate: ProxyApiTestsPigeonProxyApiDelegate { func pigeonApiProxyApiTestClass(_ registrar: ProxyApiTestsPigeonProxyApiRegistrar) -> PigeonApiProxyApiTestClass diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index cddaae515813..a7b5056bac10 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.0 # This must match the version in lib/generator_tools.dart +version: 22.7.0 # This must match the version in lib/generator_tools.dart environment: sdk: ^3.3.0 diff --git a/packages/pigeon/test/cpp_generator_test.dart b/packages/pigeon/test/cpp_generator_test.dart index 65a8f7ed8fdf..c88311f4c5be 100644 --- a/packages/pigeon/test/cpp_generator_test.dart +++ b/packages/pigeon/test/cpp_generator_test.dart @@ -249,23 +249,29 @@ void main() { }); test('Error field is private with public accessors', () { - final Root root = Root(apis: [ - AstHostApi(name: 'Api', methods: [ - Method( - name: 'doSomething', - location: ApiLocation.host, - parameters: [ - Parameter( - type: const TypeDeclaration( - baseName: 'int', - isNullable: false, - ), - name: 'someInput') - ], - returnType: const TypeDeclaration(baseName: 'int', isNullable: false), - ) - ]) - ], classes: [], enums: []); + final Root root = Root( + apis: [ + AstHostApi(name: 'Api', methods: [ + Method( + name: 'doSomething', + location: ApiLocation.host, + parameters: [ + Parameter( + type: const TypeDeclaration( + baseName: 'int', + isNullable: false, + ), + name: 'someInput') + ], + returnType: + const TypeDeclaration(baseName: 'int', isNullable: false), + ) + ]) + ], + classes: [], + enums: [], + containsHostApi: true, + ); { final StringBuffer sink = StringBuffer(); const CppGenerator generator = CppGenerator(); @@ -2070,6 +2076,7 @@ void main() { ], classes: [], enums: [], + containsFlutterApi: true, ); final StringBuffer sink = StringBuffer(); const CppGenerator generator = CppGenerator(); diff --git a/packages/pigeon/test/dart_generator_test.dart b/packages/pigeon/test/dart_generator_test.dart index 4dca4b416844..c53a0686170c 100644 --- a/packages/pigeon/test/dart_generator_test.dart +++ b/packages/pigeon/test/dart_generator_test.dart @@ -1711,17 +1711,22 @@ name: foobar }); test('connection error contains channel name', () { - final Root root = Root(apis: [ - AstHostApi(name: 'Api', methods: [ - Method( - name: 'method', - location: ApiLocation.host, - parameters: [], - returnType: - const TypeDeclaration(baseName: 'Output', isNullable: false), - ) - ]) - ], classes: [], enums: []); + final Root root = Root( + apis: [ + AstHostApi(name: 'Api', methods: [ + Method( + name: 'method', + location: ApiLocation.host, + parameters: [], + returnType: + const TypeDeclaration(baseName: 'Output', isNullable: false), + ) + ]) + ], + classes: [], + enums: [], + containsHostApi: true, + ); final StringBuffer sink = StringBuffer(); const DartGenerator generator = DartGenerator(); generator.generate( diff --git a/packages/pigeon/test/java_generator_test.dart b/packages/pigeon/test/java_generator_test.dart index 63a6e8330eab..49358cf7ddaa 100644 --- a/packages/pigeon/test/java_generator_test.dart +++ b/packages/pigeon/test/java_generator_test.dart @@ -121,45 +121,50 @@ void main() { }); test('gen one host api', () { - final Root root = Root(apis: [ - AstHostApi(name: 'Api', methods: [ - Method( - name: 'doSomething', - location: ApiLocation.host, - parameters: [ - Parameter( - type: TypeDeclaration( - baseName: 'Input', - associatedClass: emptyClass, - isNullable: false, - ), - name: '') - ], - returnType: TypeDeclaration( - baseName: 'Output', - associatedClass: emptyClass, - isNullable: false, - ), - ) - ]) - ], classes: [ - Class(name: 'Input', fields: [ - NamedType( - type: const TypeDeclaration( - baseName: 'String', - isNullable: true, - ), - name: 'input') - ]), - Class(name: 'Output', fields: [ - NamedType( - type: const TypeDeclaration( - baseName: 'String', - isNullable: true, + final Root root = Root( + apis: [ + AstHostApi(name: 'Api', methods: [ + Method( + name: 'doSomething', + location: ApiLocation.host, + parameters: [ + Parameter( + type: TypeDeclaration( + baseName: 'Input', + associatedClass: emptyClass, + isNullable: false, + ), + name: '') + ], + returnType: TypeDeclaration( + baseName: 'Output', + associatedClass: emptyClass, + isNullable: false, ), - name: 'output') - ]) - ], enums: []); + ) + ]) + ], + classes: [ + Class(name: 'Input', fields: [ + NamedType( + type: const TypeDeclaration( + baseName: 'String', + isNullable: true, + ), + name: 'input') + ]), + Class(name: 'Output', fields: [ + NamedType( + type: const TypeDeclaration( + baseName: 'String', + isNullable: true, + ), + name: 'output') + ]) + ], + enums: [], + containsHostApi: true, + ); final StringBuffer sink = StringBuffer(); const JavaOptions javaOptions = JavaOptions(className: 'Messages'); const JavaGenerator generator = JavaGenerator(); @@ -1565,6 +1570,7 @@ void main() { apis: [api], classes: [], enums: [], + containsHostApi: true, ); final StringBuffer sink = StringBuffer(); const JavaOptions javaOptions = JavaOptions(className: 'Messages'); @@ -1604,6 +1610,7 @@ void main() { ], classes: [], enums: [], + containsFlutterApi: true, ); final StringBuffer sink = StringBuffer(); const JavaGenerator generator = JavaGenerator(); diff --git a/packages/pigeon/test/kotlin_generator_test.dart b/packages/pigeon/test/kotlin_generator_test.dart index 549601b8851d..6043229fd729 100644 --- a/packages/pigeon/test/kotlin_generator_test.dart +++ b/packages/pigeon/test/kotlin_generator_test.dart @@ -1533,6 +1533,7 @@ void main() { apis: [api], classes: [], enums: [], + containsHostApi: true, ); final StringBuffer sink = StringBuffer(); const KotlinOptions kotlinOptions = @@ -1577,6 +1578,7 @@ void main() { ], classes: [], enums: [], + containsFlutterApi: true, ); final StringBuffer sink = StringBuffer(); const KotlinOptions kotlinOptions = KotlinOptions(); diff --git a/packages/pigeon/test/objc_generator_test.dart b/packages/pigeon/test/objc_generator_test.dart index 734145e2c24b..728bb50ac9c7 100644 --- a/packages/pigeon/test/objc_generator_test.dart +++ b/packages/pigeon/test/objc_generator_test.dart @@ -2861,6 +2861,7 @@ void main() { ], classes: [], enums: [], + containsFlutterApi: true, ); final StringBuffer sink = StringBuffer(); const ObjcGenerator generator = ObjcGenerator(); diff --git a/packages/pigeon/test/pigeon_lib_test.dart b/packages/pigeon/test/pigeon_lib_test.dart index 9e3fa213fc78..faf4a1be7699 100644 --- a/packages/pigeon/test/pigeon_lib_test.dart +++ b/packages/pigeon/test/pigeon_lib_test.dart @@ -232,6 +232,39 @@ abstract class Api { expect(results.errors[0].message, contains('dynamic')); }); + test('Only allow one api annotation', () { + const String source = ''' +@HostApi() +@FlutterApi() +abstract class Api { + int foo(); +} +'''; + final ParseResults results = parseSource(source); + expect(results.errors.length, 1); + expect( + results.errors[0].message, + contains( + 'API "Api" can only have one API annotation but contains: [@HostApi(), @FlutterApi()]')); + }); + + test('Only allow one api annotation plus @ConfigurePigeon', () { + const String source = ''' +@ConfigurePigeon(PigeonOptions( + dartOut: 'stdout', + javaOut: 'stdout', + dartOptions: DartOptions(), +)) +@HostApi() +abstract class Api { + void ping(); +} + +'''; + final ParseResults results = parseSource(source); + expect(results.errors.length, 0); + }); + test('enum in classes', () { const String code = ''' enum Enum1 { @@ -1567,4 +1600,69 @@ abstract class MyClass { ); }); }); + + group('Event Channel validation', () { + test('methods cannot contain parameters', () { + const String code = ''' +@EventChannelApi() +abstract class EventChannelApi { + int streamInts(int event); +} +'''; + final ParseResults parseResult = parseSource(code); + expect(parseResult.errors.length, equals(1)); + expect( + parseResult.errors.single.message, + contains( + 'Event Channel methods must not be contain parameters, in method "streamInts" in API: "EventChannelApi"'), + ); + }); + }); + + group('sealed inheritance validation', () { + test('super class must be sealed', () { + const String code = ''' +class DataClass {} +class ChildClass extends DataClass { + ChildClass(this.input); + int input; +} + +@EventChannelApi() +abstract class events { + void aMethod(ChildClass param); +} +'''; + final ParseResults parseResult = parseSource(code); + expect(parseResult.errors, isNotEmpty); + expect( + parseResult.errors[0].message, + contains('Child class: "ChildClass" must extend a sealed class.'), + ); + }); + + test('super class must be sealed', () { + const String code = ''' +sealed class DataClass { + DataClass(this.input); + int input; +} +class ChildClass extends DataClass { + ChildClass(this.input); + int input; +} + +@EventChannelApi() +abstract class events { + void aMethod(ChildClass param); +} +'''; + final ParseResults parseResult = parseSource(code); + expect(parseResult.errors, isNotEmpty); + expect( + parseResult.errors[0].message, + contains('Sealed class: "DataClass" must not contain fields.'), + ); + }); + }); } diff --git a/packages/pigeon/test/swift_generator_test.dart b/packages/pigeon/test/swift_generator_test.dart index b9f7de5d356a..6ad36f934ecc 100644 --- a/packages/pigeon/test/swift_generator_test.dart +++ b/packages/pigeon/test/swift_generator_test.dart @@ -1508,6 +1508,7 @@ void main() { ], classes: [], enums: [], + containsFlutterApi: true, ); final StringBuffer sink = StringBuffer(); const SwiftOptions kotlinOptions = SwiftOptions(); diff --git a/packages/pigeon/tool/shared/generation.dart b/packages/pigeon/tool/shared/generation.dart index 4cee0d4de568..6e16b6aaa7af 100644 --- a/packages/pigeon/tool/shared/generation.dart +++ b/packages/pigeon/tool/shared/generation.dart @@ -48,30 +48,47 @@ String _javaFilenameForName(String inputName) { } Future generateExamplePigeons() async { - return runPigeon( + int success = 0; + success = await runPigeon( input: './example/app/pigeons/messages.dart', basePath: './example/app', suppressVersion: true, ); + success = await runPigeon( + input: './example/app/pigeons/event_channel_messages.dart', + basePath: './example/app', + suppressVersion: true, + ); + return success; } Future generateTestPigeons( {required String baseDir, bool includeOverflow = false}) async { // TODO(stuartmorgan): Make this dynamic rather than hard-coded. Or eliminate // it entirely; see https://github.com/flutter/flutter/issues/115169. - const List inputs = [ - 'background_platform_channels', - 'core_tests', - 'enum', - 'flutter_unittests', // Only for Dart unit tests in shared_test_plugin_code - 'message', - 'multiple_arity', - 'non_null_fields', - 'null_fields', - 'nullable_returns', - 'primitive', - 'proxy_api_tests', - ]; + + /// A list of all pigeons to be generated along with a list of skipped languages. + const Map> inputs = + >{ + 'background_platform_channels': {}, + 'core_tests': {}, + 'enum': {}, + 'event_channel_tests': { + GeneratorLanguage.cpp, + GeneratorLanguage.gobject, + GeneratorLanguage.java, + GeneratorLanguage.objc, + }, + 'flutter_unittests': + {}, // Only for Dart unit tests in shared_test_plugin_code + 'message': {}, + 'multiple_arity': {}, + 'non_null_fields': {}, + 'null_fields': {}, + 'nullable_returns': {}, + 'primitive': {}, + 'proxy_api_tests': {}, + }; final String outputBase = p.join(baseDir, 'platform_tests', 'test_plugin'); final String alternateOutputBase = @@ -79,29 +96,31 @@ Future generateTestPigeons( final String sharedDartOutputBase = p.join(baseDir, 'platform_tests', 'shared_test_plugin_code'); - for (final String input in inputs) { - final String pascalCaseName = _snakeToPascalCase(input); + for (final MapEntry> input in inputs.entries) { + final String pascalCaseName = _snakeToPascalCase(input.key); final Set skipLanguages = - _unsupportedFiles[input] ?? {}; + _unsupportedFiles[input.key] ?? {}; + skipLanguages.addAll(input.value); final bool kotlinErrorClassGenerationTestFiles = - input == 'core_tests' || input == 'background_platform_channels'; + input.key == 'core_tests' || + input.key == 'background_platform_channels'; final String kotlinErrorName = kotlinErrorClassGenerationTestFiles ? 'FlutterError' : '${pascalCaseName}Error'; - final bool swiftErrorUseDefaultErrorName = - input == 'core_tests' || input == 'background_platform_channels'; + final bool swiftErrorUseDefaultErrorName = input.key == 'core_tests' || + input.key == 'background_platform_channels'; final String? swiftErrorClassName = swiftErrorUseDefaultErrorName ? null : '${pascalCaseName}Error'; // Generate the default language test plugin output. int generateCode = await runPigeon( - input: './pigeons/$input.dart', - dartOut: '$sharedDartOutputBase/lib/src/generated/$input.gen.dart', - dartTestOut: input == 'message' + input: './pigeons/${input.key}.dart', + dartOut: '$sharedDartOutputBase/lib/src/generated/${input.key}.gen.dart', + dartTestOut: input.key == 'message' ? '$sharedDartOutputBase/test/test_message.gen.dart' : null, dartPackageName: 'pigeon_integration_tests', @@ -112,7 +131,7 @@ Future generateTestPigeons( : '$outputBase/android/src/main/kotlin/com/example/test_plugin/$pascalCaseName.gen.kt', kotlinPackage: 'com.example.test_plugin', kotlinErrorClassName: kotlinErrorName, - kotlinIncludeErrorClass: input != 'core_tests', + kotlinIncludeErrorClass: input.key != 'core_tests', // iOS swiftOut: skipLanguages.contains(GeneratorLanguage.swift) ? null @@ -122,20 +141,20 @@ Future generateTestPigeons( // Linux gobjectHeaderOut: skipLanguages.contains(GeneratorLanguage.gobject) ? null - : '$outputBase/linux/pigeon/$input.gen.h', + : '$outputBase/linux/pigeon/${input.key}.gen.h', gobjectSourceOut: skipLanguages.contains(GeneratorLanguage.gobject) ? null - : '$outputBase/linux/pigeon/$input.gen.cc', + : '$outputBase/linux/pigeon/${input.key}.gen.cc', gobjectModule: '${pascalCaseName}PigeonTest', // Windows cppHeaderOut: skipLanguages.contains(GeneratorLanguage.cpp) ? null - : '$outputBase/windows/pigeon/$input.gen.h', + : '$outputBase/windows/pigeon/${input.key}.gen.h', cppSourceOut: skipLanguages.contains(GeneratorLanguage.cpp) ? null - : '$outputBase/windows/pigeon/$input.gen.cpp', - cppNamespace: '${input}_pigeontest', - injectOverflowTypes: includeOverflow && input == 'core_tests', + : '$outputBase/windows/pigeon/${input.key}.gen.cpp', + cppNamespace: '${input.key}_pigeontest', + injectOverflowTypes: includeOverflow && input.key == 'core_tests', ); if (generateCode != 0) { return generateCode; @@ -145,7 +164,7 @@ Future generateTestPigeons( // doesn't have a way to output separate macOS and iOS Swift output in a // single invocation. generateCode = await runPigeon( - input: './pigeons/$input.dart', + input: './pigeons/${input.key}.dart', swiftOut: skipLanguages.contains(GeneratorLanguage.swift) ? null : '$outputBase/macos/Classes/$pascalCaseName.gen.swift', @@ -153,7 +172,7 @@ Future generateTestPigeons( swiftIncludeErrorClass: input != 'core_tests', suppressVersion: true, dartPackageName: 'pigeon_integration_tests', - injectOverflowTypes: includeOverflow && input == 'core_tests', + injectOverflowTypes: includeOverflow && input.key == 'core_tests', ); if (generateCode != 0) { return generateCode; @@ -161,14 +180,14 @@ Future generateTestPigeons( // Generate the alternate language test plugin output. generateCode = await runPigeon( - input: './pigeons/$input.dart', + input: './pigeons/${input.key}.dart', // Android // This doesn't use the '.gen' suffix since Java has strict file naming // rules. javaOut: skipLanguages.contains(GeneratorLanguage.java) ? null : '$alternateOutputBase/android/src/main/java/com/example/' - 'alternate_language_test_plugin/${_javaFilenameForName(input)}.java', + 'alternate_language_test_plugin/${_javaFilenameForName(input.key)}.java', javaPackage: 'com.example.alternate_language_test_plugin', // iOS objcHeaderOut: skipLanguages.contains(GeneratorLanguage.objc) @@ -177,10 +196,10 @@ Future generateTestPigeons( objcSourceOut: skipLanguages.contains(GeneratorLanguage.objc) ? null : '$alternateOutputBase/ios/Classes/$pascalCaseName.gen.m', - objcPrefix: input == 'core_tests' ? 'FLT' : '', + objcPrefix: input.key == 'core_tests' ? 'FLT' : '', suppressVersion: true, dartPackageName: 'pigeon_integration_tests', - injectOverflowTypes: includeOverflow && input == 'core_tests', + injectOverflowTypes: includeOverflow && input.key == 'core_tests', ); if (generateCode != 0) { return generateCode; @@ -190,17 +209,17 @@ Future generateTestPigeons( // doesn't have a way to output separate macOS and iOS Swift output in a // single invocation. generateCode = await runPigeon( - input: './pigeons/$input.dart', + input: './pigeons/${input.key}.dart', objcHeaderOut: skipLanguages.contains(GeneratorLanguage.objc) ? null : '$alternateOutputBase/macos/Classes/$pascalCaseName.gen.h', objcSourceOut: skipLanguages.contains(GeneratorLanguage.objc) ? null : '$alternateOutputBase/macos/Classes/$pascalCaseName.gen.m', - objcPrefix: input == 'core_tests' ? 'FLT' : '', + objcPrefix: input.key == 'core_tests' ? 'FLT' : '', suppressVersion: true, dartPackageName: 'pigeon_integration_tests', - injectOverflowTypes: includeOverflow && input == 'core_tests', + injectOverflowTypes: includeOverflow && input.key == 'core_tests', ); if (generateCode != 0) { return generateCode; From 36ccab411241455a238aad2a840a59456bdb6c49 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Mon, 28 Oct 2024 14:54:35 -0700 Subject: [PATCH 02/20] tools --- packages/pigeon/example/README.md | 16 ++++++++-------- packages/pigeon/example/app/lib/main.dart | 4 ++-- .../ios/Classes/CoreTests.gen.swift | 18 ------------------ .../macos/Classes/CoreTests.gen.swift | 18 ------------------ packages/pigeon/tool/shared/generation.dart | 4 ++-- 5 files changed, 12 insertions(+), 48 deletions(-) diff --git a/packages/pigeon/example/README.md b/packages/pigeon/example/README.md index 76f1d5f82940..25ee15fe0191 100644 --- a/packages/pigeon/example/README.md +++ b/packages/pigeon/example/README.md @@ -281,10 +281,10 @@ private class PigeonFlutterApi { } func callFlutterMethod( - aString aStringArg: String?, completion: @escaping (Result) -> Void + aString aStringArg: String?, completion: @escaping (Result) -> Void ) { flutterAPI.flutterMethod(aString: aStringArg) { - completion(.success($0)) + completion($0) } } } @@ -294,16 +294,16 @@ private class PigeonFlutterApi { ```kotlin -private class PigeonFlutterApi { +private class PigeonFlutterApi(binding: FlutterPlugin.FlutterPluginBinding) { var flutterApi: MessageFlutterApi? = null - constructor(binding: FlutterPlugin.FlutterPluginBinding) { - flutterApi = MessageFlutterApi(binding.getBinaryMessenger()) + init { + flutterApi = MessageFlutterApi(binding.binaryMessenger) } fun callFlutterMethod(aString: String, callback: (Result) -> Unit) { - flutterApi!!.flutterMethod(aString) { echo -> callback(Result.success(echo)) } + flutterApi!!.flutterMethod(aString) { echo -> callback(echo) } } } ``` @@ -357,7 +357,7 @@ This example gives a basic overview of how to use Pigeon to set up an Event Chan ### Dart input - + ```dart @EventChannelApi() abstract class EventApi { @@ -372,7 +372,7 @@ This `Stream` can then be used as any other `Stream` would be in dart. ```dart - final Stream events = streamEvents(); +final Stream events = streamEvents(); ``` ### Swift diff --git a/packages/pigeon/example/app/lib/main.dart b/packages/pigeon/example/app/lib/main.dart index 57b415377972..f2b378e54a5e 100644 --- a/packages/pigeon/example/app/lib/main.dart +++ b/packages/pigeon/example/app/lib/main.dart @@ -87,9 +87,9 @@ class _MyHomePageState extends State { // #enddocregion main-dart Stream getEventStream() { - // #docregion main-dart_event + // #docregion main-dart-event final Stream events = streamEvents(); - // #enddocregion main-dart_event + // #enddocregion main-dart-event return events; } diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift index 2b2001d6aa44..a90e8fa07a5d 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift @@ -15,24 +15,6 @@ import Foundation #error("Unsupported platform.") #endif -/// Error class for passing custom error details to Dart side. -final class PigeonError: Error { - let code: String - let message: String? - let details: Any? - - init(code: String, message: String?, details: Any?) { - self.code = code - self.message = message - self.details = details - } - - var localizedDescription: String { - return - "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" - } -} - private func wrapResult(_ result: Any?) -> [Any?] { return [result] } diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/CoreTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/CoreTests.gen.swift index 2b2001d6aa44..a90e8fa07a5d 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/CoreTests.gen.swift +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/CoreTests.gen.swift @@ -15,24 +15,6 @@ import Foundation #error("Unsupported platform.") #endif -/// Error class for passing custom error details to Dart side. -final class PigeonError: Error { - let code: String - let message: String? - let details: Any? - - init(code: String, message: String?, details: Any?) { - self.code = code - self.message = message - self.details = details - } - - var localizedDescription: String { - return - "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" - } -} - private func wrapResult(_ result: Any?) -> [Any?] { return [result] } diff --git a/packages/pigeon/tool/shared/generation.dart b/packages/pigeon/tool/shared/generation.dart index 6e16b6aaa7af..c7414ae0f887 100644 --- a/packages/pigeon/tool/shared/generation.dart +++ b/packages/pigeon/tool/shared/generation.dart @@ -137,7 +137,7 @@ Future generateTestPigeons( ? null : '$outputBase/ios/Classes/$pascalCaseName.gen.swift', swiftErrorClassName: swiftErrorClassName, - swiftIncludeErrorClass: input != 'core_tests', + swiftIncludeErrorClass: input.key != 'core_tests', // Linux gobjectHeaderOut: skipLanguages.contains(GeneratorLanguage.gobject) ? null @@ -169,7 +169,7 @@ Future generateTestPigeons( ? null : '$outputBase/macos/Classes/$pascalCaseName.gen.swift', swiftErrorClassName: swiftErrorClassName, - swiftIncludeErrorClass: input != 'core_tests', + swiftIncludeErrorClass: input.key != 'core_tests', suppressVersion: true, dartPackageName: 'pigeon_integration_tests', injectOverflowTypes: includeOverflow && input.key == 'core_tests', From b630ae2dfc67baada2d63b13691c3e790f44293d Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Mon, 28 Oct 2024 15:42:38 -0700 Subject: [PATCH 03/20] missed file --- packages/pigeon/example/app/ios/Runner/AppDelegate.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift index a6cab8a449d7..e83e55b34376 100644 --- a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift +++ b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift @@ -77,7 +77,7 @@ class SendEvents: StreamEventsStreamHandler { _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { - GeneratedPluginRegistrant.register(with: self) + GeneratedPluginRegistrant.register(with: self) let controller = window?.rootViewController as! FlutterViewController let api = PigeonApiImplementation() From 94d39cf506cf5bc5965233f86af9ba62495b625f Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Mon, 28 Oct 2024 16:04:14 -0700 Subject: [PATCH 04/20] more ios files --- packages/pigeon/example/app/.gitignore | 2 + .../app/ios/Flutter/AppFrameworkInfo.plist | 2 +- packages/pigeon/example/app/ios/Podfile | 2 +- .../app/ios/Runner.xcodeproj/project.pbxproj | 76 ++++++++++++++++++- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- .../contents.xcworkspacedata | 3 + 6 files changed, 80 insertions(+), 7 deletions(-) diff --git a/packages/pigeon/example/app/.gitignore b/packages/pigeon/example/app/.gitignore index 24476c5d1eb5..6c319542b342 100644 --- a/packages/pigeon/example/app/.gitignore +++ b/packages/pigeon/example/app/.gitignore @@ -5,9 +5,11 @@ *.swp .DS_Store .atom/ +.build/ .buildlog/ .history .svn/ +.swiftpm/ migrate_working_dir/ # IntelliJ related diff --git a/packages/pigeon/example/app/ios/Flutter/AppFrameworkInfo.plist b/packages/pigeon/example/app/ios/Flutter/AppFrameworkInfo.plist index 9625e105df39..7c5696400627 100644 --- a/packages/pigeon/example/app/ios/Flutter/AppFrameworkInfo.plist +++ b/packages/pigeon/example/app/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/packages/pigeon/example/app/ios/Podfile b/packages/pigeon/example/app/ios/Podfile index 88359b225fa1..279576f3884f 100644 --- a/packages/pigeon/example/app/ios/Podfile +++ b/packages/pigeon/example/app/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '11.0' +# platform :ios, '12.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/packages/pigeon/example/app/ios/Runner.xcodeproj/project.pbxproj b/packages/pigeon/example/app/ios/Runner.xcodeproj/project.pbxproj index e934bc3d3877..f3a76aee17e9 100644 --- a/packages/pigeon/example/app/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/pigeon/example/app/ios/Runner.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 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 */; }; + F7E907CAAC4B02307953628E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EABF7C39D02C68F9E6B4AA70 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -33,6 +34,7 @@ /* Begin PBXFileReference section */ 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 = ""; }; + 2F6990FD9E99EB154922F8AD /* 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 = ""; }; 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 = ""; }; 477F5F832CCC1D8D006725C4 /* EventChannelMessages.g.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventChannelMessages.g.swift; sourceTree = ""; }; @@ -46,6 +48,9 @@ 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 = ""; }; + AB1EB56E8909F53184417179 /* 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 = ""; }; + C25B8BD91AF396D9449B25F5 /* 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 = ""; }; + EABF7C39D02C68F9E6B4AA70 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -53,12 +58,32 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + F7E907CAAC4B02307953628E /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 4F8299774F028C407687C96B /* Frameworks */ = { + isa = PBXGroup; + children = ( + EABF7C39D02C68F9E6B4AA70 /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5170560B875A27998DBDBE7B /* Pods */ = { + isa = PBXGroup; + children = ( + 2F6990FD9E99EB154922F8AD /* Pods-Runner.debug.xcconfig */, + AB1EB56E8909F53184417179 /* Pods-Runner.release.xcconfig */, + C25B8BD91AF396D9449B25F5 /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -76,6 +101,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, + 5170560B875A27998DBDBE7B /* Pods */, + 4F8299774F028C407687C96B /* Frameworks */, ); sourceTree = ""; }; @@ -111,12 +138,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 93861A2E607F9CF8AD253B1D /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 8678AE2DB006310D29292EB1 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -133,7 +162,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -191,6 +220,45 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; + 8678AE2DB006310D29292EB1 /* [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; + }; + 93861A2E607F9CF8AD253B1D /* [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; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -283,7 +351,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; @@ -360,7 +428,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; @@ -409,7 +477,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.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/pigeon/example/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index e42adcb34c2d..8e3ca5dfe193 100644 --- a/packages/pigeon/example/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/pigeon/example/app/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ + + From 972f5223dd4223322da3034813643542a1bf02d1 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Tue, 29 Oct 2024 20:09:22 -0700 Subject: [PATCH 05/20] test fix? --- script/tool/lib/src/podspec_check_command.dart | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/script/tool/lib/src/podspec_check_command.dart b/script/tool/lib/src/podspec_check_command.dart index b8aa218ffbc3..5aabb407e469 100644 --- a/script/tool/lib/src/podspec_check_command.dart +++ b/script/tool/lib/src/podspec_check_command.dart @@ -85,7 +85,7 @@ class PodspecCheckCommand extends PackageLoopingCommand { '''; final String path = getRelativePosixPath(podspec, from: package.directory); - printError('$path is missing seach path configuration. Any iOS ' + printError('$path is missing search path configuration. Any iOS ' 'plugin implementation that contains Swift implementation code ' 'needs to contain the following:\n\n' '$workaroundBlock\n' @@ -110,6 +110,11 @@ class PodspecCheckCommand extends PackageLoopingCommand { } Future> _podspecsToLint(RepositoryPackage package) async { + // Since the pigeon podspecs require generated files that are not included in git, + // the podspec lint fails. + if (package.displayName == 'pigeon') { + return []; + } final List podspecs = await getFilesForPackage(package).where((File entity) { final String filename = entity.basename; @@ -162,7 +167,7 @@ class PodspecCheckCommand extends PackageLoopingCommand { } /// Returns true if there is any iOS plugin implementation code written in - /// Swift. Skips files named "Package.swift", which is a Swift Pacakge Manager + /// Swift. Skips files named "Package.swift", which is a Swift Package Manager /// manifest file and does not mean the plugin is written in Swift. Future _hasIOSSwiftCode(RepositoryPackage package) async { final String iosSwiftPackageManifestPath = package From 741f59d35268414efd4b7021219421be930add2f Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Tue, 29 Oct 2024 20:26:17 -0700 Subject: [PATCH 06/20] maybe this? --- script/tool/lib/src/podspec_check_command.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/tool/lib/src/podspec_check_command.dart b/script/tool/lib/src/podspec_check_command.dart index 5aabb407e469..5e3460cdc029 100644 --- a/script/tool/lib/src/podspec_check_command.dart +++ b/script/tool/lib/src/podspec_check_command.dart @@ -112,7 +112,7 @@ class PodspecCheckCommand extends PackageLoopingCommand { Future> _podspecsToLint(RepositoryPackage package) async { // Since the pigeon podspecs require generated files that are not included in git, // the podspec lint fails. - if (package.displayName == 'pigeon') { + if (package.displayName == 'test_plugin') { return []; } final List podspecs = From a1f2b6a750d4fbdbc2fb8a80969c06a2ad55cc28 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Wed, 30 Oct 2024 14:28:50 -0700 Subject: [PATCH 07/20] adding prints to diagnose --- script/tool/lib/src/podspec_check_command.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/script/tool/lib/src/podspec_check_command.dart b/script/tool/lib/src/podspec_check_command.dart index 5e3460cdc029..cb02307ca44e 100644 --- a/script/tool/lib/src/podspec_check_command.dart +++ b/script/tool/lib/src/podspec_check_command.dart @@ -112,6 +112,8 @@ class PodspecCheckCommand extends PackageLoopingCommand { Future> _podspecsToLint(RepositoryPackage package) async { // Since the pigeon podspecs require generated files that are not included in git, // the podspec lint fails. + print(package); + print(package.displayName); if (package.displayName == 'test_plugin') { return []; } From 1aeb65a7d89d0e0929c242481f46053eb7811aa1 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Wed, 30 Oct 2024 21:41:16 -0700 Subject: [PATCH 08/20] I'm sure this one will work --- script/tool/lib/src/podspec_check_command.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/script/tool/lib/src/podspec_check_command.dart b/script/tool/lib/src/podspec_check_command.dart index cb02307ca44e..6b871a7b52e6 100644 --- a/script/tool/lib/src/podspec_check_command.dart +++ b/script/tool/lib/src/podspec_check_command.dart @@ -112,9 +112,7 @@ class PodspecCheckCommand extends PackageLoopingCommand { Future> _podspecsToLint(RepositoryPackage package) async { // Since the pigeon podspecs require generated files that are not included in git, // the podspec lint fails. - print(package); - print(package.displayName); - if (package.displayName == 'test_plugin') { + if (package.displayName == 'packages/pigeon') { return []; } final List podspecs = From b0262a1521bb9341ffc599a730a9707925d26c3a Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Mon, 11 Nov 2024 15:06:05 -0800 Subject: [PATCH 09/20] add bad formatting to check for failure --- .../src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt index c5ba2d8b0ca0..0f2e263e2499 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt @@ -17,7 +17,7 @@ import java.io.ByteArrayOutputStream import java.nio.ByteBuffer private fun wrapResult(result: Any?): List { - return listOf(result) + return listOf(result) } private fun wrapError(exception: Throwable): List { From e306b468e05298695321eeb12444bd10242c572a Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Mon, 11 Nov 2024 15:34:09 -0800 Subject: [PATCH 10/20] reorder file format --- script/tool/lib/src/format_command.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/script/tool/lib/src/format_command.dart b/script/tool/lib/src/format_command.dart index b5e6086b5b30..71a782745edf 100644 --- a/script/tool/lib/src/format_command.dart +++ b/script/tool/lib/src/format_command.dart @@ -94,6 +94,9 @@ class FormatCommand extends PackageCommand { // due to the startup overhead of the formatters. final Iterable files = await _getFilteredFilePaths(getFiles(), relativeTo: packagesDir); + if (getBoolArg(_swiftArg)) { + await _formatAndLintSwift(files); + } if (getBoolArg(_dartArg)) { await _formatDart(files); } @@ -106,9 +109,6 @@ class FormatCommand extends PackageCommand { if (getBoolArg(_clangFormatArg)) { await _formatCppAndObjectiveC(files); } - if (getBoolArg(_swiftArg)) { - await _formatAndLintSwift(files); - } if (getBoolArg(_failonChangeArg)) { final bool modified = await _didModifyAnything(); From 2cb69738d54f38e8f9359aaa8cc240f9f5e1d382 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Mon, 11 Nov 2024 16:00:25 -0800 Subject: [PATCH 11/20] revert change generators to test --- packages/pigeon/lib/kotlin_generator.dart | 2 +- packages/pigeon/lib/swift_generator.dart | 2 +- .../main/kotlin/com/example/test_plugin/CoreTests.gen.kt | 2 +- script/tool/lib/src/format_command.dart | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/pigeon/lib/kotlin_generator.dart b/packages/pigeon/lib/kotlin_generator.dart index 7d47df9f89b5..0521d60ac977 100644 --- a/packages/pigeon/lib/kotlin_generator.dart +++ b/packages/pigeon/lib/kotlin_generator.dart @@ -486,7 +486,7 @@ class KotlinGenerator extends StructuredGenerator { ); indent.format(''' -companion object { +companion object { fun fromList(${varNamePrefix}list: List): Any? { val wrapper = ${generatorOptions.fileSpecificClassNameComponent}$_overflowClassName( type = ${varNamePrefix}list[0] as Long, diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index 0d0d17587d42..c73ec36dd043 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -553,7 +553,7 @@ if (wrapped == nil) { final String className = classDefinition.name; indent.writeln('// swift-format-ignore: AlwaysUseLowerCamelCase'); indent.write( - 'static func fromList(_ ${varNamePrefix}list: [Any?]) -> $className? '); + 'static func fromList(_ ${varNamePrefix}list: [Any?]) -> $className? '); indent.addScoped('{', '}', () { enumerate(getFieldsInSerializationOrder(classDefinition), diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt index 0f2e263e2499..c5ba2d8b0ca0 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/CoreTests.gen.kt @@ -17,7 +17,7 @@ import java.io.ByteArrayOutputStream import java.nio.ByteBuffer private fun wrapResult(result: Any?): List { - return listOf(result) + return listOf(result) } private fun wrapError(exception: Throwable): List { diff --git a/script/tool/lib/src/format_command.dart b/script/tool/lib/src/format_command.dart index 71a782745edf..b5e6086b5b30 100644 --- a/script/tool/lib/src/format_command.dart +++ b/script/tool/lib/src/format_command.dart @@ -94,9 +94,6 @@ class FormatCommand extends PackageCommand { // due to the startup overhead of the formatters. final Iterable files = await _getFilteredFilePaths(getFiles(), relativeTo: packagesDir); - if (getBoolArg(_swiftArg)) { - await _formatAndLintSwift(files); - } if (getBoolArg(_dartArg)) { await _formatDart(files); } @@ -109,6 +106,9 @@ class FormatCommand extends PackageCommand { if (getBoolArg(_clangFormatArg)) { await _formatCppAndObjectiveC(files); } + if (getBoolArg(_swiftArg)) { + await _formatAndLintSwift(files); + } if (getBoolArg(_failonChangeArg)) { final bool modified = await _didModifyAnything(); From a0f37e3e45718d9a3964e3a62d60139e404ca181 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Tue, 12 Nov 2024 14:53:06 -0800 Subject: [PATCH 12/20] requested changes --- packages/pigeon/CHANGELOG.md | 6 +- packages/pigeon/README.md | 10 +- packages/pigeon/example/README.md | 8 +- .../EventChannelMessages.g.kt | 3 +- packages/pigeon/example/app/lib/main.dart | 15 ++- .../app/lib/src/event_channel_messages.g.dart | 2 +- .../app/pigeons/event_channel_messages.dart | 2 +- packages/pigeon/lib/ast.dart | 14 +-- packages/pigeon/lib/dart_generator.dart | 2 +- packages/pigeon/lib/generator.dart | 2 +- packages/pigeon/lib/generator_tools.dart | 6 +- packages/pigeon/lib/kotlin_generator.dart | 2 +- packages/pigeon/lib/pigeon_lib.dart | 22 ++-- packages/pigeon/lib/swift_generator.dart | 2 +- .../lib/example_app.dart | 33 +++--- .../lib/integration_tests.dart | 7 +- .../kotlin/com/example/test_plugin/.gitignore | 3 +- .../com/example/test_plugin/TestPlugin.kt | 15 +-- .../example/macos/Runner/AppDelegate.swift | 6 +- .../test_plugin/ios/Classes/.gitignore | 3 +- .../test_plugin/ios/Classes/TestPlugin.swift | 30 +++-- .../macos/Classes/TestPlugin.swift | 31 ++++-- packages/pigeon/test/dart_generator_test.dart | 2 +- packages/pigeon/test/pigeon_lib_test.dart | 4 +- packages/pigeon/tool/shared/generation.dart | 103 +++++++++--------- packages/pigeon/tool/shared/test_suites.dart | 2 +- packages/pigeon/tool/test.dart | 4 +- 27 files changed, 183 insertions(+), 156 deletions(-) diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index ddf6271e8a3c..91f36535861e 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,9 +1,7 @@ ## 22.7.0 -* [swift] Adds Event Channel support. -* [swift] Adds `sealed` class inheritance support. -* [kotlin] Adds Event Channel support. -* [kotlin] Adds `sealed` class inheritance support. +* [swift, kotlin] Adds event channel support. +* [swift, kotlin] Adds `sealed` class inheritance support. ## 22.6.0 diff --git a/packages/pigeon/README.md b/packages/pigeon/README.md index 0ec4c1948f39..39df85d65f41 100644 --- a/packages/pigeon/README.md +++ b/packages/pigeon/README.md @@ -26,7 +26,7 @@ Pigeon uses the `StandardMessageCodec` so it supports Custom classes, nested datatypes, and enums are also supported. -Basic inheritance with empty `sealed` parent classes is allowed only on swift and kotlin. +Basic inheritance with empty `sealed` parent classes is allowed only in the Swift, Kotlin, and Dart generators. Nullable enums in Objective-C generated code will be wrapped in a class to allow for nullability. @@ -106,10 +106,10 @@ to the api to allow for multiple instances to be created and operate in parallel 1) Method declarations on the API classes should have arguments and a return value whose types are defined in the file, are supported datatypes, or are `void`. -1) Event Channels are supported only on swift and kotlin generators. -1) Event Channel methods should be wrapped in an `abstract class` with the metadata `@EventChannelApi`. -1) Event Channel definitions should not include the `Stream` return type, just the type that is being streamed. -1) Objc and Swift have special naming conventions that can be utilized with the +1) Event channels are supported only on the Swift, Kotlin, and Dart generators. +1) Event channel methods should be wrapped in an `abstract class` with the metadata `@EventChannelApi`. +1) Event channel definitions should not include the `Stream` return type, just the type that is being streamed. +1) Objective-C and Swift have special naming conventions that can be utilized with the `@ObjCSelector` and `@SwiftFunction` respectively. ### Flutter calling into iOS steps diff --git a/packages/pigeon/example/README.md b/packages/pigeon/example/README.md index 25ee15fe0191..2decd9b9fbea 100644 --- a/packages/pigeon/example/README.md +++ b/packages/pigeon/example/README.md @@ -353,22 +353,22 @@ pigeon_example_package_message_flutter_api_flutter_method( ## Event Channel Example -This example gives a basic overview of how to use Pigeon to set up an Event Channel method. +This example gives a basic overview of how to use Pigeon to set up an event channel. ### Dart input ```dart @EventChannelApi() -abstract class EventApi { +abstract class PlatformEvent { SealedBaseClass streamEvents(); } ``` ### Dart -The generated dart code will include a method that returns a `Stream` when invoked. -This `Stream` can then be used as any other `Stream` would be in dart. +The generated Dart code will include a method that returns a `Stream` when invoked. +This `Stream` can then be used as any other `Stream` would be in Dart. ```dart diff --git a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt index c43333aa65b9..3a7b4698f93d 100644 --- a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt +++ b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt @@ -121,7 +121,8 @@ abstract class StreamEventsStreamHandler : PigeonEventChannelWrapper { @override Widget build(BuildContext context) { - String numsSoFar = ''; + String allData = ''; return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, @@ -129,16 +129,19 @@ class _MyHomePageState extends State { AsyncSnapshot snapshot) { if (snapshot.hasData) { String newString = ''; - switch (snapshot.data) { + final SealedBaseClass? data = snapshot.data; + switch (data) { case null: newString = 'null, '; case IntEvent(): - newString = '${(snapshot.data! as IntEvent).data}, '; + final IntEvent intData = data; + newString = '$intData, '; case StringEvent(): - newString = '${(snapshot.data! as StringEvent).data}, '; + final StringEvent stringData = data; + newString = '$stringData, '; } - numsSoFar += newString; - return Text(numsSoFar); + allData += newString; + return Text(allData); } else { return const CircularProgressIndicator(); } diff --git a/packages/pigeon/example/app/lib/src/event_channel_messages.g.dart b/packages/pigeon/example/app/lib/src/event_channel_messages.g.dart index 3ebb0d46bf06..be2b0bc12eb7 100644 --- a/packages/pigeon/example/app/lib/src/event_channel_messages.g.dart +++ b/packages/pigeon/example/app/lib/src/event_channel_messages.g.dart @@ -94,7 +94,7 @@ Stream streamEvents({String instanceName = ''}) { instanceName = '.$instanceName'; } const EventChannel streamEventsChannel = EventChannel( - 'dev.flutter.pigeon.pigeon_example_package.EventApi.streamEvents', + 'dev.flutter.pigeon.pigeon_example_package.PlatformEvent.streamEvents', pigeonMethodCodec); return streamEventsChannel.receiveBroadcastStream().map((dynamic event) { return event as SealedBaseClass; diff --git a/packages/pigeon/example/app/pigeons/event_channel_messages.dart b/packages/pigeon/example/app/pigeons/event_channel_messages.dart index 322510432acb..84dc93c99f78 100644 --- a/packages/pigeon/example/app/pigeons/event_channel_messages.dart +++ b/packages/pigeon/example/app/pigeons/event_channel_messages.dart @@ -37,7 +37,7 @@ class StringEvent extends SealedBaseClass { // #docregion event-definitions @EventChannelApi() -abstract class EventApi { +abstract class PlatformEvent { SealedBaseClass streamEvents(); } // #enddocregion event-definitions diff --git a/packages/pigeon/lib/ast.dart b/packages/pigeon/lib/ast.dart index 3dc4f63889bd..5e65f31c98ac 100644 --- a/packages/pigeon/lib/ast.dart +++ b/packages/pigeon/lib/ast.dart @@ -717,7 +717,7 @@ class Class extends Node { /// List of class definitions of children. /// - /// This is only meant to be used by sealed classes used in Event Channel methods. + /// This is only meant to be used by sealed classes used in event channel methods. List children = []; /// Whether the class is sealed. @@ -833,7 +833,7 @@ class Root extends Node { /// Whether the root has any Proxy API definitions. bool containsProxyApi; - /// Whether the root has any Event Channel definitions. + /// Whether the root has any event channel definitions. bool containsEventChannel; /// Returns true if the number of custom types would exceed the available enumerations @@ -842,15 +842,7 @@ class Root extends Node { classes.length - _numberOfSealedClasses() + enums.length >= totalCustomCodecKeysAllowed; - int _numberOfSealedClasses() { - int count = 0; - for (final Class klass in classes) { - if (klass.isSealed) { - count++; - } - } - return count; - } + int _numberOfSealedClasses() => classes.where((Class c) => c.isSealed).length; @override String toString() { diff --git a/packages/pigeon/lib/dart_generator.dart b/packages/pigeon/lib/dart_generator.dart index 11e2a7cd9025..7c81f19d7124 100644 --- a/packages/pigeon/lib/dart_generator.dart +++ b/packages/pigeon/lib/dart_generator.dart @@ -900,7 +900,7 @@ final BinaryMessenger? ${varNamePrefix}binaryMessenger; /// Generates Dart source code for test support libraries based on the given AST /// represented by [root], outputting the code to [sink]. [sourceOutPath] is the - /// path of the generated dart code to be tested. [testOutPath] is where the + /// path of the generated Dart code to be tested. [testOutPath] is where the /// test code will be generated. void generateTest( DartOptions generatorOptions, diff --git a/packages/pigeon/lib/generator.dart b/packages/pigeon/lib/generator.dart index fdfec7b1eb34..fb4e2408beb6 100644 --- a/packages/pigeon/lib/generator.dart +++ b/packages/pigeon/lib/generator.dart @@ -354,7 +354,7 @@ abstract class StructuredGenerator extends Generator { required String dartPackageName, }) {} - /// Writes a single Event Channel Api to [indent]. + /// Writes a single event channel Api to [indent]. void writeEventChannelApi( T generatorOptions, Root root, diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart index ffc0311e40ca..66364c1d1819 100644 --- a/packages/pigeon/lib/generator_tools.dart +++ b/packages/pigeon/lib/generator_tools.dart @@ -661,7 +661,7 @@ class DocumentCommentSpecification { /// Formats documentation comments and adds them to current Indent. /// -/// The [comments] list is meant for comments written in the input dart file. +/// The [comments] list is meant for comments written in the input Dart file. /// The [generatorComments] list is meant for comments added by the generators. /// Include white space for all tokens when called, no assumptions are made. void addDocumentationComments( @@ -679,7 +679,7 @@ void addDocumentationComments( /// Formats documentation comments and adds them to current Indent. /// -/// The [comments] list is meant for comments written in the input dart file. +/// The [comments] list is meant for comments written in the input Dart file. /// The [generatorComments] list is meant for comments added by the generators. /// Include white space for all tokens when called, no assumptions are made. Iterable asDocumentationComments( @@ -806,7 +806,7 @@ String toUpperCamelCase(String text) { }).join(); } -/// Converts strings to Upper Camel Case. +/// Converts strings to Lower Camel Case. String toLowerCamelCase(String text) { final RegExp separatorPattern = RegExp(r'[ _-]'); bool firstWord = true; diff --git a/packages/pigeon/lib/kotlin_generator.dart b/packages/pigeon/lib/kotlin_generator.dart index 0521d60ac977..7d47df9f89b5 100644 --- a/packages/pigeon/lib/kotlin_generator.dart +++ b/packages/pigeon/lib/kotlin_generator.dart @@ -486,7 +486,7 @@ class KotlinGenerator extends StructuredGenerator { ); indent.format(''' -companion object { +companion object { fun fromList(${varNamePrefix}list: List): Any? { val wrapper = ${generatorOptions.fileSpecificClassNameComponent}$_overflowClassName( type = ${varNamePrefix}list[0] as Long, diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart index f388a9906950..02347efa0a7b 100644 --- a/packages/pigeon/lib/pigeon_lib.dart +++ b/packages/pigeon/lib/pigeon_lib.dart @@ -160,7 +160,7 @@ class ProxyApi { final KotlinProxyApiOptions? kotlinOptions; } -/// Metadata to annotate a pigeon API that contains Event Channels. +/// Metadata to annotate a pigeon API that contains event channels. class EventChannelApi { /// Constructor. const EventChannelApi(); @@ -284,10 +284,10 @@ class PigeonOptions { /// Path to the file which will be processed. final String? input; - /// Path to the dart file that will be generated. + /// Path to the Dart file that will be generated. final String? dartOut; - /// Path to the dart file that will be generated for test support classes. + /// Path to the Dart file that will be generated for test support classes. final String? dartTestOut; /// Path to the ".h" Objective-C file will be generated. @@ -545,7 +545,7 @@ DartOptions _dartOptionsWithCopyrightHeader( void _errorOnEventChannelApi(List errors, String generator, Root root) { if (root.containsEventChannel) { - errors.add(Error(message: '$generator does not support Event Channels')); + errors.add(Error(message: '$generator does not support event channels')); } } @@ -594,7 +594,7 @@ class DartGeneratorAdapter implements GeneratorAdapter { DartGeneratorAdapter(); /// A string representing the name of the language being generated. - String languageString = 'dart'; + String languageString = 'Dart'; @override List fileTypeList = const [FileType.na]; @@ -677,7 +677,7 @@ class ObjcGeneratorAdapter implements GeneratorAdapter { {this.fileTypeList = const [FileType.header, FileType.source]}); /// A string representing the name of the language being generated. - String languageString = 'objc'; + String languageString = 'Objective-C'; @override List fileTypeList; @@ -735,7 +735,7 @@ class JavaGeneratorAdapter implements GeneratorAdapter { JavaGeneratorAdapter(); /// A string representing the name of the language being generated. - String languageString = 'java'; + String languageString = 'Java'; @override List fileTypeList = const [FileType.na]; @@ -781,7 +781,7 @@ class SwiftGeneratorAdapter implements GeneratorAdapter { SwiftGeneratorAdapter(); /// A string representing the name of the language being generated. - String languageString = 'swift'; + String languageString = 'Swift'; @override List fileTypeList = const [FileType.na]; @@ -824,7 +824,7 @@ class CppGeneratorAdapter implements GeneratorAdapter { {this.fileTypeList = const [FileType.header, FileType.source]}); /// A string representing the name of the language being generated. - String languageString = 'cpp'; + String languageString = 'C++'; @override List fileTypeList; @@ -877,7 +877,7 @@ class GObjectGeneratorAdapter implements GeneratorAdapter { {this.fileTypeList = const [FileType.header, FileType.source]}); /// A string representing the name of the language being generated. - String languageString = 'gobject'; + String languageString = 'GObject'; @override List fileTypeList; @@ -1110,7 +1110,7 @@ List _validateAst(Root root, String source) { if (api is AstEventChannelApi && method.parameters.isNotEmpty) { result.add(Error( message: - 'Event Channel methods must not be contain parameters, in method "${method.name}" in API: "${api.name}"', + 'event channel methods must not be contain parameters, in method "${method.name}" in API: "${api.name}"', lineNumber: _calculateLineNumberNullable(source, method.offset), )); } diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index c73ec36dd043..0d0d17587d42 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -553,7 +553,7 @@ if (wrapped == nil) { final String className = classDefinition.name; indent.writeln('// swift-format-ignore: AlwaysUseLowerCamelCase'); indent.write( - 'static func fromList(_ ${varNamePrefix}list: [Any?]) -> $className? '); + 'static func fromList(_ ${varNamePrefix}list: [Any?]) -> $className? '); indent.addScoped('{', '}', () { enumerate(getFieldsInSerializationOrder(classDefinition), diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/example_app.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/example_app.dart index 7280373b1e58..0c7722e1a01f 100644 --- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/example_app.dart +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/example_app.dart @@ -26,7 +26,7 @@ class ExampleApp extends StatefulWidget { class _ExampleAppState extends State { late final HostIntegrationCoreApi api; String status = 'Calling...'; - String numsSoFar = ''; + String allData = ''; @override void initState() { @@ -69,32 +69,37 @@ class _ExampleAppState extends State { stream: streamEvents(), builder: (BuildContext context, AsyncSnapshot snapshot) { + final EventChannelDataBase? data = snapshot.data; if (snapshot.hasData) { String newString = ''; - switch (snapshot.data) { + switch (data) { case null: newString = 'null, '; case IntEvent(): - newString = '${(snapshot.data! as IntEvent).value}, '; + final IntEvent intData = data; + newString = '${intData.value}, '; case StringEvent(): - newString = - '${(snapshot.data! as StringEvent).value}, '; + final StringEvent stringData = data; + newString = '${stringData.value}, '; case BoolEvent(): - newString = '${(snapshot.data! as BoolEvent).value}, '; + final BoolEvent boolData = data; + newString = '${boolData.value}, '; case DoubleEvent(): - newString = - '${(snapshot.data! as DoubleEvent).value}, '; + final DoubleEvent doubleData = data; + newString = '${doubleData.value}, '; case ObjectsEvent(): - newString = - '${(snapshot.data! as ObjectsEvent).value}, '; + final ObjectsEvent objectsData = data; + newString = '${objectsData.value}, '; case EnumEvent(): - newString = '${(snapshot.data! as EnumEvent).value}, '; + final EnumEvent enumData = data; + newString = '${enumData.value}, '; case ClassEvent(): - newString = '${(snapshot.data! as ClassEvent).value}, '; + final ClassEvent classData = data; + newString = '${classData.value}, '; } - numsSoFar += newString; - return Text(numsSoFar); + allData += newString; + return Text(allData); } else { return const CircularProgressIndicator(); } diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart index 01a64171d3cd..76d6376b65ac 100644 --- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart @@ -4,6 +4,8 @@ // ignore_for_file: unused_local_variable +import 'dart:async'; + import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -2841,7 +2843,7 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { expect(unused, unused); }); - /// Event Channels + /// Event channels const List eventChannelSupported = [ TargetGenerator.kotlin, @@ -2856,6 +2858,7 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { }, skip: !eventChannelSupported.contains(targetGenerator)); testWidgets('event channel handles extended sealed classes', (_) async { + final Completer completer = Completer(); int count = 0; final Stream events = streamEvents(); events.listen((EventChannelDataBase event) { @@ -2888,8 +2891,10 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { expect(event.value.aNullableInt, 0); expect(count, 6); count++; + completer.complete(); } }); + await completer.future; }, skip: !eventChannelSupported.contains(targetGenerator)); } diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/.gitignore b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/.gitignore index 255032e2b82e..bbe6aef544f5 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/.gitignore +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/.gitignore @@ -7,6 +7,7 @@ # This contains the declaration of the test classes wrapped by the ProxyApi tests and the # implemetations of their APIs. !ProxyApiTestApiImpls.kt -# Including this makes it easier to review code generation changes. + +# Including these makes it easier to review code generation changes. !ProxyApiTests.gen.kt !EventChannelTests.gen.kt diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt index ca183b9c5753..d9f149e37a42 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt @@ -882,16 +882,17 @@ object SendInts : StreamIntsStreamHandler() { object : Runnable { override fun run() { handler.post { - sink.success(count) - count++ - } - handler.postDelayed(this, 1000) - if (count >= 5) { - sink.endOfStream() + if (count >= 5) { + sink.endOfStream() + } else { + sink.success(count) + count++ + handler.postDelayed(this, 10) + } } } } - handler.postDelayed(r, 1000) + handler.postDelayed(r, 10) } } diff --git a/packages/pigeon/platform_tests/test_plugin/example/macos/Runner/AppDelegate.swift b/packages/pigeon/platform_tests/test_plugin/example/macos/Runner/AppDelegate.swift index 5cec4c48f620..21fbd0297b22 100644 --- a/packages/pigeon/platform_tests/test_plugin/example/macos/Runner/AppDelegate.swift +++ b/packages/pigeon/platform_tests/test_plugin/example/macos/Runner/AppDelegate.swift @@ -5,9 +5,13 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/.gitignore b/packages/pigeon/platform_tests/test_plugin/ios/Classes/.gitignore index 7f6cf31d4946..94e339ef912d 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/.gitignore +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/.gitignore @@ -5,6 +5,7 @@ !CoreTests.gen.swift # Keeping this makes it easier to review changes to ProxyApi generation. !ProxyApiTests.gen.swift -# Contains the class declartions for testing ProxyApis. + +# Including these makes it easier to review code generation changes. !ProxyApiTestClass.swift !EventChannelTests.gen.swift diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift index a57e8e983238..0ce3af1f1091 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift @@ -1219,16 +1219,20 @@ public class TestPluginWithSuffix: HostSmallApi { class SendInts: StreamIntsStreamHandler { var timerActive = false + var timer: Timer? override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) { var count: Int64 = 0 if !timerActive { timerActive = true - Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { _ in - sink.success(count) - count += 1 - if count >= 5 { - sink.endOfStream() + timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { _ in + DispatchQueue.main.async { + sink.success(count) + count += 1 + if count >= 5 { + sink.endOfStream() + self.timer?.invalidate() + } } } } @@ -1237,6 +1241,7 @@ class SendInts: StreamIntsStreamHandler { class SendEvents: StreamEventsStreamHandler { var timerActive = false + var timer: Timer? var eventList: [EventChannelDataBase] = [ IntEvent(value: 1), @@ -1253,12 +1258,15 @@ class SendEvents: StreamEventsStreamHandler { var count = 0 if !timerActive { timerActive = true - Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { _ in - if count >= self.eventList.count { - sink.endOfStream() - } else { - sink.success(self.eventList[count]) - count += 1 + timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { _ in + DispatchQueue.main.async { + if count >= self.eventList.count { + sink.endOfStream() + self.timer?.invalidate() + } else { + sink.success(self.eventList[count]) + count += 1 + } } } } diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift index 13b81ca1dd04..360e52e991b3 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift @@ -1215,19 +1215,22 @@ public class TestPluginWithSuffix: HostSmallApi { } } - class SendInts: StreamIntsStreamHandler { var timerActive = false + var timer: Timer? override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) { var count: Int64 = 0 if !timerActive { timerActive = true - Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { _ in - sink.success(count) - count += 1 - if count >= 5 { - sink.endOfStream() + timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { _ in + DispatchQueue.main.async { + sink.success(count) + count += 1 + if count >= 5 { + sink.endOfStream() + self.timer?.invalidate() + } } } } @@ -1236,6 +1239,7 @@ class SendInts: StreamIntsStreamHandler { class SendEvents: StreamEventsStreamHandler { var timerActive = false + var timer: Timer? var eventList: [EventChannelDataBase] = [ IntEvent(value: 1), @@ -1252,12 +1256,15 @@ class SendEvents: StreamEventsStreamHandler { var count = 0 if !timerActive { timerActive = true - Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { _ in - if count >= self.eventList.count { - sink.endOfStream() - } else { - sink.success(self.eventList[count]) - count += 1 + timer = Timer.scheduledTimer(withTimeInterval: 0.01, repeats: true) { _ in + DispatchQueue.main.async { + if count >= self.eventList.count { + sink.endOfStream() + self.timer?.invalidate() + } else { + sink.success(self.eventList[count]) + count += 1 + } } } } diff --git a/packages/pigeon/test/dart_generator_test.dart b/packages/pigeon/test/dart_generator_test.dart index c53a0686170c..7a4d44c26177 100644 --- a/packages/pigeon/test/dart_generator_test.dart +++ b/packages/pigeon/test/dart_generator_test.dart @@ -667,7 +667,7 @@ void main() { expect(code, matches('pigeonVar_channel.send[(]null[)]')); }); - test('mock dart handler', () { + test('mock Dart handler', () { final Root root = Root(apis: [ AstHostApi(name: 'Api', dartHostTestHandler: 'ApiMock', methods: [ Method( diff --git a/packages/pigeon/test/pigeon_lib_test.dart b/packages/pigeon/test/pigeon_lib_test.dart index faf4a1be7699..bf6fc7cbca16 100644 --- a/packages/pigeon/test/pigeon_lib_test.dart +++ b/packages/pigeon/test/pigeon_lib_test.dart @@ -1601,7 +1601,7 @@ abstract class MyClass { }); }); - group('Event Channel validation', () { + group('event channel validation', () { test('methods cannot contain parameters', () { const String code = ''' @EventChannelApi() @@ -1614,7 +1614,7 @@ abstract class EventChannelApi { expect( parseResult.errors.single.message, contains( - 'Event Channel methods must not be contain parameters, in method "streamInts" in API: "EventChannelApi"'), + 'event channel methods must not be contain parameters, in method "streamInts" in API: "EventChannelApi"'), ); }); }); diff --git a/packages/pigeon/tool/shared/generation.dart b/packages/pigeon/tool/shared/generation.dart index c7414ae0f887..7314a41ededb 100644 --- a/packages/pigeon/tool/shared/generation.dart +++ b/packages/pigeon/tool/shared/generation.dart @@ -20,10 +20,20 @@ enum GeneratorLanguage { swift, } +const Set allButSwiftAndDart = { + GeneratorLanguage.cpp, + GeneratorLanguage.gobject, + GeneratorLanguage.java, + GeneratorLanguage.objc, +}; + // A map of pigeons/ files to the languages that they can't yet be generated // for due to limitations of that generator. const Map> _unsupportedFiles = - >{}; + >{ + 'event_channel_tests': allButSwiftAndDart, + 'proxy_api_tests': allButSwiftAndDart, +}; String _snakeToPascalCase(String snake) { final List parts = snake.split('_'); @@ -54,7 +64,7 @@ Future generateExamplePigeons() async { basePath: './example/app', suppressVersion: true, ); - success = await runPigeon( + success += await runPigeon( input: './example/app/pigeons/event_channel_messages.dart', basePath: './example/app', suppressVersion: true, @@ -68,26 +78,19 @@ Future generateTestPigeons( // it entirely; see https://github.com/flutter/flutter/issues/115169. /// A list of all pigeons to be generated along with a list of skipped languages. - const Map> inputs = - >{ - 'background_platform_channels': {}, - 'core_tests': {}, - 'enum': {}, - 'event_channel_tests': { - GeneratorLanguage.cpp, - GeneratorLanguage.gobject, - GeneratorLanguage.java, - GeneratorLanguage.objc, - }, - 'flutter_unittests': - {}, // Only for Dart unit tests in shared_test_plugin_code - 'message': {}, - 'multiple_arity': {}, - 'non_null_fields': {}, - 'null_fields': {}, - 'nullable_returns': {}, - 'primitive': {}, - 'proxy_api_tests': {}, + const Set inputs = { + 'background_platform_channels', + 'core_tests', + 'enum', + 'event_channel_tests', + 'flutter_unittests', // Only for Dart unit tests in shared_test_plugin_code + 'message', + 'multiple_arity', + 'non_null_fields', + 'null_fields', + 'nullable_returns', + 'primitive', + 'proxy_api_tests', }; final String outputBase = p.join(baseDir, 'platform_tests', 'test_plugin'); @@ -96,31 +99,29 @@ Future generateTestPigeons( final String sharedDartOutputBase = p.join(baseDir, 'platform_tests', 'shared_test_plugin_code'); - for (final MapEntry> input in inputs.entries) { - final String pascalCaseName = _snakeToPascalCase(input.key); + for (final String input in inputs) { + final String pascalCaseName = _snakeToPascalCase(input); final Set skipLanguages = - _unsupportedFiles[input.key] ?? {}; - skipLanguages.addAll(input.value); + _unsupportedFiles[input] ?? {}; final bool kotlinErrorClassGenerationTestFiles = - input.key == 'core_tests' || - input.key == 'background_platform_channels'; + input == 'core_tests' || input == 'background_platform_channels'; final String kotlinErrorName = kotlinErrorClassGenerationTestFiles ? 'FlutterError' : '${pascalCaseName}Error'; - final bool swiftErrorUseDefaultErrorName = input.key == 'core_tests' || - input.key == 'background_platform_channels'; + final bool swiftErrorUseDefaultErrorName = + input == 'core_tests' || input == 'background_platform_channels'; final String? swiftErrorClassName = swiftErrorUseDefaultErrorName ? null : '${pascalCaseName}Error'; // Generate the default language test plugin output. int generateCode = await runPigeon( - input: './pigeons/${input.key}.dart', - dartOut: '$sharedDartOutputBase/lib/src/generated/${input.key}.gen.dart', - dartTestOut: input.key == 'message' + input: './pigeons/${input}.dart', + dartOut: '$sharedDartOutputBase/lib/src/generated/${input}.gen.dart', + dartTestOut: input == 'message' ? '$sharedDartOutputBase/test/test_message.gen.dart' : null, dartPackageName: 'pigeon_integration_tests', @@ -131,30 +132,30 @@ Future generateTestPigeons( : '$outputBase/android/src/main/kotlin/com/example/test_plugin/$pascalCaseName.gen.kt', kotlinPackage: 'com.example.test_plugin', kotlinErrorClassName: kotlinErrorName, - kotlinIncludeErrorClass: input.key != 'core_tests', + kotlinIncludeErrorClass: input != 'core_tests', // iOS swiftOut: skipLanguages.contains(GeneratorLanguage.swift) ? null : '$outputBase/ios/Classes/$pascalCaseName.gen.swift', swiftErrorClassName: swiftErrorClassName, - swiftIncludeErrorClass: input.key != 'core_tests', + swiftIncludeErrorClass: input != 'core_tests', // Linux gobjectHeaderOut: skipLanguages.contains(GeneratorLanguage.gobject) ? null - : '$outputBase/linux/pigeon/${input.key}.gen.h', + : '$outputBase/linux/pigeon/${input}.gen.h', gobjectSourceOut: skipLanguages.contains(GeneratorLanguage.gobject) ? null - : '$outputBase/linux/pigeon/${input.key}.gen.cc', + : '$outputBase/linux/pigeon/${input}.gen.cc', gobjectModule: '${pascalCaseName}PigeonTest', // Windows cppHeaderOut: skipLanguages.contains(GeneratorLanguage.cpp) ? null - : '$outputBase/windows/pigeon/${input.key}.gen.h', + : '$outputBase/windows/pigeon/${input}.gen.h', cppSourceOut: skipLanguages.contains(GeneratorLanguage.cpp) ? null - : '$outputBase/windows/pigeon/${input.key}.gen.cpp', - cppNamespace: '${input.key}_pigeontest', - injectOverflowTypes: includeOverflow && input.key == 'core_tests', + : '$outputBase/windows/pigeon/${input}.gen.cpp', + cppNamespace: '${input}_pigeontest', + injectOverflowTypes: includeOverflow && input == 'core_tests', ); if (generateCode != 0) { return generateCode; @@ -164,15 +165,15 @@ Future generateTestPigeons( // doesn't have a way to output separate macOS and iOS Swift output in a // single invocation. generateCode = await runPigeon( - input: './pigeons/${input.key}.dart', + input: './pigeons/${input}.dart', swiftOut: skipLanguages.contains(GeneratorLanguage.swift) ? null : '$outputBase/macos/Classes/$pascalCaseName.gen.swift', swiftErrorClassName: swiftErrorClassName, - swiftIncludeErrorClass: input.key != 'core_tests', + swiftIncludeErrorClass: input != 'core_tests', suppressVersion: true, dartPackageName: 'pigeon_integration_tests', - injectOverflowTypes: includeOverflow && input.key == 'core_tests', + injectOverflowTypes: includeOverflow && input == 'core_tests', ); if (generateCode != 0) { return generateCode; @@ -180,14 +181,14 @@ Future generateTestPigeons( // Generate the alternate language test plugin output. generateCode = await runPigeon( - input: './pigeons/${input.key}.dart', + input: './pigeons/${input}.dart', // Android // This doesn't use the '.gen' suffix since Java has strict file naming // rules. javaOut: skipLanguages.contains(GeneratorLanguage.java) ? null : '$alternateOutputBase/android/src/main/java/com/example/' - 'alternate_language_test_plugin/${_javaFilenameForName(input.key)}.java', + 'alternate_language_test_plugin/${_javaFilenameForName(input)}.java', javaPackage: 'com.example.alternate_language_test_plugin', // iOS objcHeaderOut: skipLanguages.contains(GeneratorLanguage.objc) @@ -196,10 +197,10 @@ Future generateTestPigeons( objcSourceOut: skipLanguages.contains(GeneratorLanguage.objc) ? null : '$alternateOutputBase/ios/Classes/$pascalCaseName.gen.m', - objcPrefix: input.key == 'core_tests' ? 'FLT' : '', + objcPrefix: input == 'core_tests' ? 'FLT' : '', suppressVersion: true, dartPackageName: 'pigeon_integration_tests', - injectOverflowTypes: includeOverflow && input.key == 'core_tests', + injectOverflowTypes: includeOverflow && input == 'core_tests', ); if (generateCode != 0) { return generateCode; @@ -209,17 +210,17 @@ Future generateTestPigeons( // doesn't have a way to output separate macOS and iOS Swift output in a // single invocation. generateCode = await runPigeon( - input: './pigeons/${input.key}.dart', + input: './pigeons/${input}.dart', objcHeaderOut: skipLanguages.contains(GeneratorLanguage.objc) ? null : '$alternateOutputBase/macos/Classes/$pascalCaseName.gen.h', objcSourceOut: skipLanguages.contains(GeneratorLanguage.objc) ? null : '$alternateOutputBase/macos/Classes/$pascalCaseName.gen.m', - objcPrefix: input.key == 'core_tests' ? 'FLT' : '', + objcPrefix: input == 'core_tests' ? 'FLT' : '', suppressVersion: true, dartPackageName: 'pigeon_integration_tests', - injectOverflowTypes: includeOverflow && input.key == 'core_tests', + injectOverflowTypes: includeOverflow && input == 'core_tests', ); if (generateCode != 0) { return generateCode; diff --git a/packages/pigeon/tool/shared/test_suites.dart b/packages/pigeon/tool/shared/test_suites.dart index c37c388d03f7..1c9dc552d617 100644 --- a/packages/pigeon/tool/shared/test_suites.dart +++ b/packages/pigeon/tool/shared/test_suites.dart @@ -483,7 +483,7 @@ Future _runCommandLineTests({bool ciMode = false}) async { int exitCode = 0; for (final List arguments in testArguments) { - print('Testing dart $pigeonScript ${arguments.join(', ')}'); + print('Testing Dart $pigeonScript ${arguments.join(', ')}'); exitCode = await runProcess('dart', [snapshot, ...arguments], streamOutput: false, logFailure: true); if (exitCode != 0) { diff --git a/packages/pigeon/tool/test.dart b/packages/pigeon/tool/test.dart index 07c7dfdf61b0..cc23846427b5 100644 --- a/packages/pigeon/tool/test.dart +++ b/packages/pigeon/tool/test.dart @@ -7,7 +7,7 @@ //////////////////////////////////////////////////////////////////////////////// /// Script for executing the Pigeon tests /// -/// usage: dart run tool/test.dart +/// usage: Dart run tool/test.dart //////////////////////////////////////////////////////////////////////////////// library; @@ -56,7 +56,7 @@ Future main(List args) async { } else if (argResults.wasParsed('help')) { print(''' Pigeon run_tests -usage: dart run tool/test.dart [-l | -t ] +usage: Dart run tool/test.dart [-l | -t ] ${parser.usage}'''); exit(0); From 6f60717eb3456539dc3dbd0b8e072c0e541238cf Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Tue, 12 Nov 2024 20:57:34 -0800 Subject: [PATCH 13/20] fix swift channel name bug and update examples to be clearer --- packages/pigeon/example/README.md | 91 ++++++++++--------- .../pigeon_example_app/MainActivity.kt | 66 +++++++++----- .../example/app/ios/Runner/AppDelegate.swift | 52 ++++++++--- .../ios/Runner/EventChannelMessages.g.swift | 2 +- packages/pigeon/example/app/lib/main.dart | 38 ++++---- packages/pigeon/lib/swift_generator.dart | 2 +- packages/pigeon/tool/shared/generation.dart | 18 ++-- 7 files changed, 159 insertions(+), 110 deletions(-) diff --git a/packages/pigeon/example/README.md b/packages/pigeon/example/README.md index 2decd9b9fbea..893895420e99 100644 --- a/packages/pigeon/example/README.md +++ b/packages/pigeon/example/README.md @@ -372,34 +372,48 @@ This `Stream` can then be used as any other `Stream` would be in Dart. ```dart -final Stream events = streamEvents(); +Stream getEventStream() async* { + final Stream events = streamEvents(); + await for (final SealedBaseClass event in events) { + switch (event) { + case IntEvent(): + final int intData = event.data; + yield '$intData, '; + case StringEvent(): + final String stringData = event.data; + yield '$stringData, '; + } + } +} ``` ### Swift ```swift -class SendEvents: StreamEventsStreamHandler { - var timerActive = false +class EventListener: StreamEventsStreamHandler { + var eventSink: PigeonEventSink? override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) { - var count = 0 - if !timerActive { - timerActive = true - Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in - if count >= 10 { - sink.endOfStream() - } else { - if (count % 2) == 0 { - sink.success(IntEvent(data: Int64(count))) - } else { - sink.success(StringEvent(data: String(count))) - } - count += 1 - } - } + eventSink = sink + } + + func onIntEvent(event: Int64) { + if let eventSink { + eventSink.success(IntEvent(data: event)) + } + } + + func onStringEvent(event: String) { + if let eventSink { + eventSink.success(StringEvent(data: event)) } } + + func onEventsDone() { + eventSink?.endOfStream() + eventSink = nil + } } ``` @@ -407,33 +421,24 @@ class SendEvents: StreamEventsStreamHandler { ```kotlin -object SendClass : StreamEventsStreamHandler() { - val handler = Handler(Looper.getMainLooper()) +class EventListener : StreamEventsStreamHandler() { + private var eventSink: PigeonEventSink? = null override fun onListen(p0: Any?, sink: PigeonEventSink) { - var count: Int = 0 - val r: Runnable = - object : Runnable { - override fun run() { - if (count >= 10) { - sink.endOfStream() - } else { - if (count % 2 == 0) { - handler.post { - sink.success(IntEvent(count.toLong())) - count++ - } - } else { - handler.post { - sink.success(StringEvent(count.toString())) - count++ - } - } - handler.postDelayed(this, 1000) - } - } - } - handler.postDelayed(r, 1000) + eventSink = sink + } + + fun onIntEvent(event: Long) { + eventSink?.success(IntEvent(data = event)) + } + + fun onStringEvent(event: String) { + eventSink?.success(StringEvent(data = event)) + } + + fun onEventsDone() { + eventSink?.endOfStream() + eventSink = null } } ``` 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 9f4e1078658a..d346167a5a14 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 @@ -58,36 +58,54 @@ private class PigeonFlutterApi(binding: FlutterPlugin.FlutterPluginBinding) { // #enddocregion kotlin-class-flutter // #docregion kotlin-class-event -object SendClass : StreamEventsStreamHandler() { - val handler = Handler(Looper.getMainLooper()) +class EventListener : StreamEventsStreamHandler() { + private var eventSink: PigeonEventSink? = null override fun onListen(p0: Any?, sink: PigeonEventSink) { - var count: Int = 0 - val r: Runnable = - object : Runnable { - override fun run() { - if (count >= 10) { - sink.endOfStream() + eventSink = sink + } + + fun onIntEvent(event: Long) { + eventSink?.success(IntEvent(data = event)) + } + + fun onStringEvent(event: String) { + eventSink?.success(StringEvent(data = event)) + } + + fun onEventsDone() { + eventSink?.endOfStream() + eventSink = null + } +} +// #enddocregion kotlin-class-event + +fun sendEvents(eventListener: EventListener) { + val handler = Handler(Looper.getMainLooper()) + var count: Int = 0 + val r: Runnable = + object : Runnable { + override fun run() { + if (count >= 100) { + handler.post { eventListener.onEventsDone() } + } else { + if (count % 2 == 0) { + handler.post { + eventListener.onIntEvent(count.toLong()) + count++ + } } else { - if (count % 2 == 0) { - handler.post { - sink.success(IntEvent(count.toLong())) - count++ - } - } else { - handler.post { - sink.success(StringEvent(count.toString())) - count++ - } + handler.post { + eventListener.onStringEvent(count.toString()) + count++ } - handler.postDelayed(this, 1000) } + handler.postDelayed(this, 1000) } } - handler.postDelayed(r, 1000) - } + } + handler.post(r) } -// #enddocregion kotlin-class-event class MainActivity : FlutterActivity() { override fun configureFlutterEngine(flutterEngine: FlutterEngine) { @@ -95,6 +113,8 @@ class MainActivity : FlutterActivity() { val api = PigeonApiImplementation() ExampleHostApi.setUp(flutterEngine.dartExecutor.binaryMessenger, api) - StreamEventsStreamHandler.register(flutterEngine.dartExecutor.binaryMessenger, SendClass) + val eventListener = EventListener() + StreamEventsStreamHandler.register(flutterEngine.dartExecutor.binaryMessenger, eventListener) + sendEvents(eventListener) } } diff --git a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift index e83e55b34376..594f6d374e75 100644 --- a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift +++ b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift @@ -47,21 +47,48 @@ private class PigeonFlutterApi { // #enddocregion swift-class-flutter // #docregion swift-class-event -class SendEvents: StreamEventsStreamHandler { - var timerActive = false +class EventListener: StreamEventsStreamHandler { + var eventSink: PigeonEventSink? override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) { - var count = 0 - if !timerActive { - timerActive = true - Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in - if count >= 10 { - sink.endOfStream() + eventSink = sink + } + + func onIntEvent(event: Int64) { + if let eventSink { + eventSink.success(IntEvent(data: event)) + } + } + + func onStringEvent(event: String) { + if let eventSink { + eventSink.success(StringEvent(data: event)) + } + } + + func onEventsDone() { + eventSink?.endOfStream() + eventSink = nil + } +} +// #enddocregion swift-class-event + +func sendEvents(_ eventListener: EventListener) { + var timerActive = false + var timer: Timer? + var count: Int64 = 0 + if !timerActive { + timerActive = true + timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in + DispatchQueue.main.async { + if count >= 100 { + eventListener.onEventsDone() + timer?.invalidate() } else { if (count % 2) == 0 { - sink.success(IntEvent(data: Int64(count))) + eventListener.onIntEvent(event: Int64(count)) } else { - sink.success(StringEvent(data: String(count))) + eventListener.onStringEvent(event: String(count)) } count += 1 } @@ -69,7 +96,6 @@ class SendEvents: StreamEventsStreamHandler { } } } -// #enddocregion swift-class-event @main @objc class AppDelegate: FlutterAppDelegate { @@ -82,7 +108,9 @@ class SendEvents: StreamEventsStreamHandler { let controller = window?.rootViewController as! FlutterViewController let api = PigeonApiImplementation() ExampleHostApiSetup.setUp(binaryMessenger: controller.binaryMessenger, api: api) - StreamEventsStreamHandler.register(with: controller.binaryMessenger, wrapper: SendEvents()) + let eventListener = EventListener() + StreamEventsStreamHandler.register(with: controller.binaryMessenger, wrapper: eventListener) + sendEvents(eventListener) return super.application(application, didFinishLaunchingWithOptions: launchOptions) diff --git a/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift b/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift index 6058be9fd454..84ab3ed34ecc 100644 --- a/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift +++ b/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift @@ -167,7 +167,7 @@ class StreamEventsStreamHandler: PigeonEventChannelWrapper { instanceName: String = "", wrapper: StreamEventsStreamHandler ) { - var channelName = "dev.flutter.pigeon.pigeon_integration_tests.EventChannelCoreApi.streamEvents" + var channelName = "dev.flutter.pigeon.pigeon_example_package.PlatformEvent.streamEvents" if !instanceName.isEmpty { channelName += ".\(instanceName)" } diff --git a/packages/pigeon/example/app/lib/main.dart b/packages/pigeon/example/app/lib/main.dart index 21ab4bbf716a..f7048f7b98e4 100644 --- a/packages/pigeon/example/app/lib/main.dart +++ b/packages/pigeon/example/app/lib/main.dart @@ -86,12 +86,21 @@ class _MyHomePageState extends State { } // #enddocregion main-dart - Stream getEventStream() { - // #docregion main-dart-event + // #docregion main-dart-event + Stream getEventStream() async* { final Stream events = streamEvents(); - // #enddocregion main-dart-event - return events; + await for (final SealedBaseClass event in events) { + switch (event) { + case IntEvent(): + final int intData = event.data; + yield '$intData, '; + case StringEvent(): + final String stringData = event.data; + yield '$stringData, '; + } + } } + // #enddocregion main-dart-event @override void initState() { @@ -123,24 +132,11 @@ class _MyHomePageState extends State { _hostCallResult ?? 'Waiting for host language...', ), if (_hostCallResult == null) const CircularProgressIndicator(), - StreamBuilder( - stream: streamEvents(), - builder: (BuildContext context, - AsyncSnapshot snapshot) { + StreamBuilder( + stream: getEventStream(), + builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData) { - String newString = ''; - final SealedBaseClass? data = snapshot.data; - switch (data) { - case null: - newString = 'null, '; - case IntEvent(): - final IntEvent intData = data; - newString = '$intData, '; - case StringEvent(): - final StringEvent stringData = data; - newString = '$stringData, '; - } - allData += newString; + allData += snapshot.data ?? ''; return Text(allData); } else { return const CircularProgressIndicator(); diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index 0d0d17587d42..f9abaaebcd16 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -1406,7 +1406,7 @@ private func nilOrValue(_ value: Any?) -> T? { static func register(with messenger: FlutterBinaryMessenger, instanceName: String = "", wrapper: ${toUpperCamelCase(func.name)}StreamHandler) { - var channelName = "dev.flutter.pigeon.pigeon_integration_tests.EventChannelCoreApi.${func.name}" + var channelName = "${makeChannelName(api, func, dartPackageName)}" if !instanceName.isEmpty { channelName += ".\\(instanceName)" } diff --git a/packages/pigeon/tool/shared/generation.dart b/packages/pigeon/tool/shared/generation.dart index 7314a41ededb..01b9115ad857 100644 --- a/packages/pigeon/tool/shared/generation.dart +++ b/packages/pigeon/tool/shared/generation.dart @@ -119,8 +119,8 @@ Future generateTestPigeons( // Generate the default language test plugin output. int generateCode = await runPigeon( - input: './pigeons/${input}.dart', - dartOut: '$sharedDartOutputBase/lib/src/generated/${input}.gen.dart', + input: './pigeons/$input.dart', + dartOut: '$sharedDartOutputBase/lib/src/generated/$input.gen.dart', dartTestOut: input == 'message' ? '$sharedDartOutputBase/test/test_message.gen.dart' : null, @@ -142,18 +142,18 @@ Future generateTestPigeons( // Linux gobjectHeaderOut: skipLanguages.contains(GeneratorLanguage.gobject) ? null - : '$outputBase/linux/pigeon/${input}.gen.h', + : '$outputBase/linux/pigeon/$input.gen.h', gobjectSourceOut: skipLanguages.contains(GeneratorLanguage.gobject) ? null - : '$outputBase/linux/pigeon/${input}.gen.cc', + : '$outputBase/linux/pigeon/$input.gen.cc', gobjectModule: '${pascalCaseName}PigeonTest', // Windows cppHeaderOut: skipLanguages.contains(GeneratorLanguage.cpp) ? null - : '$outputBase/windows/pigeon/${input}.gen.h', + : '$outputBase/windows/pigeon/$input.gen.h', cppSourceOut: skipLanguages.contains(GeneratorLanguage.cpp) ? null - : '$outputBase/windows/pigeon/${input}.gen.cpp', + : '$outputBase/windows/pigeon/$input.gen.cpp', cppNamespace: '${input}_pigeontest', injectOverflowTypes: includeOverflow && input == 'core_tests', ); @@ -165,7 +165,7 @@ Future generateTestPigeons( // doesn't have a way to output separate macOS and iOS Swift output in a // single invocation. generateCode = await runPigeon( - input: './pigeons/${input}.dart', + input: './pigeons/$input.dart', swiftOut: skipLanguages.contains(GeneratorLanguage.swift) ? null : '$outputBase/macos/Classes/$pascalCaseName.gen.swift', @@ -181,7 +181,7 @@ Future generateTestPigeons( // Generate the alternate language test plugin output. generateCode = await runPigeon( - input: './pigeons/${input}.dart', + input: './pigeons/$input.dart', // Android // This doesn't use the '.gen' suffix since Java has strict file naming // rules. @@ -210,7 +210,7 @@ Future generateTestPigeons( // doesn't have a way to output separate macOS and iOS Swift output in a // single invocation. generateCode = await runPigeon( - input: './pigeons/${input}.dart', + input: './pigeons/$input.dart', objcHeaderOut: skipLanguages.contains(GeneratorLanguage.objc) ? null : '$alternateOutputBase/macos/Classes/$pascalCaseName.gen.h', From 58501991dc393a26c9a384c2df41fe365e9448ac Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Tue, 12 Nov 2024 20:58:05 -0800 Subject: [PATCH 14/20] less targeted podspec skip --- script/tool/lib/src/podspec_check_command.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/tool/lib/src/podspec_check_command.dart b/script/tool/lib/src/podspec_check_command.dart index 6b871a7b52e6..e698abd1e340 100644 --- a/script/tool/lib/src/podspec_check_command.dart +++ b/script/tool/lib/src/podspec_check_command.dart @@ -112,7 +112,7 @@ class PodspecCheckCommand extends PackageLoopingCommand { Future> _podspecsToLint(RepositoryPackage package) async { // Since the pigeon podspecs require generated files that are not included in git, // the podspec lint fails. - if (package.displayName == 'packages/pigeon') { + if (package.path.contains('packages/pigeon/platform_tests/')) { return []; } final List podspecs = From 2d4d262b9bbf38c8d746ef0a590d850c7d4bcca3 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Tue, 12 Nov 2024 21:16:22 -0800 Subject: [PATCH 15/20] old school cool swift if let --- packages/pigeon/example/app/ios/Runner/AppDelegate.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift index 594f6d374e75..9c90b2b0e91b 100644 --- a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift +++ b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift @@ -55,13 +55,13 @@ class EventListener: StreamEventsStreamHandler { } func onIntEvent(event: Int64) { - if let eventSink { + if let eventSink = eventSink { eventSink.success(IntEvent(data: event)) } } func onStringEvent(event: String) { - if let eventSink { + if let eventSink = eventSink { eventSink.success(StringEvent(data: event)) } } From 516e14e8ee6fef0709bf5545901ca8e6afb65a9a Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Tue, 12 Nov 2024 22:16:36 -0800 Subject: [PATCH 16/20] excerpts and podspec attempt --- packages/pigeon/example/README.md | 4 ++-- script/tool/lib/src/podspec_check_command.dart | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/pigeon/example/README.md b/packages/pigeon/example/README.md index 893895420e99..aea633de7965 100644 --- a/packages/pigeon/example/README.md +++ b/packages/pigeon/example/README.md @@ -399,13 +399,13 @@ class EventListener: StreamEventsStreamHandler { } func onIntEvent(event: Int64) { - if let eventSink { + if let eventSink = eventSink { eventSink.success(IntEvent(data: event)) } } func onStringEvent(event: String) { - if let eventSink { + if let eventSink = eventSink { eventSink.success(StringEvent(data: event)) } } diff --git a/script/tool/lib/src/podspec_check_command.dart b/script/tool/lib/src/podspec_check_command.dart index 1b28c9dd3c81..01ff7d04fe21 100644 --- a/script/tool/lib/src/podspec_check_command.dart +++ b/script/tool/lib/src/podspec_check_command.dart @@ -110,7 +110,7 @@ class PodspecCheckCommand extends PackageLoopingCommand { } Future> _podspecsToLint(RepositoryPackage package) async { - // Since the pigeon podspecs require generated files that are not included in git, + // Since the pigeon platform tests podspecs require generated files that are not included in git, // the podspec lint fails. if (package.path.contains('packages/pigeon/platform_tests/')) { return []; @@ -120,7 +120,8 @@ class PodspecCheckCommand extends PackageLoopingCommand { final String filename = entity.basename; return path.extension(filename) == '.podspec' && filename != 'Flutter.podspec' && - filename != 'FlutterMacOS.podspec'; + filename != 'FlutterMacOS.podspec' && + !entity.path.contains('packages/pigeon/platform_tests/'); }).toList(); podspecs.sort((File a, File b) => a.basename.compareTo(b.basename)); From a8b07fe3060ee136c2a6f3e6c9872a1558c305a6 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Fri, 22 Nov 2024 15:47:21 -0800 Subject: [PATCH 17/20] nits and such --- packages/pigeon/example/README.md | 22 +++++++++- .../EventChannelMessages.g.kt | 6 +-- .../pigeon_example_app/MainActivity.kt | 2 + .../example/app/ios/Runner/AppDelegate.swift | 26 ++++++------ .../ios/Runner/EventChannelMessages.g.swift | 6 +-- packages/pigeon/lib/dart_generator.dart | 41 +++++++++++-------- packages/pigeon/lib/generator_tools.dart | 10 +++-- packages/pigeon/lib/kotlin_generator.dart | 6 +-- packages/pigeon/lib/swift_generator.dart | 6 +-- .../pigeon/pigeons/event_channel_tests.dart | 18 ++++---- .../lib/example_app.dart | 6 +-- .../lib/integration_tests.dart | 4 +- .../generated/event_channel_tests.gen.dart | 20 ++++----- .../test_plugin/EventChannelTests.gen.kt | 30 +++++++------- .../com/example/test_plugin/TestPlugin.kt | 2 +- .../ios/Classes/EventChannelTests.gen.swift | 30 +++++++------- .../test_plugin/ios/Classes/TestPlugin.swift | 5 +-- .../macos/Classes/EventChannelTests.gen.swift | 30 +++++++------- .../macos/Classes/TestPlugin.swift | 5 +-- packages/pigeon/tool/shared/generation.dart | 21 ++++++---- packages/pigeon/tool/shared/test_suites.dart | 2 +- packages/pigeon/tool/test.dart | 4 +- 22 files changed, 166 insertions(+), 136 deletions(-) diff --git a/packages/pigeon/example/README.md b/packages/pigeon/example/README.md index aea633de7965..5e6e596c33e6 100644 --- a/packages/pigeon/example/README.md +++ b/packages/pigeon/example/README.md @@ -368,7 +368,6 @@ abstract class PlatformEvent { ### Dart The generated Dart code will include a method that returns a `Stream` when invoked. -This `Stream` can then be used as any other `Stream` would be in Dart. ```dart @@ -389,6 +388,8 @@ Stream getEventStream() async* { ### Swift +Define the stream handler class that will handle the events. + ```swift class EventListener: StreamEventsStreamHandler { @@ -417,8 +418,18 @@ class EventListener: StreamEventsStreamHandler { } ``` +Register the handler with the generated method. + + +```swift +let eventListener = EventListener() +StreamEventsStreamHandler.register(with: controller.binaryMessenger, wrapper: eventListener) +``` + ### Kotlin +Define the stream handler class that will handle the events. + ```kotlin class EventListener : StreamEventsStreamHandler() { @@ -443,6 +454,15 @@ class EventListener : StreamEventsStreamHandler() { } ``` + +Register the handler with the generated method. + + +```kotlin +val eventListener = EventListener() +StreamEventsStreamHandler.register(flutterEngine.dartExecutor.binaryMessenger, eventListener) +``` + ## 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/EventChannelMessages.g.kt b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt index 3a7b4698f93d..5c8300f620ce 100644 --- a/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt +++ b/packages/pigeon/example/app/android/app/src/main/kotlin/dev/flutter/pigeon_example_app/EventChannelMessages.g.kt @@ -118,7 +118,7 @@ abstract class StreamEventsStreamHandler : PigeonEventChannelWrapper(wrapper) + val internalStreamHandler = PigeonStreamHandler(streamHandler) EventChannel(messenger, channelName, EventChannelMessagesPigeonMethodCodec) - .setStreamHandler(streamHandler) + .setStreamHandler(internalStreamHandler) } } } 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 d346167a5a14..9b23c1354f0a 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 @@ -113,8 +113,10 @@ class MainActivity : FlutterActivity() { val api = PigeonApiImplementation() ExampleHostApi.setUp(flutterEngine.dartExecutor.binaryMessenger, api) + // #docregion kotlin-init-event val eventListener = EventListener() StreamEventsStreamHandler.register(flutterEngine.dartExecutor.binaryMessenger, eventListener) + // #enddocregion kotlin-init-event sendEvents(eventListener) } } diff --git a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift index 9c90b2b0e91b..4be4828a323d 100644 --- a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift +++ b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift @@ -74,24 +74,20 @@ class EventListener: StreamEventsStreamHandler { // #enddocregion swift-class-event func sendEvents(_ eventListener: EventListener) { - var timerActive = false var timer: Timer? var count: Int64 = 0 - if !timerActive { - timerActive = true - timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in - DispatchQueue.main.async { - if count >= 100 { - eventListener.onEventsDone() - timer?.invalidate() + timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in + DispatchQueue.main.async { + if count >= 100 { + eventListener.onEventsDone() + timer?.invalidate() + } else { + if (count % 2) == 0 { + eventListener.onIntEvent(event: Int64(count)) } else { - if (count % 2) == 0 { - eventListener.onIntEvent(event: Int64(count)) - } else { - eventListener.onStringEvent(event: String(count)) - } - count += 1 + eventListener.onStringEvent(event: String(count)) } + count += 1 } } } @@ -108,8 +104,10 @@ func sendEvents(_ eventListener: EventListener) { let controller = window?.rootViewController as! FlutterViewController let api = PigeonApiImplementation() ExampleHostApiSetup.setUp(binaryMessenger: controller.binaryMessenger, api: api) + // #docregion swift-init-event let eventListener = EventListener() StreamEventsStreamHandler.register(with: controller.binaryMessenger, wrapper: eventListener) + // #enddocregion swift-init-event sendEvents(eventListener) return super.application(application, didFinishLaunchingWithOptions: launchOptions) diff --git a/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift b/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift index 84ab3ed34ecc..db6355e8a6f6 100644 --- a/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift +++ b/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift @@ -165,15 +165,15 @@ class StreamEventsStreamHandler: PigeonEventChannelWrapper { static func register( with messenger: FlutterBinaryMessenger, instanceName: String = "", - wrapper: StreamEventsStreamHandler + streamHandler: StreamEventsStreamHandler ) { var channelName = "dev.flutter.pigeon.pigeon_example_package.PlatformEvent.streamEvents" if !instanceName.isEmpty { channelName += ".\(instanceName)" } - let streamHandler = PigeonStreamHandler(wrapper: wrapper) + let internalStreamHandler = PigeonStreamHandler(streamHandler: wrapper) let channel = FlutterEventChannel( name: channelName, binaryMessenger: messenger, codec: eventChannelMessagesPigeonMethodCodec) - channel.setStreamHandler(streamHandler) + channel.setStreamHandler(internalStreamHandler) } } diff --git a/packages/pigeon/lib/dart_generator.dart b/packages/pigeon/lib/dart_generator.dart index 7c81f19d7124..abacf3dea31c 100644 --- a/packages/pigeon/lib/dart_generator.dart +++ b/packages/pigeon/lib/dart_generator.dart @@ -294,11 +294,12 @@ class DartGenerator extends StructuredGenerator { Indent indent, { required String dartPackageName, }) { - void writeEncodeLogic(EnumeratedType customType, int offset) { + void writeEncodeLogic( + EnumeratedType customType, int nonSerializedClassCount) { indent.writeScoped('else if (value is ${customType.name}) {', '}', () { - if (customType.enumeration - offset < maximumCodecFieldKey) { - indent - .writeln('buffer.putUint8(${customType.enumeration - offset});'); + if (customType.offset(nonSerializedClassCount) < maximumCodecFieldKey) { + indent.writeln( + 'buffer.putUint8(${customType.offset(nonSerializedClassCount)});'); if (customType.type == CustomTypes.customClass) { indent.writeln('writeValue(buffer, value.encode());'); } else if (customType.type == CustomTypes.customEnum) { @@ -309,18 +310,20 @@ class DartGenerator extends StructuredGenerator { ? '.encode()' : '.index'; indent.writeln( - 'final $_overflowClassName wrap = $_overflowClassName(type: ${customType.enumeration - offset - maximumCodecFieldKey}, wrapped: value$encodeString);'); + 'final $_overflowClassName wrap = $_overflowClassName(type: ${customType.offset(nonSerializedClassCount) - maximumCodecFieldKey}, wrapped: value$encodeString);'); indent.writeln('buffer.putUint8($maximumCodecFieldKey);'); indent.writeln('writeValue(buffer, wrap.encode());'); } }, addTrailingNewline: false); } - void writeDecodeLogic(EnumeratedType customType, int offset) { - indent.writeln('case ${customType.enumeration - offset}: '); + void writeDecodeLogic( + EnumeratedType customType, int nonSerializedClassCount) { + indent.writeln('case ${customType.offset(nonSerializedClassCount)}: '); indent.nest(1, () { if (customType.type == CustomTypes.customClass) { - if (customType.enumeration - offset == maximumCodecFieldKey) { + if (customType.offset(nonSerializedClassCount) == + maximumCodecFieldKey) { indent.writeln( 'final ${customType.name} wrapper = ${customType.name}.decode(readValue(buffer)!);'); indent.writeln('return wrapper.unwrap();'); @@ -356,14 +359,14 @@ class DartGenerator extends StructuredGenerator { indent.writeln('buffer.putUint8(4);'); indent.writeln('buffer.putInt64(value);'); }, addTrailingNewline: false); - int offset = 0; + int nonSerializedClassCount = 0; enumerate(enumeratedTypes, (int index, final EnumeratedType customType) { if (customType.associatedClass?.isSealed ?? false) { - offset += 1; + nonSerializedClassCount += 1; return; } - writeEncodeLogic(customType, offset); + writeEncodeLogic(customType, nonSerializedClassCount); }); indent.addScoped(' else {', '}', () { indent.writeln('super.writeValue(buffer, value);'); @@ -375,12 +378,13 @@ class DartGenerator extends StructuredGenerator { indent.addScoped('{', '}', () { indent.write('switch (type) '); indent.addScoped('{', '}', () { - int offset = 0; + int nonSerializedClassCount = 0; for (final EnumeratedType customType in enumeratedTypes) { if (customType.associatedClass?.isSealed ?? false) { - offset++; - } else if (customType.enumeration - offset < maximumCodecFieldKey) { - writeDecodeLogic(customType, offset); + nonSerializedClassCount++; + } else if (customType.offset(nonSerializedClassCount) < + maximumCodecFieldKey) { + writeDecodeLogic(customType, nonSerializedClassCount); } } if (root.requiresOverflowClass) { @@ -1054,13 +1058,14 @@ if (wrapped == null) { } '''); indent.writeScoped('switch (type) {', '}', () { - int offset = 0; + int nonSerializedClassCount = 0; for (int i = totalCustomCodecKeysAllowed; i < types.length; i++) { if (types[i].associatedClass?.isSealed ?? false) { - offset++; + nonSerializedClassCount++; } else { indent.writeScoped( - 'case ${i - offset - totalCustomCodecKeysAllowed}:', '', () { + 'case ${i - nonSerializedClassCount - totalCustomCodecKeysAllowed}:', + '', () { if (types[i].type == CustomTypes.customClass) { indent.writeln('return ${types[i].name}.decode(wrapped!);'); } else if (types[i].type == CustomTypes.customEnum) { diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart index 66364c1d1819..4afdc4b1be70 100644 --- a/packages/pigeon/lib/generator_tools.dart +++ b/packages/pigeon/lib/generator_tools.dart @@ -432,6 +432,9 @@ class EnumeratedType { /// The associated Enum that is represented by the [EnumeratedType]. final Enum? associatedEnum; + + /// Returns the offset of the enumeration. + int offset(int offset) => enumeration - offset; } /// Supported basic datatypes. @@ -811,11 +814,12 @@ String toLowerCamelCase(String text) { final RegExp separatorPattern = RegExp(r'[ _-]'); bool firstWord = true; return text.split(separatorPattern).map((String word) { + if (word.isEmpty) { + return ''; + } if (firstWord) { firstWord = false; - return word.isEmpty - ? '' - : word.substring(0, 1).toLowerCase() + word.substring(1); + return word.substring(0, 1).toLowerCase() + word.substring(1); } return word.isEmpty ? '' diff --git a/packages/pigeon/lib/kotlin_generator.dart b/packages/pigeon/lib/kotlin_generator.dart index 7d47df9f89b5..f0a185cdcc72 100644 --- a/packages/pigeon/lib/kotlin_generator.dart +++ b/packages/pigeon/lib/kotlin_generator.dart @@ -1060,13 +1060,13 @@ if (wrapped == null) { indent.format(''' abstract class ${toUpperCamelCase(func.name)}StreamHandler : PigeonEventChannelWrapper<${_kotlinTypeForDartType(func.returnType)}> { companion object { - fun register(messenger: BinaryMessenger, wrapper: ${toUpperCamelCase(func.name)}StreamHandler, instanceName: String = "") { + fun register(messenger: BinaryMessenger, streamHandler: ${toUpperCamelCase(func.name)}StreamHandler, instanceName: String = "") { var channelName: String = "${makeChannelName(api, func, dartPackageName)}" if (instanceName.isNotEmpty()) { channelName += ".\$instanceName" } - val streamHandler = PigeonStreamHandler<${_kotlinTypeForDartType(func.returnType)}>(wrapper) - EventChannel(messenger, channelName, ${generatorOptions.fileSpecificClassNameComponent}$_pigeonMethodChannelCodec).setStreamHandler(streamHandler) + val internalStreamHandler = PigeonStreamHandler<${_kotlinTypeForDartType(func.returnType)}>(streamHandler) + EventChannel(messenger, channelName, ${generatorOptions.fileSpecificClassNameComponent}$_pigeonMethodChannelCodec).setStreamHandler(internalStreamHandler) } } } diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index f9abaaebcd16..e188719f9dd3 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -1405,14 +1405,14 @@ private func nilOrValue(_ value: Any?) -> T? { class ${toUpperCamelCase(func.name)}StreamHandler: PigeonEventChannelWrapper<${_swiftTypeForDartType(func.returnType)}> { static func register(with messenger: FlutterBinaryMessenger, instanceName: String = "", - wrapper: ${toUpperCamelCase(func.name)}StreamHandler) { + streamHandler: ${toUpperCamelCase(func.name)}StreamHandler) { var channelName = "${makeChannelName(api, func, dartPackageName)}" if !instanceName.isEmpty { channelName += ".\\(instanceName)" } - let streamHandler = PigeonStreamHandler<${_swiftTypeForDartType(func.returnType)}>(wrapper: wrapper) + let internalStreamHandler = PigeonStreamHandler<${_swiftTypeForDartType(func.returnType)}>(streamHandler: wrapper) let channel = FlutterEventChannel(name: channelName, binaryMessenger: messenger, codec: ${_getMethodCodecVarName(generatorOptions)}) - channel.setStreamHandler(streamHandler) + channel.setStreamHandler(internalStreamHandler) } } '''); diff --git a/packages/pigeon/pigeons/event_channel_tests.dart b/packages/pigeon/pigeons/event_channel_tests.dart index afa56cd7d9be..97602221d058 100644 --- a/packages/pigeon/pigeons/event_channel_tests.dart +++ b/packages/pigeon/pigeons/event_channel_tests.dart @@ -100,39 +100,39 @@ class EventAllNullableTypes { Map? recursiveClassMap; } -sealed class EventChannelDataBase {} +sealed class PlatformEvent {} -class IntEvent extends EventChannelDataBase { +class IntEvent extends PlatformEvent { IntEvent(this.value); final int value; } -class StringEvent extends EventChannelDataBase { +class StringEvent extends PlatformEvent { StringEvent(this.value); final String value; } -class BoolEvent extends EventChannelDataBase { +class BoolEvent extends PlatformEvent { BoolEvent(this.value); final bool value; } -class DoubleEvent extends EventChannelDataBase { +class DoubleEvent extends PlatformEvent { DoubleEvent(this.value); final double value; } -class ObjectsEvent extends EventChannelDataBase { +class ObjectsEvent extends PlatformEvent { ObjectsEvent(this.value); final Object value; } -class EnumEvent extends EventChannelDataBase { +class EnumEvent extends PlatformEvent { EnumEvent(this.value); final EventEnum value; } -class ClassEvent extends EventChannelDataBase { +class ClassEvent extends PlatformEvent { ClassEvent(this.value); final EventAllNullableTypes value; } @@ -140,5 +140,5 @@ class ClassEvent extends EventChannelDataBase { @EventChannelApi() abstract class EventChannelCoreApi { int streamInts(); - EventChannelDataBase streamEvents(); + PlatformEvent streamEvents(); } diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/example_app.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/example_app.dart index 0c7722e1a01f..ddc3fdcd0296 100644 --- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/example_app.dart +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/example_app.dart @@ -65,11 +65,11 @@ class _ExampleAppState extends State { const Text( 'Counter :', ), - StreamBuilder( + StreamBuilder( stream: streamEvents(), builder: (BuildContext context, - AsyncSnapshot snapshot) { - final EventChannelDataBase? data = snapshot.data; + AsyncSnapshot snapshot) { + final PlatformEvent? data = snapshot.data; if (snapshot.hasData) { String newString = ''; switch (data) { diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart index 76d6376b65ac..17faa0421ae2 100644 --- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart @@ -2860,8 +2860,8 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) { testWidgets('event channel handles extended sealed classes', (_) async { final Completer completer = Completer(); int count = 0; - final Stream events = streamEvents(); - events.listen((EventChannelDataBase event) { + final Stream events = streamEvents(); + events.listen((PlatformEvent event) { switch (event) { case IntEvent(): expect(event.value, 1); diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_tests.gen.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_tests.gen.dart index 4e9f4fd75b56..368a3408d5f4 100644 --- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_tests.gen.dart +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/src/generated/event_channel_tests.gen.dart @@ -203,9 +203,9 @@ class EventAllNullableTypes { } } -sealed class EventChannelDataBase {} +sealed class PlatformEvent {} -class IntEvent extends EventChannelDataBase { +class IntEvent extends PlatformEvent { IntEvent({ required this.value, }); @@ -226,7 +226,7 @@ class IntEvent extends EventChannelDataBase { } } -class StringEvent extends EventChannelDataBase { +class StringEvent extends PlatformEvent { StringEvent({ required this.value, }); @@ -247,7 +247,7 @@ class StringEvent extends EventChannelDataBase { } } -class BoolEvent extends EventChannelDataBase { +class BoolEvent extends PlatformEvent { BoolEvent({ required this.value, }); @@ -268,7 +268,7 @@ class BoolEvent extends EventChannelDataBase { } } -class DoubleEvent extends EventChannelDataBase { +class DoubleEvent extends PlatformEvent { DoubleEvent({ required this.value, }); @@ -289,7 +289,7 @@ class DoubleEvent extends EventChannelDataBase { } } -class ObjectsEvent extends EventChannelDataBase { +class ObjectsEvent extends PlatformEvent { ObjectsEvent({ required this.value, }); @@ -310,7 +310,7 @@ class ObjectsEvent extends EventChannelDataBase { } } -class EnumEvent extends EventChannelDataBase { +class EnumEvent extends PlatformEvent { EnumEvent({ required this.value, }); @@ -331,7 +331,7 @@ class EnumEvent extends EventChannelDataBase { } } -class ClassEvent extends EventChannelDataBase { +class ClassEvent extends PlatformEvent { ClassEvent({ required this.value, }); @@ -440,7 +440,7 @@ Stream streamInts({String instanceName = ''}) { }); } -Stream streamEvents({String instanceName = ''}) { +Stream streamEvents({String instanceName = ''}) { if (instanceName.isNotEmpty) { instanceName = '.$instanceName'; } @@ -448,6 +448,6 @@ Stream streamEvents({String instanceName = ''}) { 'dev.flutter.pigeon.pigeon_integration_tests.EventChannelCoreApi.streamEvents', pigeonMethodCodec); return streamEventsChannel.receiveBroadcastStream().map((dynamic event) { - return event as EventChannelDataBase; + return event as PlatformEvent; }); } diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt index 0aa9876468ca..4f7c7efadb6c 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/EventChannelTests.gen.kt @@ -199,9 +199,9 @@ data class EventAllNullableTypes( * Generated class from Pigeon that represents data sent in messages. This class should not be * extended by any user class outside of the generated file. */ -sealed class EventChannelDataBase +sealed class PlatformEvent /** Generated class from Pigeon that represents data sent in messages. */ -data class IntEvent(val value: Long) : EventChannelDataBase() { +data class IntEvent(val value: Long) : PlatformEvent() { companion object { fun fromList(pigeonVar_list: List): IntEvent { val value = pigeonVar_list[0] as Long @@ -217,7 +217,7 @@ data class IntEvent(val value: Long) : EventChannelDataBase() { } /** Generated class from Pigeon that represents data sent in messages. */ -data class StringEvent(val value: String) : EventChannelDataBase() { +data class StringEvent(val value: String) : PlatformEvent() { companion object { fun fromList(pigeonVar_list: List): StringEvent { val value = pigeonVar_list[0] as String @@ -233,7 +233,7 @@ data class StringEvent(val value: String) : EventChannelDataBase() { } /** Generated class from Pigeon that represents data sent in messages. */ -data class BoolEvent(val value: Boolean) : EventChannelDataBase() { +data class BoolEvent(val value: Boolean) : PlatformEvent() { companion object { fun fromList(pigeonVar_list: List): BoolEvent { val value = pigeonVar_list[0] as Boolean @@ -249,7 +249,7 @@ data class BoolEvent(val value: Boolean) : EventChannelDataBase() { } /** Generated class from Pigeon that represents data sent in messages. */ -data class DoubleEvent(val value: Double) : EventChannelDataBase() { +data class DoubleEvent(val value: Double) : PlatformEvent() { companion object { fun fromList(pigeonVar_list: List): DoubleEvent { val value = pigeonVar_list[0] as Double @@ -265,7 +265,7 @@ data class DoubleEvent(val value: Double) : EventChannelDataBase() { } /** Generated class from Pigeon that represents data sent in messages. */ -data class ObjectsEvent(val value: Any) : EventChannelDataBase() { +data class ObjectsEvent(val value: Any) : PlatformEvent() { companion object { fun fromList(pigeonVar_list: List): ObjectsEvent { val value = pigeonVar_list[0] as Any @@ -281,7 +281,7 @@ data class ObjectsEvent(val value: Any) : EventChannelDataBase() { } /** Generated class from Pigeon that represents data sent in messages. */ -data class EnumEvent(val value: EventEnum) : EventChannelDataBase() { +data class EnumEvent(val value: EventEnum) : PlatformEvent() { companion object { fun fromList(pigeonVar_list: List): EnumEvent { val value = pigeonVar_list[0] as EventEnum @@ -297,7 +297,7 @@ data class EnumEvent(val value: EventEnum) : EventChannelDataBase() { } /** Generated class from Pigeon that represents data sent in messages. */ -data class ClassEvent(val value: EventAllNullableTypes) : EventChannelDataBase() { +data class ClassEvent(val value: EventAllNullableTypes) : PlatformEvent() { companion object { fun fromList(pigeonVar_list: List): ClassEvent { val value = pigeonVar_list[0] as EventAllNullableTypes @@ -437,7 +437,7 @@ abstract class StreamIntsStreamHandler : PigeonEventChannelWrapper { companion object { fun register( messenger: BinaryMessenger, - wrapper: StreamIntsStreamHandler, + streamHandler: StreamIntsStreamHandler, instanceName: String = "" ) { var channelName: String = @@ -445,18 +445,18 @@ abstract class StreamIntsStreamHandler : PigeonEventChannelWrapper { if (instanceName.isNotEmpty()) { channelName += ".$instanceName" } - val streamHandler = PigeonStreamHandler(wrapper) + val internalStreamHandler = PigeonStreamHandler(streamHandler) EventChannel(messenger, channelName, EventChannelTestsPigeonMethodCodec) - .setStreamHandler(streamHandler) + .setStreamHandler(internalStreamHandler) } } } -abstract class StreamEventsStreamHandler : PigeonEventChannelWrapper { +abstract class StreamEventsStreamHandler : PigeonEventChannelWrapper { companion object { fun register( messenger: BinaryMessenger, - wrapper: StreamEventsStreamHandler, + streamHandler: StreamEventsStreamHandler, instanceName: String = "" ) { var channelName: String = @@ -464,9 +464,9 @@ abstract class StreamEventsStreamHandler : PigeonEventChannelWrapper(wrapper) + val internalStreamHandler = PigeonStreamHandler(streamHandler) EventChannel(messenger, channelName, EventChannelTestsPigeonMethodCodec) - .setStreamHandler(streamHandler) + .setStreamHandler(internalStreamHandler) } } } diff --git a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt index d9f149e37a42..f175076abcda 100644 --- a/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt +++ b/packages/pigeon/platform_tests/test_plugin/android/src/main/kotlin/com/example/test_plugin/TestPlugin.kt @@ -908,7 +908,7 @@ object SendClass : StreamEventsStreamHandler() { EnumEvent(EventEnum.FORTY_TWO), ClassEvent(EventAllNullableTypes(aNullableInt = 0))) - override fun onListen(p0: Any?, sink: PigeonEventSink) { + override fun onListen(p0: Any?, sink: PigeonEventSink) { var count: Int = 0 val r: Runnable = object : Runnable { diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift index c2f8344c2044..581253256bc4 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift @@ -262,12 +262,12 @@ class EventAllNullableTypes { /// Generated class from Pigeon that represents data sent in messages. /// This class should not be extended by any user class outside of the generated file. -protocol EventChannelDataBase { +protocol PlatformEvent { } /// Generated class from Pigeon that represents data sent in messages. -struct IntEvent: EventChannelDataBase { +struct IntEvent: PlatformEvent { var value: Int64 // swift-format-ignore: AlwaysUseLowerCamelCase @@ -286,7 +286,7 @@ struct IntEvent: EventChannelDataBase { } /// Generated class from Pigeon that represents data sent in messages. -struct StringEvent: EventChannelDataBase { +struct StringEvent: PlatformEvent { var value: String // swift-format-ignore: AlwaysUseLowerCamelCase @@ -305,7 +305,7 @@ struct StringEvent: EventChannelDataBase { } /// Generated class from Pigeon that represents data sent in messages. -struct BoolEvent: EventChannelDataBase { +struct BoolEvent: PlatformEvent { var value: Bool // swift-format-ignore: AlwaysUseLowerCamelCase @@ -324,7 +324,7 @@ struct BoolEvent: EventChannelDataBase { } /// Generated class from Pigeon that represents data sent in messages. -struct DoubleEvent: EventChannelDataBase { +struct DoubleEvent: PlatformEvent { var value: Double // swift-format-ignore: AlwaysUseLowerCamelCase @@ -343,7 +343,7 @@ struct DoubleEvent: EventChannelDataBase { } /// Generated class from Pigeon that represents data sent in messages. -struct ObjectsEvent: EventChannelDataBase { +struct ObjectsEvent: PlatformEvent { var value: Any // swift-format-ignore: AlwaysUseLowerCamelCase @@ -362,7 +362,7 @@ struct ObjectsEvent: EventChannelDataBase { } /// Generated class from Pigeon that represents data sent in messages. -struct EnumEvent: EventChannelDataBase { +struct EnumEvent: PlatformEvent { var value: EventEnum // swift-format-ignore: AlwaysUseLowerCamelCase @@ -381,7 +381,7 @@ struct EnumEvent: EventChannelDataBase { } /// Generated class from Pigeon that represents data sent in messages. -struct ClassEvent: EventChannelDataBase { +struct ClassEvent: PlatformEvent { var value: EventAllNullableTypes // swift-format-ignore: AlwaysUseLowerCamelCase @@ -545,32 +545,32 @@ class StreamIntsStreamHandler: PigeonEventChannelWrapper { static func register( with messenger: FlutterBinaryMessenger, instanceName: String = "", - wrapper: StreamIntsStreamHandler + streamHandler: StreamIntsStreamHandler ) { var channelName = "dev.flutter.pigeon.pigeon_integration_tests.EventChannelCoreApi.streamInts" if !instanceName.isEmpty { channelName += ".\(instanceName)" } - let streamHandler = PigeonStreamHandler(wrapper: wrapper) + let internalStreamHandler = PigeonStreamHandler(streamHandler: wrapper) let channel = FlutterEventChannel( name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) - channel.setStreamHandler(streamHandler) + channel.setStreamHandler(internalStreamHandler) } } -class StreamEventsStreamHandler: PigeonEventChannelWrapper { +class StreamEventsStreamHandler: PigeonEventChannelWrapper { static func register( with messenger: FlutterBinaryMessenger, instanceName: String = "", - wrapper: StreamEventsStreamHandler + streamHandler: StreamEventsStreamHandler ) { var channelName = "dev.flutter.pigeon.pigeon_integration_tests.EventChannelCoreApi.streamEvents" if !instanceName.isEmpty { channelName += ".\(instanceName)" } - let streamHandler = PigeonStreamHandler(wrapper: wrapper) + let internalStreamHandler = PigeonStreamHandler(streamHandler: wrapper) let channel = FlutterEventChannel( name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) - channel.setStreamHandler(streamHandler) + channel.setStreamHandler(internalStreamHandler) } } diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift index 0ce3af1f1091..5c15ff7f77d8 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift @@ -1242,7 +1242,7 @@ class SendInts: StreamIntsStreamHandler { class SendEvents: StreamEventsStreamHandler { var timerActive = false var timer: Timer? - var eventList: [EventChannelDataBase] = + var eventList: [PlatformEvent] = [ IntEvent(value: 1), StringEvent(value: "string"), @@ -1253,8 +1253,7 @@ class SendEvents: StreamEventsStreamHandler { ClassEvent(value: EventAllNullableTypes(aNullableInt: 0)), ] - override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) - { + override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) { var count = 0 if !timerActive { timerActive = true diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift index c2f8344c2044..581253256bc4 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift @@ -262,12 +262,12 @@ class EventAllNullableTypes { /// Generated class from Pigeon that represents data sent in messages. /// This class should not be extended by any user class outside of the generated file. -protocol EventChannelDataBase { +protocol PlatformEvent { } /// Generated class from Pigeon that represents data sent in messages. -struct IntEvent: EventChannelDataBase { +struct IntEvent: PlatformEvent { var value: Int64 // swift-format-ignore: AlwaysUseLowerCamelCase @@ -286,7 +286,7 @@ struct IntEvent: EventChannelDataBase { } /// Generated class from Pigeon that represents data sent in messages. -struct StringEvent: EventChannelDataBase { +struct StringEvent: PlatformEvent { var value: String // swift-format-ignore: AlwaysUseLowerCamelCase @@ -305,7 +305,7 @@ struct StringEvent: EventChannelDataBase { } /// Generated class from Pigeon that represents data sent in messages. -struct BoolEvent: EventChannelDataBase { +struct BoolEvent: PlatformEvent { var value: Bool // swift-format-ignore: AlwaysUseLowerCamelCase @@ -324,7 +324,7 @@ struct BoolEvent: EventChannelDataBase { } /// Generated class from Pigeon that represents data sent in messages. -struct DoubleEvent: EventChannelDataBase { +struct DoubleEvent: PlatformEvent { var value: Double // swift-format-ignore: AlwaysUseLowerCamelCase @@ -343,7 +343,7 @@ struct DoubleEvent: EventChannelDataBase { } /// Generated class from Pigeon that represents data sent in messages. -struct ObjectsEvent: EventChannelDataBase { +struct ObjectsEvent: PlatformEvent { var value: Any // swift-format-ignore: AlwaysUseLowerCamelCase @@ -362,7 +362,7 @@ struct ObjectsEvent: EventChannelDataBase { } /// Generated class from Pigeon that represents data sent in messages. -struct EnumEvent: EventChannelDataBase { +struct EnumEvent: PlatformEvent { var value: EventEnum // swift-format-ignore: AlwaysUseLowerCamelCase @@ -381,7 +381,7 @@ struct EnumEvent: EventChannelDataBase { } /// Generated class from Pigeon that represents data sent in messages. -struct ClassEvent: EventChannelDataBase { +struct ClassEvent: PlatformEvent { var value: EventAllNullableTypes // swift-format-ignore: AlwaysUseLowerCamelCase @@ -545,32 +545,32 @@ class StreamIntsStreamHandler: PigeonEventChannelWrapper { static func register( with messenger: FlutterBinaryMessenger, instanceName: String = "", - wrapper: StreamIntsStreamHandler + streamHandler: StreamIntsStreamHandler ) { var channelName = "dev.flutter.pigeon.pigeon_integration_tests.EventChannelCoreApi.streamInts" if !instanceName.isEmpty { channelName += ".\(instanceName)" } - let streamHandler = PigeonStreamHandler(wrapper: wrapper) + let internalStreamHandler = PigeonStreamHandler(streamHandler: wrapper) let channel = FlutterEventChannel( name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) - channel.setStreamHandler(streamHandler) + channel.setStreamHandler(internalStreamHandler) } } -class StreamEventsStreamHandler: PigeonEventChannelWrapper { +class StreamEventsStreamHandler: PigeonEventChannelWrapper { static func register( with messenger: FlutterBinaryMessenger, instanceName: String = "", - wrapper: StreamEventsStreamHandler + streamHandler: StreamEventsStreamHandler ) { var channelName = "dev.flutter.pigeon.pigeon_integration_tests.EventChannelCoreApi.streamEvents" if !instanceName.isEmpty { channelName += ".\(instanceName)" } - let streamHandler = PigeonStreamHandler(wrapper: wrapper) + let internalStreamHandler = PigeonStreamHandler(streamHandler: wrapper) let channel = FlutterEventChannel( name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) - channel.setStreamHandler(streamHandler) + channel.setStreamHandler(internalStreamHandler) } } diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift index 360e52e991b3..3d5dc4c0d93a 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift @@ -1240,7 +1240,7 @@ class SendInts: StreamIntsStreamHandler { class SendEvents: StreamEventsStreamHandler { var timerActive = false var timer: Timer? - var eventList: [EventChannelDataBase] = + var eventList: [PlatformEvent] = [ IntEvent(value: 1), StringEvent(value: "string"), @@ -1251,8 +1251,7 @@ class SendEvents: StreamEventsStreamHandler { ClassEvent(value: EventAllNullableTypes(aNullableInt: 0)), ] - override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) - { + override func onListen(withArguments arguments: Any?, sink: PigeonEventSink) { var count = 0 if !timerActive { timerActive = true diff --git a/packages/pigeon/tool/shared/generation.dart b/packages/pigeon/tool/shared/generation.dart index 01b9115ad857..a553c08425a1 100644 --- a/packages/pigeon/tool/shared/generation.dart +++ b/packages/pigeon/tool/shared/generation.dart @@ -20,19 +20,22 @@ enum GeneratorLanguage { swift, } -const Set allButSwiftAndDart = { - GeneratorLanguage.cpp, - GeneratorLanguage.gobject, - GeneratorLanguage.java, - GeneratorLanguage.objc, -}; - // A map of pigeons/ files to the languages that they can't yet be generated // for due to limitations of that generator. const Map> _unsupportedFiles = >{ - 'event_channel_tests': allButSwiftAndDart, - 'proxy_api_tests': allButSwiftAndDart, + 'event_channel_tests': { + GeneratorLanguage.cpp, + GeneratorLanguage.gobject, + GeneratorLanguage.java, + GeneratorLanguage.objc, + }, + 'proxy_api_tests': { + GeneratorLanguage.cpp, + GeneratorLanguage.gobject, + GeneratorLanguage.java, + GeneratorLanguage.objc, + }, }; String _snakeToPascalCase(String snake) { diff --git a/packages/pigeon/tool/shared/test_suites.dart b/packages/pigeon/tool/shared/test_suites.dart index 1c9dc552d617..c37c388d03f7 100644 --- a/packages/pigeon/tool/shared/test_suites.dart +++ b/packages/pigeon/tool/shared/test_suites.dart @@ -483,7 +483,7 @@ Future _runCommandLineTests({bool ciMode = false}) async { int exitCode = 0; for (final List arguments in testArguments) { - print('Testing Dart $pigeonScript ${arguments.join(', ')}'); + print('Testing dart $pigeonScript ${arguments.join(', ')}'); exitCode = await runProcess('dart', [snapshot, ...arguments], streamOutput: false, logFailure: true); if (exitCode != 0) { diff --git a/packages/pigeon/tool/test.dart b/packages/pigeon/tool/test.dart index cc23846427b5..07c7dfdf61b0 100644 --- a/packages/pigeon/tool/test.dart +++ b/packages/pigeon/tool/test.dart @@ -7,7 +7,7 @@ //////////////////////////////////////////////////////////////////////////////// /// Script for executing the Pigeon tests /// -/// usage: Dart run tool/test.dart +/// usage: dart run tool/test.dart //////////////////////////////////////////////////////////////////////////////// library; @@ -56,7 +56,7 @@ Future main(List args) async { } else if (argResults.wasParsed('help')) { print(''' Pigeon run_tests -usage: Dart run tool/test.dart [-l | -t ] +usage: dart run tool/test.dart [-l | -t ] ${parser.usage}'''); exit(0); From 2b6f6199e13bafd171e41e3f535e1bb26fcf2601 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Tue, 26 Nov 2024 12:04:41 -0800 Subject: [PATCH 18/20] change examples to not crash on windows/linux --- packages/pigeon/example/app/lib/main.dart | 28 ++++++----- .../lib/example_app.dart | 49 +------------------ .../test_plugin/ios/Classes/TestPlugin.swift | 4 +- .../macos/Classes/TestPlugin.swift | 4 +- 4 files changed, 22 insertions(+), 63 deletions(-) diff --git a/packages/pigeon/example/app/lib/main.dart b/packages/pigeon/example/app/lib/main.dart index f7048f7b98e4..74e3df812cb2 100644 --- a/packages/pigeon/example/app/lib/main.dart +++ b/packages/pigeon/example/app/lib/main.dart @@ -4,6 +4,8 @@ // ignore_for_file: public_member_api_docs +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -132,17 +134,21 @@ class _MyHomePageState extends State { _hostCallResult ?? 'Waiting for host language...', ), if (_hostCallResult == null) const CircularProgressIndicator(), - StreamBuilder( - stream: getEventStream(), - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData) { - allData += snapshot.data ?? ''; - return Text(allData); - } else { - return const CircularProgressIndicator(); - } - }, - ) + if (Platform.isAndroid || Platform.isIOS || Platform.isMacOS) + StreamBuilder( + stream: getEventStream(), + builder: + (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasData) { + allData += snapshot.data ?? ''; + return Text(allData); + } else { + return const CircularProgressIndicator(); + } + }, + ) + else + const Text('event channels are not supported on this platform') ], ), ), diff --git a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/example_app.dart b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/example_app.dart index ddc3fdcd0296..c1a0885e2df1 100644 --- a/packages/pigeon/platform_tests/shared_test_plugin_code/lib/example_app.dart +++ b/packages/pigeon/platform_tests/shared_test_plugin_code/lib/example_app.dart @@ -59,54 +59,7 @@ class _ExampleAppState extends State { title: const Text('Pigeon integration tests'), ), body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'Counter :', - ), - StreamBuilder( - stream: streamEvents(), - builder: (BuildContext context, - AsyncSnapshot snapshot) { - final PlatformEvent? data = snapshot.data; - if (snapshot.hasData) { - String newString = ''; - switch (data) { - case null: - newString = 'null, '; - case IntEvent(): - final IntEvent intData = data; - newString = '${intData.value}, '; - case StringEvent(): - final StringEvent stringData = data; - newString = '${stringData.value}, '; - case BoolEvent(): - final BoolEvent boolData = data; - newString = '${boolData.value}, '; - case DoubleEvent(): - final DoubleEvent doubleData = data; - newString = '${doubleData.value}, '; - case ObjectsEvent(): - final ObjectsEvent objectsData = data; - newString = '${objectsData.value}, '; - case EnumEvent(): - final EnumEvent enumData = data; - newString = '${enumData.value}, '; - case ClassEvent(): - final ClassEvent classData = data; - newString = '${classData.value}, '; - } - - allData += newString; - return Text(allData); - } else { - return const CircularProgressIndicator(); - } - }, - ), - ], - ), + child: Text(status), ), ), ); diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift index 5c15ff7f77d8..4fd9a799ed05 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift @@ -29,8 +29,8 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { flutterSmallApiTwo = FlutterSmallApi( binaryMessenger: binaryMessenger, messageChannelSuffix: "suffixTwo") - StreamIntsStreamHandler.register(with: binaryMessenger, wrapper: SendInts()) - StreamEventsStreamHandler.register(with: binaryMessenger, wrapper: SendEvents()) + StreamIntsStreamHandler.register(with: binaryMessenger, streamHandler: SendInts()) + StreamEventsStreamHandler.register(with: binaryMessenger, streamHandler: SendEvents()) proxyApiRegistrar = ProxyApiTestsPigeonProxyApiRegistrar( binaryMessenger: binaryMessenger, apiDelegate: ProxyApiDelegate()) proxyApiRegistrar!.setUp() diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift index 3d5dc4c0d93a..d88bcb8f2545 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift @@ -31,8 +31,8 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { binaryMessenger: binaryMessenger, apiDelegate: ProxyApiDelegate()) proxyApiRegistrar!.setUp() - StreamIntsStreamHandler.register(with: binaryMessenger, wrapper: SendInts()) - StreamEventsStreamHandler.register(with: binaryMessenger, wrapper: SendEvents()) + StreamIntsStreamHandler.register(with: binaryMessenger, streamHandler: SendInts()) + StreamEventsStreamHandler.register(with: binaryMessenger, streamHandler: SendEvents()) } public func detachFromEngine(for registrar: FlutterPluginRegistrar) { From 8e6f4cbe3a3b8f7edac384a6ffcef9067313122c Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Tue, 26 Nov 2024 12:29:24 -0800 Subject: [PATCH 19/20] remove final wrapper --- .../example/app/ios/Runner/EventChannelMessages.g.swift | 2 +- packages/pigeon/lib/swift_generator.dart | 2 +- .../test_plugin/ios/Classes/EventChannelTests.gen.swift | 4 ++-- .../test_plugin/macos/Classes/EventChannelTests.gen.swift | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift b/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift index db6355e8a6f6..57703a41b660 100644 --- a/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift +++ b/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift @@ -171,7 +171,7 @@ class StreamEventsStreamHandler: PigeonEventChannelWrapper { if !instanceName.isEmpty { channelName += ".\(instanceName)" } - let internalStreamHandler = PigeonStreamHandler(streamHandler: wrapper) + let internalStreamHandler = PigeonStreamHandler(streamHandler: streamHandler) let channel = FlutterEventChannel( name: channelName, binaryMessenger: messenger, codec: eventChannelMessagesPigeonMethodCodec) channel.setStreamHandler(internalStreamHandler) diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index e188719f9dd3..62fa6fa4d895 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -1410,7 +1410,7 @@ private func nilOrValue(_ value: Any?) -> T? { if !instanceName.isEmpty { channelName += ".\\(instanceName)" } - let internalStreamHandler = PigeonStreamHandler<${_swiftTypeForDartType(func.returnType)}>(streamHandler: wrapper) + let internalStreamHandler = PigeonStreamHandler<${_swiftTypeForDartType(func.returnType)}>(streamHandler: streamHandler) let channel = FlutterEventChannel(name: channelName, binaryMessenger: messenger, codec: ${_getMethodCodecVarName(generatorOptions)}) channel.setStreamHandler(internalStreamHandler) } diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift index 581253256bc4..c5f04e0d94c4 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift @@ -551,7 +551,7 @@ class StreamIntsStreamHandler: PigeonEventChannelWrapper { if !instanceName.isEmpty { channelName += ".\(instanceName)" } - let internalStreamHandler = PigeonStreamHandler(streamHandler: wrapper) + let internalStreamHandler = PigeonStreamHandler(streamHandler: streamHandler) let channel = FlutterEventChannel( name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) channel.setStreamHandler(internalStreamHandler) @@ -568,7 +568,7 @@ class StreamEventsStreamHandler: PigeonEventChannelWrapper { if !instanceName.isEmpty { channelName += ".\(instanceName)" } - let internalStreamHandler = PigeonStreamHandler(streamHandler: wrapper) + let internalStreamHandler = PigeonStreamHandler(streamHandler: streamHandler) let channel = FlutterEventChannel( name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) channel.setStreamHandler(internalStreamHandler) diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift index 581253256bc4..c5f04e0d94c4 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift @@ -551,7 +551,7 @@ class StreamIntsStreamHandler: PigeonEventChannelWrapper { if !instanceName.isEmpty { channelName += ".\(instanceName)" } - let internalStreamHandler = PigeonStreamHandler(streamHandler: wrapper) + let internalStreamHandler = PigeonStreamHandler(streamHandler: streamHandler) let channel = FlutterEventChannel( name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) channel.setStreamHandler(internalStreamHandler) @@ -568,7 +568,7 @@ class StreamEventsStreamHandler: PigeonEventChannelWrapper { if !instanceName.isEmpty { channelName += ".\(instanceName)" } - let internalStreamHandler = PigeonStreamHandler(streamHandler: wrapper) + let internalStreamHandler = PigeonStreamHandler(streamHandler: streamHandler) let channel = FlutterEventChannel( name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) channel.setStreamHandler(internalStreamHandler) From 9b4a27b962a43e5df518200365d0b8b24947e802 Mon Sep 17 00:00:00 2001 From: tarrinneal Date: Tue, 26 Nov 2024 15:30:25 -0800 Subject: [PATCH 20/20] one too many --- .../example/app/ios/Runner/EventChannelMessages.g.swift | 2 +- packages/pigeon/lib/swift_generator.dart | 2 +- .../test_plugin/ios/Classes/EventChannelTests.gen.swift | 4 ++-- .../test_plugin/macos/Classes/EventChannelTests.gen.swift | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift b/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift index 57703a41b660..b2ca9d6f83b3 100644 --- a/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift +++ b/packages/pigeon/example/app/ios/Runner/EventChannelMessages.g.swift @@ -171,7 +171,7 @@ class StreamEventsStreamHandler: PigeonEventChannelWrapper { if !instanceName.isEmpty { channelName += ".\(instanceName)" } - let internalStreamHandler = PigeonStreamHandler(streamHandler: streamHandler) + let internalStreamHandler = PigeonStreamHandler(wrapper: streamHandler) let channel = FlutterEventChannel( name: channelName, binaryMessenger: messenger, codec: eventChannelMessagesPigeonMethodCodec) channel.setStreamHandler(internalStreamHandler) diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index 62fa6fa4d895..d06ee58d1b2b 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -1410,7 +1410,7 @@ private func nilOrValue(_ value: Any?) -> T? { if !instanceName.isEmpty { channelName += ".\\(instanceName)" } - let internalStreamHandler = PigeonStreamHandler<${_swiftTypeForDartType(func.returnType)}>(streamHandler: streamHandler) + let internalStreamHandler = PigeonStreamHandler<${_swiftTypeForDartType(func.returnType)}>(wrapper: streamHandler) let channel = FlutterEventChannel(name: channelName, binaryMessenger: messenger, codec: ${_getMethodCodecVarName(generatorOptions)}) channel.setStreamHandler(internalStreamHandler) } diff --git a/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift index c5f04e0d94c4..fe66f272df78 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/EventChannelTests.gen.swift @@ -551,7 +551,7 @@ class StreamIntsStreamHandler: PigeonEventChannelWrapper { if !instanceName.isEmpty { channelName += ".\(instanceName)" } - let internalStreamHandler = PigeonStreamHandler(streamHandler: streamHandler) + let internalStreamHandler = PigeonStreamHandler(wrapper: streamHandler) let channel = FlutterEventChannel( name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) channel.setStreamHandler(internalStreamHandler) @@ -568,7 +568,7 @@ class StreamEventsStreamHandler: PigeonEventChannelWrapper { if !instanceName.isEmpty { channelName += ".\(instanceName)" } - let internalStreamHandler = PigeonStreamHandler(streamHandler: streamHandler) + let internalStreamHandler = PigeonStreamHandler(wrapper: streamHandler) let channel = FlutterEventChannel( name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) channel.setStreamHandler(internalStreamHandler) diff --git a/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift b/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift index c5f04e0d94c4..fe66f272df78 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/EventChannelTests.gen.swift @@ -551,7 +551,7 @@ class StreamIntsStreamHandler: PigeonEventChannelWrapper { if !instanceName.isEmpty { channelName += ".\(instanceName)" } - let internalStreamHandler = PigeonStreamHandler(streamHandler: streamHandler) + let internalStreamHandler = PigeonStreamHandler(wrapper: streamHandler) let channel = FlutterEventChannel( name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) channel.setStreamHandler(internalStreamHandler) @@ -568,7 +568,7 @@ class StreamEventsStreamHandler: PigeonEventChannelWrapper { if !instanceName.isEmpty { channelName += ".\(instanceName)" } - let internalStreamHandler = PigeonStreamHandler(streamHandler: streamHandler) + let internalStreamHandler = PigeonStreamHandler(wrapper: streamHandler) let channel = FlutterEventChannel( name: channelName, binaryMessenger: messenger, codec: eventChannelTestsPigeonMethodCodec) channel.setStreamHandler(internalStreamHandler)