Create C# bindings for Apple frameworks in dotnet/macios. USE FOR: binding new APIs, implementing .todo file entries, creating Xcode SDK bindings, binding AVFoundation/UIKit/AppKit or any Apple framework, "bind this framework", "implement these APIs". DO NOT USE FOR: Xcode beta version bumps (use macios-xcode-beta-update skill), CI failure investigation (use macios-ci-failure-inspector skill).
Create C# bindings for Apple platform APIs in the dotnet/macios repository. This skill encodes the end-to-end workflow: from reading .todo files through implementation, building, and validating with xtro, cecil, and introspection tests on all platforms.
Use this skill when:
.todo files in tests/xtro-sharpie/api-annotations-dotnet/./configure already run)XCODE_DEVELOPER_ROOT pathmake world or make all && make install already completedCheck the .todo files to see what APIs are missing:
ls tests/xtro-sharpie/api-annotations-dotnet/*-{FrameworkName}.todo
cat tests/xtro-sharpie/api-annotations-dotnet/iOS-{FrameworkName}.todo
Each .todo file lists missing APIs per platform (iOS, tvOS, macOS, MacCatalyst). The format is:
!missing-selector! ClassName::methodName: not bound
!missing-type! ClassName not bound
!missing-field! ClassName FieldName not bound
!missing-enum-value! EnumName::ValueName not bound
❌ NEVER bind APIs that aren't in the
.todofiles unless explicitly asked. The.todofiles are the source of truth for what's missing.
Run the xtro generator to produce reference C# bindings from the SDK headers:
make -C tests/xtro-sharpie gen-all
This creates generated .cs files you can search to find the correct C# signatures, attributes, and patterns for the APIs you need to bind. Use these as reference — don't copy them verbatim.
Before implementing, understand the native API:
$XCODE_DEVELOPER_ROOT)src/frameworkname.cs for patterns used in the same frameworkBefore writing any bindings, determine the SDK version you're targeting:
# Check the current SDK versions
grep -E 'public const string (iOS|TVOS|OSX|MacCatalyst) ' tools/common/SdkVersions.cs
# Or from Make.versions
grep '_NUGET_OS_VERSION=' Make.versions
Use the version from SdkVersions.cs (e.g., 26.2) for all availability attributes. If the user specifies a different version (e.g., binding a beta branch at 26.4), use that instead. Ask the user if you're unsure which version to use.
Bindings go in these locations:
src/frameworkname.cs — API definitions (interfaces with [Export] attributes)src/FrameworkName/ — Manual code (partial classes, enums, P/Invokes, extensions)src/frameworks.sources — Maps frameworks to source files (update if adding new files)Key binding patterns:
// New property on existing class
[Export ("allowsCaptureOfClearKeyVideo")]
bool AllowsCaptureOfClearKeyVideo { get; set; }
// New method on existing class
[Export ("setCaptionPreviewProfileId:")]
void SetCaptionPreviewProfileId ([NullAllowed] string profileId);
// New notification field
[Field ("AVPlayerInterstitialEventMonitorScheduleRequestedNotification")]
[Notification]
NSString ScheduleRequestedNotification { get; }
❌ NEVER forget platform availability attributes. Every new API must have
[iOS],[Mac],[TV],[MacCatalyst], and/or[No*]attributes matching the.todofile platforms where the API appears. This includes all binding types:
- API definition interfaces and members in
src/frameworkname.cs— use[iOS (X, Y)],[Mac (X, Y)], etc.- P/Invoke wrappers and manual properties in
src/FrameworkName/*.cs— use[SupportedOSPlatform ("iosX.Y")],[SupportedOSPlatform ("macos")], etc.- Fields, constants, and enum values
❌ NEVER use
string.Empty— use"". Never useArray.Empty<T>()— use[].
❌ NEVER add placeholder XML documentation text like
"To be added."anywhere — not in<remarks>,<summary>,<returns>,[Async (XmlDocs = ...)], or any other XML doc element. Either write meaningful documentation or omit the element entirely.
❌ NEVER forget
[NullAllowed]onout NSError errorparameters. Every method that takesNSError**(bound asout NSError error) must use[NullAllowed] out NSError error. This applies to all error-returning methods — the error output is null on success.
❌ NEVER forget
#nullable enableat the top of every new C# file you create.
❌ NEVER use non-blittable types (
bool,char) as backing fields in structs. Usebyte(forbool) andushort/short(forchar) with property accessors. See references/binding-patterns.md for the correct pattern.
❌ NEVER use
XAMCORE_5_0for new code.XAMCORE_5_0is only for fixing breaking API changes on existing types that shipped in prior releases. However, when xtro reports a mismatch on an existing type (e.g., wrong enum backing type, missing[Native]), and fixing it directly would be a breaking change, you must use#if XAMCORE_5_0guards to preserve binary compatibility while queuing the fix for the future. Add a.ignoreentry for the xtro mismatch. See references/binding-patterns.md § "XAMCORE_5_0 Pattern for Existing Types".
❌ NEVER use
#pragma warning disable 0169for struct fields. Instead, wrap public methods and properties inside#if !COREBUILD(but NOT fields — bgen needs to know the struct size).
⚠️ Place a space before parentheses and brackets:
Foo (),Bar (1, 2),myarray [0].
⚠️ Method names should follow .NET naming conventions — use verb-based names, not direct Objective-C selector translations (e.g.,
BuildMenunotMenuWithContents).
⚠️ For in depth binding patterns and conventions See references/binding-patterns.md
⚠️ Struct array parameters: When an API takes a C struct pointer + count (e.g.,
MyStruct*+NSUInteger), bind the raw pointer as[Internal]withIntPtr, then create a manual public wrapper using the factory pattern withfixed. See references/binding-patterns.md § "Struct Array Parameter Binding".
When a manually coded type (struct, extension, etc.) is not available on a specific platform (e.g., tvOS), you must handle compilation on that platform:
src/FrameworkName/MyStruct.cs), wrap the struct body with #if !TVOS[UnsupportedOSPlatform ("tvos")] on the structsrc/frameworkname.cs), add a type alias at the top so compilation succeeds:#if TVOS
using MyStruct = Foundation.NSObject;
#endif
The [NoTV] attribute on the API definition interface ensures the type won't appear in the final tvOS assembly, while the alias prevents compilation errors from method signatures that reference the struct.
make -C src build
Fix any compilation errors before proceeding. Builds can take up to 60 minutes — do not timeout early.
For any manually bound APIs (P/Invokes, manual properties on partial classes, struct accessors), add tests in tests/monotouch-test/{FrameworkName}/.
⚠️ Only run monotouch-tests (Step 6d) if you added or modified test files in this step. If no manual bindings were added (i.e., all APIs were bound via
[Export]in the API definition file), skip both this step and Step 6d.
using CoreText; // framework being tested
using NUnit.Framework;
namespace MonoTouchFixtures.CoreText { // MonoTouchFixtures.{FrameworkName}
[TestFixture]
[Preserve (AllMembers = true)]
public class FontTest {
[Test]
public void UIFontType_SystemFont ()
{
TestRuntime.AssertXcodeVersion (26, 4); // match the availability version
using (var font = new CTFont ("Helvetica", 12)) {
var fontType = font.UIFontType;
Assert.AreEqual (CTUIFontType.System, fontType);
}
}
}
}
Key patterns:
MonoTouchFixtures.{FrameworkName} (e.g., MonoTouchFixtures.CoreText)TestRuntime.AssertXcodeVersion (major, minor) matching the API's availability version. This skips the test on older runtimes instead of failing.using statements for handle-based types⚠️ If adding a new test file, make sure the
.csprojattests/monotouch-test/picks it up (it typically uses wildcard includes, but verify).
See references/binding-patterns.md for more monotouch-test patterns.
⚠️ Stale build artifacts: If you encounter unexpected test failures (SIGABRT, segfaults in unrelated types, false "pre-existing" failures), always run
make worldFIRST before investigating. Never conclude a failure is "pre-existing" without rebuilding — stale_build/artifacts are the #1 cause of spurious introspection crashes after binding changes.
Run all three test suites. Run them sequentially, not in parallel.
make -C tests/xtro-sharpie run-ios
make -C tests/xtro-sharpie run-tvos
make -C tests/xtro-sharpie run-macos
make -C tests/xtro-sharpie run-maccatalyst
Verify all .todo entries for the bound framework are resolved. If any remain, they need binding or explicit .ignore entries with justification.
⚠️ Delete empty
.todofiles after resolving all entries:git rm tests/xtro-sharpie/api-annotations-dotnet/{platform}-{Framework}.todo. Do not leave empty.todofiles in the repository.
make -C tests/cecil-tests run-tests
IMPORTANT: Clean shared obj directories before each platform to avoid NETSDK1005 errors:
# iOS — build, then run via mlaunch directly for reliable output capture
rm -rf tests/common/Touch.Unit/Touch.Client/dotnet/obj tests/common/MonoTouch.Dialog/obj
make -C tests/introspection/dotnet/iOS clean
make -C tests/introspection/dotnet build-ios
# Get the app path and run via mlaunch directly:
APP_PATH=$(make -C tests/introspection/dotnet/iOS print-executable | sed 's|/introspection$||')
SIMCTL_CHILD_NUNIT_AUTOSTART=true \
SIMCTL_CHILD_NUNIT_AUTOEXIT=true \
$DOTNET_DESTDIR/Microsoft.iOS.Sdk/tools/bin/mlaunch \
--launchsim "$APP_PATH" \
--device :v2:runtime=com.apple.CoreSimulator.SimRuntime.iOS-26-4,devicetype=com.apple.CoreSimulator.SimDeviceType.iPhone-16-Pro \
--wait-for-exit:true --
# tvOS — same approach as iOS
rm -rf tests/common/Touch.Unit/Touch.Client/dotnet/obj tests/common/MonoTouch.Dialog/obj
make -C tests/introspection/dotnet/tvOS clean
make -C tests/introspection/dotnet build-tvos
APP_PATH=$(make -C tests/introspection/dotnet/tvOS print-executable | sed 's|/introspection$||')
SIMCTL_CHILD_NUNIT_AUTOSTART=true \
SIMCTL_CHILD_NUNIT_AUTOEXIT=true \
$DOTNET_DESTDIR/Microsoft.tvOS.Sdk/tools/bin/mlaunch \
--launchsim "$APP_PATH" \
--device :v2:runtime=com.apple.CoreSimulator.SimRuntime.tvOS-26-4,devicetype=com.apple.CoreSimulator.SimDeviceType.Apple-TV-4K-3rd-generation-4K \
--wait-for-exit:true --
# macOS (use run-bare for direct execution with captured output)
rm -rf tests/common/Touch.Unit/Touch.Client/dotnet/obj tests/common/MonoTouch.Dialog/obj
make -C tests/introspection/dotnet/macOS clean build
make -C tests/introspection/dotnet/macOS run-bare
# MacCatalyst (use run-bare for direct execution with captured output)
rm -rf tests/common/Touch.Unit/Touch.Client/dotnet/obj tests/common/MonoTouch.Dialog/obj
make -C tests/introspection/dotnet/MacCatalyst clean build
make -C tests/introspection/dotnet/MacCatalyst run-bare
⚠️ iOS/tvOS output capture:
make run-ios/run-tvosusesdotnet build -t:Runwhich does NOT reliably capture the app's stdout. Thecom.apple.gamedstderr message causes MSBuild to report failure (exit code -1) even when tests pass, and NUnit results are lost. Use mlaunch directly as shown above to capture test output reliably.
⚠️ mlaunch device strings: Use
xcrun simctl list runtimesandxcrun simctl list devicetypesto find the correct runtime and device type identifiers for your Xcode version. The--deviceformat is:v2:runtime=<runtime-id>,devicetype=<devicetype-id>.
⚠️
cleanandrun-baremust be run from the platform subdirectory (e.g.,tests/introspection/dotnet/macOS/), not from the parentdotnet/directory. The parent only hasbuild-%andrun-%pattern rules — there are noclean-%orrun-bare-%targets.
⚠️ macOS/MacCatalyst: Use
run-bare(notrun) —runlaunches the app without waiting or capturing stdout.run-bareruns the executable directly to capture test output.
Look for this pattern in test output to confirm results:
Tests run: X Passed: X Inconclusive: X Failed: X Ignored: X
Skip this step if no monotouch-test files were added or modified.
make -C tests/monotouch-test run
If introspection tests fail for newly bound types:
ApiCtorInitTest.cs files if needed[DesignatedInitializer] constructor crashes (segfault) when passed null, the correct fix is to remove [NullAllowed] from that parameter rather than adding introspection test exclusions. The null is genuinely not allowed by the native API.If xtro still shows unresolved entries:
.ignore entries with comments explaining why they can't be bound.todo entries for known limitations.todo files unless explicitly asked.When reporting results, use this structure:
.todo entries intentionally left unbound, with reasonsPyTorch深度学习模式与最佳实践,用于构建稳健、高效且可复现的训练流程、模型架构和数据加载。