diff --git a/docs/core/deploying/media/native-aot-ios-like-platforms/xcode-add-framework-reference.png b/docs/core/deploying/media/native-aot-ios-like-platforms/xcode-add-framework-reference.png new file mode 100644 index 0000000000000..30d921774e616 Binary files /dev/null and b/docs/core/deploying/media/native-aot-ios-like-platforms/xcode-add-framework-reference.png differ diff --git a/docs/core/deploying/media/native-aot-ios-like-platforms/xcode-add-framework-search-path.png b/docs/core/deploying/media/native-aot-ios-like-platforms/xcode-add-framework-search-path.png new file mode 100644 index 0000000000000..a4b97cef7d554 Binary files /dev/null and b/docs/core/deploying/media/native-aot-ios-like-platforms/xcode-add-framework-search-path.png differ diff --git a/docs/core/deploying/native-aot/index.md b/docs/core/deploying/native-aot/index.md index db1d86228e100..be77453107eb4 100644 --- a/docs/core/deploying/native-aot/index.md +++ b/docs/core/deploying/native-aot/index.md @@ -152,11 +152,11 @@ The following table shows supported compilation targets. | Windows | x64, Arm64 | | | Linux | x64, Arm64 | | | macOS | x64, Arm64 | | -| iOS | Arm64 | Experimental support | -| iOSSimulator | x64, Arm64 | Experimental support | -| tvOS | Arm64 | Experimental support | -| tvOSSimulator | x64, Arm64 | Experimental support | -| MacCatalyst | x64, Arm64 | Experimental support | +| [iOS](./ios-like-platforms/index.md) | Arm64 | Experimental support | +| [iOSSimulator](./ios-like-platforms/index.md) | x64, Arm64 | Experimental support | +| [tvOS](./ios-like-platforms/index.md) | Arm64 | Experimental support | +| [tvOSSimulator](./ios-like-platforms/index.md) | x64, Arm64 | Experimental support | +| [MacCatalyst](./ios-like-platforms/index.md) | x64, Arm64 | Experimental support | | Android | x64, Arm64 | Experimental, no built-in Java interop | ### [.NET 9+](#tab/net9plus) @@ -166,11 +166,13 @@ The following table shows supported compilation targets. | Windows | x64, Arm64, x86 | | | Linux | x64, Arm64, Arm | | | macOS | x64, Arm64 | | -| iOS | Arm64 | | -| iOSSimulator | x64, Arm64 | | -| tvOS | Arm64 | | -| tvOSSimulator | x64, Arm64 | | -| MacCatalyst | x64, Arm64 | | +| [iOS](./ios-like-platforms/index.md) | Arm64 | | +| [iOSSimulator](./ios-like-platforms/index.md) | x64, Arm64 | | +| [tvOS](./ios-like-platforms/index.md) | Arm64 | | +| [tvOSSimulator](./ios-like-platforms/index.md) | x64, Arm64 | | +| [MacCatalyst](./ios-like-platforms/index.md) | x64, Arm64 | | | Android | x64, Arm64, Arm | Experimental, no built-in Java interop | --- + +For more information about how specific platform is supported with Native AOT, follow the link from the table. diff --git a/docs/core/deploying/native-aot/ios-like-platforms/creating-and-consuming-custom-frameworks.md b/docs/core/deploying/native-aot/ios-like-platforms/creating-and-consuming-custom-frameworks.md new file mode 100644 index 0000000000000..5d0325a74022f --- /dev/null +++ b/docs/core/deploying/native-aot/ios-like-platforms/creating-and-consuming-custom-frameworks.md @@ -0,0 +1,245 @@ +--- +title: Create and consume custom frameworks for iOS-like platforms +description: How to create and consume custom frameworks with Native AOT for iOS-like platforms +author: ivanpovazan +ms.author: ivanpovazan +ms.date: 11/21/2024 +--- + +# Create and consume custom frameworks for iOS-like platforms + +Starting from .NET 9, Native AOT supports publishing .NET class libraries that don't depend on iOS workloads for iOS-like platforms. +This support enables you to create self-contained native libraries that can be consumed from iOS, Mac Catalyst, and tvOS applications. + +> [!IMPORTANT] +> This approach does not come with the built-in Objective-C interoperability support and additional code adaptations might be required (such as marshalling reference type arguments) to achieve interoperability. + +## Build shared libraries + +This section describes steps to create a simple .NET Class Library project with NativeAOT support and produce a native library for iOS-like platforms from it. + +1. Download .NET 9 SDK +2. Create a class library project + + ```bash + dotnet new classlib -n "MyNativeAOTLibrary" + ``` + +3. Add the following properties into the project file `MyNativeAOTLibrary.csproj` + + ```xml + true + true + ``` + +4. Edit the `MyNativeAOTLibrary/Class1.cs` source code to expose a managed method so that it can be referenced from the native code as `aotsample_add`. For example: + + ```cs + using System.Runtime.InteropServices; + namespace NaotLib; + + public class Class1 + { + [UnmanagedCallersOnly(EntryPoint = "aotsample_add")] + public static int Add(int a, int b) + { + return a + b; + } + } + ``` + +5. Publish the class library and target the desired iOS-like platform by specifying the appropriate runtime identifier (referenced below as ``): + + ```bash + dotnet publish -r MyNativeAOTLibrary/MyNativeAOTLibrary.csproj + ``` + +Successful completion of the previous step produces a pair of files: a shared library `MyNativeAOTLibrary.dylib` and its debug symbols `MyNativeAOTLibrary.dylib.dSYM`, which are located at: `MyNativeAOTLibrary/bin/Release/net9.0//publish/`. + +> [!NOTE] +> For creating universal frameworks, it is required to publish the class library for both `Arm64` and `x64` architectures for a given platform. +> This means that you need to repeat step 5 with a different runtime identifier. +> For example, you'd publish the class library with both `maccatalyst-arm64` and `maccatalyst-x64` runtime identifiers as a prerequisite for [Packaging the shared library into a custom MacCatalyst universal framework](#package-the-shared-library-into-a-custom-maccatalyst-universal-framework). + +## Create and consume a custom framework + +Apple imposes a requirement that shared libraries (.dylibs) need to be packaged into frameworks in order to be consumed from applications. + +This section describes all required steps to achieve this and a simple scenario of a iOS/MacCatalyst application consuming a shared NativeAOT library/framework. + +> [!NOTE] +> The described steps are just for demonstration purposes. The actual requirements might differ depending on the exact use case. + +### Package the shared library into custom iOS framework + +1. Create a framework folder: + + ```bash + mkdir MyNativeAOTLibrary.framework + ``` + +2. Adjust load commands: + + - `LC_RPATH` load command + + ```bash + install_name_tool -rpath @executable_path @executable_path/Frameworks MyNativeAOTLibrary/bin/Release/net9.0/ios-arm64/publish/MyNativeAOTLibrary.dylib + ``` + + - `LC_ID_DYLIB` load command + + ```bash + install_name_tool -id @rpath/MyNativeAOTLibrary.framework/MyNativeAOTLibrary MyNativeAOTLibrary/bin/Release/net9.0/ios-arm64/publish/MyNativeAOTLibrary.dylib + ``` + +3. Manually package the binary into a universal file: + + ```bash + lipo -create MyNativeAOTLibrary/bin/Release/net9.0/ios-arm64/publish/MyNativeAOTLibrary.dylib -output MyNativeAOTLibrary.framework/MyNativeAOTLibrary + ``` + +4. Add a property list file to your framework: + + - Create a `Info.plist` file + + ```bash + touch MyNativeAOTLibrary.framework/Info.plist + ``` + + - Add the contents from the [appendix](#appendix-infoplist-contents) into the created `Info.plist` file + +After the final step, the framework structure should look like this: + +``` +MyNativeAOTLibrary.framework + |_ MyNativeAOTLibrary + |_ Info.plist +``` + +### Package the shared library into a custom MacCatalyst universal framework + +Universal frameworks require binaries for both `Arm64` and `x64` architecture. +For this reason, you must publish native libraries targeting both of the following RIDs beforehand: `maccatalyst-arm64` and `maccatalyst-x64`. + +1. Create a framework folder structure: + + ```bash + mkdir -p MyNativeAOTLibrary.framework/Versions/A/Resources + ln -sfh Versions/Current/MyNativeAOTLibrary MyNativeAOTLibrary.framework/MyNativeAOTLibrary + ln -sfh Versions/Current/Resources MyNativeAOTLibrary.framework/Resources + ln -sfh A MyNativeAOTLibrary.framework/Versions/Current + ``` + +2. Adjust load commands: + + - `LC_RPATH` load command + + ```bash + install_name_tool -rpath @executable_path @executable_path/../Frameworks MyNativeAOTLibrary/bin/Release/net9.0/maccatalyst-arm64/publish/MyNativeAOTLibrary.dylib + install_name_tool -rpath @executable_path @executable_path/../Frameworks MyNativeAOTLibrary/bin/Release/net9.0/maccatalyst-x64/publish/MyNativeAOTLibrary.dylib + ``` + + - `LC_ID_DYLIB` load command + + ```bash + install_name_tool -id @rpath/MyNativeAOTLibrary.framework/Versions/A/MyNativeAOTLibrary MyNativeAOTLibrary/bin/Release/net9.0/maccatalyst-arm64/publish/MyNativeAOTLibrary.dylib + install_name_tool -id @rpath/MyNativeAOTLibrary.framework/Versions/A/MyNativeAOTLibrary MyNativeAOTLibrary/bin/Release/net9.0/maccatalyst-x64/publish/MyNativeAOTLibrary.dylib + ``` + +3. Manually package the binary into a universal file: + + ```bash + lipo -create MyNativeAOTLibrary/bin/Release/net9.0/maccatalyst-arm64/publish/MyNativeAOTLibrary.dylib MyNativeAOTLibrary/bin/Release/net9.0/maccatalyst-x64/publish/MyNativeAOTLibrary.dylib -output MyNativeAOTLibrary.framework/Versions/A/MyNativeAOTLibrary + ``` + +4. Add a property list file to your framework: + + - Create a `Info.plist` file + + ```bash + touch MyNativeAOTLibrary.framework/Versions/A/Resources/Info.plist + ``` + + - Add the contents from the [appendix](#appendix-infoplist-contents) into the created `Info.plist` file + +After the final step, the framework structure should look like this: + +``` +MyNativeAOTLibrary.framework + |_ MyNativeAOTLibrary -> Versions/Current/MyNativeAOTLibrary + |_ Resources -> Versions/Current/Resources + |_ Versions + |_ A + | |_ Resources + | | |_ Info.plist + | |_ MyNativeAOTLibrary + |_ Current -> A +``` + +### Consume custom frameworks + +1. Open `Xcode` (in this example `Xcode 16.0` is used) +2. Create a new `App` project +3. Choose the name for your app (for example, `MyiOSApp`) and choose Objective-C as the source language +4. Add a reference to the `MyNativeAOTLibrary` framework + - In the `MyiOSApp` targets **General** tab, under **Frameworks, Libraries and Embedded Content**, select **+** to add `MyNativeAOTLibrary` as the referenced framework + - In the dialog, choose **Add Other** -> **Add Files** and then browse to the location of `MyNativeAOTLibrary.framework` and select it + - Once selected, set `Embed and Sign` option for `MyNativeAOTLibrary` framework + + ![Xcode add framework reference](../../media/native-aot-ios-like-platforms/xcode-add-framework-reference.png) + +5. Add `MyNativeAOTLibrary.framework` location to the list of **Framework Search Paths** in the **Build Settings** tab + + ![Xcode add framework search path](../../media/native-aot-ios-like-platforms/xcode-add-framework-search-path.png) + +6. Edit `main.m` by calling the exposed managed method `aotsample_add` and printing the result + + ```objc + extern int aotsample_add(int a, int b); + int main(int argc, char * argv[]) { + ... + NSLog(@"2 + 5 = %d", aotsample_add(2, 5)); + ... + } + ``` + +7. Select your physical iOS device and build/run the app +8. Inspect the logs after the app has successfully launched. The app should print out: `2 + 5 = 7` + +> [!NOTE] +> For MacCatalyst, use the same steps except for step 7, where the Run Destination needs to be set as: `Mac (Mac Catalyst)`. + +## Build static libraries with NativeAOT for iOS-like platforms + +As described in [building native libraries overview](../libraries.md#building-native-libraries), it's better to build shared libraries over static ones due to several limitations. + +However, if desired, you can build a static library by following the steps for building a shared one and including an additional property in the project file: + +```xml +Static +``` + +After the project has been published, the static library `MyNativeAOTLibrary.a` can be found at: `MyNativeAOTLibrary/bin/Release/net9.0//publish`. + +This article doesn't cover how to consume the static library and configure the consumer project. + +## Appendix Info.plist contents + +```xml + + + + + CFBundleName + MyNativeAOTLibrary + CFBundleIdentifier + com.companyname.MyNativeAOTLibrary + CFBundleVersion + 1.0 + CFBundleExecutable + MyNativeAOTLibrary + CFBundlePackageType + FMWK + + +``` diff --git a/docs/core/deploying/native-aot/ios-like-platforms/index.md b/docs/core/deploying/native-aot/ios-like-platforms/index.md new file mode 100644 index 0000000000000..247e4228a06c5 --- /dev/null +++ b/docs/core/deploying/native-aot/ios-like-platforms/index.md @@ -0,0 +1,48 @@ +--- +title: Native AOT support for iOS-like platforms overview +description: Learn how Native AOT supports iOS-like platforms +author: ivanpovazan +ms.author: ivanpovazan +ms.date: 11/21/2024 +--- + +# Native AOT support for iOS-like platforms + +Starting from .NET 9, Native AOT supports targeting iOS-like platforms. The term *iOS-like platforms* refers to Apple platforms that use similar APIs such as: iOS, MacCatalyst and tvOS. + +Based on the use case, the support can be divided into: + +- support for applications and libraries referencing OS-specific APIs +- support for applications and libraries without OS-specific API dependencies + +## Support for applications and libraries referencing OS-specific APIs + +This refers to .NET MAUI projects targeting OS-specific target frameworks (like: `net9.0-ios`). +How Native AOT can be enabled for .NET MAUI apps, see [Native AOT deployment on iOS and Mac Catalyst](/dotnet/maui/deployment/nativeaot). + +## Support for applications and libraries without OS-specific API dependencies + +This refers to .NET projects targeting the general or non-OS-specific target framework (like: `net9.0`), for which Native AOT can be enabled in the following way: + +1. Include the following properties in your project file: + + ```xml + true + true + ``` + +2. Publish the project for the desired iOS-like target platform by specifying adequate runtime identifier (later referred to as ``): + + - `ios-arm64`, for iOS physical devices + - `iossimulator-arm64` or `iossimulator-x64`, for iOS simulators + - `maccatalyst-arm64` or `maccatalyst-x64`, for Mac Catalyst + - `tvos-arm64`, for tvOS physical devices + - `tvossimulator-arm64` or `tvossimulator-x64`, for tvOS simulators + + and execute the following command: + + ``` + dotnet publish -r + ``` + +For specifics of building and consuming native libraries on iOS-like platforms, see [How to create and consume custom frameworks with Native AOT for iOS-like platforms](./creating-and-consuming-custom-frameworks.md). diff --git a/docs/navigate/devops-testing/toc.yml b/docs/navigate/devops-testing/toc.yml index ac14d5b67b0c9..4ee918edae68e 100644 --- a/docs/navigate/devops-testing/toc.yml +++ b/docs/navigate/devops-testing/toc.yml @@ -539,6 +539,12 @@ items: href: ../../core/deploying/native-aot/warnings/il3055.md - name: IL3056 href: ../../core/deploying/native-aot/warnings/il3056.md + - name: iOS-like platforms + items: + - name: Overview + href: ../../core/deploying/native-aot/ios-like-platforms/index.md + - name: Create and consume custom frameworks + href: ../../core/deploying/native-aot/ios-like-platforms/creating-and-consuming-custom-frameworks.md - name: Runtime package store href: ../../core/deploying/runtime-store.md - name: Runtime Identifier (RID) catalog