# Ios Slim Bindings

> Create and update slim/native platform interop bindings for iOS in .NET MAUI and .NET for iOS projects. Guides through creating Swift/Objective-C wrappers, configuring Xcode projects, generating C# API definitions, and integrating native iOS libraries using the Native Library Interop (NLI) approach. Use when asked about iOS bindings, xcframework integration, Swift interop, Objective Sharpie, or b…

- **Type:** Skill
- **Install:** `agentstack add skill-redth-maui-skillz-ios-slim-bindings`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [Redth](https://agentstack.voostack.com/s/redth)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [Redth](https://github.com/Redth)
- **Source:** https://github.com/Redth/maui-skillz/tree/main/.agents/skills/ios-slim-bindings

## Install

```sh
agentstack add skill-redth-maui-skillz-ios-slim-bindings
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# 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 | `net9.0-ios;net9.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

The recommended project structure for Native Library Interop:

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

## Step 1: Create Project Structure from Command Line

This section shows how to create the entire binding project structure using only command-line tools—no GUI or template cloning required.

### Prerequisites

Install XcodeGen (generates Xcode projects from YAML):

```bash
brew install xcodegen
```

### Create Directory Structure

```bash
# Set your binding name
BINDING_NAME="MyBinding"

# Create the full directory structure
mkdir -p ${BINDING_NAME}/macios/native/${BINDING_NAME}/${BINDING_NAME}
mkdir -p ${BINDING_NAME}/macios/${BINDING_NAME}.MaciOS.Binding
mkdir -p ${BINDING_NAME}/sample/MauiSample

cd ${BINDING_NAME}
```

## Step 2: Create the Xcode Project with XcodeGen

### Create the XcodeGen Project Spec

Create `macios/native/${BINDING_NAME}/project.yml`:

```bash
cat > macios/native/${BINDING_NAME}/project.yml  macios/native/${BINDING_NAME}/${BINDING_NAME}/Dotnet${BINDING_NAME}.swift  String {
        return "1.0.0"
    }
    
    /// Example async method with completion handler
    @objc(fetchDataWithQuery:completion:)
    public static func fetchData(
        query: String,
        completion: @escaping (String?, NSError?) -> Void
    ) {
        // Simulate async operation
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
            completion("Result for: \(query)", nil)
        }
    }
    
    /// Example view creation
    @objc(createViewWithFrame:)
    public static func createView(frame: CGRect) -> UIView {
        let view = UIView(frame: frame)
        view.backgroundColor = .systemBlue
        return view
    }
}
EOF
```

### Create Info.plist

```bash
cat > macios/native/${BINDING_NAME}/${BINDING_NAME}/Info.plist 

    CFBundleDevelopmentRegion
    $(DEVELOPMENT_LANGUAGE)
    CFBundleExecutable
    $(EXECUTABLE_NAME)
    CFBundleIdentifier
    $(PRODUCT_BUNDLE_IDENTIFIER)
    CFBundleInfoDictionaryVersion
    6.0
    CFBundleName
    $(PRODUCT_NAME)
    CFBundlePackageType
    $(PRODUCT_BUNDLE_PACKAGE_TYPE)
    CFBundleShortVersionString
    $(MARKETING_VERSION)
    CFBundleVersion
    $(CURRENT_PROJECT_VERSION)
    NSPrincipalClass
    

EOF
```

### Generate the Xcode Project

```bash
cd macios/native/${BINDING_NAME}
xcodegen generate
cd ../../..
```

This creates `MyBinding.xcodeproj` with all the correct build settings.

### Verify the Generated Project

```bash
# List the generated files
ls -la macios/native/${BINDING_NAME}/

# Verify the scheme was created and is shared
ls -la macios/native/${BINDING_NAME}/${BINDING_NAME}.xcodeproj/xcshareddata/xcschemes/
```

## Step 3: Create the C# Binding Project

### Create the Binding .csproj

```bash
cat > macios/${BINDING_NAME}.MaciOS.Binding/${BINDING_NAME}.MaciOS.Binding.csproj 
  
    net9.0-ios;net9.0-maccatalyst
    enable
    true
    true
    
    
    MyBinding.MaciOS
    1.0.0
    Your Name
    iOS bindings for MyBinding
  

  
  
    
      MyBinding
    
  

  
  
    
  

EOF
```

### Create Initial ApiDefinition.cs

```bash
cat > macios/${BINDING_NAME}.MaciOS.Binding/ApiDefinition.cs  completion);

        // +(UIView * _Nonnull)createViewWithFrame:(CGRect)frame;
        [Static]
        [Export("createViewWithFrame:")]
        UIView CreateView(CGRect frame);
    }
}
EOF
```

## Step 4: Build and Verify

### Build the Binding Project

```bash
cd macios/${BINDING_NAME}.MaciOS.Binding
dotnet build
```

This will:
1. Invoke XcodeBuild to compile the native framework
2. Create the xcframework
3. Generate the C# binding assembly

### Verify the Build Output

```bash
# Check that the xcframework was created
find bin -name "*.xcframework" -type d

# Find the generated Swift header (for updating ApiDefinition.cs later)
find bin -name "*-Swift.h" -type f
```

## Optional: Add CocoaPods Support

If your native library uses CocoaPods dependencies:

### Create Podfile

```bash
cat > macios/native/${BINDING_NAME}/Podfile  :static
  
  # Add your pods here
  # pod 'FirebaseMessaging', '~> 10.0'
end

post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES'
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'
    end
  end
end
EOF
```

### Install Pods and Update Project Reference

```bash
cd macios/native/${BINDING_NAME}
pod install
cd ../../..

# Update the binding project to use xcworkspace instead of xcodeproj
sed -i '' 's/\.xcodeproj/\.xcworkspace/g' macios/${BINDING_NAME}.MaciOS.Binding/${BINDING_NAME}.MaciOS.Binding.csproj
```

## Complete Script: Create Binding Project

Here's a complete bash script that creates everything:

```bash
#!/bin/bash
set -e

# Configuration
BINDING_NAME="${1:-MyBinding}"
BUNDLE_ID_PREFIX="${2:-com.example}"
MIN_IOS_VERSION="${3:-15.0}"

echo "Creating iOS binding project: ${BINDING_NAME}"

# Check prerequisites
if ! command -v xcodegen &> /dev/null; then
    echo "Installing xcodegen..."
    brew install xcodegen
fi

# Create directory structure
mkdir -p ${BINDING_NAME}/macios/native/${BINDING_NAME}/${BINDING_NAME}
mkdir -p ${BINDING_NAME}/macios/${BINDING_NAME}.MaciOS.Binding
cd ${BINDING_NAME}

# Create XcodeGen project spec
cat > macios/native/${BINDING_NAME}/project.yml  macios/native/${BINDING_NAME}/${BINDING_NAME}/Dotnet${BINDING_NAME}.swift  String {
        return "1.0.0"
    }
}
EOF

# Create Info.plist
cat > macios/native/${BINDING_NAME}/${BINDING_NAME}/Info.plist 

    CFBundleDevelopmentRegion
    $(DEVELOPMENT_LANGUAGE)
    CFBundleExecutable
    $(EXECUTABLE_NAME)
    CFBundleIdentifier
    $(PRODUCT_BUNDLE_IDENTIFIER)
    CFBundleInfoDictionaryVersion
    6.0
    CFBundleName
    $(PRODUCT_NAME)
    CFBundlePackageType
    $(PRODUCT_BUNDLE_PACKAGE_TYPE)
    CFBundleShortVersionString
    $(MARKETING_VERSION)
    CFBundleVersion
    $(CURRENT_PROJECT_VERSION)

EOF

# Generate Xcode project
cd macios/native/${BINDING_NAME}
xcodegen generate
cd ../../..

# Create binding .csproj
cat > macios/${BINDING_NAME}.MaciOS.Binding/${BINDING_NAME}.MaciOS.Binding.csproj 
  
    net9.0-ios;net9.0-maccatalyst
    enable
    true
    true
    ${BINDING_NAME}.MaciOS
    1.0.0
  

  
    
      ${BINDING_NAME}
    
  

  
    
  

EOF

# Create ApiDefinition.cs
cat > macios/${BINDING_NAME}.MaciOS.Binding/ApiDefinition.cs ` pointing to the directory containing `Package.swift`.

> **Note:** The `` MSBuild item supports both `.xcodeproj` and Swift Package directories.

## Step 5: Add Native Library Dependencies

Choose the appropriate method for your library's distribution:

### Option A: CocoaPods

Create `macios/native/MyBinding/Podfile`:

```ruby
platform :ios, '15.0'

target 'MyBinding' do
  use_frameworks! :linkage => :static
  
  # Add your native library pod
  pod 'FirebaseMessaging', '~> 10.0'
  # Add other dependencies as needed
  pod 'FirebaseCore'
end

post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES'
      config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '15.0'
    end
  end
end
```

Install dependencies:

```bash
cd macios/native/MyBinding
pod install
# After this, open MyBinding.xcworkspace instead of .xcodeproj
```

### Option B: Swift Package Manager

In Xcode:
1. **File → Add Package Dependencies**
2. Enter the package repository URL
3. Select version rules
4. Add to your target

Or create `Package.swift`:

```swift
// swift-tools-version:5.9
import PackageDescription

let package = Package(
    name: "MyBinding",
    platforms: [.iOS(.v15), .macCatalyst(.v15)],
    products: [
        .library(name: "MyBinding", type: .static, targets: ["MyBinding"])
    ],
    dependencies: [
        .package(url: "https://github.com/example/SomeLibrary.git", from: "1.0.0")
    ],
    targets: [
        .target(
            name: "MyBinding",
            dependencies: [
                .product(name: "SomeLibrary", package: "SomeLibrary")
            ]
        )
    ]
)
```

### Option C: Manual XCFramework

1. Drag the `.xcframework` into the Xcode project
2. Ensure "Copy items if needed" is checked
3. Add to target's "Frameworks and Libraries" section
4. Set "Embed" to **Do Not Embed** (for static linking)

## Step 6: Implement the Swift Wrapper

Create `macios/native/MyBinding/MyBinding/DotnetMyBinding.swift`:

```swift
import Foundation
import UIKit
import TheNativeLibrary  // Import your native library

/// Main binding class exposed to .NET
/// The @objc attribute with explicit name ensures stable Objective-C naming
@objc(DotnetMyBinding)
public class DotnetMyBinding: NSObject {
    
    // MARK: - Initialization
    
    /// Initialize the native library
    /// Call this from your .NET app's startup (e.g., MauiProgram.cs)
    @objc(initializeWithApiKey:)
    public static func initialize(apiKey: String) {
        TheNativeLibrary.configure(withApiKey: apiKey)
    }
    
    /// Check if the library is initialized
    @objc(isInitialized)
    public static func isInitialized() -> Bool {
        return TheNativeLibrary.isConfigured
    }
    
    // MARK: - Synchronous Methods
    
    /// Get a simple value from the native library
    @objc(getVersion)
    public static func getVersion() -> String {
        return TheNativeLibrary.version
    }
    
    /// Process data and return result
    @objc(processDataWithInput:)
    public static func processData(input: String) -> String? {
        guard let result = TheNativeLibrary.process(input) else {
            return nil
        }
        return result.stringValue
    }
    
    // MARK: - Asynchronous Methods (Completion Handlers)
    
    /// Perform async operation with completion handler
    /// .NET can use [Async] attribute to generate async/await version
    @objc(fetchDataWithQuery:completion:)
    public static func fetchData(
        query: String,
        completion: @escaping (String?, NSError?) -> Void
    ) {
        TheNativeLibrary.fetch(query: query) { result in
            switch result {
            case .success(let data):
                completion(data.stringValue, nil)
            case .failure(let error):
                completion(nil, error as NSError)
            }
        }
    }
    
    /// Async method with complex result data
    @objc(performOperationWithConfig:completion:)
    public static func performOperation(
        config: NSDictionary,
        completion: @escaping (NSData?, NSError?) -> Void
    ) {
        guard let configDict = config as? [String: Any] else {
            let error = NSError(
                domain: "DotnetMyBinding",
                code: -1,
                userInfo: [NSLocalizedDescriptionKey: "Invalid configuration"]
            )
            completion(nil, error)
            return
        }
        
        TheNativeLibrary.performOperation(config: configDict) { result in
            switch result {
            case .success(let data):
                completion(data, nil)
            case .failure(let error):
                completion(nil, error as NSError)
            }
        }
    }
    
    // MARK: - View Creation
    
    /// Create a native view to embed in .NET MAUI
    /// Return UIView for cross-platform compatibility
    @objc(createViewWithFrame:)
    public static func createView(frame: CGRect) -> UIView {
        let nativeView = TheNativeLibrary.createCustomView()
        nativeView.frame = frame
        return nativeView
    }
    
    /// Create a configured view with options
    @objc(createViewWithFrame:options:)
    public static func createView(frame: CGRect, options: NSDictionary) -> UIView {
        let config = options as? [String: Any] ?? [:]
        let nativeView = TheNativeLibrary.createCustomView(options: config)
        nativeView.frame = frame
        return nativeView
    }
    
    // MARK: - Delegate/Callback Pattern
    
    private static var callbackHandler: ((String) -> Void)?
    
    /// Register a callback for events
    /// .NET will pass an Action that gets invoked
    @objc(registerCallbackWithHandler:)
    public static func registerCallback(handler: @escaping (String) -> Void) {
        callbackHandler = handler
        TheNativeLibrary.setEventHandler { event in
            callbackHandler?(event.description)
        }
    }
    
    /// Unregister the callback
    @objc(unregisterCallback)
    public static func unregisterCallback() {
        callbackHandler = nil
        TheNativeLibrary.setEventHandler(nil)
    }
}
```

### Swift Wrapper Design Guidelines

#### Type Mapping Rules

Only use types that .NET already knows how to marshal:

| 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 *` | `N

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [Redth](https://github.com/Redth)
- **Source:** [Redth/maui-skillz](https://github.com/Redth/maui-skillz)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-redth-maui-skillz-ios-slim-bindings
- Seller: https://agentstack.voostack.com/s/redth
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
