日時を表すDateのオブジェクトを直接アーカイブ/アンアーカイブする
前回のプロパティリストとは異なり、アーカイブ/アンアーカイブできるのは配列や辞書だけではありません。試しに日時を表すDateクラスのオブジェクトを直接アーカイブして保存し、また読み込んでアンアーカイブして元に戻してみましょう。
上の例とほとんど同じ構成のプログラムですが、今度は保存するのはDateオブジェクトです。Swiftでは、厳密にはDateはクラスではなく構造体ですが、SwiftからNSDateを使うためのブリッジとなっています。そのため、NSKeyedArchiverのようなObjective-CのAPIで利用する際には、NSDateクラスと同様に扱えるのです。
このプログラムを動かすと、2回目以降は、その前に保存したDateオブジェクトをアンアーカイブします。プレイグラウンドのデバッグ機能によって、その日時をdescriptionメソッドによって文字列に変換したものを表示して確かめることができます。
言うまでもないことですが、アーカイブして保存したオブジェクトは、アンアーカイブすることで完全に元に戻るので、データだけでなく、そのメソッドも利用できるのです。
プログラムを最初に起動した日時と最後に起動した日時を1つの辞書として保存
このあとで作るサンプルプログラムへの伏線として、もう少しだけ複雑なデータを扱ってみましょう。プログラムを最初に起動した日時と最後に起動した日時を1つの辞書に記録し、その辞書をアーカイブして保存します。その後、ファイルから読み込んでアンアーカイブした辞書の一部を変更してから、再びアーカイブ保存するというものです。まだ、ビューコントローラーは使わずに、デバッグ機能で動作を確かめられるようなプログラムです。
プログラムを最初に起動した際には、その日時を「first」と「last」という2つのキーで辞書に記録してファイルに保存します。2回目以降は、そのファイルを読み込んでアンアーカイブしています。その後で、現在の日時を「last」のキーで辞書に上書きし、再びアーカイブしてファイルに保存します。そのため、起動するたびに「最後に起動した日時」の方だけが更新されていきます。
一方、「first」のキーで記録した方のデータは、プログラムを変更しない限り、最初に記録したものをずっと維持します。
DateComponentsFormatterを導入して過去から現在までの経過時間を見やすく
上で示した最初に起動した日時と最後に起動した日時を記録するプログラムに手を加えていきます。日時の表示の、年、月、日、曜日、時、分、秒というかたちは絶対的なもので正確ではあるものの、ぱっと見ただけではそれがいつなのか実感しにくいものです。ましてや、その日時から今現在まで、どれくらいが経過しているのかは、指折り計算しないとわからないこともあります。
そこで、プログラムによって、過去の日時から現在までの経過時間を計算して表示できるようにしてみましょう。そのためには、DateComponentsFormatterクラスが使えます。このクラスは文字どおり、日時を表すコンポーネント(月、日、時間、分、秒など)をフォーマットして文字列に変換するためのものです。さらに、2つの日時の差分を計算し、それを文字列として表現する機能も含まれているのです。
その際の日時の差分の表現も、いろいろとカスタマイズが可能です。ここでは、常に2つの単位の組み合わせで表示するようにしてみます。つまり、分単位で表せる範囲では「何分何秒」で表示し、時間単位になれば「何時間何分」、日単位では「何日何時間」、そして月単位になれば「何ヶ月何日」のように表現するということです。人間の感覚としては、期間が長くなって日単位で表示しなければならない場合には、分や秒の数字は、ほとんど関心がなくなることが多いからです。
プログラムの構造や、基本的な機能は上の例のものと同じです。そこにDateComponentsFormatterクラスのオブジェクトを使って、記録した日時から現在までの差分を文字列で表現するための機能を加えているだけです。
DateComponentsFormatterクラスのオブジェクのallowedUnitsプロパティによって表示に使う単位を月、日、時、分、秒に限定し、maximumUnitCountによって、表示する単位の数を2つに制限しています。
このプログラムでは、表示はまだプレイグラウンドのデバッグ機能に頼っていますが、2つの単位を組み合わせた日時の差分の表示が実現できていることが確認できます。
初回起動から最後に起動したときまでの経過時間を表示するアプリ
今回の最後に、ここまでの成果をアプリ風のプログラムにまとめておきましょう。上の例で示した日時の差分を表現した文字列をラベルとしてビューの上に配置するものです。使っている機能や手法は、これまでに取り上げてきたものの組み合わせばかりなので、ざっと簡単な説明にとどめましょう。
まずビューコントローラーのクラスを定義して、その中に4つのラベルを用意します。
4つのラベルには、順に「前回の起動からの経過時間」という固定の文字列、実際に前回の起動からの経過時間、「最初の起動からの経過時間」という固定の文字列、実際に最初の起動からの経過時間をそれぞれ表示します。これらのラベルにはviewDidLoadメソッドの中で必要な初期化処理を施しておきます。
このプログラムでは、基本的にviewDidLoadの中に、すべての機能を実装しているようなものです。上の例で示した日時オブジェクトの保存と再生、日時の差分の計算と文字列化の処理も、すべてviewDidLoadの中に書いています。そしてその結果を2番目と4番目のラベルのテキストとして代入します。
最後に、4つのラベルをビューコントローラーのビューに張り付けるところまでがviewDidLoadの仕事です。あとは、ラベルのレイアウトのためにviewWillLayoutSubviewsメソッドを実装し、プログラムの末尾で、上で定義したビューコントローラーのオブジェクトを作成し、それをプレイグラウンドのライブビューに張り付けています。
このプログラムは、時間を置いて何回か起動してみて、日時の差分の表示を確認してください。差分が1分未満のときは秒の単位だけ、それ以外の場合は適切な2つの単位を使って差分を表現できることが確認できるはずです。
次回の予定
NSKeyedArchiverとNSKeyedUnarchiverを組み合わせて使えば、多種多様なオブジェクトを冷凍保存し、後から展開して、通常どおりに利用できることがわかりました。このような機能の応用例は、ほとんど無限に考えられるでしょう。実用的なiOSアプリの開発には、不可欠とも言えるツールになるはずです。このところ地味な内容が続いたので、次回は視覚に訴えるような効果を発揮する機能を取り上げる予定です。