NSKeyedArchiverとNSKeyedUnarchiverを使ったクラスの保存

日本語であまり記事を見かけなかったので自分のメモの為にも。

[ここで作ったテストアプリのRepo](https://github.com/Iorickgit/archiveTest)

#UserDefaultの特性

- 決まった型のみ保持される
    - NSArray
    - NSDictionary
    - NSString
    - NSNumber
    - NSDate
    - NSData
    
つまりこれに則した型でなければ、保存されない。つまりクラスなどはUserDefaultsに保存されない為、変換が必要になる。

今回はクラスを保存したいからNSDataに変換してUserDefaultsに収めることにしよう。

そこでNSKeyedArchiverとNSKeyedUnarchiverを使ってみようという根端です。

#DecoderとCoder
```swift:class.swift
import UIKit

class userList{
    var name: String!
    var age: Int!
    
    //initialize class
    init(name: String, age: Int){
        self.name = name
        self.age = age
    }
}
```

こんなクラスがあった時に、まずこのクラスをNSObjectとNSCodingから継承するようにしないといけないらしい。

そんでもって、CoderとDecoderメソッドを実装する

```swift:class.swift
class userList: NSObject, NSCoding{
    var name: String!
    var age: Int!
    
    //initialize class
    init(name: String, age: Int){
        self.name = name
        self.age = age
    }
    
    //decoder initializing
    required init(coder decoder: NSCoder) {
        self.name = decoder.decodeObject(forKey: "name") as? String ?? ""
        //self.age = decoder.decodeInteger(forKey: "age")
        self.age = decoder.decodeObject(forKey: "age") as? integer
    }
    
    //encode method
    func encode(with coder: NSCoder) {
        coder.encode(name, forKey: "name")
        coder.encode(age, forKey: "age")
    }
}
```

ここまでで一応下準備は完了する。Class内のプロパティを全てNSData型に落とし込める状態。（もしかしてここでもうすでに落とし込まれてる？）

#NSKeyedArchiver NSKeyedUnArchiver

じゃあ、この作ったクラスを実際に保存したい時どうするか。

```swift:ViewController.swift
import UIKit

class ViewController: UIViewController{
    
    var userArray: [userList] = []
    var archiver = dataArchiver()
    let userDefaults: UserDefaults = UserDefaults.standard
    
    override func viewDidLoad() {
        super.viewDidLoad()
        userArray.append(userList(name: "John", age: 34))
        userArray.append(userList(name: "Noel", age: 23))
        //ここでarchivedArrayに今アーカイブしたデータを入れる
        let archivedArray = NSKeyedArchiver.archivedData(withRootObject: userArray)
        
        //UserDefaultsに初期値として入れる。
        savedData().userDefaults.register(defaults: ["savedList": archivedArray])
        //ここで初期値を入れたuserdefaultsからデータを取り出してみる。
        userArray = archiver.unArchiveData(file: userDefaults.data(forKey: "savedList")!) as! [userList]
        print("The users name is \(userArray[0].name!) and he is \(userArray[0].age!) years old")
        //The users name is John and he is 34 years old
        print(userArray)
        //[<DecoderCoderTest.userList: 0x60400046f040>, <DecoderCoderTest.userList: 0x600000463800>]
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

```

こうすると欲しい値が返される。
あと、このコードClass.swift内にArchiver, Unarchiverのメソッドを書いてて。

```swift:Class.swift
class dataArchiver{
    func archiveData(file: Any) -> Any{
        let archivedArray = NSKeyedArchiver.archivedData(withRootObject: file)
        return archivedArray
    }
    
    func unArchiveData(file: Data) -> Any{
        let unArchivedArray = NSKeyedUnarchiver.unarchiveObject(with: file)
        return unArchivedArray!
    }
```

ViewControllerの頭でインスタンス化して、こんな感じで納めたってわけですねー。
個人的にこれの方が楽かなと思って。わかんないけど。

Gitの方に全部乗っけてるんで、このRepoみていただいた早いかと思います。

#最後に

これ[UIImage]なんかはもっと簡単にできてCoderとDecoderのメソッドが必要なかったりで。

複数画像を保持したい時に便利かなと。Archiveして、Userdefaultにぶち込んで、使いたい時にUserDefaultsから引っ張り出して、Unarchiveして、はい元どおりって感じだった気がします。

#参考

["Swift 3 saving and retrieving custom object from userDefaults" via stackoverflow](https://stackoverflow.com/questions/37980432/swift-3-saving-and-retrieving-custom-object-from-userdefaults)