From c510bd6b0cc0da652ae714f6ee8132a37e27310a Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Thu, 25 Apr 2024 22:43:19 +0900 Subject: [PATCH 01/22] [pigeon] Do not use FlutterError when generating Swift code Swift has more strict type system than Objective-C. For example, protocol conformance must not be redundant[1]. Also, Both FlutterError and Swift.Error are public, extension `FlutterError` to `Swift.Error` is also public. If someone create a such extension in the plugin code, Flutter app can't create a such extension in the app code, which forces Plugin developers to use Objective-C when they want to use pigeon, instead of Swift. To avoid this issue, this change makes pigeon to use another error type, named PigeonError, instead of FlutterError. By declaring PigeonError as internal visibility, their existence won't make compilation error even if PigeonError is declared in the app code. [1] https://docs.swift.org/swift-book/documentation/the-swift-programming-language/declarations/#Protocol-Conformance-Must-Not-Be-Redundant --- packages/pigeon/example/README.md | 6 +- .../example/app/ios/Runner/AppDelegate.swift | 4 - .../example/app/ios/Runner/Messages.g.swift | 38 ++- packages/pigeon/lib/swift_generator.dart | 59 ++++- .../ios/Classes/CoreTests.gen.swift | 238 ++++++++++-------- .../test_plugin/ios/Classes/TestPlugin.swift | 2 - .../macos/Classes/CoreTests.gen.swift | 238 ++++++++++-------- .../macos/Classes/TestPlugin.swift | 2 - 8 files changed, 345 insertions(+), 242 deletions(-) diff --git a/packages/pigeon/example/README.md b/packages/pigeon/example/README.md index 8e22079b47a..a84621339f4 100644 --- a/packages/pigeon/example/README.md +++ b/packages/pigeon/example/README.md @@ -118,8 +118,6 @@ This is the code that will use the generated Swift code to receive calls from Fl packages/pigeon/example/app/ios/Runner/AppDelegate.swift ```swift -// This extension of Error is required to do use FlutterError in any Swift code. -extension FlutterError: Error {} private class PigeonApiImplementation: ExampleHostApi { func getHostLanguage() throws -> String { @@ -128,14 +126,14 @@ private class PigeonApiImplementation: ExampleHostApi { func add(_ a: Int64, to b: Int64) throws -> Int64 { if a < 0 || b < 0 { - throw FlutterError(code: "code", message: "message", details: "details") + throw PigeonError(code: "code", message: "message", details: "details") } return a + b } func sendMessage(message: MessageData, completion: @escaping (Result) -> Void) { if message.code == Code.one { - completion(.failure(FlutterError(code: "code", message: "message", details: "details"))) + completion(.failure(PigeonError(code: "code", message: "message", details: "details"))) return } completion(.success(true)) diff --git a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift index f259b759085..6e7b978f801 100644 --- a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift +++ b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift @@ -5,10 +5,6 @@ import Flutter import UIKit -// #docregion swift-class -// This extension of Error is required to do use FlutterError in any Swift code. -extension FlutterError: Error {} - private class PigeonApiImplementation: ExampleHostApi { func getHostLanguage() throws -> String { return "Swift" diff --git a/packages/pigeon/example/app/ios/Runner/Messages.g.swift b/packages/pigeon/example/app/ios/Runner/Messages.g.swift index 313c1974e02..f2dbacf9aa6 100644 --- a/packages/pigeon/example/app/ios/Runner/Messages.g.swift +++ b/packages/pigeon/example/app/ios/Runner/Messages.g.swift @@ -14,6 +14,32 @@ import Foundation #error("Unsupported platform.") #endif +/// Error thrown by Pigeon. Encapsulates a code, message, and details. +class PigeonError: Swift.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 { + let detailsDescription: String + if let convertibleObject = details as? CustomStringConvertible { + detailsDescription = convertibleObject.description + } else if let _ = details { + detailsDescription = "" + } else { + detailsDescription = "" + } + return + "PigeonError(code: \(code), message: \(message ?? ""), details: \(detailsDescription)" + } +} + private func wrapResult(_ result: Any?) -> [Any?] { return [result] } @@ -33,8 +59,8 @@ private func wrapError(_ error: Any) -> [Any?] { ] } -private func createConnectionError(withChannelName channelName: String) -> FlutterError { - return FlutterError( +private func createConnectionError(withChannelName channelName: String) -> PigeonError { + return PigeonError( code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "") } @@ -193,7 +219,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) -> Void) + aString aStringArg: String?, completion: @escaping (Result) -> Void) } class MessageFlutterApi: MessageFlutterApiProtocol { private let binaryMessenger: FlutterBinaryMessenger @@ -203,7 +229,7 @@ class MessageFlutterApi: MessageFlutterApiProtocol { self.messageChannelSuffix = messageChannelSuffix.count > 0 ? ".\(messageChannelSuffix)" : "" } func flutterMethod( - aString aStringArg: String?, completion: @escaping (Result) -> Void + aString aStringArg: String?, completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_example_package.MessageFlutterApi.flutterMethod\(messageChannelSuffix)" @@ -217,11 +243,11 @@ class MessageFlutterApi: MessageFlutterApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index 854c2c835f2..c7902105597 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -316,7 +316,7 @@ class SwiftGenerator extends StructuredGenerator { name: func.name, parameters: func.parameters, returnType: func.returnType, - errorTypeName: 'FlutterError', + errorTypeName: 'PigeonError', isAsynchronous: true, swiftFunction: func.swiftFunction, getParameterName: _getSafeArgumentName, @@ -673,10 +673,10 @@ private func nilOrValue(_ value: Any?) -> T? { void _writeCreateConnectionError(Indent indent) { indent.newln(); indent.writeScoped( - 'private func createConnectionError(withChannelName channelName: String) -> FlutterError {', + 'private func createConnectionError(withChannelName channelName: String) -> PigeonError {', '}', () { indent.writeln( - 'return FlutterError(code: "channel-error", message: "Unable to establish connection on channel: \'\\(channelName)\'.", details: "")'); + 'return PigeonError(code: "channel-error", message: "Unable to establish connection on channel: \'\\(channelName)\'.", details: "")'); }); } @@ -694,6 +694,8 @@ private func nilOrValue(_ value: Any?) -> T? { .whereType() .any((Api api) => api.methods.isNotEmpty); + _writePigeonError(indent); + if (hasHostApi) { _writeWrapResult(indent); _writeWrapError(indent); @@ -718,7 +720,7 @@ private func nilOrValue(_ value: Any?) -> T? { name: name, parameters: parameters, returnType: returnType, - errorTypeName: 'FlutterError', + errorTypeName: 'PigeonError', isAsynchronous: true, swiftFunction: swiftFunction, getParameterName: _getSafeArgumentName, @@ -760,12 +762,12 @@ private func nilOrValue(_ value: Any?) -> T? { indent.writeln('let message: String? = nilOrValue(listResponse[1])'); indent.writeln('let details: String? = nilOrValue(listResponse[2])'); indent.writeln( - 'completion(.failure(FlutterError(code: code, message: message, details: details)))'); + 'completion(.failure(PigeonError(code: code, message: message, details: details)))'); }, addTrailingNewline: false); if (!returnType.isNullable && !returnType.isVoid) { indent.addScoped('else if listResponse[0] == nil {', '} ', () { indent.writeln( - 'completion(.failure(FlutterError(code: "null-error", message: "Flutter api returned null value for non-null return value.", details: "")))'); + 'completion(.failure(PigeonError(code: "null-error", message: "Flutter api returned null value for non-null return value.", details: "")))'); }, addTrailingNewline: false); } indent.addScoped('else {', '}', () { @@ -895,6 +897,51 @@ private func nilOrValue(_ value: Any?) -> T? { indent.writeln('$varChannelName.setMessageHandler(nil)'); }); } + + void _writePigeonError(Indent indent) { + indent.newln(); + indent.writeln( + '/// Error thrown by Pigeon. Encapsulates a code, message, and details.'); + indent.writeln('class PigeonError: Swift.Error {'); + indent.nest(1, () { + indent.writeln('let code: String'); + indent.writeln('let message: String?'); + indent.writeln('let details: Any?'); + indent.newln(); + indent.writeln('init(code: String, message: String?, details: Any?) {'); + indent.nest(1, () { + indent.writeln('self.code = code'); + indent.writeln('self.message = message'); + indent.writeln('self.details = details'); + }); + indent.writeln('}'); + indent.newln(); + indent.writeln('var localizedDescription: String {'); + indent.nest(1, () { + indent.writeln('let detailsDescription: String'); + indent.writeln( + 'if let convertibleObject = details as? CustomStringConvertible {'); + indent.nest(1, () { + indent.writeln('detailsDescription = convertibleObject.description'); + }); + indent.writeln('} else if let _ = details {'); + indent.nest(1, () { + indent.writeln('detailsDescription = ""'); + }); + indent.writeln('} else {'); + indent.nest(1, () { + indent.writeln('detailsDescription = ""'); + }); + indent.writeln('}'); + indent.writeln( + r'return "PigeonError(code: \(code), message: \(message ?? ""), details: \(detailsDescription)"'); + }); + indent.write('}'); + }); + indent.newln(); + indent.write('}'); + indent.newln(); + } } /// Calculates the name of the codec that will be generated for [api]. 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 f02daf70167..d39df18fe1f 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,6 +15,32 @@ import Foundation #error("Unsupported platform.") #endif +/// Error thrown by Pigeon. Encapsulates a code, message, and details. +class PigeonError: Swift.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 { + let detailsDescription: String + if let convertibleObject = details as? CustomStringConvertible { + detailsDescription = convertibleObject.description + } else if let _ = details { + detailsDescription = "" + } else { + detailsDescription = "" + } + return + "PigeonError(code: \(code), message: \(message ?? ""), details: \(detailsDescription)" + } +} + private func wrapResult(_ result: Any?) -> [Any?] { return [result] } @@ -34,8 +60,8 @@ private func wrapError(_ error: Any) -> [Any?] { ] } -private func createConnectionError(withChannelName channelName: String) -> FlutterError { - return FlutterError( +private func createConnectionError(withChannelName channelName: String) -> PigeonError { + return PigeonError( code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "") } @@ -2428,86 +2454,85 @@ class FlutterIntegrationCoreApiCodec: FlutterStandardMessageCodec { protocol FlutterIntegrationCoreApiProtocol { /// A no-op function taking no arguments and returning no value, to sanity /// test basic calling. - func noop(completion: @escaping (Result) -> Void) + func noop(completion: @escaping (Result) -> Void) /// Responds with an error from an async function returning a value. - func throwError(completion: @escaping (Result) -> Void) + func throwError(completion: @escaping (Result) -> Void) /// Responds with an error from an async void function. - func throwErrorFromVoid(completion: @escaping (Result) -> Void) + func throwErrorFromVoid(completion: @escaping (Result) -> Void) /// Returns the passed object, to test serialization and deserialization. func echo( - _ everythingArg: AllTypes, completion: @escaping (Result) -> Void) + _ everythingArg: AllTypes, completion: @escaping (Result) -> Void) /// Returns the passed object, to test serialization and deserialization. func echoNullable( _ everythingArg: AllNullableTypes?, - completion: @escaping (Result) -> Void) + completion: @escaping (Result) -> Void) /// Returns passed in arguments of multiple types. /// /// Tests multiple-arity FlutterApi handling. func sendMultipleNullableTypes( aBool aNullableBoolArg: Bool?, anInt aNullableIntArg: Int64?, aString aNullableStringArg: String?, - completion: @escaping (Result) -> Void) + completion: @escaping (Result) -> Void) /// Returns the passed object, to test serialization and deserialization. func echoNullable( _ everythingArg: AllNullableTypesWithoutRecursion?, - completion: @escaping (Result) -> Void) + completion: @escaping (Result) -> Void) /// Returns passed in arguments of multiple types. /// /// Tests multiple-arity FlutterApi handling. func sendMultipleNullableTypesWithoutRecursion( aBool aNullableBoolArg: Bool?, anInt aNullableIntArg: Int64?, aString aNullableStringArg: String?, - completion: @escaping (Result) -> Void) + completion: @escaping (Result) -> Void) /// Returns the passed boolean, to test serialization and deserialization. - func echo(_ aBoolArg: Bool, completion: @escaping (Result) -> Void) + func echo(_ aBoolArg: Bool, completion: @escaping (Result) -> Void) /// Returns the passed int, to test serialization and deserialization. - func echo(_ anIntArg: Int64, completion: @escaping (Result) -> Void) + func echo(_ anIntArg: Int64, completion: @escaping (Result) -> Void) /// Returns the passed double, to test serialization and deserialization. - func echo(_ aDoubleArg: Double, completion: @escaping (Result) -> Void) + func echo(_ aDoubleArg: Double, completion: @escaping (Result) -> Void) /// Returns the passed string, to test serialization and deserialization. - func echo(_ aStringArg: String, completion: @escaping (Result) -> Void) + func echo(_ aStringArg: String, completion: @escaping (Result) -> Void) /// Returns the passed byte list, to test serialization and deserialization. func echo( _ aListArg: FlutterStandardTypedData, - completion: @escaping (Result) -> Void) + completion: @escaping (Result) -> Void) /// Returns the passed list, to test serialization and deserialization. - func echo(_ aListArg: [Any?], completion: @escaping (Result<[Any?], FlutterError>) -> Void) + func echo(_ aListArg: [Any?], completion: @escaping (Result<[Any?], PigeonError>) -> Void) /// Returns the passed map, to test serialization and deserialization. func echo( - _ aMapArg: [String?: Any?], - completion: @escaping (Result<[String?: Any?], FlutterError>) -> Void) + _ aMapArg: [String?: Any?], completion: @escaping (Result<[String?: Any?], PigeonError>) -> Void + ) /// Returns the passed enum to test serialization and deserialization. - func echo(_ anEnumArg: AnEnum, completion: @escaping (Result) -> Void) + func echo(_ anEnumArg: AnEnum, completion: @escaping (Result) -> Void) /// Returns the passed boolean, to test serialization and deserialization. - func echoNullable(_ aBoolArg: Bool?, completion: @escaping (Result) -> Void) + func echoNullable(_ aBoolArg: Bool?, completion: @escaping (Result) -> Void) /// Returns the passed int, to test serialization and deserialization. - func echoNullable( - _ anIntArg: Int64?, completion: @escaping (Result) -> Void) + func echoNullable(_ anIntArg: Int64?, completion: @escaping (Result) -> Void) /// Returns the passed double, to test serialization and deserialization. func echoNullable( - _ aDoubleArg: Double?, completion: @escaping (Result) -> Void) + _ aDoubleArg: Double?, completion: @escaping (Result) -> Void) /// Returns the passed string, to test serialization and deserialization. func echoNullable( - _ aStringArg: String?, completion: @escaping (Result) -> Void) + _ aStringArg: String?, completion: @escaping (Result) -> Void) /// Returns the passed byte list, to test serialization and deserialization. func echoNullable( _ aListArg: FlutterStandardTypedData?, - completion: @escaping (Result) -> Void) + completion: @escaping (Result) -> Void) /// Returns the passed list, to test serialization and deserialization. func echoNullable( - _ aListArg: [Any?]?, completion: @escaping (Result<[Any?]?, FlutterError>) -> Void) + _ aListArg: [Any?]?, completion: @escaping (Result<[Any?]?, PigeonError>) -> Void) /// Returns the passed map, to test serialization and deserialization. func echoNullable( _ aMapArg: [String?: Any?]?, - completion: @escaping (Result<[String?: Any?]?, FlutterError>) -> Void) + completion: @escaping (Result<[String?: Any?]?, PigeonError>) -> Void) /// Returns the passed enum to test serialization and deserialization. func echoNullable( - _ anEnumArg: AnEnum?, completion: @escaping (Result) -> Void) + _ anEnumArg: AnEnum?, completion: @escaping (Result) -> Void) /// A no-op function taking no arguments and returning no value, to sanity /// test basic asynchronous calling. - func noopAsync(completion: @escaping (Result) -> Void) + func noopAsync(completion: @escaping (Result) -> Void) /// Returns the passed in generic Object asynchronously. - func echoAsync(_ aStringArg: String, completion: @escaping (Result) -> Void) + func echoAsync(_ aStringArg: String, completion: @escaping (Result) -> Void) } class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { private let binaryMessenger: FlutterBinaryMessenger @@ -2521,7 +2546,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } /// A no-op function taking no arguments and returning no value, to sanity /// test basic calling. - func noop(completion: @escaping (Result) -> Void) { + func noop(completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.noop\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -2535,14 +2560,14 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { completion(.success(Void())) } } } /// Responds with an error from an async function returning a value. - func throwError(completion: @escaping (Result) -> Void) { + func throwError(completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.throwError\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -2556,7 +2581,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: Any? = listResponse[0] completion(.success(result)) @@ -2564,7 +2589,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } } /// Responds with an error from an async void function. - func throwErrorFromVoid(completion: @escaping (Result) -> Void) { + func throwErrorFromVoid(completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.throwErrorFromVoid\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -2578,7 +2603,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { completion(.success(Void())) } @@ -2586,7 +2611,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } /// Returns the passed object, to test serialization and deserialization. func echo( - _ everythingArg: AllTypes, completion: @escaping (Result) -> Void + _ everythingArg: AllTypes, completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAllTypes\(messageChannelSuffix)" @@ -2601,11 +2626,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2617,7 +2642,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { /// Returns the passed object, to test serialization and deserialization. func echoNullable( _ everythingArg: AllNullableTypes?, - completion: @escaping (Result) -> Void + completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAllNullableTypes\(messageChannelSuffix)" @@ -2632,7 +2657,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: AllNullableTypes? = nilOrValue(listResponse[0]) completion(.success(result)) @@ -2645,7 +2670,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { func sendMultipleNullableTypes( aBool aNullableBoolArg: Bool?, anInt aNullableIntArg: Int64?, aString aNullableStringArg: String?, - completion: @escaping (Result) -> Void + completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.sendMultipleNullableTypes\(messageChannelSuffix)" @@ -2661,11 +2686,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2677,7 +2702,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { /// Returns the passed object, to test serialization and deserialization. func echoNullable( _ everythingArg: AllNullableTypesWithoutRecursion?, - completion: @escaping (Result) -> Void + completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAllNullableTypesWithoutRecursion\(messageChannelSuffix)" @@ -2692,7 +2717,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: AllNullableTypesWithoutRecursion? = nilOrValue(listResponse[0]) completion(.success(result)) @@ -2705,7 +2730,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { func sendMultipleNullableTypesWithoutRecursion( aBool aNullableBoolArg: Bool?, anInt aNullableIntArg: Int64?, aString aNullableStringArg: String?, - completion: @escaping (Result) -> Void + completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.sendMultipleNullableTypesWithoutRecursion\(messageChannelSuffix)" @@ -2721,11 +2746,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2735,7 +2760,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } } /// Returns the passed boolean, to test serialization and deserialization. - func echo(_ aBoolArg: Bool, completion: @escaping (Result) -> Void) { + func echo(_ aBoolArg: Bool, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoBool\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -2749,11 +2774,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2763,7 +2788,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } } /// Returns the passed int, to test serialization and deserialization. - func echo(_ anIntArg: Int64, completion: @escaping (Result) -> Void) { + func echo(_ anIntArg: Int64, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoInt\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -2777,11 +2802,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2792,7 +2817,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } } /// Returns the passed double, to test serialization and deserialization. - func echo(_ aDoubleArg: Double, completion: @escaping (Result) -> Void) { + func echo(_ aDoubleArg: Double, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoDouble\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -2806,11 +2831,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2820,7 +2845,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } } /// Returns the passed string, to test serialization and deserialization. - func echo(_ aStringArg: String, completion: @escaping (Result) -> Void) { + func echo(_ aStringArg: String, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoString\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -2834,11 +2859,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2850,7 +2875,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { /// Returns the passed byte list, to test serialization and deserialization. func echo( _ aListArg: FlutterStandardTypedData, - completion: @escaping (Result) -> Void + completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoUint8List\(messageChannelSuffix)" @@ -2865,11 +2890,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2879,7 +2904,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } } /// Returns the passed list, to test serialization and deserialization. - func echo(_ aListArg: [Any?], completion: @escaping (Result<[Any?], FlutterError>) -> Void) { + func echo(_ aListArg: [Any?], completion: @escaping (Result<[Any?], PigeonError>) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoList\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -2893,11 +2918,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2908,8 +2933,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } /// Returns the passed map, to test serialization and deserialization. func echo( - _ aMapArg: [String?: Any?], - completion: @escaping (Result<[String?: Any?], FlutterError>) -> Void + _ aMapArg: [String?: Any?], completion: @escaping (Result<[String?: Any?], PigeonError>) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoMap\(messageChannelSuffix)" @@ -2924,11 +2948,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2938,7 +2962,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } } /// Returns the passed enum to test serialization and deserialization. - func echo(_ anEnumArg: AnEnum, completion: @escaping (Result) -> Void) { + func echo(_ anEnumArg: AnEnum, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoEnum\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -2952,11 +2976,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2966,8 +2990,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } } /// Returns the passed boolean, to test serialization and deserialization. - func echoNullable(_ aBoolArg: Bool?, completion: @escaping (Result) -> Void) - { + func echoNullable(_ aBoolArg: Bool?, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableBool\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -2981,7 +3004,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: Bool? = nilOrValue(listResponse[0]) completion(.success(result)) @@ -2989,9 +3012,8 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } } /// Returns the passed int, to test serialization and deserialization. - func echoNullable( - _ anIntArg: Int64?, completion: @escaping (Result) -> Void - ) { + func echoNullable(_ anIntArg: Int64?, completion: @escaping (Result) -> Void) + { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableInt\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -3005,7 +3027,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: Int64? = isNullish(listResponse[0]) @@ -3018,7 +3040,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } /// Returns the passed double, to test serialization and deserialization. func echoNullable( - _ aDoubleArg: Double?, completion: @escaping (Result) -> Void + _ aDoubleArg: Double?, completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableDouble\(messageChannelSuffix)" @@ -3033,7 +3055,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: Double? = nilOrValue(listResponse[0]) completion(.success(result)) @@ -3042,7 +3064,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } /// Returns the passed string, to test serialization and deserialization. func echoNullable( - _ aStringArg: String?, completion: @escaping (Result) -> Void + _ aStringArg: String?, completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableString\(messageChannelSuffix)" @@ -3057,7 +3079,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: String? = nilOrValue(listResponse[0]) completion(.success(result)) @@ -3067,7 +3089,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { /// Returns the passed byte list, to test serialization and deserialization. func echoNullable( _ aListArg: FlutterStandardTypedData?, - completion: @escaping (Result) -> Void + completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableUint8List\(messageChannelSuffix)" @@ -3082,7 +3104,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: FlutterStandardTypedData? = nilOrValue(listResponse[0]) completion(.success(result)) @@ -3091,7 +3113,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } /// Returns the passed list, to test serialization and deserialization. func echoNullable( - _ aListArg: [Any?]?, completion: @escaping (Result<[Any?]?, FlutterError>) -> Void + _ aListArg: [Any?]?, completion: @escaping (Result<[Any?]?, PigeonError>) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableList\(messageChannelSuffix)" @@ -3106,7 +3128,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: [Any?]? = nilOrValue(listResponse[0]) completion(.success(result)) @@ -3116,7 +3138,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { /// Returns the passed map, to test serialization and deserialization. func echoNullable( _ aMapArg: [String?: Any?]?, - completion: @escaping (Result<[String?: Any?]?, FlutterError>) -> Void + completion: @escaping (Result<[String?: Any?]?, PigeonError>) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableMap\(messageChannelSuffix)" @@ -3131,7 +3153,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: [String?: Any?]? = nilOrValue(listResponse[0]) completion(.success(result)) @@ -3140,7 +3162,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } /// Returns the passed enum to test serialization and deserialization. func echoNullable( - _ anEnumArg: AnEnum?, completion: @escaping (Result) -> Void + _ anEnumArg: AnEnum?, completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableEnum\(messageChannelSuffix)" @@ -3155,7 +3177,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: AnEnum? = isNullish(listResponse[0]) ? nil : AnEnum(rawValue: listResponse[0] as! Int)! @@ -3165,7 +3187,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } /// A no-op function taking no arguments and returning no value, to sanity /// test basic asynchronous calling. - func noopAsync(completion: @escaping (Result) -> Void) { + func noopAsync(completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.noopAsync\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -3179,14 +3201,14 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { completion(.success(Void())) } } } /// Returns the passed in generic Object asynchronously. - func echoAsync(_ aStringArg: String, completion: @escaping (Result) -> Void) + func echoAsync(_ aStringArg: String, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAsyncString\(messageChannelSuffix)" @@ -3201,11 +3223,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -3341,9 +3363,8 @@ class FlutterSmallApiCodec: FlutterStandardMessageCodec { /// /// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. protocol FlutterSmallApiProtocol { - func echo( - _ msgArg: TestMessage, completion: @escaping (Result) -> Void) - func echo(string aStringArg: String, completion: @escaping (Result) -> Void) + func echo(_ msgArg: TestMessage, completion: @escaping (Result) -> Void) + func echo(string aStringArg: String, completion: @escaping (Result) -> Void) } class FlutterSmallApi: FlutterSmallApiProtocol { private let binaryMessenger: FlutterBinaryMessenger @@ -3355,9 +3376,8 @@ class FlutterSmallApi: FlutterSmallApiProtocol { var codec: FlutterStandardMessageCodec { return FlutterSmallApiCodec.shared } - func echo( - _ msgArg: TestMessage, completion: @escaping (Result) -> Void - ) { + func echo(_ msgArg: TestMessage, completion: @escaping (Result) -> Void) + { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterSmallApi.echoWrappedList\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -3371,11 +3391,11 @@ class FlutterSmallApi: FlutterSmallApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -3384,7 +3404,7 @@ class FlutterSmallApi: FlutterSmallApiProtocol { } } } - func echo(string aStringArg: String, completion: @escaping (Result) -> Void) + func echo(string aStringArg: String, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterSmallApi.echoString\(messageChannelSuffix)" @@ -3399,11 +3419,11 @@ class FlutterSmallApi: FlutterSmallApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { 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 dd88abb5a2a..c563cb1ab9a 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift @@ -5,8 +5,6 @@ import Flutter import UIKit -extension FlutterError: Error {} - /// This plugin handles the native side of the integration tests in /// example/integration_test/. public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { 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 f02daf70167..d39df18fe1f 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,6 +15,32 @@ import Foundation #error("Unsupported platform.") #endif +/// Error thrown by Pigeon. Encapsulates a code, message, and details. +class PigeonError: Swift.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 { + let detailsDescription: String + if let convertibleObject = details as? CustomStringConvertible { + detailsDescription = convertibleObject.description + } else if let _ = details { + detailsDescription = "" + } else { + detailsDescription = "" + } + return + "PigeonError(code: \(code), message: \(message ?? ""), details: \(detailsDescription)" + } +} + private func wrapResult(_ result: Any?) -> [Any?] { return [result] } @@ -34,8 +60,8 @@ private func wrapError(_ error: Any) -> [Any?] { ] } -private func createConnectionError(withChannelName channelName: String) -> FlutterError { - return FlutterError( +private func createConnectionError(withChannelName channelName: String) -> PigeonError { + return PigeonError( code: "channel-error", message: "Unable to establish connection on channel: '\(channelName)'.", details: "") } @@ -2428,86 +2454,85 @@ class FlutterIntegrationCoreApiCodec: FlutterStandardMessageCodec { protocol FlutterIntegrationCoreApiProtocol { /// A no-op function taking no arguments and returning no value, to sanity /// test basic calling. - func noop(completion: @escaping (Result) -> Void) + func noop(completion: @escaping (Result) -> Void) /// Responds with an error from an async function returning a value. - func throwError(completion: @escaping (Result) -> Void) + func throwError(completion: @escaping (Result) -> Void) /// Responds with an error from an async void function. - func throwErrorFromVoid(completion: @escaping (Result) -> Void) + func throwErrorFromVoid(completion: @escaping (Result) -> Void) /// Returns the passed object, to test serialization and deserialization. func echo( - _ everythingArg: AllTypes, completion: @escaping (Result) -> Void) + _ everythingArg: AllTypes, completion: @escaping (Result) -> Void) /// Returns the passed object, to test serialization and deserialization. func echoNullable( _ everythingArg: AllNullableTypes?, - completion: @escaping (Result) -> Void) + completion: @escaping (Result) -> Void) /// Returns passed in arguments of multiple types. /// /// Tests multiple-arity FlutterApi handling. func sendMultipleNullableTypes( aBool aNullableBoolArg: Bool?, anInt aNullableIntArg: Int64?, aString aNullableStringArg: String?, - completion: @escaping (Result) -> Void) + completion: @escaping (Result) -> Void) /// Returns the passed object, to test serialization and deserialization. func echoNullable( _ everythingArg: AllNullableTypesWithoutRecursion?, - completion: @escaping (Result) -> Void) + completion: @escaping (Result) -> Void) /// Returns passed in arguments of multiple types. /// /// Tests multiple-arity FlutterApi handling. func sendMultipleNullableTypesWithoutRecursion( aBool aNullableBoolArg: Bool?, anInt aNullableIntArg: Int64?, aString aNullableStringArg: String?, - completion: @escaping (Result) -> Void) + completion: @escaping (Result) -> Void) /// Returns the passed boolean, to test serialization and deserialization. - func echo(_ aBoolArg: Bool, completion: @escaping (Result) -> Void) + func echo(_ aBoolArg: Bool, completion: @escaping (Result) -> Void) /// Returns the passed int, to test serialization and deserialization. - func echo(_ anIntArg: Int64, completion: @escaping (Result) -> Void) + func echo(_ anIntArg: Int64, completion: @escaping (Result) -> Void) /// Returns the passed double, to test serialization and deserialization. - func echo(_ aDoubleArg: Double, completion: @escaping (Result) -> Void) + func echo(_ aDoubleArg: Double, completion: @escaping (Result) -> Void) /// Returns the passed string, to test serialization and deserialization. - func echo(_ aStringArg: String, completion: @escaping (Result) -> Void) + func echo(_ aStringArg: String, completion: @escaping (Result) -> Void) /// Returns the passed byte list, to test serialization and deserialization. func echo( _ aListArg: FlutterStandardTypedData, - completion: @escaping (Result) -> Void) + completion: @escaping (Result) -> Void) /// Returns the passed list, to test serialization and deserialization. - func echo(_ aListArg: [Any?], completion: @escaping (Result<[Any?], FlutterError>) -> Void) + func echo(_ aListArg: [Any?], completion: @escaping (Result<[Any?], PigeonError>) -> Void) /// Returns the passed map, to test serialization and deserialization. func echo( - _ aMapArg: [String?: Any?], - completion: @escaping (Result<[String?: Any?], FlutterError>) -> Void) + _ aMapArg: [String?: Any?], completion: @escaping (Result<[String?: Any?], PigeonError>) -> Void + ) /// Returns the passed enum to test serialization and deserialization. - func echo(_ anEnumArg: AnEnum, completion: @escaping (Result) -> Void) + func echo(_ anEnumArg: AnEnum, completion: @escaping (Result) -> Void) /// Returns the passed boolean, to test serialization and deserialization. - func echoNullable(_ aBoolArg: Bool?, completion: @escaping (Result) -> Void) + func echoNullable(_ aBoolArg: Bool?, completion: @escaping (Result) -> Void) /// Returns the passed int, to test serialization and deserialization. - func echoNullable( - _ anIntArg: Int64?, completion: @escaping (Result) -> Void) + func echoNullable(_ anIntArg: Int64?, completion: @escaping (Result) -> Void) /// Returns the passed double, to test serialization and deserialization. func echoNullable( - _ aDoubleArg: Double?, completion: @escaping (Result) -> Void) + _ aDoubleArg: Double?, completion: @escaping (Result) -> Void) /// Returns the passed string, to test serialization and deserialization. func echoNullable( - _ aStringArg: String?, completion: @escaping (Result) -> Void) + _ aStringArg: String?, completion: @escaping (Result) -> Void) /// Returns the passed byte list, to test serialization and deserialization. func echoNullable( _ aListArg: FlutterStandardTypedData?, - completion: @escaping (Result) -> Void) + completion: @escaping (Result) -> Void) /// Returns the passed list, to test serialization and deserialization. func echoNullable( - _ aListArg: [Any?]?, completion: @escaping (Result<[Any?]?, FlutterError>) -> Void) + _ aListArg: [Any?]?, completion: @escaping (Result<[Any?]?, PigeonError>) -> Void) /// Returns the passed map, to test serialization and deserialization. func echoNullable( _ aMapArg: [String?: Any?]?, - completion: @escaping (Result<[String?: Any?]?, FlutterError>) -> Void) + completion: @escaping (Result<[String?: Any?]?, PigeonError>) -> Void) /// Returns the passed enum to test serialization and deserialization. func echoNullable( - _ anEnumArg: AnEnum?, completion: @escaping (Result) -> Void) + _ anEnumArg: AnEnum?, completion: @escaping (Result) -> Void) /// A no-op function taking no arguments and returning no value, to sanity /// test basic asynchronous calling. - func noopAsync(completion: @escaping (Result) -> Void) + func noopAsync(completion: @escaping (Result) -> Void) /// Returns the passed in generic Object asynchronously. - func echoAsync(_ aStringArg: String, completion: @escaping (Result) -> Void) + func echoAsync(_ aStringArg: String, completion: @escaping (Result) -> Void) } class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { private let binaryMessenger: FlutterBinaryMessenger @@ -2521,7 +2546,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } /// A no-op function taking no arguments and returning no value, to sanity /// test basic calling. - func noop(completion: @escaping (Result) -> Void) { + func noop(completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.noop\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -2535,14 +2560,14 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { completion(.success(Void())) } } } /// Responds with an error from an async function returning a value. - func throwError(completion: @escaping (Result) -> Void) { + func throwError(completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.throwError\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -2556,7 +2581,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: Any? = listResponse[0] completion(.success(result)) @@ -2564,7 +2589,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } } /// Responds with an error from an async void function. - func throwErrorFromVoid(completion: @escaping (Result) -> Void) { + func throwErrorFromVoid(completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.throwErrorFromVoid\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -2578,7 +2603,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { completion(.success(Void())) } @@ -2586,7 +2611,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } /// Returns the passed object, to test serialization and deserialization. func echo( - _ everythingArg: AllTypes, completion: @escaping (Result) -> Void + _ everythingArg: AllTypes, completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAllTypes\(messageChannelSuffix)" @@ -2601,11 +2626,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2617,7 +2642,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { /// Returns the passed object, to test serialization and deserialization. func echoNullable( _ everythingArg: AllNullableTypes?, - completion: @escaping (Result) -> Void + completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAllNullableTypes\(messageChannelSuffix)" @@ -2632,7 +2657,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: AllNullableTypes? = nilOrValue(listResponse[0]) completion(.success(result)) @@ -2645,7 +2670,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { func sendMultipleNullableTypes( aBool aNullableBoolArg: Bool?, anInt aNullableIntArg: Int64?, aString aNullableStringArg: String?, - completion: @escaping (Result) -> Void + completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.sendMultipleNullableTypes\(messageChannelSuffix)" @@ -2661,11 +2686,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2677,7 +2702,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { /// Returns the passed object, to test serialization and deserialization. func echoNullable( _ everythingArg: AllNullableTypesWithoutRecursion?, - completion: @escaping (Result) -> Void + completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAllNullableTypesWithoutRecursion\(messageChannelSuffix)" @@ -2692,7 +2717,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: AllNullableTypesWithoutRecursion? = nilOrValue(listResponse[0]) completion(.success(result)) @@ -2705,7 +2730,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { func sendMultipleNullableTypesWithoutRecursion( aBool aNullableBoolArg: Bool?, anInt aNullableIntArg: Int64?, aString aNullableStringArg: String?, - completion: @escaping (Result) -> Void + completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.sendMultipleNullableTypesWithoutRecursion\(messageChannelSuffix)" @@ -2721,11 +2746,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2735,7 +2760,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } } /// Returns the passed boolean, to test serialization and deserialization. - func echo(_ aBoolArg: Bool, completion: @escaping (Result) -> Void) { + func echo(_ aBoolArg: Bool, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoBool\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -2749,11 +2774,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2763,7 +2788,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } } /// Returns the passed int, to test serialization and deserialization. - func echo(_ anIntArg: Int64, completion: @escaping (Result) -> Void) { + func echo(_ anIntArg: Int64, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoInt\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -2777,11 +2802,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2792,7 +2817,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } } /// Returns the passed double, to test serialization and deserialization. - func echo(_ aDoubleArg: Double, completion: @escaping (Result) -> Void) { + func echo(_ aDoubleArg: Double, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoDouble\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -2806,11 +2831,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2820,7 +2845,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } } /// Returns the passed string, to test serialization and deserialization. - func echo(_ aStringArg: String, completion: @escaping (Result) -> Void) { + func echo(_ aStringArg: String, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoString\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -2834,11 +2859,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2850,7 +2875,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { /// Returns the passed byte list, to test serialization and deserialization. func echo( _ aListArg: FlutterStandardTypedData, - completion: @escaping (Result) -> Void + completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoUint8List\(messageChannelSuffix)" @@ -2865,11 +2890,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2879,7 +2904,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } } /// Returns the passed list, to test serialization and deserialization. - func echo(_ aListArg: [Any?], completion: @escaping (Result<[Any?], FlutterError>) -> Void) { + func echo(_ aListArg: [Any?], completion: @escaping (Result<[Any?], PigeonError>) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoList\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -2893,11 +2918,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2908,8 +2933,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } /// Returns the passed map, to test serialization and deserialization. func echo( - _ aMapArg: [String?: Any?], - completion: @escaping (Result<[String?: Any?], FlutterError>) -> Void + _ aMapArg: [String?: Any?], completion: @escaping (Result<[String?: Any?], PigeonError>) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoMap\(messageChannelSuffix)" @@ -2924,11 +2948,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2938,7 +2962,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } } /// Returns the passed enum to test serialization and deserialization. - func echo(_ anEnumArg: AnEnum, completion: @escaping (Result) -> Void) { + func echo(_ anEnumArg: AnEnum, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoEnum\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -2952,11 +2976,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -2966,8 +2990,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } } /// Returns the passed boolean, to test serialization and deserialization. - func echoNullable(_ aBoolArg: Bool?, completion: @escaping (Result) -> Void) - { + func echoNullable(_ aBoolArg: Bool?, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableBool\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -2981,7 +3004,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: Bool? = nilOrValue(listResponse[0]) completion(.success(result)) @@ -2989,9 +3012,8 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } } /// Returns the passed int, to test serialization and deserialization. - func echoNullable( - _ anIntArg: Int64?, completion: @escaping (Result) -> Void - ) { + func echoNullable(_ anIntArg: Int64?, completion: @escaping (Result) -> Void) + { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableInt\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -3005,7 +3027,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: Int64? = isNullish(listResponse[0]) @@ -3018,7 +3040,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } /// Returns the passed double, to test serialization and deserialization. func echoNullable( - _ aDoubleArg: Double?, completion: @escaping (Result) -> Void + _ aDoubleArg: Double?, completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableDouble\(messageChannelSuffix)" @@ -3033,7 +3055,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: Double? = nilOrValue(listResponse[0]) completion(.success(result)) @@ -3042,7 +3064,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } /// Returns the passed string, to test serialization and deserialization. func echoNullable( - _ aStringArg: String?, completion: @escaping (Result) -> Void + _ aStringArg: String?, completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableString\(messageChannelSuffix)" @@ -3057,7 +3079,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: String? = nilOrValue(listResponse[0]) completion(.success(result)) @@ -3067,7 +3089,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { /// Returns the passed byte list, to test serialization and deserialization. func echoNullable( _ aListArg: FlutterStandardTypedData?, - completion: @escaping (Result) -> Void + completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableUint8List\(messageChannelSuffix)" @@ -3082,7 +3104,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: FlutterStandardTypedData? = nilOrValue(listResponse[0]) completion(.success(result)) @@ -3091,7 +3113,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } /// Returns the passed list, to test serialization and deserialization. func echoNullable( - _ aListArg: [Any?]?, completion: @escaping (Result<[Any?]?, FlutterError>) -> Void + _ aListArg: [Any?]?, completion: @escaping (Result<[Any?]?, PigeonError>) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableList\(messageChannelSuffix)" @@ -3106,7 +3128,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: [Any?]? = nilOrValue(listResponse[0]) completion(.success(result)) @@ -3116,7 +3138,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { /// Returns the passed map, to test serialization and deserialization. func echoNullable( _ aMapArg: [String?: Any?]?, - completion: @escaping (Result<[String?: Any?]?, FlutterError>) -> Void + completion: @escaping (Result<[String?: Any?]?, PigeonError>) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableMap\(messageChannelSuffix)" @@ -3131,7 +3153,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: [String?: Any?]? = nilOrValue(listResponse[0]) completion(.success(result)) @@ -3140,7 +3162,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } /// Returns the passed enum to test serialization and deserialization. func echoNullable( - _ anEnumArg: AnEnum?, completion: @escaping (Result) -> Void + _ anEnumArg: AnEnum?, completion: @escaping (Result) -> Void ) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoNullableEnum\(messageChannelSuffix)" @@ -3155,7 +3177,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { let result: AnEnum? = isNullish(listResponse[0]) ? nil : AnEnum(rawValue: listResponse[0] as! Int)! @@ -3165,7 +3187,7 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { } /// A no-op function taking no arguments and returning no value, to sanity /// test basic asynchronous calling. - func noopAsync(completion: @escaping (Result) -> Void) { + func noopAsync(completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.noopAsync\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -3179,14 +3201,14 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else { completion(.success(Void())) } } } /// Returns the passed in generic Object asynchronously. - func echoAsync(_ aStringArg: String, completion: @escaping (Result) -> Void) + func echoAsync(_ aStringArg: String, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterIntegrationCoreApi.echoAsyncString\(messageChannelSuffix)" @@ -3201,11 +3223,11 @@ class FlutterIntegrationCoreApi: FlutterIntegrationCoreApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -3341,9 +3363,8 @@ class FlutterSmallApiCodec: FlutterStandardMessageCodec { /// /// Generated protocol from Pigeon that represents Flutter messages that can be called from Swift. protocol FlutterSmallApiProtocol { - func echo( - _ msgArg: TestMessage, completion: @escaping (Result) -> Void) - func echo(string aStringArg: String, completion: @escaping (Result) -> Void) + func echo(_ msgArg: TestMessage, completion: @escaping (Result) -> Void) + func echo(string aStringArg: String, completion: @escaping (Result) -> Void) } class FlutterSmallApi: FlutterSmallApiProtocol { private let binaryMessenger: FlutterBinaryMessenger @@ -3355,9 +3376,8 @@ class FlutterSmallApi: FlutterSmallApiProtocol { var codec: FlutterStandardMessageCodec { return FlutterSmallApiCodec.shared } - func echo( - _ msgArg: TestMessage, completion: @escaping (Result) -> Void - ) { + func echo(_ msgArg: TestMessage, completion: @escaping (Result) -> Void) + { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterSmallApi.echoWrappedList\(messageChannelSuffix)" let channel = FlutterBasicMessageChannel( @@ -3371,11 +3391,11 @@ class FlutterSmallApi: FlutterSmallApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { @@ -3384,7 +3404,7 @@ class FlutterSmallApi: FlutterSmallApiProtocol { } } } - func echo(string aStringArg: String, completion: @escaping (Result) -> Void) + func echo(string aStringArg: String, completion: @escaping (Result) -> Void) { let channelName: String = "dev.flutter.pigeon.pigeon_integration_tests.FlutterSmallApi.echoString\(messageChannelSuffix)" @@ -3399,11 +3419,11 @@ class FlutterSmallApi: FlutterSmallApiProtocol { let code: String = listResponse[0] as! String let message: String? = nilOrValue(listResponse[1]) let details: String? = nilOrValue(listResponse[2]) - completion(.failure(FlutterError(code: code, message: message, details: details))) + completion(.failure(PigeonError(code: code, message: message, details: details))) } else if listResponse[0] == nil { completion( .failure( - FlutterError( + PigeonError( code: "null-error", message: "Flutter api returned null value for non-null return value.", details: ""))) } else { 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 759b4ac21ac..9d54911d2da 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift @@ -5,8 +5,6 @@ import Cocoa import FlutterMacOS -extension FlutterError: Error {} - /// This plugin handles the native side of the integration tests in /// example/integration_test/. public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { From d8968d761c5908ed38c95e22262f32ef189f3a58 Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Fri, 26 Apr 2024 00:09:31 +0900 Subject: [PATCH 02/22] [pigeon] Add option to whether emit PigeonError class in Swift code While PigeonError is defined as an internal visibility, so different modules don't have problems with the same class name, it's still troublesome to have it in the generated code if users call pigeon multiple times in the same module. To solve this problem, add a new option, `swift_emit_error_class`, to control whether to emit the PigeonError class in the generated Swift code. The default value is true, which means the PigeonError class will be emitted as before. --- packages/pigeon/README.md | 3 +-- packages/pigeon/lib/pigeon_lib.dart | 5 ++++ packages/pigeon/lib/swift_generator.dart | 12 ++++++++- .../ios/Classes/CoreTests.gen.swift | 26 ------------------- .../test_plugin/ios/Classes/TestPlugin.swift | 5 ++++ .../macos/Classes/CoreTests.gen.swift | 26 ------------------- .../macos/Classes/TestPlugin.swift | 5 ++++ packages/pigeon/tool/shared/generation.dart | 7 +++-- 8 files changed, 32 insertions(+), 57 deletions(-) diff --git a/packages/pigeon/README.md b/packages/pigeon/README.md index dfa4e9847d7..9c185613696 100644 --- a/packages/pigeon/README.md +++ b/packages/pigeon/README.md @@ -53,8 +53,7 @@ should be returned via the provided callback. To pass custom details into `PlatformException` for error handling, use `FlutterError` in your Host API. [Example](./example/README.md#HostApi_Example). -To use `FlutterError` in Swift you must first extend a standard error. -[Example](./example/README.md#AppDelegate.swift). +For swift, use `PigeonError` instead of `FlutterError`. #### Objective-C and C++ diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart index addce55d54a..94f708f83d4 100644 --- a/packages/pigeon/lib/pigeon_lib.dart +++ b/packages/pigeon/lib/pigeon_lib.dart @@ -2084,6 +2084,8 @@ ${_argParser.usage}'''; help: 'Path to generated Objective-C header file (.h).') ..addOption('objc_prefix', help: 'Prefix for generated Objective-C classes and protocols.') + ..addOption('swift_emit_error_class', + help: 'Adds an error class to the generated Swift code.') ..addOption('copyright_header', help: 'Path to file with copyright header to be prepended to generated code.') @@ -2125,6 +2127,9 @@ ${_argParser.usage}'''; results['java_use_generated_annotation'] as bool?, ), swiftOut: results['swift_out'] as String?, + swiftOptions: SwiftOptions( + swiftEmitErrorClass: results['swift_emit_error_class'] as bool?, + ), kotlinOut: results['kotlin_out'] as String?, kotlinOptions: KotlinOptions( package: results['kotlin_package'] as String?, diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index c7902105597..18e1fbd305c 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -19,16 +19,22 @@ class SwiftOptions { /// Creates a [SwiftOptions] object const SwiftOptions({ this.copyrightHeader, + this.swiftEmitErrorClass, }); /// A copyright header that will get prepended to generated code. final Iterable? copyrightHeader; + /// Whether to emit the PigeonError class in the generated code. + /// Defaults to true. + final bool? swiftEmitErrorClass; + /// Creates a [SwiftOptions] from a Map representation where: /// `x = SwiftOptions.fromList(x.toMap())`. static SwiftOptions fromList(Map map) { return SwiftOptions( copyrightHeader: map['copyrightHeader'] as Iterable?, + swiftEmitErrorClass: map['swiftEmitErrorClass'] as bool?, ); } @@ -37,6 +43,8 @@ class SwiftOptions { Map toMap() { final Map result = { if (copyrightHeader != null) 'copyrightHeader': copyrightHeader!, + if (swiftEmitErrorClass != null) + 'swiftEmitErrorClass': swiftEmitErrorClass!, }; return result; } @@ -694,7 +702,9 @@ private func nilOrValue(_ value: Any?) -> T? { .whereType() .any((Api api) => api.methods.isNotEmpty); - _writePigeonError(indent); + if (generatorOptions.swiftEmitErrorClass ?? true) { + _writePigeonError(indent); + } if (hasHostApi) { _writeWrapResult(indent); 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 d39df18fe1f..068d6852e41 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,32 +15,6 @@ import Foundation #error("Unsupported platform.") #endif -/// Error thrown by Pigeon. Encapsulates a code, message, and details. -class PigeonError: Swift.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 { - let detailsDescription: String - if let convertibleObject = details as? CustomStringConvertible { - detailsDescription = convertibleObject.description - } else if let _ = details { - detailsDescription = "" - } else { - detailsDescription = "" - } - return - "PigeonError(code: \(code), message: \(message ?? ""), details: \(detailsDescription)" - } -} - private func wrapResult(_ result: Any?) -> [Any?] { return [result] } 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 c563cb1ab9a..49cd6538e79 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift @@ -5,6 +5,11 @@ import Flutter import UIKit +// If someone want to throw FlutterError from Swift, they need to conform to Error protocol. +// However this is not recommended as this extension will be public visibility, as +// both FlutterError and Error are public. +extension FlutterError: Error {} + /// This plugin handles the native side of the integration tests in /// example/integration_test/. public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { 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 d39df18fe1f..068d6852e41 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,32 +15,6 @@ import Foundation #error("Unsupported platform.") #endif -/// Error thrown by Pigeon. Encapsulates a code, message, and details. -class PigeonError: Swift.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 { - let detailsDescription: String - if let convertibleObject = details as? CustomStringConvertible { - detailsDescription = convertibleObject.description - } else if let _ = details { - detailsDescription = "" - } else { - detailsDescription = "" - } - return - "PigeonError(code: \(code), message: \(message ?? ""), details: \(detailsDescription)" - } -} - private func wrapResult(_ result: Any?) -> [Any?] { return [result] } 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 9d54911d2da..a6b4182e055 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift @@ -5,6 +5,11 @@ import Cocoa import FlutterMacOS +// If someone want to throw FlutterError from Swift, they need to conform to Error protocol. +// However this is not recommended as this extension will be public visibility, as +// both FlutterError and Error are public. +extension FlutterError: Error {} + /// This plugin handles the native side of the integration tests in /// example/integration_test/. public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { diff --git a/packages/pigeon/tool/shared/generation.dart b/packages/pigeon/tool/shared/generation.dart index 1cd3b6e5ea9..6163e7ed76f 100644 --- a/packages/pigeon/tool/shared/generation.dart +++ b/packages/pigeon/tool/shared/generation.dart @@ -77,7 +77,7 @@ Future generateTestPigeons({required String baseDir}) async { final String sharedDartOutputBase = p.join(baseDir, 'platform_tests', 'shared_test_plugin_code'); - for (final String input in inputs) { + for (final (int index, String input) in inputs.indexed) { final String pascalCaseName = _snakeToPascalCase(input); final Set skipLanguages = _unsupportedFiles[input] ?? {}; @@ -114,6 +114,7 @@ Future generateTestPigeons({required String baseDir}) async { cppNamespace: '${input}_pigeontest', suppressVersion: true, dartPackageName: 'pigeon_integration_tests', + swiftEmitErrorClass: index == 0, ); if (generateCode != 0) { return generateCode; @@ -129,6 +130,7 @@ Future generateTestPigeons({required String baseDir}) async { : '$outputBase/macos/Classes/$pascalCaseName.gen.swift', suppressVersion: true, dartPackageName: 'pigeon_integration_tests', + swiftEmitErrorClass: index == 0, ); if (generateCode != 0) { return generateCode; @@ -202,6 +204,7 @@ Future runPigeon({ String copyrightHeader = './copyright_header.txt', String? basePath, String? dartPackageName, + bool swiftEmitErrorClass = true, }) async { // Temporarily suppress the version output via the global flag if requested. // This is done because having the version in all the generated test output @@ -236,7 +239,7 @@ Future runPigeon({ objcSourceOut: objcSourceOut, objcOptions: ObjcOptions(prefix: objcPrefix), swiftOut: swiftOut, - swiftOptions: const SwiftOptions(), + swiftOptions: SwiftOptions(swiftEmitErrorClass: swiftEmitErrorClass), basePath: basePath, dartPackageName: dartPackageName, )); From c2beea180e17fa6ed3b3400206c0d5ae98ebff75 Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Fri, 26 Apr 2024 07:26:40 +0900 Subject: [PATCH 03/22] [pigeon] Simplify localizedDescription of PigeonError in Swift --- .../example/app/ios/Runner/Messages.g.swift | 10 +--------- packages/pigeon/lib/swift_generator.dart | 18 +++--------------- 2 files changed, 4 insertions(+), 24 deletions(-) diff --git a/packages/pigeon/example/app/ios/Runner/Messages.g.swift b/packages/pigeon/example/app/ios/Runner/Messages.g.swift index f2dbacf9aa6..a29baa82f10 100644 --- a/packages/pigeon/example/app/ios/Runner/Messages.g.swift +++ b/packages/pigeon/example/app/ios/Runner/Messages.g.swift @@ -27,16 +27,8 @@ class PigeonError: Swift.Error { } var localizedDescription: String { - let detailsDescription: String - if let convertibleObject = details as? CustomStringConvertible { - detailsDescription = convertibleObject.description - } else if let _ = details { - detailsDescription = "" - } else { - detailsDescription = "" - } return - "PigeonError(code: \(code), message: \(message ?? ""), details: \(detailsDescription)" + "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" } } diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index 18e1fbd305c..98680b1dfdf 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -928,23 +928,11 @@ private func nilOrValue(_ value: Any?) -> T? { indent.newln(); indent.writeln('var localizedDescription: String {'); indent.nest(1, () { - indent.writeln('let detailsDescription: String'); - indent.writeln( - 'if let convertibleObject = details as? CustomStringConvertible {'); - indent.nest(1, () { - indent.writeln('detailsDescription = convertibleObject.description'); - }); - indent.writeln('} else if let _ = details {'); + indent.writeln('return'); indent.nest(1, () { - indent.writeln('detailsDescription = ""'); - }); - indent.writeln('} else {'); - indent.nest(1, () { - indent.writeln('detailsDescription = ""'); + indent.writeln( + r'"PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")"'); }); - indent.writeln('}'); - indent.writeln( - r'return "PigeonError(code: \(code), message: \(message ?? ""), details: \(detailsDescription)"'); }); indent.write('}'); }); From 6a8a94c8d19d43ad10d417f5cdbb4c3011f37377 Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Fri, 26 Apr 2024 07:55:25 +0900 Subject: [PATCH 04/22] [pigeon] Always make sure CoreTests.gen.swift has PigeonError class Apparently, when checking `pod lib lint`, other generated files might be missing. --- .../ios/Classes/CoreTests.gen.swift | 18 ++++++++++++++++++ .../macos/Classes/CoreTests.gen.swift | 18 ++++++++++++++++++ packages/pigeon/tool/shared/generation.dart | 6 +++--- 3 files changed, 39 insertions(+), 3 deletions(-) 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 068d6852e41..41231cf38b5 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,6 +15,24 @@ import Foundation #error("Unsupported platform.") #endif +/// Error thrown by Pigeon. Encapsulates a code, message, and details. +class PigeonError: Swift.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 068d6852e41..41231cf38b5 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,6 +15,24 @@ import Foundation #error("Unsupported platform.") #endif +/// Error thrown by Pigeon. Encapsulates a code, message, and details. +class PigeonError: Swift.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 6163e7ed76f..9c96ade3a88 100644 --- a/packages/pigeon/tool/shared/generation.dart +++ b/packages/pigeon/tool/shared/generation.dart @@ -77,7 +77,7 @@ Future generateTestPigeons({required String baseDir}) async { final String sharedDartOutputBase = p.join(baseDir, 'platform_tests', 'shared_test_plugin_code'); - for (final (int index, String input) in inputs.indexed) { + for (final String input in inputs) { final String pascalCaseName = _snakeToPascalCase(input); final Set skipLanguages = _unsupportedFiles[input] ?? {}; @@ -114,7 +114,7 @@ Future generateTestPigeons({required String baseDir}) async { cppNamespace: '${input}_pigeontest', suppressVersion: true, dartPackageName: 'pigeon_integration_tests', - swiftEmitErrorClass: index == 0, + swiftEmitErrorClass: input == 'core_tests', ); if (generateCode != 0) { return generateCode; @@ -130,7 +130,7 @@ Future generateTestPigeons({required String baseDir}) async { : '$outputBase/macos/Classes/$pascalCaseName.gen.swift', suppressVersion: true, dartPackageName: 'pigeon_integration_tests', - swiftEmitErrorClass: index == 0, + swiftEmitErrorClass: input == 'core_tests', ); if (generateCode != 0) { return generateCode; From b874f099e7a4c0cb68a4f1fcf2ad92ccea511835 Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Fri, 26 Apr 2024 07:58:43 +0900 Subject: [PATCH 05/22] [pigeon] Make PigeonError class final and add FlutterError initializer --- .../pigeon/example/app/ios/Runner/Messages.g.swift | 8 +++++++- packages/pigeon/lib/swift_generator.dart | 10 +++++++++- .../test_plugin/ios/Classes/CoreTests.gen.swift | 8 +++++++- .../test_plugin/macos/Classes/CoreTests.gen.swift | 8 +++++++- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/packages/pigeon/example/app/ios/Runner/Messages.g.swift b/packages/pigeon/example/app/ios/Runner/Messages.g.swift index a29baa82f10..68e4fead3b0 100644 --- a/packages/pigeon/example/app/ios/Runner/Messages.g.swift +++ b/packages/pigeon/example/app/ios/Runner/Messages.g.swift @@ -15,7 +15,7 @@ import Foundation #endif /// Error thrown by Pigeon. Encapsulates a code, message, and details. -class PigeonError: Swift.Error { +final class PigeonError: Swift.Error { let code: String let message: String? let details: Any? @@ -26,6 +26,12 @@ class PigeonError: Swift.Error { self.details = details } + init(flutterError: FlutterError) { + self.code = flutterError.code + self.message = flutterError.message + self.details = flutterError.details + } + var localizedDescription: String { return "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index 98680b1dfdf..28ce56fd206 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -912,7 +912,7 @@ private func nilOrValue(_ value: Any?) -> T? { indent.newln(); indent.writeln( '/// Error thrown by Pigeon. Encapsulates a code, message, and details.'); - indent.writeln('class PigeonError: Swift.Error {'); + indent.writeln('final class PigeonError: Swift.Error {'); indent.nest(1, () { indent.writeln('let code: String'); indent.writeln('let message: String?'); @@ -926,6 +926,14 @@ private func nilOrValue(_ value: Any?) -> T? { }); indent.writeln('}'); indent.newln(); + indent.writeln('init(flutterError: FlutterError) {'); + indent.nest(1, () { + indent.writeln('self.code = flutterError.code'); + indent.writeln('self.message = flutterError.message'); + indent.writeln('self.details = flutterError.details'); + }); + indent.writeln('}'); + indent.newln(); indent.writeln('var localizedDescription: String {'); indent.nest(1, () { indent.writeln('return'); 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 41231cf38b5..2895b6dbb41 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 @@ -16,7 +16,7 @@ import Foundation #endif /// Error thrown by Pigeon. Encapsulates a code, message, and details. -class PigeonError: Swift.Error { +final class PigeonError: Swift.Error { let code: String let message: String? let details: Any? @@ -27,6 +27,12 @@ class PigeonError: Swift.Error { self.details = details } + init(flutterError: FlutterError) { + self.code = flutterError.code + self.message = flutterError.message + self.details = flutterError.details + } + var localizedDescription: String { return "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" 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 41231cf38b5..2895b6dbb41 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 @@ -16,7 +16,7 @@ import Foundation #endif /// Error thrown by Pigeon. Encapsulates a code, message, and details. -class PigeonError: Swift.Error { +final class PigeonError: Swift.Error { let code: String let message: String? let details: Any? @@ -27,6 +27,12 @@ class PigeonError: Swift.Error { self.details = details } + init(flutterError: FlutterError) { + self.code = flutterError.code + self.message = flutterError.message + self.details = flutterError.details + } + var localizedDescription: String { return "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" From df102e328fb0b3448e9e1e7d47bb3f6307ebc482 Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Fri, 26 Apr 2024 08:15:43 +0900 Subject: [PATCH 06/22] [pigeon] Rename swiftEmitErrorClass to emitPigeonErrorClass And add a test for the new option. --- packages/pigeon/lib/pigeon_lib.dart | 7 +++++-- packages/pigeon/lib/swift_generator.dart | 12 ++++++------ packages/pigeon/test/pigeon_lib_test.dart | 12 ++++++++++++ packages/pigeon/tool/shared/generation.dart | 8 ++++---- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart index 94f708f83d4..42bac912741 100644 --- a/packages/pigeon/lib/pigeon_lib.dart +++ b/packages/pigeon/lib/pigeon_lib.dart @@ -2084,7 +2084,7 @@ ${_argParser.usage}'''; help: 'Path to generated Objective-C header file (.h).') ..addOption('objc_prefix', help: 'Prefix for generated Objective-C classes and protocols.') - ..addOption('swift_emit_error_class', + ..addOption('swift_emit_pigeon_error_class', help: 'Adds an error class to the generated Swift code.') ..addOption('copyright_header', help: @@ -2128,7 +2128,10 @@ ${_argParser.usage}'''; ), swiftOut: results['swift_out'] as String?, swiftOptions: SwiftOptions( - swiftEmitErrorClass: results['swift_emit_error_class'] as bool?, + emitPigeonErrorClass: + (results['swift_emit_pigeon_error_class'] as String?) + ?.toLowerCase() != + 'false', ), kotlinOut: results['kotlin_out'] as String?, kotlinOptions: KotlinOptions( diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index 28ce56fd206..bcfbc970be1 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -19,7 +19,7 @@ class SwiftOptions { /// Creates a [SwiftOptions] object const SwiftOptions({ this.copyrightHeader, - this.swiftEmitErrorClass, + this.emitPigeonErrorClass, }); /// A copyright header that will get prepended to generated code. @@ -27,14 +27,14 @@ class SwiftOptions { /// Whether to emit the PigeonError class in the generated code. /// Defaults to true. - final bool? swiftEmitErrorClass; + final bool? emitPigeonErrorClass; /// Creates a [SwiftOptions] from a Map representation where: /// `x = SwiftOptions.fromList(x.toMap())`. static SwiftOptions fromList(Map map) { return SwiftOptions( copyrightHeader: map['copyrightHeader'] as Iterable?, - swiftEmitErrorClass: map['swiftEmitErrorClass'] as bool?, + emitPigeonErrorClass: map['emitPigeonErrorClass'] as bool?, ); } @@ -43,8 +43,8 @@ class SwiftOptions { Map toMap() { final Map result = { if (copyrightHeader != null) 'copyrightHeader': copyrightHeader!, - if (swiftEmitErrorClass != null) - 'swiftEmitErrorClass': swiftEmitErrorClass!, + if (emitPigeonErrorClass != null) + 'emitPigeonErrorClass': emitPigeonErrorClass!, }; return result; } @@ -702,7 +702,7 @@ private func nilOrValue(_ value: Any?) -> T? { .whereType() .any((Api api) => api.methods.isNotEmpty); - if (generatorOptions.swiftEmitErrorClass ?? true) { + if (generatorOptions.emitPigeonErrorClass ?? true) { _writePigeonError(indent); } diff --git a/packages/pigeon/test/pigeon_lib_test.dart b/packages/pigeon/test/pigeon_lib_test.dart index 34516e8c0de..8e1a768c2dc 100644 --- a/packages/pigeon/test/pigeon_lib_test.dart +++ b/packages/pigeon/test/pigeon_lib_test.dart @@ -128,6 +128,18 @@ void main() { expect(opts.javaOptions!.useGeneratedAnnotation, isTrue); }); + test('parse args - swift_emit_pigeon_error_class', () { + // Without this option, the default should be true. + final PigeonOptions opts0 = Pigeon.parseArgs([]); + expect(opts0.swiftOptions!.emitPigeonErrorClass, isTrue); + final PigeonOptions opts1 = + Pigeon.parseArgs(['--swift_emit_pigeon_error_class=true']); + expect(opts1.swiftOptions!.emitPigeonErrorClass, isTrue); + final PigeonOptions opts2 = + Pigeon.parseArgs(['--swift_emit_pigeon_error_class=false']); + expect(opts2.swiftOptions!.emitPigeonErrorClass, isFalse); + }); + test('parse args - cpp_source_out', () { final PigeonOptions opts = Pigeon.parseArgs(['--cpp_source_out', 'foo.cpp']); diff --git a/packages/pigeon/tool/shared/generation.dart b/packages/pigeon/tool/shared/generation.dart index 9c96ade3a88..720a6e558c1 100644 --- a/packages/pigeon/tool/shared/generation.dart +++ b/packages/pigeon/tool/shared/generation.dart @@ -114,7 +114,7 @@ Future generateTestPigeons({required String baseDir}) async { cppNamespace: '${input}_pigeontest', suppressVersion: true, dartPackageName: 'pigeon_integration_tests', - swiftEmitErrorClass: input == 'core_tests', + swiftEmitPigeonErrorClass: input == 'core_tests', ); if (generateCode != 0) { return generateCode; @@ -130,7 +130,7 @@ Future generateTestPigeons({required String baseDir}) async { : '$outputBase/macos/Classes/$pascalCaseName.gen.swift', suppressVersion: true, dartPackageName: 'pigeon_integration_tests', - swiftEmitErrorClass: input == 'core_tests', + swiftEmitPigeonErrorClass: input == 'core_tests', ); if (generateCode != 0) { return generateCode; @@ -204,7 +204,7 @@ Future runPigeon({ String copyrightHeader = './copyright_header.txt', String? basePath, String? dartPackageName, - bool swiftEmitErrorClass = true, + bool swiftEmitPigeonErrorClass = true, }) async { // Temporarily suppress the version output via the global flag if requested. // This is done because having the version in all the generated test output @@ -239,7 +239,7 @@ Future runPigeon({ objcSourceOut: objcSourceOut, objcOptions: ObjcOptions(prefix: objcPrefix), swiftOut: swiftOut, - swiftOptions: SwiftOptions(swiftEmitErrorClass: swiftEmitErrorClass), + swiftOptions: SwiftOptions(emitPigeonErrorClass: swiftEmitPigeonErrorClass), basePath: basePath, dartPackageName: dartPackageName, )); From b30a0ccdf24852b9ec21dc3afe80d06a9f9b0f48 Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Sat, 27 Apr 2024 03:24:48 +0900 Subject: [PATCH 07/22] [pigeon] Make wrapError in swift also handle PigeonError --- packages/pigeon/example/app/ios/Runner/Messages.g.swift | 7 +++++++ packages/pigeon/lib/swift_generator.dart | 9 +++++++++ .../test_plugin/ios/Classes/CoreTests.gen.swift | 7 +++++++ .../test_plugin/macos/Classes/CoreTests.gen.swift | 7 +++++++ 4 files changed, 30 insertions(+) diff --git a/packages/pigeon/example/app/ios/Runner/Messages.g.swift b/packages/pigeon/example/app/ios/Runner/Messages.g.swift index 68e4fead3b0..c128c67bb55 100644 --- a/packages/pigeon/example/app/ios/Runner/Messages.g.swift +++ b/packages/pigeon/example/app/ios/Runner/Messages.g.swift @@ -50,6 +50,13 @@ private func wrapError(_ error: Any) -> [Any?] { flutterError.details, ] } + if let pigeonError = error as? PigeonError { + return [ + pigeonError.code, + pigeonError.message, + pigeonError.details, + ] + } return [ "\(error)", "\(type(of: error))", diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index bcfbc970be1..172c911ebb8 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -660,6 +660,15 @@ class SwiftGenerator extends StructuredGenerator { indent.writeln('flutterError.details,'); }); }); + indent.write('if let pigeonError = error as? PigeonError '); + indent.addScoped('{', '}', () { + indent.write('return '); + indent.addScoped('[', ']', () { + indent.writeln('pigeonError.code,'); + indent.writeln('pigeonError.message,'); + indent.writeln('pigeonError.details,'); + }); + }); indent.write('return '); indent.addScoped('[', ']', () { indent.writeln(r'"\(error)",'); 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 2895b6dbb41..402e967fdc5 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 @@ -51,6 +51,13 @@ private func wrapError(_ error: Any) -> [Any?] { flutterError.details, ] } + if let pigeonError = error as? PigeonError { + return [ + pigeonError.code, + pigeonError.message, + pigeonError.details, + ] + } return [ "\(error)", "\(type(of: error))", 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 2895b6dbb41..402e967fdc5 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 @@ -51,6 +51,13 @@ private func wrapError(_ error: Any) -> [Any?] { flutterError.details, ] } + if let pigeonError = error as? PigeonError { + return [ + pigeonError.code, + pigeonError.message, + pigeonError.details, + ] + } return [ "\(error)", "\(type(of: error))", From 30ac0509ac882b221d3ab853b845bcb93807dfb8 Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Sat, 27 Apr 2024 04:26:21 +0900 Subject: [PATCH 08/22] [pigeon] Emit Error class for Swift with different names per file Like Kotlin, to not have a conflict with the same name of the error class, we should emit different names for each error class. --- .../example/app/ios/Runner/Messages.g.swift | 48 +++++++------- packages/pigeon/lib/pigeon_lib.dart | 10 +-- packages/pigeon/lib/swift_generator.dart | 66 +++++++++++-------- .../example/ios/RunnerTests/RunnerTests.swift | 4 +- .../ios/Classes/CoreTests.gen.swift | 24 ------- .../macos/Classes/CoreTests.gen.swift | 24 ------- packages/pigeon/test/pigeon_lib_test.dart | 12 ---- packages/pigeon/tool/shared/generation.dart | 22 +++++-- 8 files changed, 84 insertions(+), 126 deletions(-) diff --git a/packages/pigeon/example/app/ios/Runner/Messages.g.swift b/packages/pigeon/example/app/ios/Runner/Messages.g.swift index c128c67bb55..0c54220e6c2 100644 --- a/packages/pigeon/example/app/ios/Runner/Messages.g.swift +++ b/packages/pigeon/example/app/ios/Runner/Messages.g.swift @@ -14,30 +14,6 @@ import Foundation #error("Unsupported platform.") #endif -/// Error thrown by Pigeon. Encapsulates a code, message, and details. -final class PigeonError: Swift.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 - } - - init(flutterError: FlutterError) { - self.code = flutterError.code - self.message = flutterError.message - self.details = flutterError.details - } - - var localizedDescription: String { - return - "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" - } -} - private func wrapResult(_ result: Any?) -> [Any?] { return [result] } @@ -70,6 +46,30 @@ private func createConnectionError(withChannelName channelName: String) -> Pigeo details: "") } +/// Error class for passing custom error details to Flutter via a thrown PlatformException. +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 + } + + init(flutterError: FlutterError) { + self.code = flutterError.code + self.message = flutterError.message + self.details = flutterError.details + } + + var localizedDescription: String { + return + "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } +} + private func isNullish(_ value: Any?) -> Bool { return value is NSNull || value == nil } diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart index 42bac912741..75b2c548516 100644 --- a/packages/pigeon/lib/pigeon_lib.dart +++ b/packages/pigeon/lib/pigeon_lib.dart @@ -705,6 +705,8 @@ class SwiftGeneratorAdapter implements GeneratorAdapter { ? _lineReader( path.posix.join(options.basePath ?? '', options.copyrightHeader)) : null, + errorClassName: swiftOptions.errorClassName, + includeErrorClass: swiftOptions.includeErrorClass, )); const SwiftGenerator generator = SwiftGenerator(); generator.generate( @@ -2084,8 +2086,6 @@ ${_argParser.usage}'''; help: 'Path to generated Objective-C header file (.h).') ..addOption('objc_prefix', help: 'Prefix for generated Objective-C classes and protocols.') - ..addOption('swift_emit_pigeon_error_class', - help: 'Adds an error class to the generated Swift code.') ..addOption('copyright_header', help: 'Path to file with copyright header to be prepended to generated code.') @@ -2127,12 +2127,6 @@ ${_argParser.usage}'''; results['java_use_generated_annotation'] as bool?, ), swiftOut: results['swift_out'] as String?, - swiftOptions: SwiftOptions( - emitPigeonErrorClass: - (results['swift_emit_pigeon_error_class'] as String?) - ?.toLowerCase() != - 'false', - ), kotlinOut: results['kotlin_out'] as String?, kotlinOptions: KotlinOptions( package: results['kotlin_package'] as String?, diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index 172c911ebb8..8b9edcaaf87 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -19,22 +19,29 @@ class SwiftOptions { /// Creates a [SwiftOptions] object const SwiftOptions({ this.copyrightHeader, - this.emitPigeonErrorClass, + this.errorClassName, + this.includeErrorClass = true, }); /// A copyright header that will get prepended to generated code. final Iterable? copyrightHeader; - /// Whether to emit the PigeonError class in the generated code. - /// Defaults to true. - final bool? emitPigeonErrorClass; + /// The name of the error class used for passing custom error parameters. + final String? errorClassName; + + /// Whether to include the error class in generation. + /// + /// This should only ever be set to false if you have another generated + /// Kotlin file in the same directory. + final bool includeErrorClass; /// Creates a [SwiftOptions] from a Map representation where: /// `x = SwiftOptions.fromList(x.toMap())`. static SwiftOptions fromList(Map map) { return SwiftOptions( copyrightHeader: map['copyrightHeader'] as Iterable?, - emitPigeonErrorClass: map['emitPigeonErrorClass'] as bool?, + errorClassName: map['errorClassName'] as String?, + includeErrorClass: map['includeErrorClass'] as bool? ?? true, ); } @@ -43,8 +50,8 @@ class SwiftOptions { Map toMap() { final Map result = { if (copyrightHeader != null) 'copyrightHeader': copyrightHeader!, - if (emitPigeonErrorClass != null) - 'emitPigeonErrorClass': emitPigeonErrorClass!, + if (errorClassName != null) 'errorClassName': errorClassName!, + 'includeErrorClass': includeErrorClass, }; return result; } @@ -324,7 +331,7 @@ class SwiftGenerator extends StructuredGenerator { name: func.name, parameters: func.parameters, returnType: func.returnType, - errorTypeName: 'PigeonError', + errorTypeName: _getErrorClassName(generatorOptions), isAsynchronous: true, swiftFunction: func.swiftFunction, getParameterName: _getSafeArgumentName, @@ -358,6 +365,7 @@ class SwiftGenerator extends StructuredGenerator { indent, func.documentationComments, _docCommentSpec); _writeFlutterMethod( indent, + generatorOptions: generatorOptions, name: func.name, channelName: makeChannelName(api, func, dartPackageName), parameters: func.parameters, @@ -647,7 +655,7 @@ class SwiftGenerator extends StructuredGenerator { }); } - void _writeWrapError(Indent indent) { + void _writeWrapError(SwiftOptions generatorOptions, Indent indent) { indent.newln(); indent.write('private func wrapError(_ error: Any) -> [Any?] '); indent.addScoped('{', '}', () { @@ -660,7 +668,8 @@ class SwiftGenerator extends StructuredGenerator { indent.writeln('flutterError.details,'); }); }); - indent.write('if let pigeonError = error as? PigeonError '); + indent.write( + 'if let pigeonError = error as? ${_getErrorClassName(generatorOptions)} '); indent.addScoped('{', '}', () { indent.write('return '); indent.addScoped('[', ']', () { @@ -687,13 +696,14 @@ private func nilOrValue(_ value: Any?) -> T? { }'''); } - void _writeCreateConnectionError(Indent indent) { + void _writeCreateConnectionError( + SwiftOptions generatorOptions, Indent indent) { indent.newln(); indent.writeScoped( - 'private func createConnectionError(withChannelName channelName: String) -> PigeonError {', + 'private func createConnectionError(withChannelName channelName: String) -> ${_getErrorClassName(generatorOptions)} {', '}', () { indent.writeln( - 'return PigeonError(code: "channel-error", message: "Unable to establish connection on channel: \'\\(channelName)\'.", details: "")'); + 'return ${_getErrorClassName(generatorOptions)}(code: "channel-error", message: "Unable to establish connection on channel: \'\\(channelName)\'.", details: "")'); }); } @@ -711,16 +721,15 @@ private func nilOrValue(_ value: Any?) -> T? { .whereType() .any((Api api) => api.methods.isNotEmpty); - if (generatorOptions.emitPigeonErrorClass ?? true) { - _writePigeonError(indent); - } - if (hasHostApi) { _writeWrapResult(indent); - _writeWrapError(indent); + _writeWrapError(generatorOptions, indent); } if (hasFlutterApi) { - _writeCreateConnectionError(indent); + _writeCreateConnectionError(generatorOptions, indent); + } + if (generatorOptions.includeErrorClass) { + _writePigeonError(generatorOptions, indent); } _writeIsNullish(indent); _writeNilOrValue(indent); @@ -728,6 +737,7 @@ private func nilOrValue(_ value: Any?) -> T? { void _writeFlutterMethod( Indent indent, { + required SwiftOptions generatorOptions, required String name, required String channelName, required List parameters, @@ -739,7 +749,7 @@ private func nilOrValue(_ value: Any?) -> T? { name: name, parameters: parameters, returnType: returnType, - errorTypeName: 'PigeonError', + errorTypeName: _getErrorClassName(generatorOptions), isAsynchronous: true, swiftFunction: swiftFunction, getParameterName: _getSafeArgumentName, @@ -781,12 +791,12 @@ private func nilOrValue(_ value: Any?) -> T? { indent.writeln('let message: String? = nilOrValue(listResponse[1])'); indent.writeln('let details: String? = nilOrValue(listResponse[2])'); indent.writeln( - 'completion(.failure(PigeonError(code: code, message: message, details: details)))'); + 'completion(.failure(${_getErrorClassName(generatorOptions)}(code: code, message: message, details: details)))'); }, addTrailingNewline: false); if (!returnType.isNullable && !returnType.isVoid) { indent.addScoped('else if listResponse[0] == nil {', '} ', () { indent.writeln( - 'completion(.failure(PigeonError(code: "null-error", message: "Flutter api returned null value for non-null return value.", details: "")))'); + 'completion(.failure(${_getErrorClassName(generatorOptions)}(code: "null-error", message: "Flutter api returned null value for non-null return value.", details: "")))'); }, addTrailingNewline: false); } indent.addScoped('else {', '}', () { @@ -917,11 +927,12 @@ private func nilOrValue(_ value: Any?) -> T? { }); } - void _writePigeonError(Indent indent) { + void _writePigeonError(SwiftOptions generatorOptions, Indent indent) { indent.newln(); indent.writeln( - '/// Error thrown by Pigeon. Encapsulates a code, message, and details.'); - indent.writeln('final class PigeonError: Swift.Error {'); + '/// Error class for passing custom error details to Flutter via a thrown PlatformException.'); + indent.writeln( + 'final class ${_getErrorClassName(generatorOptions)}: Error {'); indent.nest(1, () { indent.writeln('let code: String'); indent.writeln('let message: String?'); @@ -948,7 +959,7 @@ private func nilOrValue(_ value: Any?) -> T? { indent.writeln('return'); indent.nest(1, () { indent.writeln( - r'"PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")"'); + '"${_getErrorClassName(generatorOptions)}(code: \\(code), message: \\(message ?? ""), details: \\(details ?? "")"'); }); }); indent.write('}'); @@ -962,6 +973,9 @@ private func nilOrValue(_ value: Any?) -> T? { /// Calculates the name of the codec that will be generated for [api]. String _getCodecName(Api api) => '${api.name}Codec'; +String _getErrorClassName(SwiftOptions generatorOptions) => + generatorOptions.errorClassName ?? 'PigeonError'; + String _getArgumentName(int count, NamedType argument) => argument.name.isEmpty ? 'arg$count' : argument.name; diff --git a/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/RunnerTests.swift b/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/RunnerTests.swift index e86e5c948d1..ebf4b180893 100644 --- a/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/RunnerTests.swift +++ b/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/RunnerTests.swift @@ -48,14 +48,14 @@ class RunnerTests: XCTestCase { } class FlutterApiFromProtocol: FlutterSmallApiProtocol { - func echo(string aStringArg: String, completion: @escaping (Result) -> Void) + func echo(string aStringArg: String, completion: @escaping (Result) -> Void) { completion(.success(aStringArg)) } func echo( _ msgArg: test_plugin.TestMessage, - completion: @escaping (Result) -> Void + completion: @escaping (Result) -> Void ) { completion(.success(msgArg)) } 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 402e967fdc5..6f88a7f750f 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,30 +15,6 @@ import Foundation #error("Unsupported platform.") #endif -/// Error thrown by Pigeon. Encapsulates a code, message, and details. -final class PigeonError: Swift.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 - } - - init(flutterError: FlutterError) { - self.code = flutterError.code - self.message = flutterError.message - self.details = flutterError.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 402e967fdc5..6f88a7f750f 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,30 +15,6 @@ import Foundation #error("Unsupported platform.") #endif -/// Error thrown by Pigeon. Encapsulates a code, message, and details. -final class PigeonError: Swift.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 - } - - init(flutterError: FlutterError) { - self.code = flutterError.code - self.message = flutterError.message - self.details = flutterError.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/test/pigeon_lib_test.dart b/packages/pigeon/test/pigeon_lib_test.dart index 8e1a768c2dc..34516e8c0de 100644 --- a/packages/pigeon/test/pigeon_lib_test.dart +++ b/packages/pigeon/test/pigeon_lib_test.dart @@ -128,18 +128,6 @@ void main() { expect(opts.javaOptions!.useGeneratedAnnotation, isTrue); }); - test('parse args - swift_emit_pigeon_error_class', () { - // Without this option, the default should be true. - final PigeonOptions opts0 = Pigeon.parseArgs([]); - expect(opts0.swiftOptions!.emitPigeonErrorClass, isTrue); - final PigeonOptions opts1 = - Pigeon.parseArgs(['--swift_emit_pigeon_error_class=true']); - expect(opts1.swiftOptions!.emitPigeonErrorClass, isTrue); - final PigeonOptions opts2 = - Pigeon.parseArgs(['--swift_emit_pigeon_error_class=false']); - expect(opts2.swiftOptions!.emitPigeonErrorClass, isFalse); - }); - test('parse args - cpp_source_out', () { final PigeonOptions opts = Pigeon.parseArgs(['--cpp_source_out', 'foo.cpp']); diff --git a/packages/pigeon/tool/shared/generation.dart b/packages/pigeon/tool/shared/generation.dart index 720a6e558c1..f4db2be3f06 100644 --- a/packages/pigeon/tool/shared/generation.dart +++ b/packages/pigeon/tool/shared/generation.dart @@ -82,13 +82,17 @@ Future generateTestPigeons({required String baseDir}) async { final Set skipLanguages = _unsupportedFiles[input] ?? {}; - final bool kotlinErrorClassGenerationTestFiles = + final bool kotlinAndSwiftErrorClassGenerationTestFiles = input == 'core_tests' || input == 'background_platform_channels'; - final String kotlinErrorName = kotlinErrorClassGenerationTestFiles + final String kotlinErrorName = kotlinAndSwiftErrorClassGenerationTestFiles ? 'FlutterError' : '${pascalCaseName}Error'; + final String swiftErrorName = kotlinAndSwiftErrorClassGenerationTestFiles + ? 'PigeonError' + : '${pascalCaseName}Error'; + // Generate the default language test plugin output. int generateCode = await runPigeon( input: './pigeons/$input.dart', @@ -104,6 +108,8 @@ Future generateTestPigeons({required String baseDir}) async { swiftOut: skipLanguages.contains(GeneratorLanguage.swift) ? null : '$outputBase/ios/Classes/$pascalCaseName.gen.swift', + swiftErrorClassName: swiftErrorName, + swiftIncludeErrorClass: input != 'core_tests', // Windows cppHeaderOut: skipLanguages.contains(GeneratorLanguage.cpp) ? null @@ -114,7 +120,6 @@ Future generateTestPigeons({required String baseDir}) async { cppNamespace: '${input}_pigeontest', suppressVersion: true, dartPackageName: 'pigeon_integration_tests', - swiftEmitPigeonErrorClass: input == 'core_tests', ); if (generateCode != 0) { return generateCode; @@ -128,9 +133,10 @@ Future generateTestPigeons({required String baseDir}) async { swiftOut: skipLanguages.contains(GeneratorLanguage.swift) ? null : '$outputBase/macos/Classes/$pascalCaseName.gen.swift', + swiftErrorClassName: swiftErrorName, + swiftIncludeErrorClass: input != 'core_tests', suppressVersion: true, dartPackageName: 'pigeon_integration_tests', - swiftEmitPigeonErrorClass: input == 'core_tests', ); if (generateCode != 0) { return generateCode; @@ -190,6 +196,8 @@ Future runPigeon({ String? kotlinErrorClassName, bool kotlinIncludeErrorClass = true, String? swiftOut, + String? swiftErrorClassName, + bool swiftIncludeErrorClass = true, String? cppHeaderOut, String? cppSourceOut, String? cppNamespace, @@ -204,7 +212,6 @@ Future runPigeon({ String copyrightHeader = './copyright_header.txt', String? basePath, String? dartPackageName, - bool swiftEmitPigeonErrorClass = true, }) async { // Temporarily suppress the version output via the global flag if requested. // This is done because having the version in all the generated test output @@ -239,7 +246,10 @@ Future runPigeon({ objcSourceOut: objcSourceOut, objcOptions: ObjcOptions(prefix: objcPrefix), swiftOut: swiftOut, - swiftOptions: SwiftOptions(emitPigeonErrorClass: swiftEmitPigeonErrorClass), + swiftOptions: SwiftOptions( + errorClassName: swiftErrorClassName, + includeErrorClass: swiftIncludeErrorClass, + ), basePath: basePath, dartPackageName: dartPackageName, )); From 92c97afd1b98f47e1dabf266f58b1c2c3785f3d3 Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Sat, 27 Apr 2024 04:30:08 +0900 Subject: [PATCH 09/22] [pigeon] Restore docregion for swift-class --- packages/pigeon/example/app/ios/Runner/AppDelegate.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift index 6e7b978f801..20b718a268d 100644 --- a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift +++ b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift @@ -5,6 +5,7 @@ import Flutter import UIKit +// #docregion swift-class private class PigeonApiImplementation: ExampleHostApi { func getHostLanguage() throws -> String { return "Swift" From 86d9168b8bf134e387755accfe44dfb1111f06dd Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Sat, 27 Apr 2024 04:30:32 +0900 Subject: [PATCH 10/22] [pigeon] Add CHANGELOG.md entry --- packages/pigeon/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index fb01483089c..ab5d4b1c1fe 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,3 +1,6 @@ +## 19.0.0 +* **Breaking Change** [swift] Do not use `FlutterError` on the generated code. + ## 18.0.0 * Adds message channel suffix option to all APIs. From 20ac87a4e71c6f10962ff57f166c2abd95d86005 Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Sat, 27 Apr 2024 04:36:55 +0900 Subject: [PATCH 11/22] [pigeon] Apply format fix --- .../test_plugin/example/ios/RunnerTests/RunnerTests.swift | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/RunnerTests.swift b/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/RunnerTests.swift index ebf4b180893..4e02aa6e24b 100644 --- a/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/RunnerTests.swift +++ b/packages/pigeon/platform_tests/test_plugin/example/ios/RunnerTests/RunnerTests.swift @@ -48,8 +48,10 @@ class RunnerTests: XCTestCase { } class FlutterApiFromProtocol: FlutterSmallApiProtocol { - func echo(string aStringArg: String, completion: @escaping (Result) -> Void) - { + func echo( + string aStringArg: String, + completion: @escaping (Result) -> Void + ) { completion(.success(aStringArg)) } From 800ba60a802fff69afb75b547cb76dae3bfa8e15 Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Sat, 27 Apr 2024 04:54:28 +0900 Subject: [PATCH 12/22] [pigeon] Generate Swift error on CoreTests.gen.swift This is because `pod lib lint` runs without the generated code. Therefore, CoreTests.gen.swift must be able to compile on its own. --- .../example/app/ios/Runner/Messages.g.swift | 2 +- packages/pigeon/lib/pigeon_lib.dart | 1 - packages/pigeon/lib/swift_generator.dart | 16 +++---------- .../ios/Classes/CoreTests.gen.swift | 24 +++++++++++++++++++ .../macos/Classes/CoreTests.gen.swift | 24 +++++++++++++++++++ packages/pigeon/tool/shared/generation.dart | 19 +++++++-------- 6 files changed, 60 insertions(+), 26 deletions(-) diff --git a/packages/pigeon/example/app/ios/Runner/Messages.g.swift b/packages/pigeon/example/app/ios/Runner/Messages.g.swift index 0c54220e6c2..8b776aee6f4 100644 --- a/packages/pigeon/example/app/ios/Runner/Messages.g.swift +++ b/packages/pigeon/example/app/ios/Runner/Messages.g.swift @@ -46,7 +46,7 @@ private func createConnectionError(withChannelName channelName: String) -> Pigeo details: "") } -/// Error class for passing custom error details to Flutter via a thrown PlatformException. +/// Error class for passing custom error details to Flutter. final class PigeonError: Error { let code: String let message: String? diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart index 75b2c548516..d7c0110056f 100644 --- a/packages/pigeon/lib/pigeon_lib.dart +++ b/packages/pigeon/lib/pigeon_lib.dart @@ -706,7 +706,6 @@ class SwiftGeneratorAdapter implements GeneratorAdapter { path.posix.join(options.basePath ?? '', options.copyrightHeader)) : null, errorClassName: swiftOptions.errorClassName, - includeErrorClass: swiftOptions.includeErrorClass, )); const SwiftGenerator generator = SwiftGenerator(); generator.generate( diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index 8b9edcaaf87..16240604690 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -20,7 +20,6 @@ class SwiftOptions { const SwiftOptions({ this.copyrightHeader, this.errorClassName, - this.includeErrorClass = true, }); /// A copyright header that will get prepended to generated code. @@ -29,19 +28,12 @@ class SwiftOptions { /// The name of the error class used for passing custom error parameters. final String? errorClassName; - /// Whether to include the error class in generation. - /// - /// This should only ever be set to false if you have another generated - /// Kotlin file in the same directory. - final bool includeErrorClass; - /// Creates a [SwiftOptions] from a Map representation where: /// `x = SwiftOptions.fromList(x.toMap())`. static SwiftOptions fromList(Map map) { return SwiftOptions( copyrightHeader: map['copyrightHeader'] as Iterable?, errorClassName: map['errorClassName'] as String?, - includeErrorClass: map['includeErrorClass'] as bool? ?? true, ); } @@ -51,7 +43,6 @@ class SwiftOptions { final Map result = { if (copyrightHeader != null) 'copyrightHeader': copyrightHeader!, if (errorClassName != null) 'errorClassName': errorClassName!, - 'includeErrorClass': includeErrorClass, }; return result; } @@ -728,9 +719,8 @@ private func nilOrValue(_ value: Any?) -> T? { if (hasFlutterApi) { _writeCreateConnectionError(generatorOptions, indent); } - if (generatorOptions.includeErrorClass) { - _writePigeonError(generatorOptions, indent); - } + + _writePigeonError(generatorOptions, indent); _writeIsNullish(indent); _writeNilOrValue(indent); } @@ -930,7 +920,7 @@ private func nilOrValue(_ value: Any?) -> T? { void _writePigeonError(SwiftOptions generatorOptions, Indent indent) { indent.newln(); indent.writeln( - '/// Error class for passing custom error details to Flutter via a thrown PlatformException.'); + '/// Error class for passing custom error details to Flutter.'); indent.writeln( 'final class ${_getErrorClassName(generatorOptions)}: Error {'); indent.nest(1, () { 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 6f88a7f750f..a94b0f563b6 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 @@ -47,6 +47,30 @@ private func createConnectionError(withChannelName channelName: String) -> Pigeo details: "") } +/// Error class for passing custom error details to Flutter. +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 + } + + init(flutterError: FlutterError) { + self.code = flutterError.code + self.message = flutterError.message + self.details = flutterError.details + } + + var localizedDescription: String { + return + "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } +} + private func isNullish(_ value: Any?) -> Bool { return value is NSNull || value == nil } 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 6f88a7f750f..a94b0f563b6 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 @@ -47,6 +47,30 @@ private func createConnectionError(withChannelName channelName: String) -> Pigeo details: "") } +/// Error class for passing custom error details to Flutter. +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 + } + + init(flutterError: FlutterError) { + self.code = flutterError.code + self.message = flutterError.message + self.details = flutterError.details + } + + var localizedDescription: String { + return + "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" + } +} + private func isNullish(_ value: Any?) -> Bool { return value is NSNull || value == nil } diff --git a/packages/pigeon/tool/shared/generation.dart b/packages/pigeon/tool/shared/generation.dart index f4db2be3f06..c4cf84582f1 100644 --- a/packages/pigeon/tool/shared/generation.dart +++ b/packages/pigeon/tool/shared/generation.dart @@ -82,16 +82,17 @@ Future generateTestPigeons({required String baseDir}) async { final Set skipLanguages = _unsupportedFiles[input] ?? {}; - final bool kotlinAndSwiftErrorClassGenerationTestFiles = + final bool kotlinErrorClassGenerationTestFiles = input == 'core_tests' || input == 'background_platform_channels'; - final String kotlinErrorName = kotlinAndSwiftErrorClassGenerationTestFiles + final String kotlinErrorName = kotlinErrorClassGenerationTestFiles ? 'FlutterError' : '${pascalCaseName}Error'; - final String swiftErrorName = kotlinAndSwiftErrorClassGenerationTestFiles - ? 'PigeonError' - : '${pascalCaseName}Error'; + final bool swiftErrorUseDefaultErrorName = input == 'core_tests'; + + final String? swiftErrorClassName = + swiftErrorUseDefaultErrorName ? null : '${pascalCaseName}Error'; // Generate the default language test plugin output. int generateCode = await runPigeon( @@ -108,8 +109,7 @@ Future generateTestPigeons({required String baseDir}) async { swiftOut: skipLanguages.contains(GeneratorLanguage.swift) ? null : '$outputBase/ios/Classes/$pascalCaseName.gen.swift', - swiftErrorClassName: swiftErrorName, - swiftIncludeErrorClass: input != 'core_tests', + swiftErrorClassName: swiftErrorClassName, // Windows cppHeaderOut: skipLanguages.contains(GeneratorLanguage.cpp) ? null @@ -133,8 +133,7 @@ Future generateTestPigeons({required String baseDir}) async { swiftOut: skipLanguages.contains(GeneratorLanguage.swift) ? null : '$outputBase/macos/Classes/$pascalCaseName.gen.swift', - swiftErrorClassName: swiftErrorName, - swiftIncludeErrorClass: input != 'core_tests', + swiftErrorClassName: swiftErrorClassName, suppressVersion: true, dartPackageName: 'pigeon_integration_tests', ); @@ -197,7 +196,6 @@ Future runPigeon({ bool kotlinIncludeErrorClass = true, String? swiftOut, String? swiftErrorClassName, - bool swiftIncludeErrorClass = true, String? cppHeaderOut, String? cppSourceOut, String? cppNamespace, @@ -248,7 +246,6 @@ Future runPigeon({ swiftOut: swiftOut, swiftOptions: SwiftOptions( errorClassName: swiftErrorClassName, - includeErrorClass: swiftIncludeErrorClass, ), basePath: basePath, dartPackageName: dartPackageName, From 8fa6b756d006c9346df95e32426a6ce6e6904821 Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Sat, 27 Apr 2024 04:58:12 +0900 Subject: [PATCH 13/22] [pigeon] Fix unittests for swift generator --- packages/pigeon/test/swift_generator_test.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/pigeon/test/swift_generator_test.dart b/packages/pigeon/test/swift_generator_test.dart index a4eb67427b9..c377d6f29fc 100644 --- a/packages/pigeon/test/swift_generator_test.dart +++ b/packages/pigeon/test/swift_generator_test.dart @@ -394,7 +394,7 @@ void main() { ); final String code = sink.toString(); expect(code, - contains('completion: @escaping (Result) -> Void')); + contains('completion: @escaping (Result) -> Void')); expect(code, contains('completion(.success(Void()))')); expect(code, isNot(contains('if ('))); }); @@ -476,7 +476,7 @@ void main() { expect( code, contains( - 'func doSomething(completion: @escaping (Result) -> Void)')); + 'func doSomething(completion: @escaping (Result) -> Void)')); expect(code, contains('channel.sendMessage(nil')); expect(code, isNot(contains('if ('))); }); @@ -961,7 +961,7 @@ void main() { expect( code, contains( - 'func doit(completion: @escaping (Result<[Int64?], FlutterError>) -> Void)')); + 'func doit(completion: @escaping (Result<[Int64?], PigeonError>) -> Void)')); expect(code, contains('let result = listResponse[0] as! [Int64?]')); expect(code, contains('completion(.success(result))')); }); @@ -1049,7 +1049,7 @@ void main() { expect( code, contains( - 'func add(x xArg: Int64, y yArg: Int64, completion: @escaping (Result) -> Void)')); + 'func add(x xArg: Int64, y yArg: Int64, completion: @escaping (Result) -> Void)')); expect(code, contains('channel.sendMessage([xArg, yArg] as [Any?]) { response in')); }); @@ -1189,7 +1189,7 @@ void main() { expect( code, contains( - 'func doit(foo fooArg: Int64?, completion: @escaping (Result) -> Void)')); + 'func doit(foo fooArg: Int64?, completion: @escaping (Result) -> Void)')); }); test('nonnull fields', () { @@ -1569,6 +1569,6 @@ void main() { expect( code, contains( - 'return FlutterError(code: "channel-error", message: "Unable to establish connection on channel: \'\\(channelName)\'.", details: "")')); + 'return PigeonError(code: "channel-error", message: "Unable to establish connection on channel: \'\\(channelName)\'.", details: "")')); }); } From 4bd3dbcb01c59bf7892051233988798dfc91dd97 Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Sat, 27 Apr 2024 05:09:37 +0900 Subject: [PATCH 14/22] [pigeon] Run update-excerpts --- packages/pigeon/example/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/pigeon/example/README.md b/packages/pigeon/example/README.md index a84621339f4..7e8153c49b9 100644 --- a/packages/pigeon/example/README.md +++ b/packages/pigeon/example/README.md @@ -118,7 +118,6 @@ This is the code that will use the generated Swift code to receive calls from Fl packages/pigeon/example/app/ios/Runner/AppDelegate.swift ```swift - private class PigeonApiImplementation: ExampleHostApi { func getHostLanguage() throws -> String { return "Swift" @@ -126,14 +125,14 @@ private class PigeonApiImplementation: ExampleHostApi { func add(_ a: Int64, to b: Int64) throws -> Int64 { if a < 0 || b < 0 { - throw PigeonError(code: "code", message: "message", details: "details") + throw FlutterError(code: "code", message: "message", details: "details") } return a + b } func sendMessage(message: MessageData, completion: @escaping (Result) -> Void) { if message.code == Code.one { - completion(.failure(PigeonError(code: "code", message: "message", details: "details"))) + completion(.failure(FlutterError(code: "code", message: "message", details: "details"))) return } completion(.success(true)) From fae49eae2ccaa80e1f59150bb743ffaaddd489b6 Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Sat, 27 Apr 2024 05:09:56 +0900 Subject: [PATCH 15/22] [pigeon] Bump version correctly --- packages/pigeon/lib/generator_tools.dart | 2 +- packages/pigeon/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart index 76c9abf1886..7cc3e526052 100644 --- a/packages/pigeon/lib/generator_tools.dart +++ b/packages/pigeon/lib/generator_tools.dart @@ -13,7 +13,7 @@ import 'ast.dart'; /// The current version of pigeon. /// /// This must match the version in pubspec.yaml. -const String pigeonVersion = '18.0.0'; +const String pigeonVersion = '19.0.0'; /// Read all the content from [stdin] to a String. String readStdin() { diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml index f368b97536a..d5e85e7d879 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: 18.0.0 # This must match the version in lib/generator_tools.dart +version: 19.0.0 # This must match the version in lib/generator_tools.dart environment: sdk: ^3.1.0 From 78c3bb6f9bb784a13006bc6eb8502536f369fc86 Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Thu, 9 May 2024 23:58:21 +0900 Subject: [PATCH 16/22] Remove conversion from FlutterError to PigeonError in Swift --- packages/pigeon/example/app/ios/Runner/Messages.g.swift | 6 ------ packages/pigeon/lib/swift_generator.dart | 8 -------- .../test_plugin/ios/Classes/CoreTests.gen.swift | 6 ------ .../test_plugin/macos/Classes/CoreTests.gen.swift | 6 ------ 4 files changed, 26 deletions(-) diff --git a/packages/pigeon/example/app/ios/Runner/Messages.g.swift b/packages/pigeon/example/app/ios/Runner/Messages.g.swift index 6628ebedb24..219351015b6 100644 --- a/packages/pigeon/example/app/ios/Runner/Messages.g.swift +++ b/packages/pigeon/example/app/ios/Runner/Messages.g.swift @@ -58,12 +58,6 @@ final class PigeonError: Error { self.details = details } - init(flutterError: FlutterError) { - self.code = flutterError.code - self.message = flutterError.message - self.details = flutterError.details - } - var localizedDescription: String { return "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index 9b7475e6c2f..6054f0378d8 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -911,14 +911,6 @@ private func nilOrValue(_ value: Any?) -> T? { }); indent.writeln('}'); indent.newln(); - indent.writeln('init(flutterError: FlutterError) {'); - indent.nest(1, () { - indent.writeln('self.code = flutterError.code'); - indent.writeln('self.message = flutterError.message'); - indent.writeln('self.details = flutterError.details'); - }); - indent.writeln('}'); - indent.newln(); indent.writeln('var localizedDescription: String {'); indent.nest(1, () { indent.writeln('return'); 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 4157724b7ca..e0feecd10f0 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 @@ -59,12 +59,6 @@ final class PigeonError: Error { self.details = details } - init(flutterError: FlutterError) { - self.code = flutterError.code - self.message = flutterError.message - self.details = flutterError.details - } - var localizedDescription: String { return "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" 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 4157724b7ca..e0feecd10f0 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 @@ -59,12 +59,6 @@ final class PigeonError: Error { self.details = details } - init(flutterError: FlutterError) { - self.code = flutterError.code - self.message = flutterError.message - self.details = flutterError.details - } - var localizedDescription: String { return "PigeonError(code: \(code), message: \(message ?? ""), details: \(details ?? "")" From f13a06a50cb8a513a35f3d7d4bc1a3d2f48bf428 Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Fri, 10 May 2024 00:00:46 +0900 Subject: [PATCH 17/22] Update the comment for the PigeonError class in Swift --- packages/pigeon/example/app/ios/Runner/Messages.g.swift | 2 +- packages/pigeon/lib/swift_generator.dart | 2 +- .../platform_tests/test_plugin/ios/Classes/CoreTests.gen.swift | 2 +- .../test_plugin/macos/Classes/CoreTests.gen.swift | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/pigeon/example/app/ios/Runner/Messages.g.swift b/packages/pigeon/example/app/ios/Runner/Messages.g.swift index 219351015b6..f0f78d6df44 100644 --- a/packages/pigeon/example/app/ios/Runner/Messages.g.swift +++ b/packages/pigeon/example/app/ios/Runner/Messages.g.swift @@ -46,7 +46,7 @@ private func createConnectionError(withChannelName channelName: String) -> Pigeo details: "") } -/// Error class for passing custom error details to Flutter. +/// Error class for passing custom error details to Dart side. final class PigeonError: Error { let code: String let message: String? diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index 6054f0378d8..fd377a7f9a5 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -895,7 +895,7 @@ private func nilOrValue(_ value: Any?) -> T? { void _writePigeonError(SwiftOptions generatorOptions, Indent indent) { indent.newln(); indent.writeln( - '/// Error class for passing custom error details to Flutter.'); + '/// Error class for passing custom error details to Dart side.'); indent.writeln( 'final class ${_getErrorClassName(generatorOptions)}: Error {'); indent.nest(1, () { 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 e0feecd10f0..7cdb418ea8a 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 @@ -47,7 +47,7 @@ private func createConnectionError(withChannelName channelName: String) -> Pigeo details: "") } -/// Error class for passing custom error details to Flutter. +/// Error class for passing custom error details to Dart side. final class PigeonError: Error { let code: String let message: String? 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 e0feecd10f0..7cdb418ea8a 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 @@ -47,7 +47,7 @@ private func createConnectionError(withChannelName channelName: String) -> Pigeo details: "") } -/// Error class for passing custom error details to Flutter. +/// Error class for passing custom error details to Dart side. final class PigeonError: Error { let code: String let message: String? From 63df9154bf047a7ea3a7d26ffd5b8a567433583e Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Fri, 10 May 2024 00:29:44 +0900 Subject: [PATCH 18/22] Update the documentation for using PigeonError in Swift And replace existing usage of FlutterError. --- packages/pigeon/README.md | 2 +- packages/pigeon/example/README.md | 7 ++++--- .../example/app/ios/Runner/AppDelegate.swift | 4 ++-- .../test_plugin/ios/Classes/TestPlugin.swift | 19 +++++++------------ .../macos/Classes/TestPlugin.swift | 19 +++++++------------ 5 files changed, 21 insertions(+), 30 deletions(-) diff --git a/packages/pigeon/README.md b/packages/pigeon/README.md index 9c185613696..2eb938092a1 100644 --- a/packages/pigeon/README.md +++ b/packages/pigeon/README.md @@ -53,7 +53,7 @@ should be returned via the provided callback. To pass custom details into `PlatformException` for error handling, use `FlutterError` in your Host API. [Example](./example/README.md#HostApi_Example). -For swift, use `PigeonError` instead of `FlutterError`. +For swift, use `PigeonError` instead of `FlutterError` when throwing an error. See [Example#Swift](./example/README.md#Swift) for more details. #### Objective-C and C++ diff --git a/packages/pigeon/example/README.md b/packages/pigeon/example/README.md index 7e8153c49b9..ebe7b5afc51 100644 --- a/packages/pigeon/example/README.md +++ b/packages/pigeon/example/README.md @@ -115,7 +115,8 @@ Future sendMessage(String messageText) { ### Swift This is the code that will use the generated Swift code to receive calls from Flutter. -packages/pigeon/example/app/ios/Runner/AppDelegate.swift +Unlike other languages, when throwing an error (both synchronous and asynchronous), use `PigeonError` instead of `FlutterError`, as `FlutterError` is not conforming to `Swift.Error`. +Previously, a workaround was to declare an extension to `FlutterError` to conform to `Swift.Error`, but, as of Pigeon 19.0.0, `PigeonError` is provided for this purpose. Older code still using `FlutterError` will continue to work, but it is recommended to switch to `PigeonError` and remove the extension. ```swift private class PigeonApiImplementation: ExampleHostApi { @@ -125,14 +126,14 @@ private class PigeonApiImplementation: ExampleHostApi { func add(_ a: Int64, to b: Int64) throws -> Int64 { if a < 0 || b < 0 { - throw FlutterError(code: "code", message: "message", details: "details") + throw PigeonError(code: "code", message: "message", details: "details") } return a + b } func sendMessage(message: MessageData, completion: @escaping (Result) -> Void) { if message.code == Code.one { - completion(.failure(FlutterError(code: "code", message: "message", details: "details"))) + completion(.failure(PigeonError(code: "code", message: "message", details: "details"))) return } completion(.success(true)) diff --git a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift index 20b718a268d..51119e23fa3 100644 --- a/packages/pigeon/example/app/ios/Runner/AppDelegate.swift +++ b/packages/pigeon/example/app/ios/Runner/AppDelegate.swift @@ -13,14 +13,14 @@ private class PigeonApiImplementation: ExampleHostApi { func add(_ a: Int64, to b: Int64) throws -> Int64 { if a < 0 || b < 0 { - throw FlutterError(code: "code", message: "message", details: "details") + throw PigeonError(code: "code", message: "message", details: "details") } return a + b } func sendMessage(message: MessageData, completion: @escaping (Result) -> Void) { if message.code == Code.one { - completion(.failure(FlutterError(code: "code", message: "message", details: "details"))) + completion(.failure(PigeonError(code: "code", message: "message", details: "details"))) return } completion(.success(true)) 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 08e1755d603..36e6a445b53 100644 --- a/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/ios/Classes/TestPlugin.swift @@ -5,11 +5,6 @@ import Flutter import UIKit -// If someone want to throw FlutterError from Swift, they need to conform to Error protocol. -// However this is not recommended as this extension will be public visibility, as -// both FlutterError and Error are public. -extension FlutterError: Error {} - /// This plugin handles the native side of the integration tests in /// example/integration_test/. public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { @@ -53,15 +48,15 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { } func throwError() throws -> Any? { - throw FlutterError(code: "code", message: "message", details: "details") + throw PigeonError(code: "code", message: "message", details: "details") } func throwErrorFromVoid() throws { - throw FlutterError(code: "code", message: "message", details: "details") + throw PigeonError(code: "code", message: "message", details: "details") } func throwFlutterError() throws -> Any? { - throw FlutterError(code: "code", message: "message", details: "details") + throw PigeonError(code: "code", message: "message", details: "details") } func echo(_ anInt: Int64) -> Int64 { @@ -189,15 +184,15 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { } func throwAsyncError(completion: @escaping (Result) -> Void) { - completion(.failure(FlutterError(code: "code", message: "message", details: "details"))) + completion(.failure(PigeonError(code: "code", message: "message", details: "details"))) } func throwAsyncErrorFromVoid(completion: @escaping (Result) -> Void) { - completion(.failure(FlutterError(code: "code", message: "message", details: "details"))) + completion(.failure(PigeonError(code: "code", message: "message", details: "details"))) } func throwAsyncFlutterError(completion: @escaping (Result) -> Void) { - completion(.failure(FlutterError(code: "code", message: "message", details: "details"))) + completion(.failure(PigeonError(code: "code", message: "message", details: "details"))) } func echoAsync(_ everything: AllTypes, completion: @escaping (Result) -> Void) { @@ -626,7 +621,7 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { } else { completion( .failure( - FlutterError( + PigeonError( code: "", message: "Multi-instance responses were not matching: \(resOne), \(resTwo)", details: nil))) 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 bed76184fef..5a649736641 100644 --- a/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift +++ b/packages/pigeon/platform_tests/test_plugin/macos/Classes/TestPlugin.swift @@ -5,11 +5,6 @@ import Cocoa import FlutterMacOS -// If someone want to throw FlutterError from Swift, they need to conform to Error protocol. -// However this is not recommended as this extension will be public visibility, as -// both FlutterError and Error are public. -extension FlutterError: Error {} - /// This plugin handles the native side of the integration tests in /// example/integration_test/. public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { @@ -52,15 +47,15 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { } func throwError() throws -> Any? { - throw FlutterError(code: "code", message: "message", details: "details") + throw PigeonError(code: "code", message: "message", details: "details") } func throwErrorFromVoid() throws { - throw FlutterError(code: "code", message: "message", details: "details") + throw PigeonError(code: "code", message: "message", details: "details") } func throwFlutterError() throws -> Any? { - throw FlutterError(code: "code", message: "message", details: "details") + throw PigeonError(code: "code", message: "message", details: "details") } func echo(_ anInt: Int64) -> Int64 { @@ -188,15 +183,15 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { } func throwAsyncError(completion: @escaping (Result) -> Void) { - completion(.failure(FlutterError(code: "code", message: "message", details: "details"))) + completion(.failure(PigeonError(code: "code", message: "message", details: "details"))) } func throwAsyncErrorFromVoid(completion: @escaping (Result) -> Void) { - completion(.failure(FlutterError(code: "code", message: "message", details: "details"))) + completion(.failure(PigeonError(code: "code", message: "message", details: "details"))) } func throwAsyncFlutterError(completion: @escaping (Result) -> Void) { - completion(.failure(FlutterError(code: "code", message: "message", details: "details"))) + completion(.failure(PigeonError(code: "code", message: "message", details: "details"))) } func echoAsync(_ everything: AllTypes, completion: @escaping (Result) -> Void) { @@ -625,7 +620,7 @@ public class TestPlugin: NSObject, FlutterPlugin, HostIntegrationCoreApi { } else { completion( .failure( - FlutterError( + PigeonError( code: "", message: "Multi-instance responses were not matching: \(resOne), \(resTwo)", details: nil))) From 428736d6c585896e78fe81ffc4f06f1fdb31caca Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Sat, 11 May 2024 02:17:04 +0900 Subject: [PATCH 19/22] Replace several usage of Indent.nest with Indent.writeScoped --- packages/pigeon/lib/swift_generator.dart | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index fd377a7f9a5..9c56952f96c 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -896,34 +896,27 @@ private func nilOrValue(_ value: Any?) -> T? { indent.newln(); indent.writeln( '/// Error class for passing custom error details to Dart side.'); - indent.writeln( - 'final class ${_getErrorClassName(generatorOptions)}: Error {'); - indent.nest(1, () { + indent.writeScoped( + 'final class ${_getErrorClassName(generatorOptions)}: Error {', '}', + () { indent.writeln('let code: String'); indent.writeln('let message: String?'); indent.writeln('let details: Any?'); indent.newln(); - indent.writeln('init(code: String, message: String?, details: Any?) {'); - indent.nest(1, () { + indent.writeScoped( + 'init(code: String, message: String?, details: Any?) {', '}', () { indent.writeln('self.code = code'); indent.writeln('self.message = message'); indent.writeln('self.details = details'); }); - indent.writeln('}'); indent.newln(); - indent.writeln('var localizedDescription: String {'); - indent.nest(1, () { - indent.writeln('return'); - indent.nest(1, () { + indent.writeScoped('var localizedDescription: String {', '}', () { + indent.writeScoped('return', '', () { indent.writeln( '"${_getErrorClassName(generatorOptions)}(code: \\(code), message: \\(message ?? ""), details: \\(details ?? "")"'); - }); + }, addTrailingNewline: false); }); - indent.write('}'); }); - indent.newln(); - indent.write('}'); - indent.newln(); } } From 88cd27deae83487751a922f29bc95a20ff1744b2 Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Sat, 11 May 2024 02:19:38 +0900 Subject: [PATCH 20/22] Update Changelog and README of pigeon package --- packages/pigeon/CHANGELOG.md | 1 + packages/pigeon/example/README.md | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index 7437282ec60..2e255c0b43a 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,4 +1,5 @@ ## 19.0.0 + * **Breaking Change** [swift] Do not use `FlutterError` on the generated code. ## 18.0.1 diff --git a/packages/pigeon/example/README.md b/packages/pigeon/example/README.md index ebe7b5afc51..46e8c643b1d 100644 --- a/packages/pigeon/example/README.md +++ b/packages/pigeon/example/README.md @@ -115,8 +115,7 @@ Future sendMessage(String messageText) { ### Swift This is the code that will use the generated Swift code to receive calls from Flutter. -Unlike other languages, when throwing an error (both synchronous and asynchronous), use `PigeonError` instead of `FlutterError`, as `FlutterError` is not conforming to `Swift.Error`. -Previously, a workaround was to declare an extension to `FlutterError` to conform to `Swift.Error`, but, as of Pigeon 19.0.0, `PigeonError` is provided for this purpose. Older code still using `FlutterError` will continue to work, but it is recommended to switch to `PigeonError` and remove the extension. +Unlike other languages, when throwing an error, use `PigeonError` instead of `FlutterError`, as `FlutterError` does not conform to `Swift.Error`. ```swift private class PigeonApiImplementation: ExampleHostApi { From a78cae7def2f8f2b43cd3d5328b0bfd2f717f6cd Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Sat, 11 May 2024 06:00:59 +0900 Subject: [PATCH 21/22] Addressing review comments --- packages/pigeon/CHANGELOG.md | 2 +- .../example/app/ios/Runner/Messages.g.swift | 14 +++++++------- packages/pigeon/lib/swift_generator.dart | 18 +++++++++--------- .../ios/Classes/CoreTests.gen.swift | 14 +++++++------- .../macos/Classes/CoreTests.gen.swift | 14 +++++++------- 5 files changed, 31 insertions(+), 31 deletions(-) diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md index 2e255c0b43a..826c16c7668 100644 --- a/packages/pigeon/CHANGELOG.md +++ b/packages/pigeon/CHANGELOG.md @@ -1,6 +1,6 @@ ## 19.0.0 -* **Breaking Change** [swift] Do not use `FlutterError` on the generated code. +* **Breaking Change** [swift] Removes `FlutterError` in favor of `PigeonError`. ## 18.0.1 diff --git a/packages/pigeon/example/app/ios/Runner/Messages.g.swift b/packages/pigeon/example/app/ios/Runner/Messages.g.swift index f0f78d6df44..7510022da7f 100644 --- a/packages/pigeon/example/app/ios/Runner/Messages.g.swift +++ b/packages/pigeon/example/app/ios/Runner/Messages.g.swift @@ -19,13 +19,6 @@ private func wrapResult(_ result: Any?) -> [Any?] { } private func wrapError(_ error: Any) -> [Any?] { - if let flutterError = error as? FlutterError { - return [ - flutterError.code, - flutterError.message, - flutterError.details, - ] - } if let pigeonError = error as? PigeonError { return [ pigeonError.code, @@ -33,6 +26,13 @@ private func wrapError(_ error: Any) -> [Any?] { pigeonError.details, ] } + if let flutterError = error as? FlutterError { + return [ + flutterError.code, + flutterError.message, + flutterError.details, + ] + } return [ "\(error)", "\(type(of: error))", diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index 9c56952f96c..2e1e4705c91 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -625,15 +625,6 @@ class SwiftGenerator extends StructuredGenerator { indent.newln(); indent.write('private func wrapError(_ error: Any) -> [Any?] '); indent.addScoped('{', '}', () { - indent.write('if let flutterError = error as? FlutterError '); - indent.addScoped('{', '}', () { - indent.write('return '); - indent.addScoped('[', ']', () { - indent.writeln('flutterError.code,'); - indent.writeln('flutterError.message,'); - indent.writeln('flutterError.details,'); - }); - }); indent.write( 'if let pigeonError = error as? ${_getErrorClassName(generatorOptions)} '); indent.addScoped('{', '}', () { @@ -644,6 +635,15 @@ class SwiftGenerator extends StructuredGenerator { indent.writeln('pigeonError.details,'); }); }); + indent.write('if let flutterError = error as? FlutterError '); + indent.addScoped('{', '}', () { + indent.write('return '); + indent.addScoped('[', ']', () { + indent.writeln('flutterError.code,'); + indent.writeln('flutterError.message,'); + indent.writeln('flutterError.details,'); + }); + }); indent.write('return '); indent.addScoped('[', ']', () { indent.writeln(r'"\(error)",'); 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 7cdb418ea8a..302049a13d1 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 @@ -20,13 +20,6 @@ private func wrapResult(_ result: Any?) -> [Any?] { } private func wrapError(_ error: Any) -> [Any?] { - if let flutterError = error as? FlutterError { - return [ - flutterError.code, - flutterError.message, - flutterError.details, - ] - } if let pigeonError = error as? PigeonError { return [ pigeonError.code, @@ -34,6 +27,13 @@ private func wrapError(_ error: Any) -> [Any?] { pigeonError.details, ] } + if let flutterError = error as? FlutterError { + return [ + flutterError.code, + flutterError.message, + flutterError.details, + ] + } return [ "\(error)", "\(type(of: error))", 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 7cdb418ea8a..302049a13d1 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 @@ -20,13 +20,6 @@ private func wrapResult(_ result: Any?) -> [Any?] { } private func wrapError(_ error: Any) -> [Any?] { - if let flutterError = error as? FlutterError { - return [ - flutterError.code, - flutterError.message, - flutterError.details, - ] - } if let pigeonError = error as? PigeonError { return [ pigeonError.code, @@ -34,6 +27,13 @@ private func wrapError(_ error: Any) -> [Any?] { pigeonError.details, ] } + if let flutterError = error as? FlutterError { + return [ + flutterError.code, + flutterError.message, + flutterError.details, + ] + } return [ "\(error)", "\(type(of: error))", From 4ed78529e3ba4c012bd85e8b19f2114932fa92e7 Mon Sep 17 00:00:00 2001 From: Byoungchan Lee Date: Wed, 15 May 2024 05:59:40 +0900 Subject: [PATCH 22/22] Relocate PigeonError class in Swift files --- .../example/app/ios/Runner/Messages.g.swift | 36 +++++++++---------- packages/pigeon/lib/swift_generator.dart | 3 +- .../ios/Classes/CoreTests.gen.swift | 36 +++++++++---------- .../macos/Classes/CoreTests.gen.swift | 36 +++++++++---------- 4 files changed, 56 insertions(+), 55 deletions(-) diff --git a/packages/pigeon/example/app/ios/Runner/Messages.g.swift b/packages/pigeon/example/app/ios/Runner/Messages.g.swift index 7510022da7f..51b3cfa61de 100644 --- a/packages/pigeon/example/app/ios/Runner/Messages.g.swift +++ b/packages/pigeon/example/app/ios/Runner/Messages.g.swift @@ -14,6 +14,24 @@ 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] } @@ -46,24 +64,6 @@ private func createConnectionError(withChannelName channelName: String) -> Pigeo details: "") } -/// 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 isNullish(_ value: Any?) -> Bool { return value is NSNull || value == nil } diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart index 2e1e4705c91..db5701e371b 100644 --- a/packages/pigeon/lib/swift_generator.dart +++ b/packages/pigeon/lib/swift_generator.dart @@ -687,6 +687,8 @@ private func nilOrValue(_ value: Any?) -> T? { .whereType() .any((Api api) => api.methods.isNotEmpty); + _writePigeonError(generatorOptions, indent); + if (hasHostApi) { _writeWrapResult(indent); _writeWrapError(generatorOptions, indent); @@ -695,7 +697,6 @@ private func nilOrValue(_ value: Any?) -> T? { _writeCreateConnectionError(generatorOptions, indent); } - _writePigeonError(generatorOptions, indent); _writeIsNullish(indent); _writeNilOrValue(indent); } 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 302049a13d1..c54100d2db4 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,6 +15,24 @@ 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] } @@ -47,24 +65,6 @@ private func createConnectionError(withChannelName channelName: String) -> Pigeo details: "") } -/// 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 isNullish(_ value: Any?) -> Bool { return value is NSNull || value == nil } 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 302049a13d1..c54100d2db4 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,6 +15,24 @@ 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] } @@ -47,24 +65,6 @@ private func createConnectionError(withChannelName channelName: String) -> Pigeo details: "") } -/// 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 isNullish(_ value: Any?) -> Bool { return value is NSNull || value == nil }