Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions KDS/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,5 @@ let package = Package(
.product(name: "SnapshotTesting", package: "swift-snapshot-testing")
]
)
],
swiftLanguageModes: [
.v5
]
)
6 changes: 3 additions & 3 deletions KDS/Sources/KDS/Colors/SemanticColor.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import SwiftUI
import UIKit

public protocol AdaptiveColor {
public protocol AdaptiveColor: Sendable {
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AdaptiveColor is a public protocol; making it inherit from Sendable is a source-breaking API change for any downstream module that conforms its own types to AdaptiveColor (those types must now be Sendable too). If the goal is only to satisfy Swift 6 checks for KDS’s concrete color types, consider keeping AdaptiveColor non-Sendable and adding Sendable only to SemanticColor/LegacyColor (or introducing a separate internal/sendable protocol).

Suggested change
public protocol AdaptiveColor: Sendable {
public protocol AdaptiveColor {

Copilot uses AI. Check for mistakes.
/// Returns a dynamically-provided `UIColor`, which responds to light/dark mode.
var dynamicColor: UIColor { get }
}
Expand Down Expand Up @@ -30,7 +30,7 @@ public extension AdaptiveColor {

/// A semantic color from the Kickstarter design system, like "surface/primary".
/// Includes a light and dark mode color pair, as well as an identifying title.
public struct SemanticColor: AdaptiveColor {
public struct SemanticColor: AdaptiveColor, Sendable {
public let dynamicColor: UIColor

public let name: String
Expand Down Expand Up @@ -62,7 +62,7 @@ public struct SemanticColor: AdaptiveColor {
}

/// Used for old design system colors which can't be mapped directly to the Kickstarter color palette.
public struct LegacyColor: AdaptiveColor {
public struct LegacyColor: AdaptiveColor, Sendable {
public let name: String
public let dynamicColor: UIColor

Expand Down
4 changes: 2 additions & 2 deletions KDS/Sources/KDS/Controllers/ColorsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -176,13 +176,13 @@ public struct ColorsView: View {
}
}

extension SemanticColor: @retroactive Identifiable {
extension SemanticColor: Identifiable {
public var id: String {
return self.name
}
}

extension LegacyColor: @retroactive Identifiable {
extension LegacyColor: Identifiable {
public var id: String {
return self.name
}
Expand Down
8 changes: 4 additions & 4 deletions KDS/Sources/KDS/Fonts/InterFont.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Synchronization
import UIKit

public enum InterFont: CustomFont, CaseIterable {
Expand Down Expand Up @@ -75,15 +76,14 @@ public enum InterFont: CustomFont, CaseIterable {
}
}

var registeredInterfont = false

extension InterFont: CustomFontAccessible {
private static let registeredInterfont = Atomic(false)
public static var isRegistered: Bool {
get {
registeredInterfont
registeredInterfont.load(ordering: .acquiring)
}
set {
registeredInterfont = newValue
_ = registeredInterfont.exchange(newValue, ordering: .acquiringAndReleasing)
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isRegistered setter uses exchange and discards the returned old value. If you only need to write the flag, prefer an atomic store operation (with an appropriate ordering) to make intent clearer and avoid the extra read/modify overhead.

Suggested change
_ = registeredInterfont.exchange(newValue, ordering: .acquiringAndReleasing)
registeredInterfont.store(newValue, ordering: .releasing)

Copilot uses AI. Check for mistakes.
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching the backing flag to an Atomic removes the data race on the boolean, but the overall registration flow is still not thread-safe: two callers can both observe isRegistered == false and proceed to register concurrently, potentially triggering the “calling this more than once” failure path. Consider using an atomic compare-and-swap (or another synchronization mechanism) to ensure only one thread performs registration.

Suggested change
_ = registeredInterfont.exchange(newValue, ordering: .acquiringAndReleasing)
if newValue {
var expected = false
_ = registeredInterfont.compareExchange(
expected: &expected,
desired: true,
ordering: .acquiringAndReleasing
)
} else {
registeredInterfont.store(false, ordering: .releasing)
}

Copilot uses AI. Check for mistakes.
}
}

Expand Down