-
Notifications
You must be signed in to change notification settings - Fork 203
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
[Discussion] Generate C# bindings for SwiftUI framework #2594
Comments
This issue has been updated with a better-defined goal -- feel free to provide feedback on the general direction or identified projections. |
The prevalent opinion in previous discussions has been that mapping of protocols and associated types to C# is going to be hard. Where is the catch? Nit: public struct Never
{
private Never() {}
} This fails to compile with "The parameterless struct constructor must be 'public'.". |
My understanding is that some scenarios would be harder to implement than others. Let's start with simple scenarios first. Generic methods are used in the Generic methods with protocol constraints require an implicit protocol witness table parameter for conformance. There are two ways to retrieve them: statically and dynamically. Protocol conformance records are emitted in Methods with protocols used as types - instead of passing C# interface pointers within the projection tooling, we need to pass a boxed existential container. We need to implement their construction within the projection tooling, probably at runtime, which will depend on protocol witness tables retrieval. An addition to above-mentioned scenarios could be types with generics, which require passing concrete type metadata and calling For protocols with associated types, I haven't yet encountered differences in public API and underlying calling convention. I may be missing something here. This is a brief overview of these scenarios, and I will initiate the design on protocols while keeping this issue open for further requirements collection. |
I agree that we should be always able to setup the right arguments and call the method. I am more worried about the projected API surface that we expose to user when protocols and generics are in the mix. Is the C# code that people end up writing against the projected API surface going to look reasonable? |
We will continue this effort with a revised goal in .NET 10 that represents a real-world example: dotnet/runtime#95636 |
Objective
The goal is to generate bindings for
SwiftUI
APIs that can be utilized in .NET mobile applications, enabling .NET applications to call into Swift components. To achieve this, we define a dev template and attempt to implement it; through this effort, we will identify the Swift language constructs required to project in C#. Once identified, these constructs need to be designed and implemented within the projection tooling. The MAUI framework is based onUIKit
Objective-C interop, and we want to integrate Swift interop with the existing interop. Later, we may decide to provide Swift bindings forUIKit
, but it is beyond the scope at this stage.Dev template
We will use a .NET MAUI application to discover requirements and validate design proposals. We will implement a dev template with a button, text, and image from
SwiftUI
framework. To add custom MAUI controls and views, a MAUI handlers will be created by implementing C# projections of the views integrated withUIView
fromUIKit
framework usingUIHostingController
:This code snippet illustrates adding custom MAUI handlers:
This code snippet should instantiate a new
SwiftUI
view and return aUIView
usingUIHostingController
:This is an example of the development template MAUI application we aim to implement:
Projections
To implement the dev template, we need to project
Text
,Button
, andImage
types along withView
protocol andUIHostingController
class fromSwiftUI
framework. The following sections represent a general overview of constructs we should project, and their design will be discussed in separate design/implementation-related issues.The
SwiftUI
framework declares the UI and behavior of an app on every platform. It can be integrated with theUIKit
andAppKit
frameworks by implementing theUIHostingController
andNSHostingController
classes. The framework declares theApp
andScene
protocols, which represent the structure of the app. A type that conforms to theApp
protocol, annotated withmain
, implements a static main function that serves as the entry point to the application. We aim to enable existing .NET applications to call Swift components, focusing on views and controls that can be utilized within the .NET ecosystem.This is a sample mobile app in Swift utilizing
SwiftUI
framework:Terms
Some
/Any
are commonly used in the framework like presented below.Conceptualizing the difference is to think of both
some
andany
as "boxed" opaque types that conform to a protocol:some
indicates a type known at compile time, similar to generics in C#.any
indicates a type not known at compile time, similar to interfaces in C#.View
The
View
protocol fromSwiftUI
framework has a computed propertyvar body: Body
whereBody
is an associated type. Protocols in Swift can be projected as C# interfaces. Associated type doesn't have direct mapping in C# and could be represented with generics:To illustrate difference between associated type and generics, here is an example:
The corresponding LLVM-IR code:
When calling methods with generics parameters, type metadata for each generic parameter is passed in declaration order between parameters and context registers. If the generic parameter is constrained by a protocol, there is another parameter for each conformance which is protocol witness table, following the type metadata. Generic types are opaque and an indirect result buffer is utilized, meaning that functions return void.
Text
The
Text
is a frozen struct that conforms to theView
protocol. It has a two inlinable properties:storage
andmodifiers
. Both types are frozen enums, where modifiers represent a Swift array. It can be projected as a C# struct:The
Text
conforms to theView
protocol without a concrete type for associated type. Swift has a frozen enumNever
which has no values and can’t be constructed. To project this type in C#, the lowering algorithm could be updated to avoid lowering structs annotated with a custom attribute.Additionally, structs may contain pointers to instances of classes, in which cases are considered non-POD and and value witness table should be consulted to copy or destroy them.
Button
The
Button
is a non-frozen struct that conforms to theView
protocol. It should be projected as a C# class; layout and implementation can be determined at runtime.Image
The
Image
is a frozen struct that displays an image and conforms to theView
protocol. It can be projected as a C# struct:UIHostingController
To integrate
SwiftUI
views and controls with a MAUI app, we can utilizeUIHostingController
class, which contain a SwiftUI view as its root. This class can be projected as a C# class that inherits fromUIViewController
:Since
UIViewController
is projected via the Objective-C runtime, it is important to understand the potential complications and design the object lifecycle carefully to avoid interference between these projections.Future work
We can consider projecting additional SwiftUI APIs to extend functionality. For example, projecting gesture actions or immersive spaces can improve .NET applications' capabilities.
Tasks
List of tasks to support identified bindings:
The text was updated successfully, but these errors were encountered: