Learn Swift / サンプルプログラム Memo Pad 6
このコーナーでは、Swift による、Mac OS X アプリケーションの作成方法を、説明しています。
掲載開始日、2016年02月13日
最終更新日、2015年02月14日
home 目次 前へ 次へ mail
ファイル入出力
現在の Memo Pad は、終了すると、すべてのメモが消えてしまいます。ここでは、Memo Pad が終了するときに、メモをデータ化して、ハードディスクへ保存し、そして起動するときには、そのデータを読み込み、メモを復元するようにします。このように、データをハードディスクに保存 (書き込み) したり、ハードディスクからデータを読み込む技術のことを「ファイル入出力」といいます。
エンコーダーとデコーダー
Memo Pad のデータは文字列です。このような場合、データを文字列 (テキスト) のまま保存する方法と、シリアライズ (serialize、バイト列化) して保存する二通りの方法があります。シリアライズ (バイト列化) とは、データを2進数に変換することです。今回は、本格的に、シリアライズして保存する方法を選びます。
データをシリアライズするには、エンコーダーとデコーダーと呼ばれるものが必要です。エンコーダー (encoder) は、データをシリアライズする機能です。デコーダー (decoder) は、シリアライズされているデータを元の形に復元する機能です。
ここでは、Model.swift にエンコーダーとデコーダーを実装します。Model.swift をエディタエリアに表示させて、次のリストのコードを追加してください。太字部分が追加するコードです。冒頭のコメント部分は省略しています。
import Cocoa @objc(Model) class Model: NSObject, NSCoding { var note:AnyObject = String() override init() { super.init() } required init?(coder aDecoder: NSCoder) { note = aDecoder.decodeObjectForKey("note")! } func encodeWithCoder(aCoder: NSCoder) { aCoder.encodeObject(note, forKey: "note") } }
- class Model: NSObject, NSCoding
- Model クラスに、NSCoding プロトコルを採用しています。これにより Model クラスのプロパティのデータを、シリアライズ (serialize、バイト列化) するコードと、シリアライズされたデータを元に戻す (復元する) コードを記述できるようになります。逆に言えば、NSCoding プロトコルの採用によってこのクラスには、シリアライズに関すコードを必ず記述しなければいけないことになります。もし、このクラスに、シリアライズに関するコードを記述しなければ、コンパイルエラーになります。なお、データをシリアライズ (バイト列化) することをエンコード (encode) といい、シリアライズされたデータ(バイト列)を元のデータに戻すことをデコード (decode) と言います。ここでは、Model クラスに note プロパティのデータをシリアライズするためのエンコーダーメソッドと、シリアライズされたデータを復元するためのデコーダーメソッドを記述します。
- override init() { super.init() }
- 通常、すべてのプロパティに初期値がある場合は、init() メソッドは省略することができます。しかしここでは、NSCording で指定されている デコーダーの init?() メソッドも記述しなければいけません。その関係で、この init() メソッドを省略することはできません。
- required init?(coder aDecoder: NSCoder) {
- デコーダーメソッドです。2014年の Swift 登場以来、さまざまなデコーダーが使われてきたと思います。しかし、2016年2月現在、Xcode 7.2.1 でコンパイラを通るのは、このデコーダーメソッドだけではないかと思います。このメソッドは、Apple のクラスリファレンスに掲載されています。
- note = aDecoder.decodeObjectForKey("note")!
- デコーダーの実装です。行末に「!」を記述してアンラップ (非オプショナル化) している以外には、変わったところはありません。
- func encodeWithCoder(aCoder: NSCoder) {
- エンコーダーです。特に変わったところはないと思います。
- aCoder.encodeObject(note, forKey: "note")
- エンコーダーの実装です。特に変わったところはないと思います。
エンコーダーとデコーダーの中で、実際にどのような処理がされているかは、Apple によってブラックボックス化されています。これで、Model.swift に、note プロティのデータをシリアライズするコードと、note プロパティのデータを復元するコードの記述が完了しました。しかしこれでハードディスクへのデータの保存と、ハードディスクからのデータの読み込みができるようになったわけではありません。エンコーダーは、データを保存する前に、データをシリアライズするためのコードです。そして、デコーダはハードディスクから読み込んだデータを、元のデータの形に復元させるためのコードです。データの保存と読み込みは、また別途記述しなければなりません。
NSKeyedArchiver と NSKeyedUnarchiver
ハードディスクへのデータの保存 (書き込み) には、NSKeyedArchiver クラスのメソッドを使います。ハーディディスクからのデータの読み込みには、NSKeyedUnarchiver クラスのメソッドを使います。ところでこのような「ファイル入出力」のためのコードはどこに記述すれば良いのでしょうか。MVC によると、ファイル入出力」のためのコードは、コントローラクラスに記述するように指針が出ています。しかし、ArrayController には、コードを記述できる場所がありません。そこで今回は、ViewContoroller に「ファイル入出力」のためのコードを記述することにします。コントローラは一つである必要ありません。一つのアプリケーションに複数のコントローラが存在することは全く問題ありません。
ナビゲータエリアで「ViewController.swift」を選択してください。エディタエリアに、ViewController.swift のコードが表示されます。そして、ViewController.swift に次のリストのコードを記述してください。太字部分が追加するコードです。冒頭のコメント部分は省略しています。・・・はコードが省略されていることを意味しています。
import Cocoa class ViewController: NSViewController { dynamic var notes:[AnyObject] = [] var filePath:String = String() func save() { NSKeyedArchiver.archiveRootObject(notes, toFile: filePath) } func read() { if let file = NSKeyedUnarchiver.unarchiveObjectWithFile(filePath) { notes = file as! [AnyObject] } } override func viewWillAppear() { super.viewWillAppear() filePath = NSHomeDirectory() + "/Library/Application Support/viva Cocoa" do { try NSFileManager.defaultManager().createDirectoryAtPath( filePath, withIntermediateDirectories: true, attributes: nil) } catch { print("Error") } filePath += "/Memo Pad.data" read() } override func viewWillDisappear() { super.viewWillDisappear() save() } override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } override var representedObject: AnyObject? { didSet { // Update the view, if already loaded. } } }
- dynamic var notes:[AnyObject] = []
- AnyObject 型の空の (実際のデータのない) 配列として、notes 変数を定義しています。冒頭の dynamic は、Cocoa バインディングで使われるプロパティに、付けることになっているキーワードです。今まで付けていなかったのは、@objc を代わりに使っていたからです。
- var filePath:String = String()
- データを保存する場所までのパス (path、道筋) を保持するための、String 型の変数です。
- func save() {
- 保存のためのユーザー定義メソッドです。
- NSKeyedArchiver.archiveRootObject(notes, toFile: filePath)
- archiveRootObject は、NSKeyedArchiver クラスに定義されているクラスメソッドです。第一引数のデータを、第二引数の場所へ保存 (書き込み) します。
- func read() {
- 読み込みのためのユーザー定義メソッドです。
- if let file = NSKeyedUnarchiver.unarchiveObjectWithFile(filePath) {
- unarchiveObjectWithFile は、NSKeyedUnarchiver のクラスメソッドです。引数で指定したパスのファイル (データ) を読み込みます。if 文で受けているのは、データがまだない可能性もあるからです。
- notes = file as! [AnyObject]
- file 引数のデータを、AnyObject 型の配列として割り当てて、notes 変数に代入しています。
- override func viewWillAppear() {
- ViewControler のスーパークラスである NSViewController で定義されているメソッドです。このメソッドはビュー (つまりウィンドウ) が、表示される直前に呼び出されます。このタイミングでハードディスクからファイル (データ) を読み込むことにします。
- super.viewWillAppear()
- まず最初に、スーパークラスの viewWillAppear メソッドを呼び出しています。
- filePath = NSHomeDirectory() + "/Library/Application Support/viva Cocoa"
- NSHomeDirectory() はお使いの Mac の、ホームディレクトリまでのパスを返します。そのパスに /Library/Application Support/viva Cocoa というパスを継ぎ足して、fiePath に代入しています。
- do { try NSFileManager.defaultManager().createDirectoryAtPath( filePath, withIntermediateDirectories: true, attributes: nil) }
- NSFileManager は、ファイルの管理をするクラスです。defaultManager は、FileManager の唯一のインスタンスです。createDeirectoryAtPath は、引数で指定したパスにディレクトリを作ります。このコードが do try のエラー構文の中に入っているのは、ディレクトリの作成が必ず成功するとは限らないからです。
- } catch { print("Error") }
- エラーが起こった時の作業を catch 節に記述しています。ここではとりあえず、デバッグエリアに「Error」と表示されるようにしました。
- filePath += "/Memo Pad.data"
- ディレクトリの作成に成功した後に、パスにデータのファイル名を追加しています。
- read()
- read メソッドを呼び出しています。
- override func viewWillDisappear() {
- ViewController のスーパークラスである、NSViewController に定義されているメソッドです。このメソッドはビュー (ウィンドウ) が消える直前、つまりウィンドウが閉じられる直前に呼び出されます。
- super.viewWillDisappear()
- まずスーパークラスの viewWilDisapear を呼び出しています。
- save()
- save メソッドを呼び出しています。
Array Controller の設定
ナビゲータエリアで Main.storyboard を選択して、ドキュメントアウトラインで ArrayController を選択します。
そして、バインディングインスペクタで Controller Content グループの Content Array を開いてください。次に、Bind to にチェックを入れて ViewController を選びます。Controller Key は空白のままにしておいてください。そして、Model Key Path に「notes」と入力してエンターキーを押します。エンターキーを押すのは入力を確定させるためです。
完成
これで、Memo Pad は完成しました。実行して色々と試してみてください。ウィンドウを閉じると自動的にメモが保存されます。そして再び Memo Pad を実行すると、前回のメモが読み込まれます。
お疲れ様です。
これで、Memo Pad を終了します。次のページで何をするかは未定です。
無断転載禁止、リンクフリー Copyright 2016. vivacocoa.jp All right reserved.