NSObjectを継承した独自クラスをNSUserDefaultsに保存する[Swift]
iPhoneアプリで手軽にローカルにデータを保存する際にNSUserDefaults
が重宝します。
ただ、通常の方法だと単純なデータ(文字列、Dictionaryなど)を格納することはできるのですが、NSObjectを継承して作ったような独自クラスの保存はできません。
NSUserDefaultsで扱えるようにするにはNSCoding
というプロトコルに応答することと、それをアーカイブして保存する2つの工程が必要です。
1. NSCodingを実装する
import Foundation import SwiftyJSON class PhotoModel: NSObject, NSCoding { var id : Int? var picture : Dictionary<String, AnyObject>? class func photosFromJSON(list : Array<JSON>) -> [PhotoModel] { var array = [PhotoModel]() for item in list { let photo = PhotoModel() photo.assignAttributes(item) array.append(photo) } return array } func assignAttributes(params : JSON) { self.id = params["id"].intValue self.picture = params["picture"].dictionaryObject } func encodeWithCoder(aCoder: NSCoder) { aCoder.encodeInteger(id!, forKey: "id") aCoder.encodeObject(picture, forKey: "picture") } required init(coder aDecoder: NSCoder) { id = aDecoder.decodeIntegerForKey("id") picture = aDecoder.decodeObjectForKey("picture") as? [String : AnyObject] super.init() } override init() { super.init() } }
これはAPIサーバから受け取ったJSONを処理してモデルを表現する簡単なクラスですが、encodeWithCoder(aCoder: NSCoder)
とinit(coder aDecoder: NSCoder)
の2つを実装すればいいわけです。
ただ、init(coder aDecoder: NSCoder)
の他にinit()
も残しておいたほうがPhotoModel()
のような単純なイニシャライズができるので書いておきましょう。
2. NSKeyedArchiverで保存、NSKeyedUnarchiverで取り出す
次にエンコード可能なオブジェクトをアーカイブしてNSData型にして格納します。ご覧のように格納、受け取るオブジェクト自体はArray型([PhotoModel]
)ですが、userDefaultsで扱う際にはNSData
なのでarrayForKey:
でなくobjectForKey
でOKです。
func userDefault() -> (NSUserDefaults) { return NSUserDefaults.standardUserDefaults() } func setDraftPhotos(photos : [PhotoModel]) { let archive = NSKeyedArchiver.archivedDataWithRootObject(photos) userDefault().setObject(archive, forKey: "draftPhotos") } func draftPhotos() -> ([PhotoModel]) { if let data = userDefault().objectForKey("draftPhotos") as? NSData { let unarchive = NSKeyedUnarchiver.unarchiveObjectWithData(data) return unarchive as! [PhotoModel] } else { return [PhotoModel]() } }
まとめ
これはあくまでクライアント型で2,3個のデータを保存したい際に便利な手法です。 もっと大規模に保存するならCoreDataの使用を検討しましょう。
また、モデル自体を保存するよりもid配列を保存して実行のたびにサーバからロードするほうがいい場合もあるのでサービスの要件と相談すべきです。