diff --git a/Example/Example.xcodeproj/xcshareddata/xcschemes/OSUI_Example.xcscheme b/Example/Example.xcodeproj/xcshareddata/xcschemes/OSUI_Example.xcscheme
index f8c687531..f9ebc5585 100644
--- a/Example/Example.xcodeproj/xcshareddata/xcschemes/OSUI_Example.xcscheme
+++ b/Example/Example.xcodeproj/xcshareddata/xcschemes/OSUI_Example.xcscheme
@@ -27,8 +27,13 @@
buildConfiguration = "OpenSwiftUIDebug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
- shouldUseLaunchSchemeArgsEnv = "YES"
- shouldAutocreateTestPlan = "YES">
+ shouldUseLaunchSchemeArgsEnv = "YES">
+
+
+
+
+ shouldUseLaunchSchemeArgsEnv = "YES">
+
+
+
+
+ shouldUseLaunchSchemeArgsEnv = "YES">
+
+
+
+
+ shouldUseLaunchSchemeArgsEnv = "YES">
+
+
+
+
) */
diff --git a/Sources/COpenSwiftUI/Shims/UIKit/UIKit_Private.h b/Sources/COpenSwiftUI/Shims/UIKit/UIKit_Private.h
index bf3f753f3..18c399afa 100644
--- a/Sources/COpenSwiftUI/Shims/UIKit/UIKit_Private.h
+++ b/Sources/COpenSwiftUI/Shims/UIKit/UIKit_Private.h
@@ -88,6 +88,15 @@ OPENSWIFTUI_ASSUME_NONNULL_BEGIN
#endif
@end
+@interface UIImage (OpenSwiftUI_SPI)
+@property (nonatomic, readonly) BOOL _hasImageAsset;
+@property (nonatomic, readonly, nullable) IOSurfaceRef ioSurface;
+@property (nonatomic, readonly) NSDirectionalEdgeInsets contentInsets;
++ (nullable UIImage *)imageNamed:(NSString *)name inBundle:(nullable NSBundle *)bundle OPENSWIFTUI_SWIFT_NAME(init(named:in:));
++ (nullable UIImage *)_systemImageNamed:(NSString *)name OPENSWIFTUI_SWIFT_NAME(init(_systemName:));
++ (nullable UIImage *)_systemImageNamed:(NSString *)name variableValue:(double)value withConfiguration:(nullable UIImageConfiguration *)configuration OPENSWIFTUI_SWIFT_NAME(init(_systemName:variableValue:configuration:));
+@end
+
@interface UITraitCollection (OpenSwiftUI_SPI)
@property (nonatomic, readonly, nullable) NSObject *_environmentWrapper_openswiftui_safe_wrapper OPENSWIFTUI_SWIFT_NAME(_environmentWrapper);
@end
diff --git a/Sources/OpenSwiftUI/Data/Environment/UIKitEnvironment.swift b/Sources/OpenSwiftUI/Data/Environment/UIKitEnvironment.swift
index 80aca27b7..db19d87ba 100644
--- a/Sources/OpenSwiftUI/Data/Environment/UIKitEnvironment.swift
+++ b/Sources/OpenSwiftUI/Data/Environment/UIKitEnvironment.swift
@@ -29,6 +29,15 @@ extension UITraitCollection {
return resolvedTraitCollection(with: environment, wrapper: wrapper)
}
+ @inline(__always)
+ package func resolvedImageAssetOnlyTraitCollection(environment: EnvironmentValues) -> UITraitCollection {
+ resolvedTraitCollection(
+ with: environment,
+ wrapper: unsafeBitCast(_environmentWrapper, to: EnvironmentWrapper?.self),
+ forImageAssetsOnly: true
+ )
+ }
+
private func resolvedTraitCollection(
with environment: EnvironmentValues,
wrapper: EnvironmentWrapper?,
diff --git a/Sources/OpenSwiftUI/Integration/Graphic/AppKit/AppKitAppearanceConversions.swift b/Sources/OpenSwiftUI/Integration/Graphic/AppKit/AppKitAppearanceConversions.swift
index 427339947..0b2dfb84e 100644
--- a/Sources/OpenSwiftUI/Integration/Graphic/AppKit/AppKitAppearanceConversions.swift
+++ b/Sources/OpenSwiftUI/Integration/Graphic/AppKit/AppKitAppearanceConversions.swift
@@ -5,7 +5,7 @@
// Status: WIP
// ID: FE0226775232C57AACFCDAD271FF7831 (SwiftUI)
-#if canImport(AppKit)
+#if canImport(AppKit) && !targetEnvironment(macCatalyst)
import AppKit
import COpenSwiftUI
diff --git a/Sources/OpenSwiftUI/Integration/Graphic/AppKit/AppKitColorConversions.swift b/Sources/OpenSwiftUI/Integration/Graphic/AppKit/AppKitColorConversions.swift
index 5c3879a62..4c9a62701 100644
--- a/Sources/OpenSwiftUI/Integration/Graphic/AppKit/AppKitColorConversions.swift
+++ b/Sources/OpenSwiftUI/Integration/Graphic/AppKit/AppKitColorConversions.swift
@@ -6,7 +6,7 @@
// Status: Complete
// ID: 7137BB7EE57FAC34F81DC437C151F7AB (SwiftUI)
-#if canImport(AppKit)
+#if canImport(AppKit) && !targetEnvironment(macCatalyst)
public import OpenSwiftUICore
public import AppKit
diff --git a/Sources/OpenSwiftUI/Integration/Representable/Platform/AnyPlatformViewHost.swift b/Sources/OpenSwiftUI/Integration/Representable/Platform/AnyPlatformViewHost.swift
index 9af9194d5..b3547f93f 100644
--- a/Sources/OpenSwiftUI/Integration/Representable/Platform/AnyPlatformViewHost.swift
+++ b/Sources/OpenSwiftUI/Integration/Representable/Platform/AnyPlatformViewHost.swift
@@ -54,7 +54,7 @@ struct PlatformViewLayoutInvalidator {
// FIXME: Gesture System
-#if canImport(AppKit)
+#if canImport(AppKit) && !targetEnvironment(macCatalyst)
class NSViewResponder {}
#elseif canImport(UIKit)
class UIViewResponder {}
diff --git a/Sources/OpenSwiftUI/View/Image/NSImageConversions.swift b/Sources/OpenSwiftUI/View/Image/NSImageConversions.swift
new file mode 100644
index 000000000..adbb6575b
--- /dev/null
+++ b/Sources/OpenSwiftUI/View/Image/NSImageConversions.swift
@@ -0,0 +1,70 @@
+//
+// NSImageConversions.swift
+// OpenSwiftUI
+//
+// Audited for 6.5.4
+// Status: Complete
+
+#if canImport(AppKit) && !targetEnvironment(macCatalyst)
+public import AppKit
+public import OpenSwiftUICore
+import OpenRenderBoxShims
+
+// MARK: - Image + NSImage
+
+@available(OpenSwiftUI_v1_0, *)
+@available(iOS, unavailable)
+@available(tvOS, unavailable)
+@available(watchOS, unavailable)
+@available(visionOS, unavailable)
+extension Image {
+
+ /// Creates a OpenSwiftUI image from an AppKit image instance.
+ /// - Parameter nsImage: The AppKit image to wrap with a OpenSwiftUI ``Image``.
+ /// instance.
+ public init(nsImage: NSImage) {
+ self.init(nsImage)
+ }
+}
+
+// MARK: - NSImage + ImageProvider
+
+extension NSImage: ImageProvider {
+
+ package func resolve(in context: ImageResolutionContext) -> Image.Resolved {
+ let displayScale = context.environment.displayScale
+ let layer = VectorImageLayer(nsImage: self, scale: displayScale)
+ let isTemplate = context.environment.imageIsTemplate(renderingMode: isTemplate ? .template : nil)
+ var graphicsImage = GraphicsImage(
+ contents: .vectorLayer(layer),
+ scale: displayScale,
+ unrotatedPixelSize: size * displayScale,
+ orientation: .up,
+ isTemplate: isTemplate,
+ resizingInfo: nil
+ )
+ graphicsImage.allowedDynamicRange = context.effectiveAllowedDynamicRange(for: graphicsImage)
+ if context.environment.shouldRedactContent {
+ graphicsImage.redact(in: context.environment)
+ }
+ let label = AccessibilityImageLabel(resolvedAccessibilityDescription)
+ return Image.Resolved(
+ image: graphicsImage,
+ decorative: false,
+ label: label,
+ basePlatformItemImage: self
+ )
+ }
+
+ package var resolvedAccessibilityDescription: String? {
+ guard let description = accessibilityDescription, !description.isEmpty else {
+ return _defaultAccessibilityDescription
+ }
+ return description
+ }
+
+ package func resolveNamedImage(in context: ImageResolutionContext) -> Image.NamedResolved? {
+ nil
+ }
+}
+#endif
diff --git a/Sources/OpenSwiftUI/View/Image/UIImageConversions.swift b/Sources/OpenSwiftUI/View/Image/UIImageConversions.swift
new file mode 100644
index 000000000..cd75cac10
--- /dev/null
+++ b/Sources/OpenSwiftUI/View/Image/UIImageConversions.swift
@@ -0,0 +1,172 @@
+//
+// UIImageConversions.swift
+// OpenSwiftUI
+//
+// Audited for 6.5.4
+// Status: Complete
+// ID: 47E85C485E11398B3F3140DCB9554BB7 (SwiftUI)
+
+#if canImport(UIKit)
+public import UIKit
+public import OpenSwiftUICore
+
+// MARK: - Image + UIImage
+
+@available(OpenSwiftUI_v1_0, *)
+@available(macOS, unavailable)
+extension Image {
+
+ /// Creates a OpenSwiftUI image from a UIKit image instance.
+ /// - Parameter uiImage: The UIKit image to wrap with a OpenSwiftUI ``Image``
+ /// instance.
+ public init(uiImage: UIImage) {
+ self.init(uiImage)
+ }
+}
+
+// MARK: - UIImage + ImageProvider
+
+extension UIImage: ImageProvider {
+
+ package func resolve(in context: ImageResolutionContext) -> Image.Resolved {
+ let resolvedImage: UIImage
+ if !isSymbolImage, _hasImageAsset, let imageAsset {
+ let overridden = traitCollection.resolvedImageAssetOnlyTraitCollection(environment: context.environment)
+ resolvedImage = imageAsset.image(with: overridden)
+ } else {
+ resolvedImage = self
+ }
+ return resolvedImage._resolve(in: context)
+ }
+
+ package func resolveNamedImage(in context: ImageResolutionContext) -> Image.NamedResolved? {
+ nil
+ }
+
+ // MARK: - Private
+
+ private func _resolve(in context: ImageResolutionContext) -> Image.Resolved {
+ let orientation = Image.Orientation(imageOrientation)
+ let contents: GraphicsImage.Contents?
+ if let cgImage {
+ contents = .cgImage(cgImage)
+ } else if let ioSurface {
+ contents = .ioSurface(ioSurface)
+ } else {
+ contents = nil
+ }
+
+ let scale = scale
+ var imageSize = size
+ var layoutMetrics: Image.LayoutMetrics?
+ if isSymbolImage {
+ let insets = contentInsets
+ let baselineOffset = baselineOffsetFromBottom ?? 0.0
+ let capHeight = imageSize.height - (insets.top + baselineOffset)
+ layoutMetrics = Image.LayoutMetrics(
+ baselineOffset: baselineOffset,
+ capHeight: capHeight,
+ contentSize: imageSize,
+ alignmentOrigin: CGPoint(x: insets.leading, y: insets.top)
+ )
+ imageSize.width -= insets.leading + insets.trailing
+ imageSize.height -= insets.top + insets.bottom
+ }
+ let unrotatedSize = imageSize.unapply(orientation)
+ let isTemplate = context.environment.imageIsTemplate(renderingMode: .init(renderingMode))
+ var graphicsImage = GraphicsImage(
+ contents: contents,
+ scale: scale,
+ unrotatedPixelSize: unrotatedSize * scale,
+ orientation: orientation,
+ isTemplate: isTemplate,
+ resizingInfo: resizingInfo
+ )
+ graphicsImage.allowedDynamicRange = context.effectiveAllowedDynamicRange(for: graphicsImage)
+ if context.environment.shouldRedactContent {
+ graphicsImage.redact(in: context.environment)
+ }
+ let label = AccessibilityImageLabel(accessibilityLabel)
+ var resolved = Image.Resolved(
+ image: graphicsImage,
+ decorative: false,
+ label: label,
+ basePlatformItemImage: self
+ )
+ if let layoutMetrics {
+ resolved.layoutMetrics = layoutMetrics
+ }
+ return resolved
+ }
+
+ private var resizingInfo: Image.ResizingInfo? {
+ guard capInsets != .zero else { return nil }
+ let mode: Image.ResizingMode = (resizingMode == .tile) ? .tile : .stretch
+ let edgeInsets = EdgeInsets(
+ top: capInsets.top, leading: capInsets.left,
+ bottom: capInsets.bottom, trailing: capInsets.right
+ )
+ return Image.ResizingInfo(capInsets: edgeInsets, mode: mode)
+ }
+}
+
+// MARK: - GraphicsImage + UIImage
+
+extension GraphicsImage {
+ private func image(with name: String, variableValue: Float?, at location: Image.Location) -> UIImage? {
+ if let variableValue {
+ switch location {
+ case .bundle(let bundle):
+ return UIImage(named: name, in: bundle, variableValue: Double(variableValue), configuration: nil)
+ case .system:
+ return UIImage(systemName: name, variableValue: Double(variableValue), configuration: nil)
+ case .privateSystem:
+ return UIImage(_systemName: name, variableValue: Double(variableValue), configuration: nil)
+ }
+ } else {
+ switch location {
+ case .bundle(let bundle):
+ return UIImage(named: name, in: bundle)
+ case .system:
+ return UIImage(systemName: name)
+ case .privateSystem:
+ return UIImage(_systemName: name)
+ @unknown default:
+ _openSwiftUIUnimplementedFailure()
+ }
+ }
+ }
+}
+
+// MARK: - Image.TemplateRenderingMode + UIImage.RenderingMode
+
+extension Image.TemplateRenderingMode {
+ @inline(__always)
+ fileprivate init?(_ renderingMode: UIImage.RenderingMode) {
+ switch renderingMode {
+ case .alwaysOriginal: self = .original
+ case .alwaysTemplate: self = .template
+ default: return nil
+ }
+ }
+}
+
+// MARK: - Image.Orientation + UIImage.Orientation
+
+extension Image.Orientation {
+ @inline(__always)
+ fileprivate init(_ uiImageOrientation: UIImage.Orientation) {
+ switch uiImageOrientation {
+ case .up: self = .up
+ case .down: self = .down
+ case .left: self = .left
+ case .right: self = .right
+ case .upMirrored: self = .upMirrored
+ case .downMirrored: self = .downMirrored
+ case .leftMirrored: self = .leftMirrored
+ case .rightMirrored: self = .rightMirrored
+ @unknown default: self = .up
+ }
+ }
+}
+#endif
diff --git a/Sources/OpenSwiftUI/View/Image/VectorImageLayer.swift b/Sources/OpenSwiftUI/View/Image/VectorImageLayer.swift
new file mode 100644
index 000000000..5b1635d19
--- /dev/null
+++ b/Sources/OpenSwiftUI/View/Image/VectorImageLayer.swift
@@ -0,0 +1,73 @@
+//
+// VectorImageLayer.swift
+// OpenSwiftUI
+//
+// Audited for 6.5.4
+// Status: Complete
+// ID: 53095E34581C439FFBDB89F0B27FB221 (SwiftUI)
+
+#if canImport(AppKit) && !targetEnvironment(macCatalyst)
+@_spi(ForOpenSwiftUIOnly)
+public import OpenSwiftUICore
+import OpenRenderBoxShims
+public import AppKit
+
+// MARK: - VectorImageLayer + NSImage
+
+extension VectorImageLayer {
+ @inline(__always)
+ package init(nsImage: NSImage, scale: CGFloat) {
+ let contents = NSImageContents(image: nsImage, scale: scale)
+ self.init(contents)
+ }
+}
+
+// MARK: - NSImageContents
+
+private final class NSImageContents: VectorImageContents {
+ var _image: NSImage
+ var _scale: CGFloat
+ var _displayList: (any ORBDisplayListContents)?
+
+ init(image: NSImage, scale: CGFloat) {
+ _image = image
+ _scale = scale
+ super.init()
+ }
+
+ override var size: CGSize {
+ _image.size
+ }
+
+ override var displayList: any ORBDisplayListContents {
+ if let cached = _displayList {
+ return cached
+ }
+ let displayList = ORBDisplayList()
+ displayList.defaultColorSpace = .SRGB
+ let size = _image.size
+ var rect = CGRect(origin: .zero, size: size)
+ if let cgImage = _image.cgImage(
+ forProposedRect: &rect,
+ context: nil,
+ hints: [.ctm: AffineTransform(scale: _scale)]
+ ) {
+ let context: CGContext = displayList.beginCGContext(withAlpha: 1.0)
+ context.draw(cgImage, in: CGRect(origin: .zero, size: size), byTiling: false)
+ displayList.endCGContext()
+ }
+ let contents = displayList.moveContents()
+ _displayList = contents
+ return _displayList!
+ }
+
+ override func image(size: CGSize, imageScale: CGFloat, prefersMask: Bool) -> CGImage? {
+ var rect = CGRect(origin: .zero, size: size)
+ return _image.cgImage(
+ forProposedRect: &rect,
+ context: nil,
+ hints: [.ctm: AffineTransform(scale: imageScale)]
+ )
+ }
+}
+#endif
diff --git a/Sources/OpenSwiftUICore/Util/RenderBoxShims.swift b/Sources/OpenSwiftUICore/Util/RenderBoxShims.swift
deleted file mode 100644
index 0220387f7..000000000
--- a/Sources/OpenSwiftUICore/Util/RenderBoxShims.swift
+++ /dev/null
@@ -1,7 +0,0 @@
-//
-// RenderBoxShims.swift
-// OpenSwiftUICore
-
-public protocol ORBDisplayListContents {} // RenderBox.RBDisplayListContents
-
-package class ORBDisplayListInterpolator {}
diff --git a/Sources/OpenSwiftUICore/View/Text/Text/Text+Localized.swift b/Sources/OpenSwiftUICore/View/Text/Text/Text+Localized.swift
index 0702e5dfd..036716155 100644
--- a/Sources/OpenSwiftUICore/View/Text/Text/Text+Localized.swift
+++ b/Sources/OpenSwiftUICore/View/Text/Text/Text+Localized.swift
@@ -35,7 +35,9 @@ public struct LocalizedStringKey: Equatable, ExpressibleByStringInterpolation {
private var arguments: [LocalizedStringKey.FormatArgument]
public init(_ value: String) {
- _openSwiftUIUnimplementedFailure()
+ self.key = value
+ self.arguments = []
+ _openSwiftUIUnimplementedWarning()
}
@_semantics("openswiftui.localized_string_key.init_literal")
diff --git a/Sources/OpenSwiftUICore/View/Text/Text/Text+View.swift b/Sources/OpenSwiftUICore/View/Text/Text/Text+View.swift
index 23ae3e6f6..7b6a182ae 100644
--- a/Sources/OpenSwiftUICore/View/Text/Text/Text+View.swift
+++ b/Sources/OpenSwiftUICore/View/Text/Text/Text+View.swift
@@ -9,6 +9,7 @@
package import Foundation
package import OpenAttributeGraphShims
public import OpenCoreGraphicsShims
+package import OpenRenderBoxShims
import UIFoundation_Private
// MARK: - Text + View [WIP]
diff --git a/Tests/COpenSwiftUITests/Shims/AppKitPrivateTests.swift b/Tests/COpenSwiftUITests/Shims/AppKitPrivateTests.swift
index 0928b5e7d..d51fb5111 100644
--- a/Tests/COpenSwiftUITests/Shims/AppKitPrivateTests.swift
+++ b/Tests/COpenSwiftUITests/Shims/AppKitPrivateTests.swift
@@ -2,7 +2,7 @@
// AppKitPrivateTests.swift
// OpenSwiftUI_SPITests
-#if canImport(AppKit)
+#if canImport(AppKit) && !targetEnvironment(macCatalyst)
import AppKit
import COpenSwiftUI
import Testing
diff --git a/Tests/OpenSwiftUITests/View/Image/ImageConversionsTests.swift b/Tests/OpenSwiftUITests/View/Image/ImageConversionsTests.swift
new file mode 100644
index 000000000..ffed31405
--- /dev/null
+++ b/Tests/OpenSwiftUITests/View/Image/ImageConversionsTests.swift
@@ -0,0 +1,49 @@
+//
+// ImageConversionsTests.swift
+// OpenSwiftUITests
+
+#if canImport(Darwin)
+import Testing
+@testable import OpenSwiftUI
+@_spi(ForOpenSwiftUIOnly)
+import OpenSwiftUICore
+
+#if canImport(AppKit) && !targetEnvironment(macCatalyst)
+import AppKit
+#elseif canImport(UIKit)
+import UIKit
+#endif
+
+@MainActor
+struct ImageConversionsTests {
+ @Test
+ func initWithSystemUIImage() {
+ #if canImport(AppKit) && !targetEnvironment(macCatalyst)
+ let platformImage = NSImage(named: NSImage.addTemplateName)!
+ let image = Image(nsImage: platformImage)
+ let box = image.provider as? ImageProviderBox
+ #else
+ let platformImage = UIImage(systemName: "star")!
+ let image = Image(uiImage: platformImage)
+ let box = image.provider as? ImageProviderBox
+ #endif
+ #expect(box != nil)
+ #expect(box?.base === platformImage)
+ }
+
+ @Test
+ func initWithEmptyUIImage() {
+ #if canImport(AppKit) && !targetEnvironment(macCatalyst)
+ let platformImage = NSImage(size: CGSize(width: 10, height: 10))
+ let image = Image(nsImage: platformImage)
+ let box = image.provider as? ImageProviderBox
+ #else
+ let platformImage = UIImage()
+ let image = Image(uiImage: platformImage)
+ let box = image.provider as? ImageProviderBox
+ #endif
+ #expect(box != nil)
+ #expect(box?.base === platformImage)
+ }
+}
+#endif