Install
$ agentstack add skill-redth-maui-skillz-ios-slim-bindings ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo 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.
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:
- Invoke XcodeBuild to compile the native framework
- Create the xcframework
- 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:
- File → Add Package Dependencies
- Enter the package repository URL
- Select version rules
- 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
- Drag the
.xcframeworkinto the Xcode project - Ensure "Copy items if needed" is checked
- Add to target's "Frameworks and Libraries" section
- 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.
- Author: Redth
- Source: Redth/maui-skillz
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.