Loading

SwiftUI 结构体自动生成可编辑界面

2个关键点:一个是读取、一个是写入。
显示界面:读取结构体的字段名,字段类型,即可判断、显示相应的UI控件;
用户写入数据:需要知道每个UI控件和哪个字段进行绑定,另外,对字段需要有写的权限。

尝试Mirror【失败】

第一个尝试的方案是运用反射技术,在Swift中,使用的是Mirror。Mirror可以读取结构体的属性,但不能修改结构体。

尝试Dictionary【失败】

第二个尝试的方案是使用字典Dictionary。即可读取,也能写入。
具体步骤是:

  1. 初始化结构体
  2. 通过JSONSerialization序列化成字典
  3. 遍历字典显示界面。。。[卡住]

单单是在读取这里,就遇到一个棘手的问题。Swift是强类型的语言,所以读取Dictionary的嵌套字段,需要一长串的类型转换。这就很难写出简洁易懂的代码。
比如:

    var dic:[String: Any] = ["a": [1,2]]
    var value = (dic["a"] as! [Int])[0] 

难点是,我们如何去解析包含自定义类型的数组呢?
要知道 [Location] 、[User]和 [String] 可是不同类型.
原始类型毕竟数量有限,每个类型判断一次没问题。
但对于自定义的类型。以我目前的能力,不知道如何写出兼容不同字典的解析代码?很是头大。

if let dic["a"] as [Location] {
    //...
} else if dic["a"] as [User] {
    ....
}
// ...无穷无尽的自定义类型

新的思考

能否像弱类型语言那样(如JavaScript),我可以这样去访问一个字段 dic["a"][0]
而不用在乎 dic["a"]是不是数组,也不关心它是个什么类型的数组,假如它是个数组,那就返回相应索引的值;假如不是个数组,就返回nil。
这样的话,有2个好处:

  1. 对于读取,因为我们可以不必在乎是数组类型,管你是[Location]还是[User],我知道你是个数组就行。这样可以写出通用的遍历代码。
  2. 对于写入,我们可以很容易的通过拼接的方式来修改嵌套字段。

站在巨人的肩膀

在网上查阅资料,发现SwiftyJSON框架。
官方使用示例:

let json = JSON(data: dataFromNetworking)
if let userName = json[0]["user"]["name"].string {
  //Now you got your value
}

看起来符合我的预期。

它的原理是把字典封装到它自定义的JSON结构体里,
通过JSON.object属性进行get、set操作。
这个object实际上通过类型判断,返回、修改不同的成员变量。

以下是SwiftyJSON的部分源码:

    fileprivate var rawArray: [Any] = []
    fileprivate var rawDictionary: [String: Any] = [:]
    fileprivate var rawString: String = ""
    fileprivate var rawNumber: NSNumber = 0
    fileprivate var rawNull: NSNull = NSNull()
    fileprivate var rawBool: Bool = false
       /// Object in JSON
    public var object: Any {
        get {
            switch type {
            case .array:      return rawArray
            case .dictionary: return rawDictionary
            case .string:     return rawString
            case .number:     return rawNumber
            case .bool:       return rawBool
            default:          return rawNull
            }
        }
        set {
            //...    
        }
    }

最终可行方案

初始化结构体,并转为JSON,
使用KeyPath,作为字段的唯一索引。格式如:"user.name"、"user.groups.0"。

并且,我为KeyPath方案写了个工具函数,1.可获取JSON所有字段的KeyPath,2. 可通过JSON[KeyPath]的方式修改字段值。

这样,就完全攻克读、写的技术难点了

  1. 遍历JSON中的字段值,判断类型显示相应UI;【其实就是遍历KeyPath】
  2. 能通过UI控件修改原JSON相应字段;[其实就是把JSON和KeyPath传给UI]

源码

https://gitee.com/tamiapp/swift-ui-struct-to-view

posted @ 2021-05-17 13:29  逆行  阅读(241)  评论(0编辑  收藏  举报