diff --git a/Sources/SwiftNavigation/TextState.swift b/Sources/SwiftNavigation/TextState.swift
index abeb154eb..d222b2266 100644
--- a/Sources/SwiftNavigation/TextState.swift
+++ b/Sources/SwiftNavigation/TextState.swift
@@ -126,28 +126,36 @@ public struct TextState: Equatable, Hashable, Sendable {
comment: StaticString?
)
#endif
- case localizedStringResource(LocalizedStringResourceBox)
+ #if canImport(Darwin)
+ case localizedStringResource(LocalizedStringResourceBox)
+ #endif
case verbatim(String)
static func == (lhs: Self, rhs: Self) -> Bool {
switch (lhs, rhs) {
case (.concatenated(let l1, let l2), .concatenated(let r1, let r2)):
return l1 == r1 && l2 == r2
- case (.concatenated, .localizedStringResource),
- (.localizedStringResource, .concatenated),
- (.concatenated, .verbatim),
+ #if canImport(Darwin)
+ case (.concatenated, .localizedStringResource),
+ (.localizedStringResource, .concatenated):
+ // NB: We do not attempt to equate concatenated cases.
+ return false
+ #endif
+ case (.concatenated, .verbatim),
(.verbatim, .concatenated):
// NB: We do not attempt to equate concatenated cases.
return false
case (.verbatim(let lhs), .verbatim(let rhs)):
return lhs == rhs
- case (.verbatim(let string), .localizedStringResource(let resource)),
- (.localizedStringResource(let resource), .verbatim(let string)):
- return string == resource.asString()
+ #if canImport(Darwin)
+ case (.verbatim(let string), .localizedStringResource(let resource)),
+ (.localizedStringResource(let resource), .verbatim(let string)):
+ return string == resource.asString()
- case (.localizedStringResource(let lhs), .localizedStringResource(let rhs)):
- return lhs.asString() == rhs.asString()
+ case (.localizedStringResource(let lhs), .localizedStringResource(let rhs)):
+ return lhs.asString() == rhs.asString()
+ #endif
#if canImport(SwiftUI)
case (.concatenated, .localizedStringKey),
@@ -193,8 +201,10 @@ public struct TextState: Equatable, Hashable, Sendable {
hasher.combine(key.formatted(tableName: tableName, bundle: bundle, comment: comment))
#endif
- case .localizedStringResource(let resource):
- hasher.combine(resource.asString())
+ #if canImport(Darwin)
+ case .localizedStringResource(let resource):
+ hasher.combine(resource.asString())
+ #endif
case .verbatim(let string):
hasher.combine(string)
@@ -205,41 +215,43 @@ public struct TextState: Equatable, Hashable, Sendable {
// MARK: - LocalizedStringResourceBox
-private struct LocalizedStringResourceBox: @unchecked Sendable {
- // REVISIT: Make 'Any' into 'any Sendable' when minimum deployment target is iOS 18
- let value: Any
-
- @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
- init(_ resource: LocalizedStringResource) {
- self.value = resource
- }
+#if canImport(Darwin)
+ private struct LocalizedStringResourceBox: @unchecked Sendable {
+ // REVISIT: Make 'Any' into 'any Sendable' when minimum deployment target is iOS 18
+ let value: Any
- func asText() -> Text {
- guard
- #available(iOS 16, macOS 13, tvOS 16, watchOS 9, *),
- let resource = value as? LocalizedStringResource
- else {
- preconditionFailure(
- "LocalizedStringResourceBox should only be exposed where LocalizedStringResource is available."
- )
+ @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
+ init(_ resource: LocalizedStringResource) {
+ self.value = resource
}
- return Text(resource)
- }
+ func asText() -> Text {
+ guard
+ #available(iOS 16, macOS 13, tvOS 16, watchOS 9, *),
+ let resource = value as? LocalizedStringResource
+ else {
+ preconditionFailure(
+ "LocalizedStringResourceBox should only be exposed where LocalizedStringResource is available."
+ )
+ }
- func asString() -> String {
- guard
- #available(iOS 16, macOS 13, tvOS 16, watchOS 9, *),
- let resource = value as? LocalizedStringResource
- else {
- preconditionFailure(
- "LocalizedStringResourceBox should only be exposed where LocalizedStringResource is available."
- )
+ return Text(resource)
}
- return String(localized: resource)
+ func asString() -> String {
+ guard
+ #available(iOS 16, macOS 13, tvOS 16, watchOS 9, *),
+ let resource = value as? LocalizedStringResource
+ else {
+ preconditionFailure(
+ "LocalizedStringResourceBox should only be exposed where LocalizedStringResource is available."
+ )
+ }
+
+ return String(localized: resource)
+ }
}
-}
+#endif
// MARK: - API
@@ -269,14 +281,16 @@ extension TextState {
}
#endif
- @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
- public init(
- _ resource: LocalizedStringResource
- ) {
- self.storage = .localizedStringResource(
- LocalizedStringResourceBox(resource)
- )
- }
+ #if canImport(Darwin)
+ @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
+ public init(
+ _ resource: LocalizedStringResource
+ ) {
+ self.storage = .localizedStringResource(
+ LocalizedStringResourceBox(resource)
+ )
+ }
+ #endif
public static func + (lhs: Self, rhs: Self) -> Self {
.init(storage: .concatenated(lhs, rhs))
@@ -554,19 +568,19 @@ extension TextState {
}
case .accessibilityLabel(let value):
if #available(iOS 15, macOS 12, tvOS 15, watchOS 8, *) {
- switch value.storage {
- case .verbatim(let string):
- return text.accessibilityLabel(string)
- case .localizedStringKey(let key, let tableName, let bundle, let comment):
- return text.accessibilityLabel(
- Text(key, tableName: tableName, bundle: bundle, comment: comment)
- )
- case .localizedStringResource(let resourceBox):
- return text.accessibilityLabel(
- resourceBox.asText()
- )
- case .concatenated(_, _):
- assertionFailure("`.accessibilityLabel` does not support concatenated `TextState`")
+ switch value.storage {
+ case .verbatim(let string):
+ return text.accessibilityLabel(string)
+ case .localizedStringKey(let key, let tableName, let bundle, let comment):
+ return text.accessibilityLabel(
+ Text(key, tableName: tableName, bundle: bundle, comment: comment)
+ )
+ case .localizedStringResource(let resourceBox):
+ return text.accessibilityLabel(
+ resourceBox.asText()
+ )
+ case .concatenated(_, _):
+ assertionFailure("`.accessibilityLabel` does not support concatenated `TextState`")
return text
}
} else {
@@ -678,8 +692,10 @@ extension String {
)
#endif
- case .localizedStringResource(let resourceBox):
- self = resourceBox.asString()
+ #if canImport(Darwin)
+ case .localizedStringResource(let resourceBox):
+ self = resourceBox.asString()
+ #endif
case .verbatim(let string):
self = string
@@ -740,8 +756,10 @@ extension TextState: CustomDumpRepresentable {
case .localizedStringKey(let key, let tableName, let bundle, let comment):
output = key.formatted(tableName: tableName, bundle: bundle, comment: comment)
#endif
- case .localizedStringResource(let resourceBox):
- output = resourceBox.asString()
+ #if canImport(Darwin)
+ case .localizedStringResource(let resourceBox):
+ output = resourceBox.asString()
+ #endif
case .verbatim(let string):
output = string
diff --git a/Tests/SwiftNavigationTests/ButtonStateTests.swift b/Tests/SwiftNavigationTests/ButtonStateTests.swift
index de515badc..0b68eeec0 100644
--- a/Tests/SwiftNavigationTests/ButtonStateTests.swift
+++ b/Tests/SwiftNavigationTests/ButtonStateTests.swift
@@ -4,32 +4,35 @@
import Testing
struct ButtonStateTests {
- @Test
- func testAsyncAnimationWarning() async {
- let button = ButtonState(action: .send((), animation: .easeInOut)) {
- TextState("Animate!")
- }
- await withKnownIssue {
- await button.withAction { _ in
- await Task.yield()
+ #if canImport(SwiftUI)
+ @Test
+ func testAsyncAnimationWarning() async {
+ let button = ButtonState(action: .send((), animation: .easeInOut)) {
+ TextState("Animate!")
}
- } matching: { issue in
- issue.description.hasSuffix(
- """
- An animated action was performed asynchronously: …
- Action:
- ButtonStateAction.send(
- (),
- animation: Animation.easeInOut
- )
+ await withKnownIssue {
+ await button.withAction { _ in
+ await Task.yield()
+ }
+ } matching: { issue in
+ issue.description.hasSuffix(
+ """
+ An animated action was performed asynchronously: …
+
+ Action:
+ ButtonStateAction.send(
+ (),
+ animation: Animation.easeInOut
+ )
- Asynchronous actions cannot be animated. Evaluate this action in a synchronous closure, \
- or use 'SwiftUI.withAnimation' explicitly.
- """
- )
+ Asynchronous actions cannot be animated. Evaluate this action in a synchronous closure, \
+ or use 'SwiftUI.withAnimation' explicitly.
+ """
+ )
+ }
}
- }
+ #endif
}
#endif
diff --git a/Tests/SwiftNavigationTests/TextStateTests.swift b/Tests/SwiftNavigationTests/TextStateTests.swift
index ae0a2decb..0a465ce7c 100644
--- a/Tests/SwiftNavigationTests/TextStateTests.swift
+++ b/Tests/SwiftNavigationTests/TextStateTests.swift
@@ -14,76 +14,80 @@ final class TextStateTests: XCTestCase {
"""
)
- dump = ""
- customDump(
- TextState("Hello, ")
- + TextState("world").bold().italic()
- + TextState("!"),
- to: &dump
- )
- XCTAssertEqual(
- dump,
- """
- "Hello, _**world**_!"
- """
- )
+ #if canImport(Darwin)
+ dump = ""
+ customDump(
+ TextState("Hello, ")
+ + TextState("world").bold().italic()
+ + TextState("!"),
+ to: &dump
+ )
+ XCTAssertEqual(
+ dump,
+ """
+ "Hello, _**world**_!"
+ """
+ )
- dump = ""
- customDump(
- TextState("Offset by 10.5").baselineOffset(10.5)
- + TextState("\n") + TextState("Headline").font(.headline)
- + TextState("\n") + TextState("No font").font(nil)
- + TextState("\n") + TextState("Light font weight").fontWeight(.light)
- + TextState("\n") + TextState("No font weight").fontWeight(nil)
- + TextState("\n") + TextState("Red").foregroundColor(.red)
- + TextState("\n") + TextState("No color").foregroundColor(nil)
- + TextState("\n") + TextState("Italic").italic()
- + TextState("\n") + TextState("Kerning of 2.5").kerning(2.5)
- + TextState("\n") + TextState("Stricken").strikethrough()
- + TextState("\n") + TextState("Stricken green").strikethrough(color: .green)
- + TextState("\n") + TextState("Not stricken blue").strikethrough(false, color: .blue)
- + TextState("\n") + TextState("Tracking of 5.5").tracking(5.5)
- + TextState("\n") + TextState("Underlined").underline()
- + TextState("\n") + TextState("Underlined pink").underline(color: .pink)
- + TextState("\n") + TextState("Not underlined purple").underline(false, color: .pink),
- to: &dump
- )
- expectNoDifference(
- dump,
- #"""
- """
- Offset by 10.5
- Headline
- No font
- Light font weight
- No font weight
- Red
- No color
- _Italic_
- Kerning of 2.5
- ~~Stricken~~
- Stricken green
- Not stricken blue
- Tracking of 5.5
- Underlined
- Underlined pink
- Not underlined purple
- """
- """#
- )
+ dump = ""
+ customDump(
+ TextState("Offset by 10.5").baselineOffset(10.5)
+ + TextState("\n") + TextState("Headline").font(.headline)
+ + TextState("\n") + TextState("No font").font(nil)
+ + TextState("\n") + TextState("Light font weight").fontWeight(.light)
+ + TextState("\n") + TextState("No font weight").fontWeight(nil)
+ + TextState("\n") + TextState("Red").foregroundColor(.red)
+ + TextState("\n") + TextState("No color").foregroundColor(nil)
+ + TextState("\n") + TextState("Italic").italic()
+ + TextState("\n") + TextState("Kerning of 2.5").kerning(2.5)
+ + TextState("\n") + TextState("Stricken").strikethrough()
+ + TextState("\n") + TextState("Stricken green").strikethrough(color: .green)
+ + TextState("\n") + TextState("Not stricken blue").strikethrough(false, color: .blue)
+ + TextState("\n") + TextState("Tracking of 5.5").tracking(5.5)
+ + TextState("\n") + TextState("Underlined").underline()
+ + TextState("\n") + TextState("Underlined pink").underline(color: .pink)
+ + TextState("\n") + TextState("Not underlined purple").underline(false, color: .pink),
+ to: &dump
+ )
+ expectNoDifference(
+ dump,
+ #"""
+ """
+ Offset by 10.5
+ Headline
+ No font
+ Light font weight
+ No font weight
+ Red
+ No color
+ _Italic_
+ Kerning of 2.5
+ ~~Stricken~~
+ Stricken green
+ Not stricken blue
+ Tracking of 5.5
+ Underlined
+ Underlined pink
+ Not underlined purple
+ """
+ """#
+ )
+ #endif
}
- @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
- func testTextStateLocalizedStringResource() {
- var dump = ""
- let resource = LocalizedStringResource("hello.world", defaultValue: "Hello, world!")
- customDump(TextState(resource), to: &dump)
- XCTAssertEqual(
- dump,
- """
- "Hello, world!"
- """
- )
- }
+ #if canImport(Darwin)
+ @available(iOS 16, macOS 13, tvOS 16, watchOS 9, *)
+ func testTextStateLocalizedStringResource() {
+ var dump = ""
+ let resource = LocalizedStringResource("hello.world", defaultValue: "Hello, world!")
+ customDump(TextState(resource), to: &dump)
+ XCTAssertEqual(
+ dump,
+ """
+ "Hello, world!"
+ """
+ )
+ }
+ #endif
#endif
}