Skip to content

Commit

Permalink
[docs] Add building iOS-like native libraries with NativeAOT (#43397)
Browse files Browse the repository at this point in the history
Add building iOS-like native libraries with NativeAOT documentation

---------

Co-authored-by: Jan Kotas <[email protected]>
Co-authored-by: Michal Strehovský <[email protected]>
Co-authored-by: Genevieve Warren <[email protected]>
  • Loading branch information
4 people authored Nov 24, 2024
1 parent a80ab45 commit 6b2c951
Show file tree
Hide file tree
Showing 6 changed files with 311 additions and 10 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 12 additions & 10 deletions docs/core/deploying/native-aot/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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.
Original file line number Diff line number Diff line change
@@ -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
<PublishAot>true</PublishAot>
<PublishAotUsingRuntimePack>true</PublishAotUsingRuntimePack>
```

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 `<rid>`):

```bash
dotnet publish -r <rid> 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/<rid>/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
<NativeLib>Static</NativeLib>
```

After the project has been published, the static library `MyNativeAOTLibrary.a` can be found at: `MyNativeAOTLibrary/bin/Release/net9.0/<rid>/publish`.

This article doesn't cover how to consume the static library and configure the consumer project.
## Appendix Info.plist contents
```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>MyNativeAOTLibrary</string>
<key>CFBundleIdentifier</key>
<string>com.companyname.MyNativeAOTLibrary</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>CFBundleExecutable</key>
<string>MyNativeAOTLibrary</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
</dict>
</plist>
```
48 changes: 48 additions & 0 deletions docs/core/deploying/native-aot/ios-like-platforms/index.md
Original file line number Diff line number Diff line change
@@ -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
<PublishAot>true</PublishAot>
<PublishAotUsingRuntimePack>true</PublishAotUsingRuntimePack>
```

2. Publish the project for the desired iOS-like target platform by specifying adequate runtime identifier (later referred to as `<rid>`):

- `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 <rid>
```

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).
6 changes: 6 additions & 0 deletions docs/navigate/devops-testing/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 6b2c951

Please sign in to comment.