| name | ios-slim-bindings |
| description | Create iOS slim bindings for MAUI. USE FOR: slim iOS binding, Native Library Interop, Swift/Objective-C wrappers, XcodeGen project.yml, Podfile, CocoaPods static linking, BUILD_LIBRARY_FOR_DISTRIBUTION, XcodeProject MSBuild, `@objc`/`[Export]` selector crashes, async completion handlers. DO NOT USE FOR: Android bindings, general MAUI apps, or NuGet packaging. |
When to use this skill
Activate this skill when the user asks:
- How do I create iOS bindings for a native library?
- How do I wrap an iOS SDK for use in .NET MAUI?
- How do I create slim bindings for iOS?
- How do I use Native Library Interop for iOS?
- How do I bind a Swift library to .NET?
- How do I use Objective Sharpie for iOS bindings?
- How do I integrate an xcframework into .NET MAUI?
- How do I create a Swift wrapper for a native iOS library?
- How do I update iOS bindings when the native SDK changes?
- How do I fix iOS binding build errors?
- How do I expose native iOS APIs to C#?
- How do I handle CocoaPods dependencies in iOS bindings?
- How do I handle Swift Package Manager dependencies?
Overview
This skill guides the creation of Native Library Interop (Slim Bindings) for iOS. This modern approach creates a thin native Swift/Objective-C wrapper exposing only the APIs you need from a native iOS library, making bindings easier to create and maintain.
When to Use Slim Bindings vs Traditional Bindings
| Scenario | Recommended Approach |
|---|
| Need only a subset of library functionality | Slim Bindings |
| Easier maintenance when SDK updates | Slim Bindings |
| Prefer working in Swift/Objective-C for wrapper | Slim Bindings |
| Better isolation from breaking changes | Slim Bindings |
| Need entire library API surface | Traditional Bindings |
| Creating bindings for third-party developers | Traditional Bindings |
| Already maintaining traditional bindings | Traditional Bindings |
Inputs
| Parameter | Required | Example | Notes |
|---|
| libraryName | yes | FirebaseMessaging, Lottie | Name of the native iOS library to bind |
| bindingProjectName | yes | MyBinding.MaciOS | Name for the C# binding project |
| dependencySource | no | cocoapods, spm, xcframework | How the native library is distributed |
| targetFrameworks | no | net10.0-ios;net10.0-maccatalyst | Target frameworks (default: latest .NET iOS + Mac Catalyst) |
| exposedApis | no | List of specific APIs | Which native APIs to expose (helps scope the wrapper) |
Project Structure
MyBinding/
macios/
native/
MyBinding/ # Xcode project
MyBinding.xcodeproj/
project.pbxproj
MyBinding/
DotnetMyBinding.swift # Swift wrapper implementation
Podfile # If using CocoaPods
Package.swift # If using Swift Package Manager
MyBinding.MaciOS.Binding/
MyBinding.MaciOS.Binding.csproj
ApiDefinition.cs
sample/
MauiSample/ # Sample MAUI app
MauiSample.csproj
MainPage.xaml.cs
README.md
Step-by-step Process
For detailed code examples and full implementation walkthroughs, see references/ios-slim-bindings-implementation.md.
Step 1: Create Project Structure
Install XcodeGen (brew install xcodegen) and create the directory structure:
BINDING_NAME="MyBinding"
mkdir -p ${BINDING_NAME}/macios/native/${BINDING_NAME}/${BINDING_NAME}
mkdir -p ${BINDING_NAME}/macios/${BINDING_NAME}.MaciOS.Binding
Step 2: Create the Xcode Project
Create a project.yml for XcodeGen with framework target, then run xcodegen generate. Key build settings:
BUILD_LIBRARY_FOR_DISTRIBUTION: YES
MACH_O_TYPE: staticlib
DEFINES_MODULE: YES
SWIFT_VERSION: "5.0"
Step 3: Create the C# Binding Project
Create a .csproj with <IsBindingProject>true</IsBindingProject> and reference the Xcode project:
<XcodeProject Include="../native/MyBinding/MyBinding.xcodeproj">
<SchemeName>MyBinding</SchemeName>
</XcodeProject>
Create an initial ApiDefinition.cs with [BaseType], [Static], and [Export] attributes.
Step 4: Build and Verify
dotnet build
This invokes XcodeBuild, creates the xcframework, and generates the C# binding assembly.
Step 5: Add Native Library Dependencies
Choose the appropriate method:
| Method | When to Use |
|---|
| CocoaPods | Library distributed via CocoaPods. Use use_frameworks! :linkage => :static. After pod install, reference .xcworkspace instead of .xcodeproj. |
| Swift Package Manager | Library distributed via SPM. Add via Xcode UI or Package.swift. |
| Manual XCFramework | Pre-built xcframework. Drag into Xcode project, set "Do Not Embed" for static linking. |
Step 6: Implement the Swift Wrapper
Create DotnetMyBinding.swift with these requirements:
- All classes must inherit from
NSObject and have @objc(ClassName) annotation
- All methods must be
public with @objc(selector:) annotation
- Use only Objective-C compatible types (see Type Mapping table below)
- Use completion handlers
(Result?, NSError?) -> Void for async operations
- Convert all errors to
NSError for proper propagation to C#
Step 7: Generate ApiDefinition.cs
After building, find the generated Swift header and use Objective Sharpie:
sharpie bind --output=sharpie-output --namespace=MyBinding --sdk=iphoneos18.0 \
--scope=Headers "path/to/MyBinding-Swift.h"
Clean up the generated output: add [Async] attributes, [NullAllowed] for nullable types, remove [Verify] attributes after review, and remove InitWithCoder constructors.
Step 8: Build the Final Binding
dotnet build -c Release
Step 9: Use in Your MAUI App
Add a conditional <ProjectReference>, initialize in MauiProgram.cs with #if IOS || MACCATALYST, and use [Async]-generated methods with await.
Updating Bindings When Native SDK Changes
- Update native dependency version (CocoaPods:
pod update, SPM: update version, or replace xcframework)
- Update Swift wrapper if APIs changed
- Clean and rebuild:
dotnet clean && dotnet build
- Regenerate Objective Sharpie output and diff with existing
ApiDefinition.cs
- Merge changes: add new bindings, update signatures, preserve custom attributes.
- Test with sample app
For detailed update instructions, see references/ios-slim-bindings-implementation.md.
Troubleshooting
| Error | Cause | Solution |
|---|
| "Framework not found" | XCFramework path incorrect or missing architectures | Verify path in <XcodeProject>, check lipo -info |
| "Undefined symbols for architecture" | Missing linked frameworks or symbol visibility | Add system frameworks, verify public + @objc annotations |
| "No type or protocol named" | Missing import or protocol mismatch | Add using directives, use interface types |
| "Duplicate symbol" | Multiple framework references | Remove duplicates, resolve version conflicts |
| "Native class hasn't been loaded" (runtime) | Framework not embedded or missing @objc | Set <ForceLoad>true</ForceLoad>, check annotations |
| "unrecognized selector" (runtime) | Selector mismatch between C# and Swift | Verify [Export("...")] matches @objc(...) exactly |
| "Library not loaded: @rpath" (runtime) | Swift runtime or embedding issue | Add linker flags, set "Embed & Sign" for dynamic frameworks |
| Callbacks not working | Wrong thread, GC'd reference, or missing @escaping | Use DispatchQueue.main.async, hold strong reference |
For detailed troubleshooting with code examples, see references/ios-slim-bindings-implementation.md.
Quick Reference
Swift Wrapper Annotation Requirements
@objc(ClassName)
public class ClassName: NSObject {
@objc(methodNameWithParam:anotherParam:)
public func methodName(param: String, anotherParam: Int) -> Bool { ... }
@objc(staticMethodWithValue:)
public static func staticMethod(value: String) -> String { ... }
}
Type Mapping
| Swift Type | Objective-C Type | C# Type |
|---|
String | NSString * | string |
Bool | BOOL | bool |
Int, Int32 | int | int |
Int64 | long long | long |
Double | double | double |
Float | float | float |
Data | NSData * | NSData |
[String: Any] | NSDictionary * | NSDictionary |
[Any] | NSArray * | NSArray |
UIView | UIView * | UIView |
UIImage | UIImage * | UIImage |
URL | NSURL * | NSUrl |
| Custom Class | Must inherit NSObject | Interface with [BaseType] |
(Result, Error?) -> Void | Block | Action<Result?, NSError?> |
ApiDefinition Attributes
| Attribute | Purpose |
|---|
[BaseType(typeof(NSObject))] | Specifies base class |
[Static] | Static method/property |
[Export("selector:")] | Objective-C selector |
[Async] | Generate async wrapper for completion handler methods |
[NullAllowed] | Nullable parameter/return |
[Protocol] | Objective-C protocol |
[Model] | Protocol implementation |
[Abstract] | Required protocol method |
[Internal] | Don't expose publicly |
[Sealed] | Prevent subclassing |
XcodeProject MSBuild Properties
| Property | Description | Default |
|---|
SchemeName | Xcode scheme to build | Required |
Configuration | Build configuration | Release |
Kind | Framework or Static | Auto-detected |
SmartLink | Enable smart linking | true |
ForceLoad | Force load all symbols | false |
Resources
Official Documentation
Tools
- XcodeGen — Generate Xcode projects from YAML specification
Templates and Examples
Reference Files
Output Format
When assisting with iOS slim bindings, provide:
- Project structure - File/folder layout for the binding
- Swift wrapper code - Complete
DotnetMyBinding.swift implementation
- Xcode configuration - Build settings and dependency setup
- C# binding project -
.csproj and ApiDefinition.cs files
- Usage examples - How to call the binding from MAUI/C#
- Troubleshooting guidance - Common issues and solutions for the specific library
Always verify:
- Swift classes have
@objc(ClassName) annotations
- Methods have
@objc(selector:) annotations matching Objective-C conventions
- Types are marshallable between Swift and C#
- Async operations use completion handlers with
[Async] attribute
- Error handling uses
NSError for proper propagation