Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/pigeon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 28.0.0

* **Breaking Change** Updates Kotlin and Swift generators to generate `suspend` functions and `async throws` signatures for `@FlutterApi` methods by default.
* Use `@asyncCallback` if callback-style signatures are required.

## 27.3.0

* Adds support for sharing constants across platforms.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,11 @@ private class PigeonApiImplementation : ExampleHostApi {
return a + b
}

override fun sendMessage(message: MessageData, callback: (Result<Boolean>) -> Unit) {
override suspend fun sendMessage(message: MessageData): Boolean {
if (message.code == Code.ONE) {
callback(Result.failure(FlutterError("code", "message", "details")))
return
throw FlutterError("code", "message", "details")
}
callback(Result.success(true))
return true
}
}
// #enddocregion kotlin-class
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ import io.flutter.plugin.common.MessageCodec
import io.flutter.plugin.common.StandardMessageCodec
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine

const val aStringConstant: String = "stringConstantValue"
const val anIntConstant: Long = 42L
Expand Down Expand Up @@ -297,7 +303,7 @@ interface ExampleHostApi {

fun add(a: Long, b: Long): Long

fun sendMessage(message: MessageData, callback: (Result<Boolean>) -> Unit)
suspend fun sendMessage(message: MessageData): Boolean

companion object {
/** The codec used by ExampleHostApi. */
Expand Down Expand Up @@ -364,14 +370,14 @@ interface ExampleHostApi {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val messageArg = args[0] as MessageData
api.sendMessage(messageArg) { result: Result<Boolean> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(MessagesPigeonUtils.wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(MessagesPigeonUtils.wrapResult(data))
}
CoroutineScope(Dispatchers.Main).launch {
val wrapped: List<Any?> =
try {
listOf(api.sendMessage(messageArg))
} catch (exception: Throwable) {
MessagesPigeonUtils.wrapError(exception)
}
reply.reply(wrapped)
}
}
} else {
Expand All @@ -391,29 +397,29 @@ class MessageFlutterApi(
val codec: MessageCodec<Any?> by lazy { MessagesPigeonCodec() }
}

fun flutterMethod(aStringArg: String?, callback: (Result<String>) -> Unit) {
suspend fun flutterMethod(aStringArg: String?): String {
val separatedMessageChannelSuffix =
if (messageChannelSuffix.isNotEmpty()) ".$messageChannelSuffix" else ""
val channelName =
"dev.flutter.pigeon.pigeon_example_package.MessageFlutterApi.flutterMethod$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(aStringArg)) {
if (it is List<*>) {
if (it.size > 1) {
callback(Result.failure(FlutterError(it[0] as String, it[1] as String, it[2] as String?)))
} else if (it[0] == null) {
callback(
Result.failure(
FlutterError(
"null-error",
"Flutter api returned null value for non-null return value.",
"")))
return suspendCancellableCoroutine { continuation ->
val channelName =
"dev.flutter.pigeon.pigeon_example_package.MessageFlutterApi.flutterMethod$separatedMessageChannelSuffix"
val channel = BasicMessageChannel<Any?>(binaryMessenger, channelName, codec)
channel.send(listOf(aStringArg)) {
if (it is List<*>) {
if (it.size > 1) {
continuation.resumeWithException(
FlutterError(it[0] as String, it[1] as String, it[2] as String?))
} else if (it[0] == null) {
continuation.resumeWithException(
FlutterError(
"null-error", "Flutter api returned null value for non-null return value.", ""))
} else {
val output = it[0] as String
continuation.resume(output)
}
} else {
val output = it[0] as String
callback(Result.success(output))
continuation.resumeWithException(MessagesPigeonUtils.createConnectionError(channelName))
}
} else {
callback(Result.failure(MessagesPigeonUtils.createConnectionError(channelName)))
}
}
}
Expand Down
64 changes: 31 additions & 33 deletions packages/pigeon/example/app/ios/Runner/Messages.g.swift
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ class MessagesPigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable {
protocol ExampleHostApi {
func getHostLanguage() throws -> String
func add(_ a: Int64, to b: Int64) throws -> Int64
func sendMessage(message: MessageData, completion: @escaping (Result<Bool, Error>) -> Void)
func sendMessage(message: MessageData) async throws -> Bool
}

/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.
Expand Down Expand Up @@ -356,11 +356,11 @@ class ExampleHostApiSetup {
sendMessageChannel.setMessageHandler { message, reply in
let args = message as! [Any?]
let messageArg = args[0] as! MessageData
api.sendMessage(message: messageArg) { result in
switch result {
case .success(let res):
reply(wrapResult(res))
case .failure(let error):
Task {
do {
let result = try await api.sendMessage(message: messageArg)
reply(wrapResult(result))
} catch {
reply(wrapError(error))
}
}
Expand All @@ -373,8 +373,7 @@ class ExampleHostApiSetup {

/// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift.
protocol MessageFlutterApiProtocol {
func flutterMethod(
aString aStringArg: String?, completion: @escaping (Result<String, PigeonError>) -> Void)
func flutterMethod(aString aStringArg: String?) async throws -> String
}
class MessageFlutterApi: MessageFlutterApiProtocol {
private let binaryMessenger: FlutterBinaryMessenger
Expand All @@ -386,32 +385,31 @@ class MessageFlutterApi: MessageFlutterApiProtocol {
var codec: MessagesPigeonCodec {
return MessagesPigeonCodec.shared
}
func flutterMethod(
aString aStringArg: String?, completion: @escaping (Result<String, PigeonError>) -> Void
) {
let channelName: String =
"dev.flutter.pigeon.pigeon_example_package.MessageFlutterApi.flutterMethod\(messageChannelSuffix)"
let channel = FlutterBasicMessageChannel(
name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([aStringArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
completion(.failure(createConnectionError(withChannelName: channelName)))
return
}
if listResponse.count > 1 {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
completion(.failure(PigeonError(code: code, message: message, details: details)))
} else if listResponse[0] == nil {
completion(
.failure(
PigeonError(
func flutterMethod(aString aStringArg: String?) async throws -> String {
return try await withCheckedThrowingContinuation { continuation in
let channelName: String =
"dev.flutter.pigeon.pigeon_example_package.MessageFlutterApi.flutterMethod\(messageChannelSuffix)"
let channel = FlutterBasicMessageChannel(
name: channelName, binaryMessenger: binaryMessenger, codec: codec)
channel.sendMessage([aStringArg] as [Any?]) { response in
guard let listResponse = response as? [Any?] else {
continuation.resume(throwing: createConnectionError(withChannelName: channelName))
return
}
if listResponse.count > 1 {
let code: String = listResponse[0] as! String
let message: String? = nilOrValue(listResponse[1])
let details: String? = nilOrValue(listResponse[2])
continuation.resume(throwing: PigeonError(code: code, message: message, details: details))
} else if listResponse[0] == nil {
continuation.resume(
throwing: PigeonError(
code: "null-error",
message: "Flutter api returned null value for non-null return value.", details: "")))
} else {
let result = listResponse[0] as! String
completion(.success(result))
message: "Flutter api returned null value for non-null return value.", details: ""))
} else {
let result = listResponse[0] as! String
continuation.resume(returning: result)
}
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions packages/pigeon/lib/src/ast.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class Method extends Node {
required this.location,
this.isRequired = true,
this.isAsynchronous = false,
this.isAsynchronousCallback = false,
this.isStatic = false,
this.offset,
this.objcSelector = '',
Expand All @@ -56,6 +57,9 @@ class Method extends Node {
/// Whether the receiver of this method is expected to return synchronously or not.
bool isAsynchronous;

/// Whether this asynchronous method uses callback-based completions instead of native async/await or suspend functions.
bool isAsynchronousCallback;

/// The offset in the source file where the field appears.
int? offset;

Expand Down
2 changes: 1 addition & 1 deletion packages/pigeon/lib/src/generator_tools.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import 'generator.dart';
/// The current version of pigeon.
///
/// This must match the version in pubspec.yaml.
const String pigeonVersion = '27.3.0';
const String pigeonVersion = '28.0.0';

/// Default plugin package name.
const String defaultPluginPackageName = 'dev.flutter.pigeon';
Expand Down
Loading
Loading