Native interop

Keep UIKit ownership, page generation, trimming, and hot reload predictable.

SkeleKit deliberately uses UIKit as its native layer. NativeView can place an existing UIView in the layout tree, while ContentView.Controller gives page code access to its hosting UIViewController after realization.

See the NativeView guide for the wrapper itself and pages and lifecycle for when a controller exists.

protected override void OnAppeared()
{
    base.OnAppeared();

    UIViewController? controller = Controller;
    // Configure a platform integration that requires the active controller.
}

Do controller-dependent work in page lifecycle callbacks, not in the constructor: the page may not have a host yet during construction. Prefer INavigator, ISharer, ISystemPicker, and IHaptics when the framework already provides the native behavior you need.

Native gestures

Use the normal SkeleKit gesture properties for taps, presses, pans, pinches, and rotation. For a recognizer SkeleKit does not wrap, attach it without forcing the view to realize:

UISwipeGestureRecognizer swipe = new(() => DismissCard())
{
    Direction = UISwipeGestureRecognizerDirection.Down
};

card.AddNativeGesture(swipe);

AddNativeGesture retains the recognizer and attaches it whenever the view's native peer is realized. Calling card.Native.AddGestureRecognizer(...) instead realizes the native view immediately and only addresses that native instance.

The recognizer remains a UIKit object. Keep any separate target or delegate peers alive for as long as it needs them, and configure failure dependencies or simultaneous recognition through UIKit.

Ownership

Use new NativeView(() => new CustomUIKitView()) when the native view belongs to the wrapper. SkeleKit disposes the created view on unrealization and asks the factory for a fresh instance if it is realized again.

Use new NativeView(existingView) to borrow a view owned by a controller or service. The wrapper removes it from the hierarchy but never disposes it. Remove native event handlers and dispose any auxiliary delegates or services according to the lifetime of that owner.

Do not attach the same SkeleKit view instance to multiple parents or present the same page instance twice. Views have one native host and one position in the tree. Page registrations are transient by default for exactly this reason; singleton pages are appropriate only when one retained instance is intentional.

Generated pages and trimming

[Page] registrations are generated at compile time. The generator validates that the type derives from ContentView, is not abstract, and exposes the constructor required for its view-model registration. Prefer these generated factories over discovering pages through application reflection.

Release iOS builds are trimmed. Keep page constructors, services, and navigation relationships visible through normal compiled references and DI registration. When a separate library depends on reflection-only members, apply the standard .NET trimming annotations in that library and verify a release build on device.

Hot reload boundaries

SkeleKit hot reload is enabled only for Debug simulator builds whose runtime identifier starts with iossimulator. Setting <EnableHotReload>true</EnableHotReload> enables the interpreter and metadata update support for that configuration; it does not change release or physical-device builds.

Hot reload replaces live page trees after a successful delta. Treat it as a development feedback loop, not as a substitute for cold-start, navigation lifetime, trimming, and device tests.

The project setting and Rider workflow are documented here.