AgentStack
SKILL verified MIT Self-run

Ios Slim Bindings

skill-redth-maui-skillz-ios-slim-bindings · by Redth

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…

No reviews yet
0 installs
11 views
0.0% view→install

Install

$ agentstack add skill-redth-maui-skillz-ios-slim-bindings

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Ios Slim Bindings? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

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

brew install xcodegen

Create Directory Structure

# 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:

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

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

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

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

Verify the Generated Project

# 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

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

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

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

# 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

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

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:

#!/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:

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

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.

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

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.