When using UITraitBridgedEnvironmentKey to pass a trait value to the swift environment, it causes a crash when trying to access the value from the environment.
The issue seems to be related to how swift uses the UITraitBridgedEnvironmentKey protocol since the crash occurs in swift::_getWitnessTable () from lazy protocol witness table accessor…. It can occur when calling any function that is generic using the UITraitBridgedEnvironmentKey type.
I originally encountered the issue when trying to use a UITraitBridgedEnvironmentKey in SwiftUI, but have been able to reproduce the issue with any function with a similar signature.
https://developer.apple.com/documentation/swiftui/environmentvalues/subscript(_:)-9zku
Steps to Reproduce
Requirements for the issue to occur
- Project with a minimum iOS version of iOS 16
- Build the project with Xcode 26
- Run on iOS 18
Add the following code to a project and call foo(key: MyCustomTraitKey.self) from anywhere.
@available(iOS 17.0, *)
func foo<K>(key: K.Type) where K: UITraitBridgedEnvironmentKey {
// Crashes before this is called
}
@available(iOS 17.0, *)
public enum MyCustomTraitKey: UITraitBridgedEnvironmentKey {
public static let defaultValue: Bool = false
public static func read(from traitCollection: UITraitCollection) -> Bool { false }
public static func write(to mutableTraits: inout UIMutableTraits, value: Bool) {}
}
// The crash will occur when calling this. It can be added to a project anywhere
// The sample project calls it from scene(_:willConnectTo:options:)
foo(key: MyCustomTraitKey.self)
For example, I added it to the SceneDelegate in a UIKit Project
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if #available(iOS 17, *) {
// The following line of code can be placed anywhere in a project, `SceneDelegate` is just a convenient place to put it to reproduce the issue.
foo(key: MyCustomTraitKey.self)
// ^ CRASH: Thread 1: EXC_BAD_ACCESS (code=1, address=0x10)
}
}
}
Actual Behaviour
The app crashes with the stack trace showing the place calling foo but before foo is actually called. (ie, a breakpoint or print in foo is never hit)
#0 0x000000019595fbc4 in swift::_getWitnessTable ()
#1 0x0000000104954128 in lazy protocol witness table accessor for type MyCustomTraitKey and conformance MyCustomTraitKey ()
#2 0x0000000104953bc4 in SceneDelegate.scene(_:willConnectTo:options:) at .../SceneDelegate.swift:20
The app does not crash when run on iOS 17, or 26 or when the minimum ios version is raised to iOS 17 or higher.
It also doesn't crash on iOS 16 since it's not calling foo since UITraitBridgedEnvironmentKey was added in iOS 17.
Expected behaviour
The app should not crash. It should call foo on iOS 17, 18, and 26.