Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add .NET Swift interop tooling components and layout #312

Closed
38 changes: 26 additions & 12 deletions proposed/swift-interop.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,33 +152,47 @@ If possible, Swift tuples should be represented as `ValueTuple`s in .NET. If thi

#### Projection Tooling Components

The projection tooling should be split into these components:
The projection tooling will be based on the [Binding Tools for Swift](https://github.com/xamarin/binding-tools-for-swift) (BTfS). The BTfS contains a set of tools that can consume a compiled Apple Swift library and generate wrappers (bindings) that allow it to be surfaced as a .NET library. This tool will be implemented as a self-hosted .NET CLI tool, automating the generation of wrappers by parsing the Swift library interface. It generates C# bindings that can be utilized as a .NET library and Swift bindings that C# bindings invoke in cases where direct P/Invoke into Swift library is not possible.
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved

The projection tooling should be split into these components.

##### Importing Swift into .NET
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved

1. A tool that takes in a `.swiftinterface` file or Swift sources and produces C# code.
2. A library that provides the basic support for Swift interop that the generated code builds on.
3. User tooling to easily generate Swift projections for a given set of `.framework`s.
- This tooling would build a higher-level interface on top of the tool in item 1 that is more user-friendly and project-system-integrated.
4. (optional) A NuGet package, possibly referencable by `FrameworkReference` or automatically included when targeting macOS, Mac Catalyst, iOS, or tvOS platforms that exposes the platform APIs for each `.framework` that is exposed from Swift to .NET.
- This would be required to provide a single source of truth for Swift types so they can be exposed across an assembly boundary.
Importing Swift into .NET is done through the following steps:

1. The tool analyzes a source library `.swift` and maps entry points to mangled names.
2. Then, the tool aggregates the public API; it take in a `.swiftmodule` file (similar to a C header file) generated by the Swift compiler.
3. For cases where direct P/Invoke from C# into Swift is not possible, the tool generates source code `.swift` wrappers and compiles them.
4. Subsequently, the tool aggregates the public API for the generated wrappers.
5. Finally, the tool generates C# bindings source code based on the aggregated public API and compiles them.

##### Exporting .NET to Swift
##### Exporting .NET to Swift

There are two components to exporting .NET to Swift: Implementing existing Swift types in .NET and passing instances of those types to Swift, and exposing novel types from .NET code to Swift code to be created from Swift. Exposing novel types from .NET code to Swift code is considered out of scope at this time.

For implementing existing Swift types in .NET, we will require one of the following tooling options:

1. A Roslyn source generator to generate any supporting code needed to produce any required metadata, such as type metadata and witness tables, to pass instances of Swift-type-implementing .NET types defined in the current project to Swift.
2. An IL-post-processing tool to generate the supporting code and metadata from the compiled assembly.
2. An IL-post-processing tool to generate the supporting code and metadata from the compiled assembly.

If we were to use an IL-post-processing tool here, we would break Hot Reload in assemblies that implement Swift types, even for .NET-only code, due to introducing new tokens that the Hot Reload "client" (aka Roslyn) does not know about. As a result, we should prefer the Roslyn source generator approach. The existing tool [supports exporting .NET types to Swift](https://github.com/xamarin/binding-tools-for-swift/blob/main/docs/FutureIdeas.md#exposing-c-to-swift) that can work as a source generator.

If we were to use an IL-post-processing tool here, we would break Hot Reload in assemblies that implement Swift types, even for .NET-only code, due to introducing new tokens that the Hot Reload "client" (aka Roslyn) does not know about. As a result, we should prefer the Roslyn source generator approach.
For a C# class that can be inherited gets implemented in C#, the tool generates a mirror class in Swift and overrides it with another class that vectors all the calls through a simulated vtable. This class implements the constructor and overrides the virtual methods by sending calls to a function pointer which ends up in C#.

##### Limitations of the tool

The current version of the projection tooling has limitations and, in some cases, requires Swift wrappers in addition to C# bindings. Here are some examples:
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved
1. Passing a struct by value in more registers than P/Invoke will allow. In this case, it requires "unpacking" from the Swift side.
2. Exporting .NET types into Swift.
3. Handling closures.

### Distribution

The calling convention work will be implemented by the .NET runtimes in dotnet/runtime.
The calling convention work will be implemented by the .NET runtimes in [dotnet/runtime](https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/Swift/SwiftTypes.cs). The projection tooling will be implemented and shipped as part of the Xamarin publishing infrastructure. The projection tooling will be accessible as a .NET CLI tool and distributed as a NuGet package. It should either be automatically included as part of the TPMs for Apple platforms or should be easily referencable.

### Validation

The projection tooling will not ship as part of the runtime. It should be available as a separate NuGet package, possibly as a .NET CLI tool package. The projections should either be included automatically as part of the TPMs for macOS, iOS, and tvOS, or should be easily referenceable.
The interop will be showcased through MAUI samples. List of libraries and MAUI samples to support in .NET 9 are outlined in https://github.com/dotnet/runtime/issues/95636.

## Q & A

Expand Down
Loading