Guide users through creating .NET C# bindings for Swift or Objective-C Apple platform libraries (iOS, macOS, Mac Catalyst, tvOS). Takes a user from an SPM package URL or xcframework to a validated NuGet package using the SwiftBindings.Sdk. Handles prerequisites, xcframework building, binding generation, error diagnosis, and optional binding review. Auto-detects whether the framework is Swift, ObjC, or mixed, and runs the correct pipeline. Triggers on "bind Swift library", "Swift to C#", "create Swift binding", "Swift NuGet package", "use Swift from .NET", "Swift interop", "Swift .NET MAUI", "bind ObjC library", "Objective-C binding", "ObjC to C#", "bind Objective-C", "ObjC NuGet", "bind iOS framework", "bind macOS framework", "bind tvOS framework", "bind Mac Catalyst framework", "SwiftUI from .NET", "Swift NativeAOT", "upgrade Swift bindings".
How this skill is triggered — by the user, by Claude, or both
Slash command
/swift-binding-assistant:swift-binding-assistantThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Guide users through creating .NET C# bindings for Swift or Objective-C Apple platform libraries using the SwiftBindings.Sdk. Supports iOS, macOS, Mac Catalyst, and tvOS. The generator auto-detects framework type (Swift, ObjC, or mixed) and runs the correct pipeline — no flags needed.
Guide users through creating .NET C# bindings for Swift or Objective-C Apple platform libraries using the SwiftBindings.Sdk. Supports iOS, macOS, Mac Catalyst, and tvOS. The generator auto-detects framework type (Swift, ObjC, or mixed) and runs the correct pipeline — no flags needed.
The authoritative docs live in the project wiki. Always fetch the latest version from the raw URLs below when you need to reference them — do NOT rely on memorized content, as it may be outdated. Use whatever fetch mechanism is available (e.g., WebFetch, curl, browser, or built-in web tools).
| Doc | URL |
|---|---|
| Getting Started | https://raw.githubusercontent.com/wiki/justinwojo/swift-dotnet-bindings/Getting-Started.md |
| FAQ | https://raw.githubusercontent.com/wiki/justinwojo/swift-dotnet-bindings/FAQ.md |
| Troubleshooting | https://raw.githubusercontent.com/wiki/justinwojo/swift-dotnet-bindings/Troubleshooting.md |
| Known Limitations | https://raw.githubusercontent.com/wiki/justinwojo/swift-dotnet-bindings/Known-Limitations.md |
| Supported Features | https://raw.githubusercontent.com/wiki/justinwojo/swift-dotnet-bindings/Supported-Features.md |
| How Bindings Map | https://raw.githubusercontent.com/wiki/justinwojo/swift-dotnet-bindings/How-Bindings-Map.md |
| Customization | https://raw.githubusercontent.com/wiki/justinwojo/swift-dotnet-bindings/Customization.md |
| SwiftUI Interop | https://raw.githubusercontent.com/wiki/justinwojo/swift-dotnet-bindings/SwiftUI-Interop.md |
| NativeAOT Deployment | https://raw.githubusercontent.com/wiki/justinwojo/swift-dotnet-bindings/NativeAOT-Deployment.md |
| Ownership & Disposal | https://raw.githubusercontent.com/wiki/justinwojo/swift-dotnet-bindings/Ownership.md |
| Upgrading | https://raw.githubusercontent.com/wiki/justinwojo/swift-dotnet-bindings/Upgrading.md |
| Architecture | https://raw.githubusercontent.com/wiki/justinwojo/swift-dotnet-bindings/Architecture.md |
| Multi-Framework Libraries | https://raw.githubusercontent.com/wiki/justinwojo/swift-dotnet-bindings/Multi-Framework-Libraries.md |
| Publishing | https://raw.githubusercontent.com/wiki/justinwojo/swift-dotnet-bindings/Publishing.md |
Fetch docs proactively when:
User Input
├── SPM package URL → Build xcframework (spm-to-xcframework)
├── GitHub release with xcframework → Download it
├── Local xcframework → Use directly
└── Vendor xcframework → Verify requirements
│
▼
Check prerequisites
│
▼
Create binding project (dotnet new swift-binding)
Copy xcframework into project
Configure dependencies if needed
│
▼
dotnet build
│
┌────┴────┐
Success Errors → Diagnose (fetch Troubleshooting.md)
│ │
▼ │
dotnet pack ◄─────────┘
│
▼
NuGet package ready
│
▼
Ask: "Would you like me to review the generated binding
for completeness and usability?"
Framework type is AUTO-DETECTED during build — no flags needed:
Swift → Parser/Marshaler/Emitter pipeline → {Module}.cs + Swift wrapper
ObjC → Clang AST pipeline → ApiDefinition.cs + StructsAndEnums.cs + BgenDelegates.cs
Mixed → Both pipelines run → two projects emitted
Ask the user what they're starting with:
Package.swift (e.g., https://github.com/kean/Nuke).xcframework directory on diskAlso ask:
Run these checks and report results to the user:
# Host OS check — Apple platform binding generation requires macOS + Xcode.
# If this prints anything other than "Darwin", STOP and tell the user the
# workflow can't proceed on this host (Linux/Windows have no Xcode toolchain
# and can't compile the Swift wrapper xcframework).
uname -s
# Xcode check — requires Xcode 26 or later (not just Command Line Tools)
xcode-select -p
# Must show /Applications/Xcode.app/Contents/Developer or similar
# If it shows /Library/Developer/CommandLineTools, tell the user:
# sudo xcode-select -s /Applications/Xcode.app
# Verify Xcode version
xcodebuild -version
# Must be Xcode 26.x or later
# .NET SDK check
dotnet --version
# Must be 10.x or later
# Platform workload check
dotnet workload list
# Must include the workload for the target platform:
# iOS: "ios" → dotnet workload install ios
# macOS: "macos" → dotnet workload install macos
# Mac Catalyst: "maccatalyst" → dotnet workload install maccatalyst
# tvOS: "tvos" → dotnet workload install tvos
# Template check
dotnet new list swift-binding
# If not found: dotnet new install SwiftBindings.Templates
If any prerequisite is missing, guide the user through installing it before proceeding. Do not continue until all prerequisites pass.
Hard stop on non-macOS hosts: if uname -s is not Darwin, the workflow cannot proceed — Apple platform bindings require Xcode, which only runs on macOS. Inform the user and stop. Do not try to suggest cross-compilation, Docker, or remote-build workarounds; there isn't a supported path today.
If the user provides an SPM package URL:
Clone the spm-to-xcframework tool (if not already present):
git clone https://github.com/justinwojo/spm-to-xcframework.git /tmp/spm-to-xcframework
Determine the latest release tag for the Swift package. Do not use tail -5 on unsorted ls-remote output — git returns tags in lexicographic order, so 1.10.0 would sort before 1.2.0 and you'd pick the wrong version. Use a version-aware sort instead:
# Sort tags by semver descending; suffix=- pushes prereleases (-rc1, -beta) below stable releases
git -c versionsort.suffix=- ls-remote --tags --sort=-version:refname --refs <PACKAGE_URL> \
| head -10
# Output: abc1234 refs/tags/12.7.3
# def5678 refs/tags/12.7.2
# ...
# The first line after filtering prereleases is your latest stable tag.
If the project uses non-semver tags or has unusual versioning, fall back to the GitHub releases page (https://github.com/<owner>/<repo>/releases/latest) or ask the user which version they want.
Build the xcframework (capture output — this can take several minutes):
cd /tmp/spm-to-xcframework && swift run spm-to-xcframework \
--url <PACKAGE_URL> \
--version <TAG> \
--output /tmp/xcframeworks 2>&1 | tee /tmp/spm-build.txt
For a local Package.swift directory:
cd /tmp/spm-to-xcframework && swift run spm-to-xcframework \
--path /path/to/package \
--output /tmp/xcframeworks 2>&1 | tee /tmp/spm-build.txt
The output xcframework(s) will be in /tmp/xcframeworks/.
If the library has multiple products, ask the user which one to bind (or use --product <NAME> to target one).
If the library has dependencies that the user also wants to bind, use --include-deps.
The tool handles BUILD_LIBRARY_FOR_DISTRIBUTION=YES and dynamic framework building automatically.
Verify the xcframework meets requirements:
# Check it exists and has expected structure
ls <PATH>/Library.xcframework/
# Check it's a dynamic framework (not static)
# Look for a .framework bundle with a Mach-O binary inside
# Use the appropriate slice directory for your target platform:
# iOS: ios-arm64_x86_64-simulator or ios-arm64
# macOS: macos-arm64
# Mac Catalyst: ios-arm64-maccatalyst
# tvOS: tvos-arm64_x86_64-simulator or tvos-arm64
file <PATH>/Library.xcframework/<slice-dir>/Library.framework/Library
# Should say "dynamically linked shared library"
# If it says "current ar archive" → it's static, needs rebuild
# Check for Swift module (use appropriate slice directory)
ls <PATH>/Library.xcframework/<slice-dir>/Library.framework/Modules/*.swiftmodule 2>/dev/null
# If present → Swift framework (uses ABI JSON pipeline)
# If empty → ObjC framework (uses Clang AST pipeline) — both are fully supported
# For ObjC frameworks, verify headers and module map exist
ls <PATH>/Library.xcframework/<slice-dir>/Library.framework/Headers/ 2>/dev/null
ls <PATH>/Library.xcframework/<slice-dir>/Library.framework/Modules/module.modulemap 2>/dev/null
# ObjC frameworks need public headers AND a module.modulemap for binding generation
# The generator uses module.modulemap as the validity check for ObjC frameworks
If the xcframework is static:
BUILD_LIBRARY_FOR_DISTRIBUTION=YES.# Pick a working directory
mkdir -p ~/swift-bindings && cd ~/swift-bindings
# Create the project — naming convention:
# Single library: <LibraryName>.Swift.<Platform> (e.g., Nuke.Swift.iOS)
# Multi-vendor SDK set: SwiftBindings.<Vendor>.<Module> (e.g., SwiftBindings.Stripe.Core)
# Platform suffixes: iOS, macOS, MacCatalyst, tvOS
# Use --platform to set the target (default: ios)
# iOS (default)
dotnet new swift-binding -n <LibraryName>.Swift.iOS
# macOS
dotnet new swift-binding -n <LibraryName>.Swift.macOS --platform macos
# Mac Catalyst
dotnet new swift-binding -n <LibraryName>.Swift.MacCatalyst --platform maccatalyst
# tvOS
dotnet new swift-binding -n <LibraryName>.Swift.tvOS --platform tvos
# Copy the xcframework into the project
cp -r <PATH_TO_XCFRAMEWORK> <LibraryName>.Swift.<Platform>/
The generated .csproj will look like:
<Project Sdk="SwiftBindings.Sdk/X.Y.Z">
<PropertyGroup>
<TargetFramework>net10.0-ios</TargetFramework> <!-- or net10.0-macos, net10.0-maccatalyst, net10.0-tvos -->
</PropertyGroup>
</Project>
The SDK version (X.Y.Z) is pinned by the template at install time.
The SDK auto-detects the target platform from the TFM — no additional configuration needed beyond setting the correct TFM.
The same .csproj and SDK work for both Swift and ObjC frameworks. The SDK auto-detects the framework type during the build and runs the correct pipeline. No additional configuration is needed for ObjC frameworks.
If the library imports other Swift frameworks, the user needs to provide them.
Auto-detection (default): The SDK ships with <SwiftAutoDetectDependencies>true</SwiftAutoDetectDependencies> enabled by default. The build analyzes the xcframework's binary linkage, finds matching sibling binding projects in the solution, and auto-injects <ProjectReference> items. If a needed dependency is missing, it emits SWIFTBIND080 with a suggested fix. In most multi-project solutions, you do not need to declare dependencies manually.
Option 1: <ProjectReference> (multi-project solutions — preferred)
For multi-product vendors (e.g., Stripe) where you're binding several frameworks in the same solution:
<ItemGroup>
<ProjectReference Include="../DependencyA.Swift.iOS/DependencyA.Swift.iOS.csproj" />
</ItemGroup>
The SDK automatically resolves dependency xcframework search paths and module databases during wrapper compilation, propagates native references to the consumer's app bundle, and converts the reference into a transitive <PackageReference> during dotnet pack — no manual configuration needed.
Option 2: <SwiftFrameworkDependency> (external/pre-built or internal dependencies)
For dependencies that are pre-built xcframeworks (not sibling projects), or internal helper frameworks (e.g., ObjC-only support modules) that won't be published as their own NuGet package:
<ItemGroup>
<!-- External, published as its own NuGet package -->
<SwiftFrameworkDependency Include="../DependencyA.xcframework"
PackageId="DependencyA.Swift.iOS"
PackageVersion="1.0.0" />
<!-- Internal helper, not published — omit metadata intentionally -->
<SwiftFrameworkDependency Include="../InternalHelper.xcframework" />
</ItemGroup>
Each dependency also needs to be a built xcframework. If the user used spm-to-xcframework --include-deps, these will already exist in the output directory.
For published dependencies, both PackageId and PackageVersion are required for NuGet pack to declare the dependency (SWIFTBIND040 warns if missing). For internal helpers that won't be published separately, the warning is intentional — consumers will need to add a <NativeReference> for the internal framework manually. See the Multi-Framework Libraries wiki page for the full pattern.
Always capture build output to a temp file — it can be very long:
cd <LibraryName>.Swift.<Platform>
dotnet build 2>&1 | tee /tmp/swift-binding-build.txt
Then read the output file to check the result.
For Swift frameworks, the build automatically:
.cs and .swift wrapper files)For ObjC frameworks, the build automatically:
clang -ast-dump=json on the framework's umbrella headerApiDefinition.cs (always), plus StructsAndEnums.cs and BgenDelegates.cs if applicableFor mixed frameworks (both Swift and ObjC surface), both pipelines run and two projects are emitted.
Tell the user the build succeeded and move to Step 5.
Immediately fetch the troubleshooting guide from the URL in the doc table above, then diagnose the error. Here's a quick-reference for the most common errors:
| Error | Cause | Fix |
|---|---|---|
| SWIFTBIND001 | No xcframework in project dir | Copy xcframework into the project, or add explicit <SwiftFramework> item |
| SWIFTBIND002 | Multiple xcframeworks found | One xcframework per project — create separate binding projects for each |
| SWIFTBIND003 | xcframework path doesn't exist | Check the path in <SwiftFramework> item |
| SWIFTBIND010 | Unsupported target framework | Use Apple platform TFM: net10.0-ios, net10.0-macos, net10.0-maccatalyst, net10.0-tvos |
| SWIFTBIND011 | Consumer targets older platform version than library requires | Update SupportedOSPlatformVersion to the version shown in the warning |
| SWIFTBIND030 | Missing architectures for packing | Set <SwiftWrapperArchitectures>all</SwiftWrapperArchitectures> |
| SWIFTBIND031 | Wrapper xcframework missing device or simulator slice | Verify xcframework has both slices, or set <IsPackable>false</IsPackable> for local-only |
| SWIFTBIND035 | Cannot resolve platform version for NuGet pack | Use a versioned TFM (net10.0-ios26.0) or install the platform workload |
| SWIFTBIND040 | <SwiftFrameworkDependency> missing PackageId/PackageVersion | Add metadata if the dep is published as its own NuGet package; expected/intentional for internal-only helpers |
| SWIFTBIND050 | Swift wrapper compilation failed | Missing dependency framework — add <ProjectReference> or <SwiftFrameworkDependency> |
| SWIFTBIND051 | Wrapper required but failed | Fix wrapper compilation, or set <SwiftWrapperRequired>false</SwiftWrapperRequired> to downgrade to warning |
| SWIFTBIND052 | SwiftUI bridge compilation failed | Bridge sessions will throw DllNotFoundException; main bindings unaffected |
| SWIFTBIND060 | Dependency not provided (generator) OR skipped types count (SDK) | Context-dependent: from the generator, means a dependency xcframework is missing — provide via <ProjectReference> or <SwiftFrameworkDependency>. From the SDK build targets, reports how many types were skipped — check binding-report.json. |
| SWIFTBIND080 | Cross-module dependency, no sibling project found | Add <ProjectReference> to the dependency binding project |
| SWIFTBIND090-094 | Internal validation issue | Generated P/Invoke may not work at runtime — file an issue with the xcframework |
| SWIFTBIND100 | <SwiftPackage> used (not available yet) | Build xcframework from SPM first, then use <SwiftFramework> |
| SWIFTBIND101 | Static xcframework detected on the Swift path | Generator auto-falls back to ObjC pipeline for ObjC static libs (Firebase, etc.) — usually transparent. Only a real failure if the static lib is genuinely Swift (must rebuild as dynamic with MACH_O_TYPE = mh_dylib) or if the ObjC fallback also fails (then you'll see "no ObjC module.modulemap and no Swift module") |
| SWIFTBIND102 | No Swift module found | ObjC framework (auto-detected) or malformed xcframework |
| SWIFTBIND103 | swift-frontend failed to extract ABI | Update Xcode, check xcode-select -p |
| Generator crash / 0 types | Missing BUILD_LIBRARY_FOR_DISTRIBUTION=YES | Rebuild xcframework with the flag |
| CS0246 missing type | Apple framework type not in .NET SDK | Members using that type can be ignored — they'll work when .NET adds the type |
| bgen errors (BI1xxx) | ObjC binding tool issues | Usually type mapping — check ApiDefinition.cs for unsupported patterns |
After fixing, rebuild and repeat until successful.
dotnet pack 2>&1 | tee /tmp/swift-binding-pack.txt
The SDK defaults to SwiftWrapperArchitectures=all, so packing should work out of the box. If you get SWIFTBIND030 or SWIFTBIND031, verify the xcframework has both device and simulator slices. If you get SWIFTBIND035, use a versioned TFM (net10.0-ios26.0) or install the platform workload.
To override the auto-extracted version (if the xcframework uses Xcode's default "1.0", you'll see warning SWIFTBIND020):
<PropertyGroup>
<PackageVersion>2.5.0</PackageVersion>
</PropertyGroup>
The package contains everything consumers need: C# bindings DLL, source xcframework, wrapper xcframework, optional SwiftUI bridge xcframework, module database ({Module}Database.xml) for downstream binding projects, and a consumer .targets file that auto-injects NativeReference items.
The output is a .nupkg file (e.g., bin/Release/<tfm>/<LibraryName>.Swift.<Platform>.1.0.0.nupkg).
Tell the user the NuGet package is ready and where to find it.
For advanced packaging needs (CI release workflows, multi-package version coordination, local NuGet testing, publishing to nuget.org), fetch the Publishing wiki page from the doc table above. Highlights:
Directory.Build.props for multi-package reposNuGet.config setup for iterating before publishingAfter a successful build, ask the user:
"The binding built successfully and the NuGet package is ready. Would you like me to review the generated binding for completeness and usability?"
Only proceed with review if the user says yes. Do not review automatically.
The binding report is the most important diagnostic. It lives at:
obj/<config>/<tfm>/swift-binding/binding-report.json
Where <config> is Debug or Release and <tfm> is your target framework (e.g., net10.0-ios, net10.0-macos).
Read it and summarize:
AnyTypeFallback entries suggest missing framework dependenciesCommon skip reasons:
| Skip Reason | Meaning |
|---|---|
UnsupportedSignature | Parameter or return type the generator can't handle yet |
UnsupportedType | Type uses an unsupported Swift pattern |
AnyTypeFallback | Type couldn't be resolved — check for missing dependencies |
UnsupportedClosure | Closure with unsupported argument types |
UnsupportedExistential | Existential type the generator can't project |
UnsatisfiedGenericConstraint | Generic type argument can't satisfy C# constraints |
AsyncProperty | Async computed property (not yet supported) |
StaticProtocolMember | Static protocol members can't be dispatched through witness tables |
DuplicateSignature | Another member already emitted with the same C# signature |
SwiftUIView | SwiftUI View (handled by the bridge, not normal binding) |
SynthesizedCodable | Codable encode/init(from:) pruned for cleaner API — by design |
UnsupportedAsyncStream | AsyncStream/AsyncSequence can't be projected |
UnderscorePrefixInternal | Underscore-prefixed type/member treated as internal |
ExtensionDefault | Extension method providing a default implementation (pruned) |
The generated .cs file is at:
obj/<config>/<tfm>/swift-binding/<ModuleName>.cs
Scan for:
CallConvCdecl (via native ARM64 thunks or @_cdecl wrappers), so SB0001 methods are a small minority.ObjC bindings produce up to three C# files in the intermediate output directory (obj/<config>/<tfm>/swift-binding/). ApiDefinition.cs is always present; StructsAndEnums.cs and BgenDelegates.cs are conditional — only emitted if the framework has enums/structs/constants or block-based callbacks respectively. A missing file is expected, not a failure.
obj/<config>/<tfm>/swift-binding/ApiDefinition.cs (always)
obj/<config>/<tfm>/swift-binding/StructsAndEnums.cs (if enums/structs/constants exist)
obj/<config>/<tfm>/swift-binding/BgenDelegates.cs (if block callbacks exist)
ApiDefinition.cs — Review for:
WeakDelegate/Wrap pattern)@required protocol members, not @optionalNS_UNAVAILABLE init<summary> and <param> XML tags from ObjC headers[Introduced(PlatformName.iOS, x, y)], [Deprecated(...)], [Obsoleted(...)] attributesCopy, Assign, Weak, Strong on propertiesNSArray<NSString *> → string[]_Bool * → out bool, CGPoint * → out CGPointStructsAndEnums.cs — Review for:
SDWebImageOptions → Options inside the namespace)int, long, ulong should match the ObjC typedef[Native] on NSInteger/NSUInteger-backed enums__Internal or library nameBgenDelegates.cs — Review for:
ObjC bindings emit diagnostic information about skipped symbols. Look for:
[Internal] instead of being skipped)ObjC Known Limitations (MAUI bgen platform constraints, not generator bugs):
[Category] as static classes)[Category] can't have constructors)va_list incompatible with P/Invoke)Fetch Known Limitations and Supported Features from the URLs in the doc table above to explain any gaps.
Summarize in a clear format:
After packaging, show the user how to consume the binding:
<!-- In a .NET MAUI or Apple platform app .csproj -->
<!-- Use the package matching your target platform -->
<PackageReference Include="<LibraryName>.Swift.iOS" Version="1.0.0" />
<!-- or <LibraryName>.Swift.macOS, <LibraryName>.Swift.MacCatalyst, <LibraryName>.Swift.tvOS -->
using <LibraryName>;
using Swift.Runtime;
// Simple usage — Swift classes work like any .NET class
var result = SomeClass.DoSomething();
// Batch operations — use SwiftDisposeScope for efficient cleanup
using (new SwiftDisposeScope())
{
foreach (var item in items)
{
var processed = SomeClass.Process(item);
// All objects disposed automatically at scope exit
}
}
Important consumer notes:
SwiftDisposeScope or using. The SB1001 Roslyn analyzer surfaces undisposed locals as an info diagnostic (not a warning) — it's a hint for deterministic cleanup, not a correctness requirement. Fetch the Ownership guide (see doc table) if the user asks.<PropertyGroup>
<PublishAot>true</PublishAot>
<PublishAotUsingRuntimePack>true</PublishAotUsingRuntimePack>
<TrimMode>partial</TrimMode>
<NoWarn>$(NoWarn);IL2026;IL2087;IL2091;IL3050</NoWarn>
</PropertyGroup>
Fetch the NativeAOT Deployment guide (see doc table) if the user asks.| ID | Meaning | Auto-Suppressed? |
|---|---|---|
SB0001 | CallConvSwift fallback — method uses direct CallConvSwift P/Invoke (no @_cdecl wrapper or native thunk available). May crash on Mono. Safe on NativeAOT. | Yes — in NativeAOT builds via SwiftBindingsInteropMode |
SB0002 | Missing symbol — P/Invoke entry point not found. Will throw EntryPointNotFoundException. | No |
SB0003 | Non-dispatchable protocol member — can't be called on protocol-typed values. Use a concrete type instead. | No |
SB0004 | Empty protocol interface — all members were skipped. Interface exists for type identity only. | No |
SB1001 | Undisposed ISwiftObject — Roslyn analyzer info diagnostic (not a warning). Suggests using or Dispose() for deterministic cleanup. GC finalizer handles correctness. | No |
If the user comes in with an existing project that has errors:
Fetch the Customization guide from the URL in the doc table above.
Key customization options:
<NamespacePattern>MyCompany.{Module}</NamespacePattern> on <SwiftFramework> item<SwiftGenerateDocComments>false</SwiftGenerateDocComments>dotnet build -p:SwiftWrapperArchitectures=simulator for simulator-only builds (~2x faster)dotnet build -p:SwiftGeneratorVerbosity=2 for debug-level generator logging<SwiftAutoDetectDependencies>false</SwiftAutoDetectDependencies> if you need to manage <ProjectReference>/<SwiftFrameworkDependency> items manually (rare)ApiDefinition.cs and the error outputBgenDelegates.cs, but may need manual dedupUIButton, NSString)_-prefixed names are suppressed by default (private API convention)Fetch the SwiftUI Interop guide from the URL in the doc table above.
SwiftUI views are automatically detected and bridged — the user doesn't need to do anything extra. Key features:
{View}Session wraps the SwiftUI view in a UIHostingControllerUpdate{Param}() methods for reactive updates.AnimationSpeed(2.0), .Playing(), etc.await ScannerViewSession.CreateAsync(...) for views needing async initHashable → String, Numeric → Int, View → EmptyViewbridge-hints.json for skipping views, forcing templates, or adding importsAlways fetch the Multi-Framework Libraries wiki page (URL in the doc table above) when the user is binding more than one framework from the same vendor — it has the authoritative guidance and should be your primary reference.
Quick orientation:
For multi-framework SDKs (e.g., Stripe, Firebase), each xcframework gets its own binding project. Organize them in a vendor directory:
payments-bindings/
├── PaymentsCore/
│ ├── SwiftBindings.Payments.Core.csproj
│ └── PaymentsCore.xcframework/
├── PaymentsCrypto/ ← internal ObjC-only helper, no csproj
│ └── PaymentsCrypto.xcframework/
├── PaymentsAuth/
│ ├── SwiftBindings.Payments.Auth.csproj ← depends on Core + Crypto
│ └── PaymentsAuth.xcframework/
└── PaymentsUI/
├── SwiftBindings.Payments.UI.csproj
└── PaymentsUI.xcframework/
Auto-detection does most of the work. The SDK ships with <SwiftAutoDetectDependencies>true</SwiftAutoDetectDependencies> enabled — it analyzes binary linkage, finds matching sibling binding projects, and auto-injects <ProjectReference> items. In most cases the user can just put projects in the same solution and build.
When you do declare dependencies explicitly:
<ProjectReference> for sibling binding projects in the same solution. Provides cross-module type resolution, wrapper search paths, native reference propagation, and converts to a transitive <PackageReference> during pack — all automatic.<SwiftFrameworkDependency> for internal helper xcframeworks (no binding project) or external pre-built xcframeworks. Add PackageId/PackageVersion metadata if the dependency is published as its own NuGet package; omit metadata for internal-only helpers (the SWIFTBIND040 warning is intentional in that case).Example with both:
<!-- SwiftBindings.Payments.Auth.csproj -->
<Project Sdk="SwiftBindings.Sdk/X.Y.Z">
<PropertyGroup>
<TargetFramework>net10.0-ios</TargetFramework>
<PackageId>SwiftBindings.Payments.Auth</PackageId>
<Version>2.0.0</Version>
</PropertyGroup>
<ItemGroup>
<!-- Sibling public dependency, also published as its own package -->
<ProjectReference Include="../PaymentsCore/SwiftBindings.Payments.Core.csproj" />
<!-- Internal ObjC-only helper, won't be published separately -->
<SwiftFrameworkDependency Include="../PaymentsCrypto/PaymentsCrypto.xcframework" />
</ItemGroup>
</Project>
Internal vs public dependencies. Internal frameworks (no .swiftmodule, ObjC-only support libraries) still need their xcframeworks at compile and runtime, but get no binding project. They're declared via <SwiftFrameworkDependency> without metadata. Important: internal frameworks are NOT bundled into the published NuGet package — consumers of the published package must add a <NativeReference> item in their app project to include the internal framework at runtime.
Two-pass build pattern for CI. A single dotnet build on the leaf consumer with <ProjectReference> items works because MSBuild orders dependencies automatically. CI systems that build each .csproj individually may need two passes — the first generates bindings (tolerating wrapper failures from missing dependencies), the second completes wrapper compilation:
# Pass 1: tolerate failures, generates bindings
for project in PaymentsCore PaymentsAuth PaymentsUI; do
dotnet build $project/SwiftBindings.Payments.$project.csproj || true
done
# Pass 2: deferred wrapper compilation succeeds
for project in PaymentsCore PaymentsAuth PaymentsUI; do
dotnet build $project/SwiftBindings.Payments.$project.csproj
done
Version coordination. Set the version once in Directory.Build.props or pass /p:Version=X.Y.Z to all dotnet pack calls. Pack leaf dependencies first so dependents can resolve transitive <PackageReference> versions. See the Publishing wiki page for the full release workflow pattern (tag-based GitHub Actions, dry-run support, multi-pass CI builds).
Fetch the Upgrading guide from the URL in the doc table above.
Quick steps:
dotnet new install SwiftBindings.Templates (updates template).csproj: <Project Sdk="SwiftBindings.Sdk/X.Y.Z">dotnet build (runtime version updates automatically)dotnet pack if distributing via NuGetFetch the Ownership guide from the URL in the doc table above.
Quick summary:
swift_release — no explicit disposal needed for typical useusing for deterministic cleanup of scarce resources.SwiftDisposeScope for efficient cleanup of many objectsCreates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub justinwojo/claude-skills --plugin smart-permissions