Skip to content

Instantly share code, notes, and snippets.

@Kyle-Ye
Last active March 21, 2026 18:42
Show Gist options
  • Select an option

  • Save Kyle-Ye/8a7f12487a4ce2ca6ac9e087b09d4a20 to your computer and use it in GitHub Desktop.

Select an option

Save Kyle-Ye/8a7f12487a4ce2ca6ac9e087b09d4a20 to your computer and use it in GitHub Desktop.
Using CAHostingLayer in SwiftUI — access SwiftUI's SPI CALayer without Apple internal SDK

Using CAHostingLayer in SwiftUI

Background

SwiftUI provides more than just UIHostingController / _UIHostingView (iOS) and NSHostingController / NSHostingView (macOS) for embedding SwiftUI views into UIKit/AppKit.

Starting from iOS 18.0 / macOS 15.0, SwiftUI also offers CAHostingLayer<Content: View> — a CALayer subclass that can host SwiftUI content directly at the layer level. This API is currently marked as SPI (@_spi(ForUIKitOnly) / @_spi(ForAppKitOnly)) and is used internally by UIKit, AppKit, and WebKit.

Accessing CAHostingLayer without Apple Internal SDK

Since CAHostingLayer is an SPI, it's not available through public SwiftUI headers. Apple's WebKit project uses a clever technique: a .swiftinterface file with -module-abi-name SwiftUI that re-exports the SPI symbols under a separate module name, linking to the real SwiftUI framework at runtime.

Setup

  1. Place the SwiftUI_SPI.swiftinterface file in your project (e.g. Modules/Platform/cocoa/).

  2. Add the directory to your build settings:

    SWIFT_INCLUDE_PATHS = $(inherited) $(SRCROOT)/Modules/Platform/cocoa
    
  3. Import the module:

    import SwiftUI_SPI

Usage

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
}

References

// swift-interface-format-version: 1.0
// swift-module-flags: -enable-objc-interop -enable-library-evolution -swift-version 5 -module-name SwiftUI_SPI -module-abi-name SwiftUI
import QuartzCore
import Swift
@_exported import SwiftUI
@_hasMissingDesignatedInitializers @available(iOS 18.0, macOS 15.0, tvOS 18.0, watchOS 11.0, visionOS 2.0, *)
public class CAHostingLayer<Content> : QuartzCore.CALayer where Content : SwiftUI.View {
public init(rootView: Content, environment: SwiftUI.EnvironmentValues = .init())
@objc override dynamic public func layoutSublayers()
public var rootView: Content {
get
set
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment