今回のエンジニアブログを担当する村田です。
本日は、Swiftではデータ永続化の記述はどうなるか
まとめてみました。
確認バージョン:Xcode 6 beta 4, beta 5
Objective-Cには、簡単なデータ永続化として
- NSKeyedArchiverを使ったオブジェクトのアーカイブ
- プロパティリストを使ったデータ保存
- NSUserDefaultsを使ったデータ保存
があります。
もちろんSwiftでも健在です。
「Core Data」や「SQLite」を使った方法もありますが、
お手軽なデータ永続化としては上記の方法ではないでしょうか。
それぞれ、Swiftではどのような記述になるでしょうか。
でもその前に...
パスの取得方法
プログラムから操作するパスとして、以下の3つがあります。
- /Documents
アプリがファイルを作成して保存できる領域 - /Library/Caches
アプリが一時的に使用する領域 - /tmp
一時ファイルの保存先に使用する領域
まず、Objective-Cの例です。
// /Documentsまでのパス取得方法 NSArray *paths1 = NSSearchPathForDirectoriesInDomains( NSDocumentDirectory, NSUserDomainMask, YES); NSString *documentsPath = [paths1 objectAtIndex:0]; // /Library/Cachesまでのパス取得方法 NSArray *paths2 = NSSearchPathForDirectoriesInDomains( NSCachesDirectory, NSUserDomainMask, YES); NSString *cachesPath = [paths2 objectAtIndex:0]; // /tmpまでのパス取得方法 NSString *tmpPath = NSTemporaryDirectory();
Swiftではこうなります。
// /Documentsまでのパス取得方法 let paths1 = NSSearchPathForDirectoriesInDomains( .DocumentDirectory, .UserDomainMask, true) let documentsPath = paths1[0] // /Library/Cachesまでのパス取得方法 let paths2 = NSSearchPathForDirectoriesInDomains( .CachesDirectory, .UserDomainMask, true) let cachesPath = paths2[0] // /tmpまでのパス取得方法 let tmp = NSTemporaryDirectory()
NSSearchPathForDirectoriesInDomains関数も健在です。
- 第一引数
enum型であるNSSearchPathDirectoryの値 - 第二引数
struct型であるNSSearchPathDomainMaskの値
上の記述は省略した記述になりますが、省略せず記述すると下のようになります。
// /Documentsまでのパス取得方法 let paths1 = NSSearchPathForDirectoriesInDomains( NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)
取得したパスにファイル名を追加したい場合は、StringのstringByAppendingPathComponent関数を使います。
// /Documentsまでのパス取得 let paths1 = NSSearchPathForDirectoriesInDomains( .DocumentDirectory, .UserDomainMask, true) // /Documentsまでのパスにファイル名"sample.dat"を付与 let path = paths1[0].stringByAppendingPathComponent("sample.dat")
以降のサンプルソースで、_pathという変数が出てきますが、
保存ファイルまでのパスが設定されていると思って下さい。
NSKeyedArchiverを使ったオブジェクトのアーカイブ
例として、Dictionary型の値をNSKeyedArchiverでアーカイブしてみます。
// 保存するデータ var user = [ "Name": "名古屋太郎", "Address": "名古屋市中区", "Tel": "052-xxx-xxxx", ] // NSKeyedArchiverクラスを使ってデータを保存する。 // 第一引数に保存するデータ、第二引数にファイルパスを渡します。 let success = NSKeyedArchiver.archiveRootObject(user, toFile: _path) if success { println("保存に成功") }
// NSKeyedUnarchiverクラスを使って保存したデータを読み込む。 let user = NSKeyedUnarchiver.unarchiveObjectWithFile(_path) as [String: String] for (key, value) in user { println("\(key):\(value)") }
Objective-Cの時と同じクラス、同じ関数が使用できます。
- アーカイブ時
NSKeyedArchiverクラスのarchiveRootObject - アンアーカイブ
NSKeyedUnarchiverクラスのunarchiveObjectWithFile
NSObjectクラスを継承し、NSCodingプロトコルを実装すれば自作したクラスもアーカイブ可能です。
class SampleUser: NSObject, NSCoding { var id: Int! var name : String! init(id: Int, name: String) { super.init() self.id = id self.name = name } // MARK: - Implements NSCoding protocol func encodeWithCoder(aCoder: NSCoder!) { // Int型のエンコード aCoder.encodeInteger(self.id, forKey: "ID") // String型のエンコード aCoder.encodeObject(self.name, forKey: "NAME") } init(coder aDecoder: NSCoder!) { super.init() // Int型のデコード self.id = aDecoder.decodeIntegerForKey("ID") // String型のデコード self.name = aDecoder.decodeObjectForKey("NAME") as String } }
// 保存するデータ var users = [ SampleUser(id: 1, name: "Aichi"), SampleUser(id: 2, name: "Gifu"), SampleUser(id: 3, name: "Mie"), ] // NSKeyedArchiverクラスを使ってデータを保存する。 let success = NSKeyedArchiver.archiveRootObject(users, toFile: _path) if success { println("保存に成功") }
// NSKeyedUnarchiverクラスを使って保存したデータを読み込む。 let users = NSKeyedUnarchiver.unarchiveObjectWithFile(path) as [SampleUser] for user in users { println("ID: \(user.id), NAME: \(user.name)") }
プロパティリストを使ったデータ保存
例として、NSDictionary型の値をプロパティリストで保存してみます。
// 保存するデータ var user: NSDictionary = [ "Name": "名古屋花子", "Address": "名古屋市中村区", "Tel": "052-yyy-yyyy", ] // 第一引数に保存データ、第二引数にファイルのパス let success = user.writeToFile(_path, atomically: true) if success { println("保存に成功") }
// NSDictionaryを保存したのでNSDictionaryクラスを使って読み込む。 var user = NSDictionary(contentsOfFile: _path) for (key, value) in user { println("\(key):\(value)") }
ポイントとして、NSDictionaryで宣言すること。
プロパティリストで保存する時に使用するwriteToFile関数は、NSArrayやNSDictionaryでないと持っていません。
NSUserDefaultsを使ったデータ保存
例として、Dictionary型の値をNSUserDefaultsへ保存してみます。
// 保存するデータ var user = [ "Name": "東京一郎", "Address": "東京都", "Tel": "03-xxxx-xxxx", ] // NSUserDefaultsクラスのインスタンスを取得 let defaults = NSUserDefaults.standardUserDefaults() // 保存するオブジェクトをキーと共に設定 defaults.setObject(user, forKey: "User") // ファイルに書き出す let success = defaults.synchronize() if success { println("保存に成功") }
// NSUserDefaultsクラスのインスタンスを取得 let defaults = NSUserDefaults.standardUserDefaults(); // キーを使って目的のオブジェクトを取得 let user = defaults.dictionaryForKey("User") for (key, value) in user { println("\(key):\(value)") }
まとめ
Objective-Cに慣れ親しんだ方には、見慣れたクラス、関数が登場するので、それほど迷う事なく扱えるのではと思います。
今回のサンプルは、Dictionaryを保存する例で記述しましたが、もちろんArrayも問題なく扱えます。
データの永続化として
- NSKeyedArchiverを使ったオブジェクトのアーカイブ
- プロパティリストを使ったデータ保存
- NSUserDefaultsを使ったデータ保存
を紹介しましたが、SQLiteも扱いたいですよね!
iOSのSQLiteと言えば、ラッパーライブラリのFMDBが有名ではないでしょうか。
次回は、SwiftでFMDBを使ってみたいと思います。