From 8730861635e22f63c85649fc1b2782ce49847151 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 7 Oct 2025 12:51:37 +0800 Subject: [PATCH 1/6] Fix signpost --- .../Representable/Platform/PlatformViewRepresentable.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/OpenSwiftUI/Integration/Representable/Platform/PlatformViewRepresentable.swift b/Sources/OpenSwiftUI/Integration/Representable/Platform/PlatformViewRepresentable.swift index 3fca4bdae..5fcddcdd2 100644 --- a/Sources/OpenSwiftUI/Integration/Representable/Platform/PlatformViewRepresentable.swift +++ b/Sources/OpenSwiftUI/Integration/Representable/Platform/PlatformViewRepresentable.swift @@ -221,7 +221,7 @@ struct PlatformViewChild: StatefulRule { object: nil, "PlatformUpdate: (%p) %{public}@ [ %p ]", [ - AnyAttribute.current!.graph.graphIdentity(), + attribute.graph.graphIdentity(), "\(Content.self)", platformView.map { UInt(bitPattern: Unmanaged.passUnretained($0).toOpaque()) } ?? 0, ] From 2fa7d31fae9837e877721660501f125878490730 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 7 Oct 2025 16:20:36 +0800 Subject: [PATCH 2/6] Update UnsafePointer usage --- .../Data/Util/ScrapeableContent.swift | 12 ++++++------ .../Render/RendererEffect/RendererEffect.swift | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Sources/OpenSwiftUICore/Data/Util/ScrapeableContent.swift b/Sources/OpenSwiftUICore/Data/Util/ScrapeableContent.swift index 8664f83da..206d2f249 100644 --- a/Sources/OpenSwiftUICore/Data/Util/ScrapeableContent.swift +++ b/Sources/OpenSwiftUICore/Data/Util/ScrapeableContent.swift @@ -100,16 +100,16 @@ private struct ScrapeableAttachmentViewModifier: MultiViewModifier, PrimitiveVie static func scrapeContent(from ident: AnyAttribute) -> ScrapeableContent.Item? { let pointer = ident.info.body.assumingMemoryBound(to: Attachment.self) - guard let content = pointer.pointee.content else { + guard let content = pointer[].content else { return nil } return .init( content, - ids: pointer.pointee.localID, - pointer.pointee.parentID, - position: pointer.pointee.$position, - size: pointer.pointee.$size, - transform: pointer.pointee.$transform + ids: pointer[].localID, + pointer[].parentID, + position: pointer[].$position, + size: pointer[].$size, + transform: pointer[].$transform ) } } diff --git a/Sources/OpenSwiftUICore/Render/RendererEffect/RendererEffect.swift b/Sources/OpenSwiftUICore/Render/RendererEffect/RendererEffect.swift index b24a04cbc..4ad98fcf2 100644 --- a/Sources/OpenSwiftUICore/Render/RendererEffect/RendererEffect.swift +++ b/Sources/OpenSwiftUICore/Render/RendererEffect/RendererEffect.swift @@ -222,16 +222,16 @@ private struct RendererEffectDisplayList: Rule, AsyncAttribute, Scrapeab static func scrapeContent(from ident: AnyAttribute) -> ScrapeableContent.Item? { let pointer = ident.info.body.assumingMemoryBound(to: Self.self) - guard let content = pointer.pointee.effect.scrapeableContent else { + guard let content = pointer[].effect.scrapeableContent else { return nil } return .init( content, - ids: pointer.pointee.localID, - pointer.pointee.parentID, - position: pointer.pointee.$position, - size: pointer.pointee.$size, - transform: pointer.pointee.$transform + ids: pointer[].localID, + pointer[].parentID, + position: pointer[].$position, + size: pointer[].$size, + transform: pointer[].$transform ) } } From 7d5c8697f0f31c551325b8d4ae627c3757344860 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 7 Oct 2025 16:30:42 +0800 Subject: [PATCH 3/6] Add ScrapeableAttribute support for PlatformViewChild --- .../Platform/PlatformViewRepresentable.swift | 76 ++++++++++++++++++- 1 file changed, 74 insertions(+), 2 deletions(-) diff --git a/Sources/OpenSwiftUI/Integration/Representable/Platform/PlatformViewRepresentable.swift b/Sources/OpenSwiftUI/Integration/Representable/Platform/PlatformViewRepresentable.swift index 5fcddcdd2..08a0dd444 100644 --- a/Sources/OpenSwiftUI/Integration/Representable/Platform/PlatformViewRepresentable.swift +++ b/Sources/OpenSwiftUI/Integration/Representable/Platform/PlatformViewRepresentable.swift @@ -214,6 +214,13 @@ struct PlatformViewChild: StatefulRule { self.tracker = .init() } + var representedViewProvider: Content.PlatformViewProvider? { + guard let platformView else { + return nil + } + return platformView.representedViewProvider + } + typealias Value = ViewLeafView mutating func updateValue() { @@ -262,8 +269,73 @@ struct PlatformViewChild: StatefulRule { } } - private func reset() { - // TODO + mutating func resetPlatformView() { + guard let coordinator, + let representedViewProvider else { + return + } + view.resetViewProvider(representedViewProvider, coordinator: coordinator) { + Content.dismantleViewProvider(representedViewProvider, coordinator: coordinator) + reset() + } + } +} + +extension PlatformViewChild: ObservedAttribute { + mutating func destroy() { + links.destroy() + if let coordinator, let representedViewProvider { + Update.syncMain { + Content.dismantleViewProvider(representedViewProvider, coordinator: coordinator) + } + reset() + } + bridge.invalidate() + } + + private mutating func reset() { + coordinator = nil + platformView = nil + } +} + +extension PlatformViewChild: InvalidatableAttribute { + static func willInvalidate(attribute: AnyAttribute) { + let pointer = attribute.info.body + .assumingMemoryBound(to: PlatformViewChild.self) + pointer[].bridge.invalidate() + } +} + +extension PlatformViewChild: RemovableAttribute { + static func willRemove(attribute: AnyAttribute) { + let pointer = attribute.info.body + .assumingMemoryBound(to: PlatformViewChild.self) + pointer[].bridge.removedStateDidChange() + } + + static func didReinsert(attribute: AnyAttribute) { + let pointer = attribute.info.body + .assumingMemoryBound(to: PlatformViewChild.self) + pointer[].bridge.removedStateDidChange() + } +} + +extension PlatformViewChild: ScrapeableAttribute { + static func scrapeContent(from ident: AnyAttribute) -> ScrapeableContent.Item? { + let pointer = ident.info.body + .assumingMemoryBound(to: PlatformViewChild.self) + guard let platformView = pointer[].platformView else { + return nil + } + return .init( + .platformView(platformView), + ids: .none, + pointer[].parentID, + position: pointer[].$position, + size: pointer[].$size, + transform: pointer[].$transform, + ) } } From 22ad78c7c867e6d6d81506a935e59c052fda0dd1 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 7 Oct 2025 19:52:22 +0800 Subject: [PATCH 4/6] Update PlatformViewChild.updateValue implementation Fix missing update call for Representable --- .../OpenSwiftUI/Event/Focus/FocusGroup.swift | 31 +++++ .../Platform/PlatformViewRepresentable.swift | 115 +++++++++++++++--- 2 files changed, 126 insertions(+), 20 deletions(-) create mode 100644 Sources/OpenSwiftUI/Event/Focus/FocusGroup.swift diff --git a/Sources/OpenSwiftUI/Event/Focus/FocusGroup.swift b/Sources/OpenSwiftUI/Event/Focus/FocusGroup.swift new file mode 100644 index 000000000..9230342f4 --- /dev/null +++ b/Sources/OpenSwiftUI/Event/Focus/FocusGroup.swift @@ -0,0 +1,31 @@ +// +// FocusGroup.swift +// OpenSwiftUI +// +// Audited for 6.5.4 +// Status: Complete +// ID: 843E2CF8C2FABDEAA3F932BB96663C44 (SwiftUI) + +import OpenSwiftUICore + +enum FocusGroupIdentifier { + case explicit(ID) + case inferred + + struct ID: Hashable { + var base: Int + } +} + +extension EnvironmentValues { + var focusGroupID: FocusGroupIdentifier? { + get { self[FocusGroupIDKey.self] } + set { self[FocusGroupIDKey.self] = newValue } + } +} + +private struct FocusGroupIDKey: EnvironmentKey { + static var defaultValue: FocusGroupIdentifier? { + nil + } +} diff --git a/Sources/OpenSwiftUI/Integration/Representable/Platform/PlatformViewRepresentable.swift b/Sources/OpenSwiftUI/Integration/Representable/Platform/PlatformViewRepresentable.swift index 08a0dd444..5006e3ab2 100644 --- a/Sources/OpenSwiftUI/Integration/Representable/Platform/PlatformViewRepresentable.swift +++ b/Sources/OpenSwiftUI/Integration/Representable/Platform/PlatformViewRepresentable.swift @@ -3,7 +3,7 @@ // OpenSwiftUI // // Audited for 6.5.4 -// Status: WIP +// Status: Complete // ID: A513612C07DFA438E70B9FA90719B40D (SwiftUI) #if os(iOS) || os(visionOS) @@ -158,9 +158,8 @@ extension PlatformViewRepresentable where PlatformViewProvider: PlatformViewCont static var isViewController: Bool { true } } -// MARK: - PlatformViewChild [WIP] +// MARK: - PlatformViewChild -// TODO: ScrapeableAttribute struct PlatformViewChild: StatefulRule { @Attribute var view: Content @Attribute var environment: EnvironmentValues @@ -233,27 +232,71 @@ struct PlatformViewChild: StatefulRule { platformView.map { UInt(bitPattern: Unmanaged.passUnretained($0).toOpaque()) } ?? 0, ] ) { - // TODO - if coordinator == nil { - coordinator = view.makeCoordinator() + var (view, viewChanged) = $view.changedValue() + let (phase, phaseChanged) = $phase.changedValue() + var (environment, environmentChanged) = $environment.changedValue() + let (focusedValues, focusedValuesChanged) = $focusedValues?.changedValue() ?? (.init(), false) + if phase.resetSeed != resetSeed { + links.reset() + resetPlatformView() + resetSeed = phase.resetSeed } - if platformView == nil { + let linksChanged = withUnsafeMutablePointer(to: &view) { pointer in + links.update(container: pointer, phase: phase) + } + var changed = linksChanged || !hasValue || viewChanged || phaseChanged || AnyAttribute.currentWasModified + let transaction = Graph.withoutUpdate { + if coordinator == nil { + coordinator = view.makeCoordinator() + } + return self.transaction + } + environment.preferenceBridge = bridge + let context: PlatformViewRepresentableContext + if let platformView { + if environmentChanged, tracker.hasDifferentUsedValues(environment.plist) { + tracker.reset() + changed = true + } + if platformView.isPlatformFocusContainerHost { + environment.focusGroupID = .inferred + } + let env = EnvironmentValues(environment.plist, tracker: tracker) + Graph.withoutUpdate { + if phaseChanged || environmentChanged { + platformView.updateEnvironment( + env.removingTracker(), + viewPhase: phase + ) + } + if focusedValuesChanged { + platformView.focusedValues = focusedValues + } + } + context = PlatformViewRepresentableContext( + coordinator: coordinator!, + preferenceBridge: bridge, + transaction: transaction, + environmentStorage: .eager(env) + ) + } else { + tracker.reset() + changed = true + let env = EnvironmentValues(environment.plist, tracker: tracker) + context = PlatformViewRepresentableContext( + coordinator: coordinator!, + preferenceBridge: bridge, + transaction: transaction, + environmentStorage: .eager(env) + ) let host = ViewGraph.viewRendererHost - withObservation { + platformView = withObservation { Graph.withoutUpdate { - let representableContext = PlatformViewRepresentableContext( - coordinator: coordinator!, - preferenceBridge: bridge, - transaction: transaction, - environmentStorage: .eager(environment) - ) - representableContext.values.asCurrent { - let provider = view.makeViewProvider(context: representableContext) - let environment = environment.removingTracker() - platformView = PlatformViewHost( - provider, + context.values.asCurrent { + PlatformViewHost( + view.makeViewProvider(context: context), host: host, - environment: environment, + environment: env.removingTracker(), viewPhase: phase, importer: importer ) @@ -261,6 +304,30 @@ struct PlatformViewChild: StatefulRule { } } } + guard changed else { + return + } + let host = ViewGraph.viewRendererHost + withObservation { + Graph.withoutUpdate { + guard let provider = representedViewProvider else { + return + } + if let host { + Update.ensure { + host.performExternalUpdate { + context.values.asCurrent { + view.updateViewProvider(provider, context: context) + } + } + } + } else { + context.values.asCurrent { + view.updateViewProvider(provider, context: context) + } + } + } + } value = ViewLeafView( content: view, platformView: platformView!, @@ -281,6 +348,14 @@ struct PlatformViewChild: StatefulRule { } } +extension PlatformViewHost { + var isPlatformFocusContainerHost: Bool { + // TODO + _openSwiftUIUnimplementedWarning() + return false + } +} + extension PlatformViewChild: ObservedAttribute { mutating func destroy() { links.destroy() From 5ea8ad13c169ec0051bd230b9ad7aa0521b4cae2 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 7 Oct 2025 20:16:42 +0800 Subject: [PATCH 5/6] Implement PlatformView.isPlatformFocusContainerHost --- .../Platform/PlatformViewRepresentable.swift | 42 ++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/Sources/OpenSwiftUI/Integration/Representable/Platform/PlatformViewRepresentable.swift b/Sources/OpenSwiftUI/Integration/Representable/Platform/PlatformViewRepresentable.swift index 5006e3ab2..b86150b33 100644 --- a/Sources/OpenSwiftUI/Integration/Representable/Platform/PlatformViewRepresentable.swift +++ b/Sources/OpenSwiftUI/Integration/Representable/Platform/PlatformViewRepresentable.swift @@ -3,24 +3,27 @@ // OpenSwiftUI // // Audited for 6.5.4 -// Status: Complete +// Status: WIP // ID: A513612C07DFA438E70B9FA90719B40D (SwiftUI) #if os(iOS) || os(visionOS) import UIKit typealias PlatformView = UIView +typealias PlatformScrollView = UIScrollView typealias PlatformViewController = UIViewController typealias PlatformHostingController = UIHostingController typealias PlatformViewResponder = UIViewResponder #elseif os(macOS) import AppKit typealias PlatformView = NSView +typealias PlatformScrollView = NSScrollView typealias PlatformViewController = NSViewController typealias PlatformHostingController = NSHostingController typealias PlatformViewResponder = NSViewResponder #else import Foundation typealias PlatformView = NSObject +typealias PlatformScrollView = NSObject typealias PlatformViewController = NSObject typealias PlatformHostingController = NSObject typealias PlatformViewResponder = NSObject @@ -72,7 +75,7 @@ protocol PlatformViewRepresentable: View { typealias LayoutOptions = _PlatformViewRepresentableLayoutOptions } -// MARK: - PlatformViewRepresentable + Extension +// MARK: - PlatformViewRepresentable + Extension [WIP] extension PlatformViewRepresentable { static var dynamicProperties: DynamicPropertyCache.Fields { @@ -348,14 +351,6 @@ struct PlatformViewChild: StatefulRule { } } -extension PlatformViewHost { - var isPlatformFocusContainerHost: Bool { - // TODO - _openSwiftUIUnimplementedWarning() - return false - } -} - extension PlatformViewChild: ObservedAttribute { mutating func destroy() { links.destroy() @@ -414,6 +409,33 @@ extension PlatformViewChild: ScrapeableAttribute { } } +// MARK: - PlatformViewHost + FocusContainer + +extension PlatformViewHost { + private struct UnarySubtreeSequence: Sequence { + weak var root: PlatformView? + + func makeIterator() -> AnyIterator { + var current = root + return AnyIterator { [weak current] ()-> PlatformView? in + #if canImport(Darwin) + guard let node = current else { + return nil + } + current = node.subviews.first + return node + #else + return nil + #endif + } + } + } + + var isPlatformFocusContainerHost: Bool { + UnarySubtreeSequence(root: self).first { $0 is PlatformScrollView } != nil + } +} + // MARK: - ViewLeafView struct ViewLeafView: PrimitiveView, UnaryView where Content: PlatformViewRepresentable { From 7473ebd28ace34649b37f32bd386614d0ec241a5 Mon Sep 17 00:00:00 2001 From: Kyle Date: Tue, 7 Oct 2025 21:06:54 +0800 Subject: [PATCH 6/6] Implement ViewRenderHost.enclosingHosts --- .../View/Graph/ViewRendererHost.swift | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Sources/OpenSwiftUICore/View/Graph/ViewRendererHost.swift b/Sources/OpenSwiftUICore/View/Graph/ViewRendererHost.swift index 79967d948..475df51c4 100644 --- a/Sources/OpenSwiftUICore/View/Graph/ViewRendererHost.swift +++ b/Sources/OpenSwiftUICore/View/Graph/ViewRendererHost.swift @@ -413,8 +413,16 @@ extension ViewRendererHost { return bridge.viewGraph == nil } - private var enclosingHosts: [ViewRendererHost] { - _openSwiftUIUnimplementedFailure() + private var enclosingHosts: [any ViewRendererHost] { + guard let preferenceBridge = viewGraph.preferenceBridge, + let parentViewGraph = preferenceBridge.viewGraph, + let parentHost = parentViewGraph as? ViewRendererHost + else { + return [self] + } + var hosts = parentHost.enclosingHosts + hosts.append(self) + return hosts } package func performExternalUpdate(_ update: () -> Void) {