使用 Swift KeyPath 获取对象的属性名称

问题描述 投票:0回答:0

问题:

更新 Firebase 中特定对象的属性。为此,我只想更新一个对象内的一个值,而不必与当前对象交互。 firebase 的必需部分,除了集合和文档之外,还有 keynew value

我想出了一个可行的实现 - 我想要一些改进的想法。 特别是那里,static func createEmpty() -> Self,这让我很困扰。初始化模型以查找密钥对我来说似乎效率不高。我希望有一个敏捷的好人能给我指路。

外观如下:

这个协议将是key property reader

protocol KeyPathValueReadable {
    static func createEmpty() -> Self
}

extension KeyPathValueReadable {
   fileprivate subscript<T:Any>(at path : KeyPath<Self,T> ) -> String{
       print(self[keyPath: path])
       return "\(self[keyPath: path])"
   }
   func readValue<T : Any & Hashable>(at keyPath : KeyPath<Self,T>) -> String{
       let mirror = Mirror(reflecting: self)
       var dict : [T: String] = [:]
       for case let (label, value) in mirror.children {
           if let label = label {
               dict[value as! T] = label
           }
       }
       return dict[self[keyPath: keyPath]] ?? "no key at path"
   }
}

这是要更新的模型示例:

struct UserModel : Codable, KeyPathValueReadable {
    
    var name : String
    
     static func createEmpty() -> Self {
         return Self.init(name: "")
    }
}

这里是客户端实现和使用:

protocol UpdateDataClientDelegate {
    associatedtype D : Codable & KeyPathValueReadable
}

extension UpdateDataClientDelegate {
    func update<T : Hashable>(in collection : String, for document : String, at path : KeyPath<D,T>, newValue : Any) {
        let key =  D.createEmpty().readValue(at: path)
        print(key)
        //update key with newValue
    }
}

struct UpdateUserClient : UpdateDataClientDelegate {
    typealias D = UserModel
}

let client = UpdateUserClient()

client.update(in: "pouf", for: "plaf", at: \.name, newValue: "chocolate")

编辑

如果存在类型冗余,先前的实现会导致字典中的覆盖。 为了解决这个问题,我应用了 createEmpty() 还必须为我们查找名称的变量设置一个值。

因此,我在可读键的扩展名中添加了一个下标 - 因此它会为路径设置一个值。

extension KeyPathValueReadable {
    subscript<T>(dynamicMember path: WritableKeyPath<Self, T>) -> T{
        get { self[ keyPath: path] }
        set { self[ keyPath: path ] =  newValue }
    }
}

然后就可以这样使用了:

func update<T : Hashable>(in collection : String, for document : String, at path : WritableKeyPath<D,T>, with value : T) throws {
        Task {
            var dict = [String : T]()
            var object =  D.createEmpty()
            object[dynamicMember: path] = value
            dict[object.readValue(at: path)] = value
            try await apiClient.updateData(dict)
        }
    }
ios swift design-patterns clean-architecture keypaths
© www.soinside.com 2019 - 2024. All rights reserved.