Я пытаюсь использовать @AppStorage со структурой данных, содержащей String и UUID, добавляя RawRepresentable к структуре. Похоже, что RawRepresentable вызывает неправильное поведение функции JSONEncoder.encode().
Следующий минимальный воспроизводимый пример вызывает бесконечный цикл при вызове print(p.rawValue). В этом случае "rawValue 1" печатается повторно, пока приложение не выйдет из строя.
Printer(name: "HP", id: CBAA56C5-7E14-4351-BFF4-75413AE8B5C5)
rawValue 1
rawValue 1
rawValue 1
...
Удаление : RawRepresentable приводит к правильной работе вызова, но это необходимо для использования этой структуры с @AppStorage.
Printer(name: "HP", id: CBAA56C5-7E14-4351-BFF4-75413AE8B5C5)
rawValue 1
rawValue 2
rawValue 3
{"name":"HP","id":"CBAA56C5-7E14-4351-BFF4-75413AE8B5C5"}
Я делаю что-то неправильно? Или, возможно, это известная ошибка, которую можно обойти?
Примечание: это упрощенный пример. Моя структура Printer немного сложнее.
import SwiftUI
struct Printer: Codable {
let name: String
let id: UUID
}
extension Printer: RawRepresentable {
public init?(rawValue: String) {
guard let data = rawValue.data(using: .utf8),
let result = try? JSONDecoder().decode(Printer.self, from: data)
else {
return nil
}
self = result
}
public var rawValue: String {
print("rawValue 1")
guard let data = try? JSONEncoder().encode(self) else { return "{}" }
print("rawValue 2")
guard let result = String(data: data, encoding: .utf8) else { return "{}" }
print("rawValue 3")
return result
}
}
struct ContentView: View {
// Ideally I want to use @AppStorage here...
// @AppStorage("printer") var p = Printer(name: "HP", id: UUID())
@State private var p = Printer(name: "HP", id: UUID())
var body: some View {
VStack {
Text("Hello World!")
.onAppear {
print(p)
print(p.rawValue)
}
}
}
}





Ваша проблема в этом определении из stdlib:
extension RawRepresentable where Self : Decodable, Self.RawValue == String {
/// Creates a new instance by decoding from the given decoder, when the
/// type's `RawValue` is `String`.
///
/// This initializer throws an error if reading from the decoder fails, or
/// if the data read is corrupted or otherwise invalid.
///
/// - Parameter decoder: The decoder to read data from.
public init(from decoder: any Decoder) throws
}
Вещи RawRepresentable автоматически получают метод encode(to:), который реализуется путем кодирования их rawValue, что является рекурсивным.
Вам нужно будет написать методы encode(to:) и init(from:) вручную, чтобы переопределить реализации RawRepresentable по умолчанию.
(Я ожидаю, что есть более простой способ, не использующий RawRepresentable, но он не сразу приходит на ум.)
Использование self в rawValue, скорее всего, там, где оно есть.