2個關鍵點:一個是讀取、一個是寫入。
顯示界面:讀取結構體的字段名,字段類型,即可判斷、顯示相應的UI控件;
用戶寫入數據:需要知道每個UI控件和哪個字段進行綁定,另外,對字段需要有寫的權限。
嘗試Mirror【失敗】
第一個嘗試的方案是運用反射技術,在Swift中,使用的是Mirror。Mirror可以讀取結構體的屬性,但不能修改結構體。
嘗試Dictionary【失敗】
第二個嘗試的方案是使用字典Dictionary
具體步驟是:
- 初始化結構體
- 通過JSONSerialization序列化成字典
- 遍歷字典顯示界面。。。[卡住]
單單是在讀取這裏,就遇到一個棘手的問題。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個好處:
- 對於讀取,因爲我們可以不必在乎是數組類型,管你是[Location]還是[User],我知道你是個數組就行。這樣可以寫出通用的遍歷代碼。
- 對於寫入,我們可以很容易的通過拼接的方式來修改嵌套字段。
站在巨人的肩膀
在網上查閱資料,發現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]的方式修改字段值。
這樣,就完全攻克讀、寫的技術難點了
- 遍歷JSON中的字段值,判斷類型顯示相應UI;【其實就是遍歷KeyPath】
- 能通過UI控件修改原JSON相應字段;[其實就是把JSON和KeyPath傳給UI]