writeHostApi method
void
writeHostApi(
- SwiftOptions generatorOptions,
- Root root,
- Indent indent,
- AstHostApi api, {
- required String dartPackageName,
override
Write the swift code that represents a host Api, api
.
Example:
protocol Foo {
Int32 add(x: Int32, y: Int32)
}
Implementation
@override
void writeHostApi(
SwiftOptions generatorOptions,
Root root,
Indent indent,
AstHostApi api, {
required String dartPackageName,
}) {
final String apiName = api.name;
final bool isCustomCodec = getCodecClasses(api, root).isNotEmpty;
if (isCustomCodec) {
_writeCodec(indent, api, root);
}
const List<String> generatedComments = <String>[
' Generated protocol from Pigeon that represents a handler of messages from Flutter.'
];
addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
generatorComments: generatedComments);
indent.write('protocol $apiName ');
indent.addScoped('{', '}', () {
for (final Method method in api.methods) {
final _SwiftFunctionComponents components =
_SwiftFunctionComponents.fromMethod(method);
final List<String> argSignature =
components.arguments.map((_SwiftFunctionArgument argument) {
final String? label = argument.label;
final String name = argument.name;
final String type = _nullsafeSwiftTypeForDartType(argument.type);
return '${label == null ? '' : '$label '}$name: $type';
}).toList();
final String returnType = method.returnType.isVoid
? 'Void'
: _nullsafeSwiftTypeForDartType(method.returnType);
final String escapeType =
method.returnType.isVoid ? 'Void' : returnType;
addDocumentationComments(
indent, method.documentationComments, _docCommentSpec);
if (method.isAsynchronous) {
argSignature.add(
'completion: @escaping (Result<$escapeType, Error>) -> Void');
indent.writeln('func ${components.name}(${argSignature.join(', ')})');
} else if (method.returnType.isVoid) {
indent.writeln(
'func ${components.name}(${argSignature.join(', ')}) throws');
} else {
indent.writeln(
'func ${components.name}(${argSignature.join(', ')}) throws -> $returnType');
}
}
});
indent.newln();
indent.writeln(
'$_docCommentPrefix Generated setup class from Pigeon to handle messages through the `binaryMessenger`.');
indent.write('class ${apiName}Setup ');
indent.addScoped('{', '}', () {
final String codecName = _getCodecName(api);
indent.writeln('$_docCommentPrefix The codec used by $apiName.');
String codecArgumentString = '';
if (getCodecClasses(api, root).isNotEmpty) {
codecArgumentString = ', codec: codec';
indent.writeln(
'static var codec: FlutterStandardMessageCodec { $codecName.shared }');
}
indent.writeln(
'$_docCommentPrefix Sets up an instance of `$apiName` to handle messages through the `binaryMessenger`.');
indent.write(
'static func setUp(binaryMessenger: FlutterBinaryMessenger, api: $apiName?) ');
indent.addScoped('{', '}', () {
for (final Method method in api.methods) {
final _SwiftFunctionComponents components =
_SwiftFunctionComponents.fromMethod(method);
final String channelName =
makeChannelName(api, method, dartPackageName);
final String varChannelName = '${method.name}Channel';
addDocumentationComments(
indent, method.documentationComments, _docCommentSpec);
indent.writeln(
'let $varChannelName = FlutterBasicMessageChannel(name: "$channelName", binaryMessenger: binaryMessenger$codecArgumentString)');
indent.write('if let api = api ');
indent.addScoped('{', '}', () {
indent.write('$varChannelName.setMessageHandler ');
final String messageVarName =
method.parameters.isNotEmpty ? 'message' : '_';
indent.addScoped('{ $messageVarName, reply in', '}', () {
final List<String> methodArgument = <String>[];
if (components.arguments.isNotEmpty) {
indent.writeln('let args = message as! [Any?]');
enumerate(components.arguments,
(int index, _SwiftFunctionArgument arg) {
final String argName =
_getSafeArgumentName(index, arg.namedType);
final String argIndex = 'args[$index]';
final String fieldType = _swiftTypeForDartType(arg.type);
_writeGenericCasting(
indent: indent,
value: argIndex,
variableName: argName,
fieldType: fieldType,
type: arg.type);
if (arg.label == '_') {
methodArgument.add(argName);
} else {
methodArgument.add('${arg.label ?? arg.name}: $argName');
}
});
}
final String tryStatement = method.isAsynchronous ? '' : 'try ';
// Empty parens are not required when calling a method whose only
// argument is a trailing closure.
final String argumentString =
methodArgument.isEmpty && method.isAsynchronous
? ''
: '(${methodArgument.join(', ')})';
final String call =
'${tryStatement}api.${components.name}$argumentString';
if (method.isAsynchronous) {
final String resultName =
method.returnType.isVoid ? 'nil' : 'res';
final String successVariableInit =
method.returnType.isVoid ? '' : '(let res)';
indent.write('$call ');
indent.addScoped('{ result in', '}', () {
indent.write('switch result ');
indent.addScoped('{', '}', nestCount: 0, () {
final String nullsafe =
method.returnType.isNullable ? '?' : '';
final String enumTag =
method.returnType.isEnum ? '$nullsafe.rawValue' : '';
indent.writeln('case .success$successVariableInit:');
indent.nest(1, () {
indent.writeln('reply(wrapResult($resultName$enumTag))');
});
indent.writeln('case .failure(let error):');
indent.nest(1, () {
indent.writeln('reply(wrapError(error))');
});
});
});
} else {
indent.write('do ');
indent.addScoped('{', '}', () {
if (method.returnType.isVoid) {
indent.writeln(call);
indent.writeln('reply(wrapResult(nil))');
} else {
String enumTag = '';
if (method.returnType.isEnum) {
enumTag = '.rawValue';
}
enumTag =
method.returnType.isNullable && method.returnType.isEnum
? '?$enumTag'
: enumTag;
indent.writeln('let result = $call');
indent.writeln('reply(wrapResult(result$enumTag))');
}
}, addTrailingNewline: false);
indent.addScoped(' catch {', '}', () {
indent.writeln('reply(wrapError(error))');
});
}
});
}, addTrailingNewline: false);
indent.addScoped(' else {', '}', () {
indent.writeln('$varChannelName.setMessageHandler(nil)');
});
}
});
});
}