Skip to content

Instantly share code, notes, and snippets.

@davidbalbert
Last active November 12, 2024 00:27
Show Gist options
  • Save davidbalbert/eef9c238531217a42b83d6903ef777dc to your computer and use it in GitHub Desktop.
Save davidbalbert/eef9c238531217a42b83d6903ef777dc to your computer and use it in GitHub Desktop.
Hack to extract a String from SwiftUI.Text
// Cursed! Do not use!
// Supports most, but not all Text initializers.
import SwiftUI
extension FormatStyle {
func format(any value: Any) -> FormatOutput? {
if let v = value as? FormatInput {
return format(v)
}
return nil
}
}
extension LocalizedStringKey {
var resolved: String? {
let mirror = Mirror(reflecting: self)
guard let key = mirror.descendant("key") as? String else {
return nil
}
guard let args = mirror.descendant("arguments") as? [Any] else {
return nil
}
let values = args.map { arg -> Any? in
let mirror = Mirror(reflecting: arg)
if let value = mirror.descendant("storage", "value", ".0") {
return value
}
guard let format = mirror.descendant("storage", "formatStyleValue", "format") as? any FormatStyle,
let input = mirror.descendant("storage", "formatStyleValue", "input") else {
return nil
}
return format.format(any: input)
}
let va = values.compactMap { arg -> CVarArg? in
switch arg {
case let i as Int: return i
case let i as Int64: return i
case let i as Int8: return i
case let i as Int16: return i
case let i as Int32: return i
case let u as UInt: return u
case let u as UInt64: return u
case let u as UInt8: return u
case let u as UInt16: return u
case let u as UInt32: return u
case let f as Float: return f
case let f as CGFloat: return f
case let d as Double: return d
case let o as NSObject: return o
default: return nil
}
}
if va.count != values.count {
return nil
}
return String.localizedStringWithFormat(key, va)
}
}
extension Text {
var string: String? {
let mirror = Mirror(reflecting: self)
if let s = mirror.descendant("storage", "verbatim") as? String {
return s
} else if let attrStr = mirror.descendant("storage", "anyTextStorage", "str") as? AttributedString {
return String(attrStr.characters)
} else if let key = mirror.descendant("storage", "anyTextStorage", "key") as? LocalizedStringKey {
return key.resolved
} else if let format = mirror.descendant("storage", "anyTextStorage", "storage", "format") as? any FormatStyle,
let input = mirror.descendant("storage", "anyTextStorage", "storage", "input") {
return format.format(any: input) as? String
} else if let formatter = mirror.descendant("storage", "anyTextStorage", "formatter") as? Formatter,
let object = mirror.descendant("storage", "anyTextStorage", "object") {
return formatter.string(for: object)
}
return nil
}
}
Text("Hello world!").string // => "Hello world!"
Text(AttributedString("Hello world!")).string // => "Hello world!"
Text("Hello \("world")!").string // => "Hello world!"
Text("Hello \(1 + 2)").string // => "Hello 3
Text("Hello \(Date())").string // => "Hello Friday, January 19, 2024 at 11:25:59 Eastern Standard Time"
Text("Hello \(0.8, format: .percent)").string // => "Hello 80%"
Text(0.8, format: .percent).string // => "80%"
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .none
Text(Date(), formatter: formatter).string // => "Jan 19, 2024"
Text(NSDate(), formatter: formatter).string // => "Jan 19, 2024"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment