From f51a343adfd392d0bbfe1405e40b305404c8e39f Mon Sep 17 00:00:00 2001 From: phranck Date: Sat, 7 Feb 2026 13:43:34 +0100 Subject: [PATCH] Fix: Resolve Swift 6 Equatable conformance errors for View types Add explicit nonisolated == implementations using MainActor.assumeIsolated for all View types with @preconcurrency Equatable conformances: - Box - Card - ContainerView - _ContainerViewCore - Dialog - Panel - ProgressView - VStack, HStack, ZStack This fixes CI failures on macOS where the derived Equatable conformance cannot access @MainActor-isolated properties from nonisolated context. --- Sources/TUIkit/Views/Box.swift | 10 +++++++- Sources/TUIkit/Views/Card.swift | 12 ++++++++- Sources/TUIkit/Views/ContainerView.swift | 26 ++++++++++++++++++-- Sources/TUIkit/Views/Dialog.swift | 11 ++++++++- Sources/TUIkit/Views/Panel.swift | 11 ++++++++- Sources/TUIkit/Views/ProgressView.swift | 11 ++++++++- Sources/TUIkit/Views/Stacks.swift | 31 +++++++++++++++++++++--- 7 files changed, 102 insertions(+), 10 deletions(-) diff --git a/Sources/TUIkit/Views/Box.swift b/Sources/TUIkit/Views/Box.swift index f3c381f9..d03d8d0b 100644 --- a/Sources/TUIkit/Views/Box.swift +++ b/Sources/TUIkit/Views/Box.swift @@ -164,4 +164,12 @@ struct BufferView: View, Renderable { // MARK: - Equatable Conformance -extension Box: @preconcurrency Equatable where Content: Equatable {} +extension Box: Equatable where Content: Equatable { + nonisolated public static func == (lhs: Box, rhs: Box) -> Bool { + MainActor.assumeIsolated { + lhs.content == rhs.content && + lhs.borderStyle == rhs.borderStyle && + lhs.borderColor == rhs.borderColor + } + } +} diff --git a/Sources/TUIkit/Views/Card.swift b/Sources/TUIkit/Views/Card.swift index 52d15d5d..a0804d20 100644 --- a/Sources/TUIkit/Views/Card.swift +++ b/Sources/TUIkit/Views/Card.swift @@ -127,7 +127,17 @@ public struct Card: View { // MARK: - Equatable Conformance -extension Card: @preconcurrency Equatable where Content: Equatable, Footer: Equatable {} +extension Card: Equatable where Content: Equatable, Footer: Equatable { + nonisolated public static func == (lhs: Card, rhs: Card) -> Bool { + MainActor.assumeIsolated { + lhs.title == rhs.title && + lhs.content == rhs.content && + lhs.footer == rhs.footer && + lhs.config == rhs.config && + lhs.backgroundColor == rhs.backgroundColor + } + } +} // MARK: - Rendering diff --git a/Sources/TUIkit/Views/ContainerView.swift b/Sources/TUIkit/Views/ContainerView.swift index 80840b9c..5fc97dd9 100644 --- a/Sources/TUIkit/Views/ContainerView.swift +++ b/Sources/TUIkit/Views/ContainerView.swift @@ -246,7 +246,18 @@ struct ContainerView: View { // MARK: - Equatable Conformance -extension ContainerView: @preconcurrency Equatable where Content: Equatable, Footer: Equatable {} +extension ContainerView: Equatable where Content: Equatable, Footer: Equatable { + nonisolated static func == (lhs: ContainerView, rhs: ContainerView) -> Bool { + MainActor.assumeIsolated { + lhs.title == rhs.title && + lhs.titleColor == rhs.titleColor && + lhs.content == rhs.content && + lhs.footer == rhs.footer && + lhs.style == rhs.style && + lhs.padding == rhs.padding + } + } +} // MARK: - Convenience Initializer (no footer) @@ -463,4 +474,15 @@ private struct _ContainerViewCore: View, Renderable // MARK: - Equatable Conformance -extension _ContainerViewCore: @preconcurrency Equatable where Content: Equatable, Footer: Equatable {} +extension _ContainerViewCore: Equatable where Content: Equatable, Footer: Equatable { + nonisolated static func == (lhs: _ContainerViewCore, rhs: _ContainerViewCore) -> Bool { + MainActor.assumeIsolated { + lhs.title == rhs.title && + lhs.titleColor == rhs.titleColor && + lhs.content == rhs.content && + lhs.footer == rhs.footer && + lhs.style == rhs.style && + lhs.padding == rhs.padding + } + } +} diff --git a/Sources/TUIkit/Views/Dialog.swift b/Sources/TUIkit/Views/Dialog.swift index 49e3bd53..9dbda9bb 100644 --- a/Sources/TUIkit/Views/Dialog.swift +++ b/Sources/TUIkit/Views/Dialog.swift @@ -102,7 +102,16 @@ public struct Dialog: View { // MARK: - Equatable Conformance -extension Dialog: @preconcurrency Equatable where Content: Equatable, Footer: Equatable {} +extension Dialog: Equatable where Content: Equatable, Footer: Equatable { + nonisolated public static func == (lhs: Dialog, rhs: Dialog) -> Bool { + MainActor.assumeIsolated { + lhs.title == rhs.title && + lhs.content == rhs.content && + lhs.footer == rhs.footer && + lhs.config == rhs.config + } + } +} // MARK: - Rendering diff --git a/Sources/TUIkit/Views/Panel.swift b/Sources/TUIkit/Views/Panel.swift index 98220494..55c7988d 100644 --- a/Sources/TUIkit/Views/Panel.swift +++ b/Sources/TUIkit/Views/Panel.swift @@ -122,7 +122,16 @@ public struct Panel: View { // MARK: - Equatable Conformance -extension Panel: @preconcurrency Equatable where Content: Equatable, Footer: Equatable {} +extension Panel: Equatable where Content: Equatable, Footer: Equatable { + nonisolated public static func == (lhs: Panel, rhs: Panel) -> Bool { + MainActor.assumeIsolated { + lhs.title == rhs.title && + lhs.content == rhs.content && + lhs.footer == rhs.footer && + lhs.config == rhs.config + } + } +} // MARK: - Convenience Initializer (no footer) diff --git a/Sources/TUIkit/Views/ProgressView.swift b/Sources/TUIkit/Views/ProgressView.swift index 4f659615..254bdde8 100644 --- a/Sources/TUIkit/Views/ProgressView.swift +++ b/Sources/TUIkit/Views/ProgressView.swift @@ -227,7 +227,16 @@ extension ProgressView { // MARK: - Equatable Conformance -extension ProgressView: @preconcurrency Equatable where Label: Equatable, CurrentValueLabel: Equatable {} +extension ProgressView: Equatable where Label: Equatable, CurrentValueLabel: Equatable { + nonisolated public static func == (lhs: ProgressView, rhs: ProgressView) -> Bool { + MainActor.assumeIsolated { + lhs.fractionCompleted == rhs.fractionCompleted && + lhs.style == rhs.style && + lhs.label == rhs.label && + lhs.currentValueLabel == rhs.currentValueLabel + } + } +} // MARK: - Rendering diff --git a/Sources/TUIkit/Views/Stacks.swift b/Sources/TUIkit/Views/Stacks.swift index 4c0459d5..1527b226 100644 --- a/Sources/TUIkit/Views/Stacks.swift +++ b/Sources/TUIkit/Views/Stacks.swift @@ -230,9 +230,34 @@ public struct Alignment: Sendable, Equatable { // MARK: - Equatable Conformances -extension VStack: @preconcurrency Equatable where Content: Equatable {} -extension HStack: @preconcurrency Equatable where Content: Equatable {} -extension ZStack: @preconcurrency Equatable where Content: Equatable {} +extension VStack: Equatable where Content: Equatable { + nonisolated public static func == (lhs: VStack, rhs: VStack) -> Bool { + MainActor.assumeIsolated { + lhs.alignment == rhs.alignment && + lhs.spacing == rhs.spacing && + lhs.content == rhs.content + } + } +} + +extension HStack: Equatable where Content: Equatable { + nonisolated public static func == (lhs: HStack, rhs: HStack) -> Bool { + MainActor.assumeIsolated { + lhs.alignment == rhs.alignment && + lhs.spacing == rhs.spacing && + lhs.content == rhs.content + } + } +} + +extension ZStack: Equatable where Content: Equatable { + nonisolated public static func == (lhs: ZStack, rhs: ZStack) -> Bool { + MainActor.assumeIsolated { + lhs.alignment == rhs.alignment && + lhs.content == rhs.content + } + } +} // MARK: - VStack Rendering