init(
wrappedValue: Value,
_ key: String,
store: UserDefaults? = nil
) where Value : RawRepresentable, Value.RawValue == String
One clever thing (that does not work) is to use a custom Codable type that conforms to RawRepresentable, like below
public protocol RawCodable: Codable, RawRepresentable {}
public extension RawCodable {
init?(rawValue: String) {
guard
let data = rawValue.data(using: .utf8),
let result = try? JSONDecoder().decode(Self.self, from: data)
else {
return nil
}
self = result
}
var rawValue: String {
guard
let data = try? JSONEncoder().encode(self),
let result = String(data: data, encoding: .utf8)
else {
return ""
}
return result
}
}
struct Book: RawCodable {}
struct BooksView: View {
@AppStorage("favorites") var favorite: Book?
var body: some View {
List {
}
}
}
The above will cause infinite loop as JSONEncoder will use rawValue to encode, see Codable.swift
extension RawRepresentable<String> where Self: Encodable {
/// Encodes this value into the given encoder, when the type's `RawValue`
/// is `String`.
///
/// This function throws an error if any values are invalid for the given
/// encoder's format.
///
/// - Parameter encoder: The encoder to write data to.
public func encode(to encoder: any Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(self.rawValue)
}
}
To avoid this, we can implement our own encode(to:) to avoid infinite loop.
Another way is to introduce a container struct that conforms to RawRepresentable
public struct RawCodable<Value: Codable>: RawRepresentable {
public var wrappedValue: Value
public init(
wrappedValue: Value
) {
self.wrappedValue = wrappedValue
}
public init?(rawValue: String) {
guard
let data = rawValue.data(using: .utf8),
let value = try? JSONDecoder().decode(Value.self, from: data)
else {
return nil
}
self.wrappedValue = value
}
public var rawValue: String {
guard
let data = try? JSONEncoder().encode(wrappedValue),
let string = String(data: data, encoding: .utf8)
else {
return ""
}
return string
}
}
AppStorage and SceneStorage accepts RawRepresentable where value is Int or String.
init(wrappedValue:_:store:)
One clever thing (that does not work) is to use a custom Codable type that conforms to RawRepresentable, like below
The above will cause infinite loop as
JSONEncoder
will userawValue
to encode, see Codable.swiftTo avoid this, we can implement our own
encode(to:)
to avoid infinite loop.Another way is to introduce a container struct that conforms to
RawRepresentable