Skip to content

Commit

Permalink
[pigeon] Added the ability for objc to return errors. (flutter#113)
Browse files Browse the repository at this point in the history
  • Loading branch information
gaaclarke authored Feb 27, 2020
1 parent 362dde3 commit ec31654
Show file tree
Hide file tree
Showing 7 changed files with 96 additions and 10 deletions.
2 changes: 1 addition & 1 deletion packages/pigeon/example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ flutter pub run pigeon \
@end

@implementation MyApi
-(SearchReply*)search:(SearchRequest*)request {
-(SearchReply*)search:(SearchRequest*)request error:(FlutterError **)error {
SearchReply *reply = [[SearchReply alloc] init];
reply.result =
[NSString stringWithFormat:@"Hi %@!", request.query];
Expand Down
14 changes: 12 additions & 2 deletions packages/pigeon/lib/dart_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,18 @@ void _writeHostApi(Indent indent, Api api) {
'BasicMessageChannel(\'$channelName\', StandardMessageCodec());');
indent.dec();
indent.dec();
indent.writeln('Map replyMap = await channel.send(requestMap);');
indent.writeln('return ${func.returnType}._fromMap(replyMap);');
indent.writeln('');
indent.format('''Map replyMap = await channel.send(requestMap);
if (replyMap['error'] != null) {
\tMap error = replyMap['${Keys.error}'];
\tthrow PlatformException(
\t\t\tcode: error['${Keys.errorCode}'],
\t\t\tmessage: error['${Keys.errorMessage}'],
\t\t\tdetails: error['${Keys.errorDetails}']);
} else {
\treturn ${func.returnType}._fromMap(replyMap[\'${Keys.result}\']);
}
''');
});
}
});
Expand Down
30 changes: 29 additions & 1 deletion packages/pigeon/lib/generator_tools.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ class Indent {
/// String used for newlines (ex "\n").
final String newline = '\n';

/// String used to represent a tab.
final String tab = ' ';

/// Increase the indentation level.
void inc() {
_count += 1;
Expand All @@ -45,11 +48,18 @@ class Indent {
String str() {
String result = '';
for (int i = 0; i < _count; i++) {
result += ' ';
result += tab;
}
return result;
}

/// Replaces the newlines and tabs of input and adds it to the stream.
void format(String input) {
for (String line in input.split('\n')) {
writeln(line.replaceAll('\t', tab));
}
}

/// Scoped increase of the ident level. For the execution of [func] the
/// indentation will be incremented.
void scoped(String begin, String end, Function func) {
Expand Down Expand Up @@ -126,3 +136,21 @@ const String generatedCodeWarning =

/// String to be printed after `generatedCodeWarning`.
const String seeAlsoWarning = 'See also: https://pub.dev/packages/pigeon';

/// Collection of keys used in dictionaries across generators.
class Keys {
/// The key in the result hash for the 'result' value.
static const String result = 'result';

/// The key in the result hash for the 'error' value.
static const String error = 'error';

/// The key in an error hash for the 'code' value.
static const String errorCode = 'code';

/// The key in an error hash for the 'message' value.
static const String errorMessage = 'message';

/// The key in an error hash for the 'details' value.
static const String errorDetails = 'details';
}
25 changes: 23 additions & 2 deletions packages/pigeon/lib/java_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,21 @@ void _writeHostApi(Indent indent, Api api) {
final String returnType = method.returnType;
indent.writeln(
'$argType input = $argType.fromMap((HashMap)message);');
indent.writeln('$returnType output = api.${method.name}(input);');
indent.writeln('reply.reply(output.toMap());');
indent.writeln(
'HashMap<String, HashMap> wrapped = new HashMap<String, HashMap>();');
indent.write('try ');
indent.scoped('{', '}', () {
indent
.writeln('$returnType output = api.${method.name}(input);');
indent
.writeln('wrapped.put("${Keys.result}", output.toMap());');
});
indent.write('catch (Exception exception) ');
indent.scoped('{', '}', () {
indent.writeln(
'wrapped.put("${Keys.error}", wrapError(exception));');
});
indent.writeln('reply.reply(wrapped);');
});
});
});
Expand Down Expand Up @@ -200,5 +213,13 @@ void generateJava(JavaOptions options, Root root, StringSink sink) {
_writeFlutterApi(indent, api);
}
}

indent.format('''private static HashMap wrapError(Exception exception) {
\tHashMap<String, Object> errorMap = new HashMap<String, Object>();
\terrorMap.put("${Keys.errorMessage}", exception.toString());
\terrorMap.put("${Keys.errorCode}", null);
\terrorMap.put("${Keys.errorDetails}", null);
\treturn errorMap;
}''');
});
}
32 changes: 29 additions & 3 deletions packages/pigeon/lib/objc_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ void generateObjcHeader(ObjcOptions options, Root root, StringSink sink) {
indent.writeln('// $seeAlsoWarning');
indent.writeln('#import <Foundation/Foundation.h>');
indent.writeln('@protocol FlutterBinaryMessenger;');
indent.writeln('@class FlutterError;');
indent.writeln('@class FlutterStandardTypedData;');
indent.writeln('');

Expand Down Expand Up @@ -104,7 +105,8 @@ void generateObjcHeader(ObjcOptions options, Root root, StringSink sink) {
for (Method func in api.methods) {
final String returnType = _className(options.prefix, func.returnType);
final String argType = _className(options.prefix, func.argType);
indent.writeln('-($returnType *)${func.name}:($argType*)input;');
indent.writeln(
'-($returnType *)${func.name}:($argType*)input error:(FlutterError **)error;');
}
indent.writeln('@end');
indent.writeln('');
Expand Down Expand Up @@ -175,8 +177,10 @@ void _writeHostApiSource(Indent indent, ObjcOptions options, Api api) {
final String returnType =
_className(options.prefix, func.returnType);
indent.writeln('$argType *input = [$argType fromMap:message];');
indent.writeln('$returnType *output = [api ${func.name}:input];');
indent.writeln('callback([output toMap]);');
indent.writeln('FlutterError *error;');
indent.writeln(
'$returnType *output = [api ${func.name}:input error:&error];');
indent.writeln('callback(wrapResult([output toMap], error));');
});
});
indent.write('else ');
Expand Down Expand Up @@ -247,6 +251,28 @@ void generateObjcSource(ObjcOptions options, Root root, StringSink sink) {
indent.writeln('#import <Flutter/Flutter.h>');
indent.writeln('');

indent.writeln('#if !__has_feature(objc_arc)');
indent.writeln('#error File requires ARC to be enabled.');
indent.writeln('#endif');
indent.addln('');

indent.format(
'''static NSDictionary* wrapResult(NSDictionary *result, FlutterError *error) {
\tNSDictionary *errorDict = (NSDictionary *)[NSNull null];
\tif (error) {
\t\terrorDict = [NSDictionary dictionaryWithObjectsAndKeys:
\t\t\t\t(error.code ? error.code : [NSNull null]), @"${Keys.errorCode}",
\t\t\t\t(error.message ? error.message : [NSNull null]), @"${Keys.errorMessage}",
\t\t\t\t(error.details ? error.details : [NSNull null]), @"${Keys.errorDetails}",
\t\t\t\tnil];
\t}
\treturn [NSDictionary dictionaryWithObjectsAndKeys:
\t\t\t(result ? result : [NSNull null]), @"${Keys.result}",
\t\t\terrorDict, @"${Keys.error}",
\t\t\tnil];
}''');
indent.addln('');

for (Class klass in root.classes) {
final String className = _className(options.prefix, klass.name);
indent.writeln('@interface $className ()');
Expand Down
1 change: 1 addition & 0 deletions packages/pigeon/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ test_pigeon_ios() {
-isysroot $(xcrun --sdk iphoneos --show-sdk-path) \
-F $framework_path \
-Werror \
-fobjc-arc \
-c $temp_dir/pigeon.m \
-o $temp_dir/pigeon.o

Expand Down
2 changes: 1 addition & 1 deletion packages/pigeon/test/objc_generator_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ void main() {
expect(code, contains('@interface Input'));
expect(code, contains('@interface Output'));
expect(code, contains('@protocol Api'));
expect(code, matches('Output.*doSomething.*Input'));
expect(code, matches('Output.*doSomething.*Input.*FlutterError'));
expect(code, contains('ApiSetup('));
});

Expand Down

0 comments on commit ec31654

Please sign in to comment.