知阅百微 见微知著

SwiftUI has another hosting boundary below the usual controller and view APIs: CAHostingLayer<Content: View>. Instead of embedding SwiftUI by adding a UIHostingController, _UIHostingView, NSHostingController, or NSHostingView, this SPI lets framework code host a SwiftUI root view directly inside a CALayer.

That distinction matters when the surrounding system already speaks in layer trees. A hosting controller brings view-controller lifecycle. A hosting view brings platform view behavior. CAHostingLayer is a lower-level integration point for code that wants SwiftUI-rendered content without inserting another platform view wrapper.

Where the SPI Shows Up

The API is available starting in iOS 18 and macOS 15, and it is currently used internally by UIKit, AppKit, and WebKit. It is still SPI rather than public API, with annotations such as @_spi(ForUIKitOnly) and @_spi(ForAppKitOnly), so normal app targets will not see it from the public SwiftUI module.

WebKit uses a useful workaround: define a local .swiftinterface module with -module-abi-name SwiftUI, re-export SwiftUI, and declare the SPI symbols under that separate module name. The client imports the local module, but the symbols still link against the real SwiftUI framework at runtime.

The minimal shape looks like this:

import SwiftUI_SPI

@available(iOS 18.0, macOS 15.0, *)
func makeHostingLayer() -> CALayer {
    let layer = CAHostingLayer(rootView: Color.red)
    layer.bounds = CGRect(origin: .zero, size: CGSize(width: 200, height: 200))
    return layer
}

For OpenSwiftUI experiments, the follow-up detail is simpler: OpenSwiftUI exposes the same API through SPI, so the extra Swift interface workaround is not needed there.

When This Is Useful

I treat this as a debugging and reverse-engineering tool first. It is especially useful when reading WebKit, UIKit, or AppKit integration code that wants SwiftUI content as part of a layer-backed rendering pipeline.

Do not treat it as production app API. CAHostingLayer is SPI, and the annotations are scoped for framework-internal consumers. It can change, disappear, or behave differently across OS releases.