Skip to content

Commit

Permalink
[pigeon] Adds platform for imports that aren't support on a platform (#…
Browse files Browse the repository at this point in the history
…8338)

While working on updating `webview_flutter_wkwebview` to use ProxyApis, the `UIKit` import would be invalid on macOS. This PR now surrounds `ProxyApi` imports with a check if every class of an import excludes a platform.
  • Loading branch information
bparrishMines authored Dec 27, 2024
1 parent 2d5e714 commit 9dffa39
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 11 deletions.
3 changes: 2 additions & 1 deletion packages/pigeon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## NEXT
## 22.7.1

* [swift] Adds support for platform checks of imports of ProxyApis.
* Updates minimum supported SDK version to Flutter 3.22/Dart 3.4.

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

/// Read all the content from [stdin] to a String.
String readStdin() {
Expand Down
45 changes: 37 additions & 8 deletions packages/pigeon/lib/swift_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -154,14 +154,7 @@ class SwiftGenerator extends StructuredGenerator<SwiftOptions> {
}) {
indent.writeln('import Foundation');

final Iterable<String> proxyApiImports = root.apis
.whereType<AstProxyApi>()
.map((AstProxyApi proxyApi) => proxyApi.swiftOptions?.import)
.nonNulls
.toSet();
for (final String import in proxyApiImports) {
indent.writeln('import $import');
}
_writeProxyApiImports(indent, root.apis.whereType<AstProxyApi>());
indent.newln();

indent.format('''
Expand Down Expand Up @@ -2487,6 +2480,42 @@ private func nilOrValue<T>(_ value: Any?) -> T? {
});
});
}

void _writeProxyApiImports(Indent indent, Iterable<AstProxyApi> apis) {
final Map<String, List<AstProxyApi>> apisOfImports =
<String, List<AstProxyApi>>{};
for (final AstProxyApi proxyApi in apis) {
final String? import = proxyApi.swiftOptions?.import;
if (import != null) {
if (apisOfImports.containsKey(import)) {
apisOfImports[import]!.add(proxyApi);
} else {
apisOfImports[import] = <AstProxyApi>[proxyApi];
}
}
}

for (final String import in apisOfImports.keys) {
// If every ProxyApi that shares an import excludes a platform for
// support, surround the import with `#if !os(...) #endif`.
final List<String> unsupportedPlatforms = <String>[
if (!apisOfImports[import]!
.any((AstProxyApi api) => api.swiftOptions?.supportsIos ?? true))
'!os(iOS)',
if (!apisOfImports[import]!
.any((AstProxyApi api) => api.swiftOptions?.supportsMacos ?? true))
'!os(macOS)',
];

if (unsupportedPlatforms.isNotEmpty) {
indent.writeln('#if ${unsupportedPlatforms.join(' || ')}');
}
indent.writeln('import $import');
if (unsupportedPlatforms.isNotEmpty) {
indent.writeln('#endif');
}
}
}
}

typedef _VersionRequirement = ({TypeDeclaration type, Version version});
Expand Down
2 changes: 1 addition & 1 deletion packages/pigeon/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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: 22.7.0 # This must match the version in lib/generator_tools.dart
version: 22.7.1 # This must match the version in lib/generator_tools.dart

environment:
sdk: ^3.4.0
Expand Down
135 changes: 135 additions & 0 deletions packages/pigeon/test/swift/proxy_api_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,141 @@ void main() {
);
});

group('imports', () {
test('add check if every class does not support iOS', () {
final Root root = Root(
apis: <Api>[
AstProxyApi(
name: 'Api',
swiftOptions: const SwiftProxyApiOptions(
import: 'MyImport',
supportsIos: false,
),
constructors: <Constructor>[],
fields: <ApiField>[],
methods: <Method>[],
),
],
classes: <Class>[],
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
const SwiftGenerator generator = SwiftGenerator();
generator.generate(
const SwiftOptions(),
root,
sink,
dartPackageName: DEFAULT_PACKAGE_NAME,
);
final String code = sink.toString();

expect(code, contains('#if !os(iOS)\nimport MyImport\n#endif'));
});

test('add check if every class does not support macOS', () {
final Root root = Root(
apis: <Api>[
AstProxyApi(
name: 'Api',
swiftOptions: const SwiftProxyApiOptions(
import: 'MyImport',
supportsMacos: false,
),
constructors: <Constructor>[],
fields: <ApiField>[],
methods: <Method>[],
),
],
classes: <Class>[],
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
const SwiftGenerator generator = SwiftGenerator();
generator.generate(
const SwiftOptions(),
root,
sink,
dartPackageName: DEFAULT_PACKAGE_NAME,
);
final String code = sink.toString();

expect(code, contains('#if !os(macOS)\nimport MyImport\n#endif'));
});

test('add check if for multiple unsupported platforms', () {
final Root root = Root(
apis: <Api>[
AstProxyApi(
name: 'Api',
swiftOptions: const SwiftProxyApiOptions(
import: 'MyImport',
supportsIos: false,
supportsMacos: false,
),
constructors: <Constructor>[],
fields: <ApiField>[],
methods: <Method>[],
),
],
classes: <Class>[],
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
const SwiftGenerator generator = SwiftGenerator();
generator.generate(
const SwiftOptions(),
root,
sink,
dartPackageName: DEFAULT_PACKAGE_NAME,
);
final String code = sink.toString();

expect(
code,
contains('#if !os(iOS) || !os(macOS)\nimport MyImport\n#endif'),
);
});

test('do not add check if at least one class is supported', () {
final Root root = Root(
apis: <Api>[
AstProxyApi(
name: 'Api',
swiftOptions: const SwiftProxyApiOptions(
import: 'MyImport',
supportsIos: false,
),
constructors: <Constructor>[],
fields: <ApiField>[],
methods: <Method>[],
),
AstProxyApi(
name: 'Api2',
swiftOptions: const SwiftProxyApiOptions(
import: 'MyImport',
),
constructors: <Constructor>[],
fields: <ApiField>[],
methods: <Method>[],
),
],
classes: <Class>[],
enums: <Enum>[],
);
final StringBuffer sink = StringBuffer();
const SwiftGenerator generator = SwiftGenerator();
generator.generate(
const SwiftOptions(),
root,
sink,
dartPackageName: DEFAULT_PACKAGE_NAME,
);
final String code = sink.toString();

expect(code, isNot(contains('#if !os(iOS)\nimport MyImport')));
});
});

group('inheritance', () {
test('extends', () {
final AstProxyApi api2 = AstProxyApi(
Expand Down

0 comments on commit 9dffa39

Please sign in to comment.