plistについて

社内の勉強会でINIファイルが出てきてわからなかったときに、Macだとplistみたいなものと教えていただきました。業務でplistを使ったことはありますが、どういうものなのかしっかりと理解できていなかったので、記事を作成することで理解を深めていきたいと思います。

# 目次
１．plistとは？
２．Macの表現形式(XML、バイナリ、JSON)
３．plistの歴史
４．Xcodeでのplistの作成の仕方・読み込み方
５．まとめ
６．おわりに
７．参考にしたサイト

## 1. plistとは？
plistはPropertyListの略で、設定を保存するためのファイルのことで、拡張子は「.plist」です。
プロパティリストは「key」「Type」「Value」の3つの項目から成り立っていて、XML形式で保存されています。
データ構造はNSArray型かNSDictionary型を使う必要があります。
ユーザーの設定をするのによく使われており、windowsにおける[レジストリ](https://ja.wikipedia.org/wiki/%E3%83%AC%E3%82%B8%E3%82%B9%E3%83%88%E3%83%AA)と使い方が似ています。

## 2．Macの表現形式(XML、バイナリ、JSON)
次に表現形式について説明します。
プロパティリストが表現するデータは抽象的なもので、ファイルフォーマットは固定されていません。
### ・XML
XML形式はMacOS Xの標準plistで、Appleが定義した文書構造の定義を持ち、当時流行りだった[XML](https://ja.wikipedia.org/wiki/Extensible_Markup_Language)で書かれています。
以下がXML形式のplistです。
1行目がXMLの宣言で、2行目のURLが文書構造の定義です。
配列は<array>タグでくくられた中に要素を記述します。
```swift
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>キー１</key>
    <string>値１</string>
    <key>キー2</key>
    <dict>
        <key>サブキー１</key>
        <string>値２</string>
    </dict>
    <key>キー3</key>
    <array>
        <string>配列メンバー1</stirng>
        <string>配列メンバー2</stirng>
        <string>配列メンバー3</stirng>
    </array>
</dict>
</plist>
```
### ・バイナリ
先ほど、XML形式の説明でMacOS Xの標準plistと書きましたが、macOS 10.0で登場したAppleが定義した文書構造を持つXML形式は処理が重いため、macOS 10.2で読み込みの早いバイナリフォーマットが登場しました。
「plutil」を使うことでplistのバイナリ変換を行うことができます。
```swift
///XML->バイナリ
plutil -convert binary1 test.Plist
///バイナリ->XML
plutil -convert xml1 test.Plist
```
### ・JSON
最後にJSON形式について説明します。
JSONはJavaScriptObjectNotationの略で、JavaScriptのオブジェクトの書き方を元にしたデータ定義方法のことです。
JSONは中括弧「{}」の中にキーと値をコロン「:」で区切って記述します。オブジェクトは中括弧で囲みますが、配列の場合は「[]」を使います。複数設定するときはカンマ「,」を使用します。
JSON形式のplistは以下のように書きます。
```swift
{"キー1" : "値１",
 "キー2" : {
    "サブキー１" : "値２"
    },
 "キー3" : [
    "配列メンバー1",
    "配列メンバー2",
    "配列メンバー3"
    ]
}
```

## 3. plistの歴史
plistについて調べていると紹介した３つ以外にもplistの表示形式があったため紹介します。

### ・NeXT
NeXT形式はNeXTSTEPというOSを作っていた会社が作成したplistで、最初に作られたplistです。
NeXTSTEPというOSはmacOSやiOSの基になっているOSで、NeXTをAppleが買収してXML形式のplistを作成しました。
plistの文法は以下のようになっています。
NeXT形式では数値や真偽値に対応する表現はありませんでした。
```
{
    "キー1" = "値1";
    "キー2" = {
        "サブキー1" = "値2";
        };
    "キー3" = (
        "配列メンバー1",
        "配列メンバー2",
        "配列メンバー3"
    );
}
```
### ・GNUstep
GNUstep形式はNeXTSTEPのフォーマットを採用したplistです。
NeXT形式のplistに加えてNSVauleとNSDateをサポートしています。
GNUstepはMacOSのフォーマットも読み書きすることができます。
GNUstepに付属するツール「plget」を使うことでplist内の階層化されたキーに対応する値が取得できます。
```
$ cat com.example.app.plist | plget key
$ cat com.example.app.plist | plget key | plget key
```

## 4. Xcodeでのplistの作成・書き込み・読み込みの仕方
### plist作成の仕方
まずXcodeでのplistの作成の仕方について説明します。
ここで作成したplistはXML形式で、大枠がNSArray型/NSDictionary型になります。
    １．File>New>File…を選択して新規作成画面を開きます。以下のような画面が出てきます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2871987/8d270dba-4d16-d3f7-1a06-c03e2b9fb6a1.png" width=50%>
    ２．下にスクロールするとResourceという項目があり、その名にProperty Listがあるので選択してNextを押します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2871987/b88687e7-6f76-e9cc-fe1c-3c8101264e7e.png" width=50%>
    ３．以下のような画面が出てくるので、plistの名前を設定してCreateを押します。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2871987/eaf3bea5-28c5-b59f-d19d-6591cd2ce080.png" width=50%>
    ４．plistを作成すると、作成しているプログラムの中に空のプロパティリストが作成されます。
開くと以下のようになっており、Rootの横に出てくる＋から設定を追加することができます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2871987/555edd24-04e8-e926-4a25-22f7241981b2.png" width=50%>

### plistの読み書きの仕方
これでXcodeでplistを作成することができたので、plistへ書き込み方と読み込み方を説明します。
Property Listに保存したいデータのモデルを、Codableに準拠する形で作成します。
今回は例として、名前と年齢の要素を持つ名簿をモデルにします。
```swift
struct listOfNames: Codable{
    var name: String
    var age: Int
}
```
```swift
import Foundation

class BookManager {
    // 扱うProperty ListのURL Path
    static private var plistURL: URL {
        let documents = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
        return documents.appendingPathComponent("listOfNames.plist")
    }
    ///plistへの書き込み
    static func write(ListOfNames: listOfNames) {
        let encoder = PropertyListEncoder()
        guard let data = try? encoder.encode(ListOfNames) else { return }
        if FileManager.default.fileExists(atPath: plistURL.path) {
            try? data.write(to: plistURL)
        } else {
            FileManager.default.createFile(atPath: plistURL.path, contents: data, attributes: nil)
        }
    }
    ///plistの読み込み
    static func load() -> listOfNames{
        let decoder = PropertyListDecoder()
        guard let data = try? Data.init(contentsOf: plistURL),
              let ListOfNames = try? decoder.decode(listOfNames.self, from: data) else {
              return listOfNames(name: "", age: 0)
        }
        return ListOfNames
    }
}
```
#### plistの書き込み方
plistへの書き込みにはPropertyListEncoderを使用します。
保存したいデータをエンコードしてきて、既にプロパティリストが存在する場合は上書きし、ない場合は新しく作成します。
```swift
static func write(ListOfNames: listOfNames) {
        let encoder = PropertyListEncoder()
        guard let data = try? encoder.encode(ListOfNames) else { return }
        if FileManager.default.fileExists(atPath: plistURL.path) {
            try? data.write(to: plistURL)
        } else {
            FileManager.default.createFile(atPath: plistURL.path, contents: data, attributes: nil)
        }
    }
```

#### plistの読み込み方
plistの読み込みにはPropertyListDecoderを使用します。
保存先のプロパティリストからデータを読み込んでデコードします。
```swift
 static func load() -> listOfNames{
        let decoder = PropertyListDecoder()
        guard let data = try? Data.init(contentsOf: plistURL),
              let ListOfNames = try? decoder.decode(listOfNames.self, from: data) else {
              return listOfNames(name: "", age: 0)
        }
        return ListOfNames
    }
```
## 5. まとめ

・plistは設定を保存するためのファイルで拡張子は「.plist」

・「key」「Type」「Value」の３つの項目から成り立つ

・XML/バイナリ/JSON形式があり、状況に応じて使い分ける

・plistの読み書きにはPropertyListEncoder/PropertyListDecoderを使用する


## 6. おわりに
plistは元々作成されている設定を確認するために使用することが多かったため、設定が書かれているファイルという印象だけでしたが、いろんな表示形式があったり、XML形式でも欠点があったりするなど記事を作成して理解を深めることができたので良かったです。


## 参考にしたサイト
https://qiita.com/Howasuto/items/29f1db794925052b4e6d
https://ja.wikipedia.org/wiki/%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3%E3%83%AA%E3%82%B9%E3%83%88
https://www.itmedia.co.jp/enterprise/articles/0705/14/news013.html
https://products.sint.co.jp/topsic/blog/json
https://tech.amefure.com/swift-property-list
https://qiita.com/yuchiro22/items/cf0034c140ed90a256e4