【问题标题】:Store custom data type in @AppStorage with optional initializer?使用可选初始化程序在@AppStorage 中存储自定义数据类型?
【发布时间】:2021-01-05 23:15:36
【问题描述】:

我正在尝试将自定义数据类型存储到 AppStorage。为此,模型符合RawRepresentable(遵循此tutorial)。它工作正常,但是当我初始化 @AppStorage 变量时,它需要一个初始 UserModel 值。我想让变量可选,所以如果用户退出,它可以是 nil 。这可能吗?

在一个类/视图中,我可以这样初始化:

@AppStorage("user_model") private(set) var user: UserModel = UserModel(id: "", name: "", email: "")

但我想这样初始化:

@AppStorage("user_model") private(set) var user: UserModel?

型号:

struct UserModel: Codable {
        
    let id: String
    let name: String
    let email: String
    
    enum CodingKeys: String, CodingKey {
        case id
        case name
        case email
    }
    
    init(from decoder: Decoder) throws {        
        let values = try decoder.container(keyedBy: CodingKeys.self)
        
        do {
           id = try String(values.decode(Int.self, forKey: .id))
        } catch DecodingError.typeMismatch {
           id = try String(values.decode(String.self, forKey: .id))
        }
        self.name = try values.decode(String.self, forKey: .name)
        self.email = try values.decode(String.self, forKey: .email)
    }
    
    init(id: String, name: String, email: String) {
        self.id = id
        self.name = name
        self.email = email
    }
    
}

// MARK: RAW REPRESENTABLE

extension UserModel: RawRepresentable {
    
    // RawRepresentable allows a UserModel to be store in AppStorage directly.
    
    public init?(rawValue: String) {
        guard let data = rawValue.data(using: .utf8),
            let result = try? JSONDecoder().decode(UserModel.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
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(id, forKey: .id)
        try container.encode(name, forKey: .name)
        try container.encode(email, forKey: .email)
    }
    
}

【问题讨论】:

    标签: swift swiftui nsuserdefaults


    【解决方案1】:

    以下代码有效,因为您添加了一致性UserModel: RawRepresentable

    @AppStorage("user_model") private(set) var user: UserModel = UserModel(id: "", name: "", email: "")
    

    如果您希望以下操作起作用,您需要对 UserModel? 执行相同操作:

    @AppStorage("user_model") private(set) var user: UserModel? = nil
    

    这是一个可能的解决方案:

    extension Optional: RawRepresentable where Wrapped == UserModel {
        public init?(rawValue: String) {
            guard let data = rawValue.data(using: .utf8),
                  let result = try? JSONDecoder().decode(UserModel.self, from: data)
            else {
                return nil
            }
            self = result
        }
    
        public var rawValue: String {
            guard let data = try? JSONEncoder().encode(self),
                  let result = String(data: data, encoding: .utf8)
            else {
                return "[]"
            }
            return result
        }
    
        func encode(to encoder: Encoder) throws {
            var container = encoder.container(keyedBy: UserModel.CodingKeys.self)
            try container.encode(self?.id, forKey: .id)
            try container.encode(self?.name, forKey: .name)
            try container.encode(self?.email, forKey: .email)
        }
    }
    

    注意:我重用了您对 UserModel: RawRepresentable 已有的实现 - 对于这种情况,它可能需要一些更正。

    也因为你符合Optional: RawRepresentable 你需要做 UserModel public 也是如此。

    【讨论】:

      【解决方案2】:

      任何可选Codable 的可能通用方法:

      extension Optional: RawRepresentable where Wrapped: Codable {
          public var rawValue: String {
              guard let data = try? JSONEncoder().encode(self),
                    let json = String(data: data, encoding: .utf8)
              else {
                  return "{}"
              }
              return json
          }
      
          public init?(rawValue: String) {
              guard let data = rawValue.data(using: .utf8),
                    let value = try? JSONDecoder().decode(Self.self, from: data)
              else {
                  return nil
              }
              self = value
          }
      }
      

      有了这个,any Codable 现在可以保存在应用存储中:

      @AppStorage("user_model") var user: UserModel? = nil
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-11-16
        • 1970-01-01
        • 2020-03-29
        • 1970-01-01
        • 2016-08-25
        • 1970-01-01
        • 1970-01-01
        • 2011-08-28
        相关资源
        最近更新 更多