利用 PropertyListDecoder 解析 plist

開發 iOS App 時,我們有時會建立 plist 檔儲存 App 需要存取的固定資料,比方開發訂飲料 App 時,將飲料的 menu 存在 plist 檔,然後再利用 Swift 程式讀取,顯示在表格或 picker 上。

不過要如何將 plist 檔的內容變成我們自訂的型別呢 ? 透過 PropertyListDecoder 的幫忙,只要幾行程式就能搞定,接下來就讓我們親手實驗看看吧。

建立儲存可不可熟成紅茶飲料 menu 的 plist

File > New > File。

選擇 iOS Resources 下的 Property List。

將 plist 取名為 Drinks。

在 plist 裡輸入飲料的 menu

預設的 plist 如下圖所示,第一層 Root 的型別是 Dictionary。

plist 內容的第一層可以是 Array 或 Dictionary,由於我們想用 array 儲存 menu 裡的各種飲料,因此我們將 Root 的 Type 改成 Array。

接著我們點選 Root 的 +,新增 array 裡的第一筆飲料資料。

我們希望飲料的資料包含名字和價錢,因此我們將它的 Type 從 String 改成可以容納多個欄位的 Dictionary。

點選 Item 0 旁的三角形,讓它變成向下的三角形再點選 +,因為這樣才代表我們要新增 Dictionary 的欄位,而不是新增 array 裡的資料

在 Item 0 輸入熟成紅茶的資訊。

有了第一個飲料,第二個第三個就簡單多了,因為可以使用我們最愛的複製貼上。請先點選 Item 0 旁的三角形,讓它變成向右的三角形。

然後按下 cmd +c & cmd + v 產生 Item 1,此時 Item 1 也會是 Dictionary,而且已經包含欄位 name & price,我們只要修改內容即可。

以同樣的方法複製貼上,輸入 Item 2 的飲料。

可不可熟成紅茶還有很多飲料,不過彼得潘累了,我們就先用三種飲料測試吧。

定義 plist 內容對應的 Decodable 型別 Drink

為了將 plist 的內容變成自訂的型別,我們必須定義符合 plist 內容格式且遵從 protocol Decodable 的型別。定義此型別的原理跟定義 JSON 對應的資料型別類似,差別只在 plist & JSON 有著不同的格式,plist 背後的格式是 XML。

plist 裡的 Dictionary 型別將被定義成對應的自訂型別,因此我們定義遵從 protocol Decodable 的 Drink 型別。

struct Drink: Decodable {

}

Dictionary 的 key 將成為自訂型別 property 的名字,property 的型別則由 Dictionary value 的 Type 決定。如下圖所示,Plist 裡資料的型別有七種,這七種都可以對應到常見的 Swift 基本型別。

因此我們在 Drink 裡宣告型別為 String & Int 的 property name & price。

struct Drink: Decodable {
let name: String
let price: Int
}

利用 PropertyListDecoder 析解 plist

let url = Bundle.main.url(forResource: "Drinks", withExtension: "plist")!

if let data = try? Data(contentsOf: url),
let drinks = try? PropertyListDecoder().decode([Drink].self, from: data) {
print(drinks)
}

說明

let url = Bundle.main.url(forResource: "Drinks", withExtension: "plist")!

取得 Drinks.plist 檔的 URL。

if let data = try? Data(contentsOf: url), 
let drinks = try? PropertyListDecoder().decode([Drink].self, from: data) {
print(drinks)
}

從 URL 讀取 Data 後,利用 PropertyListDecoder 的 decode function 將 Data 變成型別 [Drink] 的 array。decode function 的宣告如下,第一個參數 type 控制轉換後的資料型別,因此我們傳入 [Drink].self 將得到型別 [Drink] 的飲料清單。

func decode<T>(_ type: T.Type, from data: Data) throws -> T where T : Decodable

結果

--

--

彼得潘的 iOS App Neverland
彼得潘的 Swift iOS App 開發問題解答集

彼得潘的iOS App程式設計入門,文組生的iOS App程式設計入門講師,彼得潘的 Swift 程式設計入門,App程式設計入門作者,http://apppeterpan.strikingly.com