void _writeHostApiSource()

in packages/pigeon/lib/objc_generator.dart [580:706]


void _writeHostApiSource(Indent indent, ObjcOptions options, Api api) {
  assert(api.location == ApiLocation.host);
  final String apiName = _className(options.prefix, api.name);

  void writeChannelAllocation(Method func, String varName) {
    indent.writeln('FlutterBasicMessageChannel *$varName =');
    indent.inc();
    indent.writeln('[FlutterBasicMessageChannel');
    indent.inc();
    indent.writeln('messageChannelWithName:@"${makeChannelName(api, func)}"');
    indent.writeln('binaryMessenger:binaryMessenger');
    indent
        .writeln('codec:${_getCodecGetterName(options.prefix, api.name)}()];');
    indent.dec();
    indent.dec();
  }

  void writeChannelApiBinding(Method func, String channel) {
    void unpackArgs(String variable, Iterable<String> argNames) {
      indent.writeln('NSArray *args = $variable;');
      map3(wholeNumbers.take(func.arguments.length), argNames, func.arguments,
          (int count, String argName, NamedType arg) {
        final _ObjcPtr argType = _objcTypeForDartType(options.prefix, arg.type);
        return '${argType.ptr}$argName = args[$count];';
      }).forEach(indent.writeln);
    }

    void writeAsyncBindings(Iterable<String> selectorComponents,
        String callSignature, _ObjcPtr returnType) {
      if (func.returnType.isVoid) {
        const String callback = 'callback(wrapResult(nil, error));';
        if (func.arguments.isEmpty) {
          indent.writeScoped(
              '[api ${selectorComponents.first}:^(FlutterError *_Nullable error) {',
              '}];', () {
            indent.writeln(callback);
          });
        } else {
          indent.writeScoped(
              '[api $callSignature ${selectorComponents.last}:^(FlutterError *_Nullable error) {',
              '}];', () {
            indent.writeln(callback);
          });
        }
      } else {
        const String callback = 'callback(wrapResult(output, error));';
        if (func.arguments.isEmpty) {
          indent.writeScoped(
              '[api ${selectorComponents.first}:^(${returnType.ptr}_Nullable output, FlutterError *_Nullable error) {',
              '}];', () {
            indent.writeln(callback);
          });
        } else {
          indent.writeScoped(
              '[api $callSignature ${selectorComponents.last}:^(${returnType.ptr}_Nullable output, FlutterError *_Nullable error) {',
              '}];', () {
            indent.writeln(callback);
          });
        }
      }
    }

    void writeSyncBindings(String call, _ObjcPtr returnType) {
      indent.writeln('FlutterError *error;');
      if (func.returnType.isVoid) {
        indent.writeln('$call;');
        indent.writeln('callback(wrapResult(nil, error));');
      } else {
        indent.writeln('${returnType.ptr}output = $call;');
        indent.writeln('callback(wrapResult(output, error));');
      }
    }

    // TODO(gaaclarke): Incorporate this into _getSelectorComponents.
    final String lastSelectorComponent =
        func.isAsynchronous ? 'completion' : 'error';
    final String selector = _getSelector(func, lastSelectorComponent);
    indent.writeln(
        'NSCAssert([api respondsToSelector:@selector($selector)], @"$apiName api (%@) doesn\'t respond to @selector($selector)", api);');
    indent.write(
        '[$channel setMessageHandler:^(id _Nullable message, FlutterReply callback) ');
    indent.scoped('{', '}];', () {
      final _ObjcPtr returnType =
          _objcTypeForDartType(options.prefix, func.returnType);
      final Iterable<String> selectorComponents =
          _getSelectorComponents(func, lastSelectorComponent);
      final Iterable<String> argNames =
          indexMap(func.arguments, _getSafeArgName);
      final String callSignature =
          map2(selectorComponents.take(argNames.length), argNames,
              (String selectorComponent, String argName) {
        return '$selectorComponent:$argName';
      }).join(' ');
      if (func.arguments.isNotEmpty) {
        unpackArgs('message', argNames);
      }
      if (func.isAsynchronous) {
        writeAsyncBindings(selectorComponents, callSignature, returnType);
      } else {
        final String syncCall = func.arguments.isEmpty
            ? '[api ${selectorComponents.first}:&error]'
            : '[api $callSignature error:&error]';
        writeSyncBindings(syncCall, returnType);
      }
    });
  }

  const String channelName = 'channel';
  indent.write(
      'void ${apiName}Setup(id<FlutterBinaryMessenger> binaryMessenger, NSObject<$apiName> *api) ');
  indent.scoped('{', '}', () {
    for (final Method func in api.methods) {
      indent.write('');
      indent.scoped('{', '}', () {
        writeChannelAllocation(func, channelName);
        indent.write('if (api) ');
        indent.scoped('{', '}', () {
          writeChannelApiBinding(func, channelName);
        });
        indent.write('else ');
        indent.scoped('{', '}', () {
          indent.writeln('[$channelName setMessageHandler:nil];');
        });
      });
    }
  });
}