はじめに
MagicalRecordとmogenerotorを利用したSwiftのToDoリストアプリ(プロジェクト名はToDoList)を説明していきます。MagicalRecordとはCoreDataを楽に使えるライブラリであり、mogeneratorとはCoreDataエンティティのクラスをつくるツールです。この2つを利用することで簡単にCoreDataを扱うことができます。
以下の図は実際に作成したものです。
TableViewやStoryboardでの画面遷移、AutoLayoutなどの詳細な説明は省略します。詳細などは以下のチュートリアルを参考にしてもらえれば幸いです。
準備
準備として、Cocoapodsとmogeneratorのインストールが必要です。
Cocoapodsは以下のコマンドを実行することでインストールされます。
gem install cocoapods
mogeneratorのインストールの方法としては以下のコマンドを実行する
brew install mogenerator
もしくは以下のURLから直接ダウンロードしてくる方法があります。
https://rentzsch.github.io/mogenerator/
執筆時点ではCocoapodsのバージョンは0.39.0、mogeneratorのバージョンが1.28になっています。
MagicalRecordのインストールとセットアップ
MagicalRecordのインストールはPodfileに以下を書きます。
pod 'MagicalRecord'
以下、コマンドを実行することによりMagicalRecordがインストールされます。
pod install
MagicalRecordはObjective-Cによって書かれているライブラリのためヘッダーファイルを作成し、そこに記述する必要があります。
#import "MagicalRecord.h"
Objective-C Bridging-Headerに自身で作成したヘッダーファイルを記述するのを忘れないよう気をつけてください。
これでインストールは完了です。
次はセットアップです。セットアップは非常に簡単で、AppDelegate.swiftに下記のコードを1行追加するだけで完了です。
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
MagicalRecord.setupCoreDataStackWithAutoMigratingSqliteStoreNamed("ToDoList.sqlite")
return true
}
MagicalRecordを使わずにCoreDataのセットアップを行う場合、管理オブジェクトであるNSManagedObjectContextや、永続性を保証するストアコーディネータと呼ばれるオブジェクトなどを利用し複数行にわたってセットアップを行わなければなりません。そのセットアップがたった1行で書けるという意味でMagicalRecordがいかに便利なライブラリであるかが分かります。
ToDoListのモデルを作成
ToDoDataというCoreDataエンティティを作成します。title(タイトル)とtimestamp(時間)という属性をToDoDataエンティティが持つ設計になっています。
右側のClass名を記述するのを忘れないように注意してください。Class名がないと次のmogeneratorを実行する際にエラーとなってしまいます。
mogeneratorの利用
冒頭でも述べたように、mogeneratorはCoreDataエンティティクラスを自動生成してくれるツールです。新しいバージョンになってより利用しやすい形になっています。.xcdatamodeledがあるフォルダで以下のコマンドをたたくことによってカスタムクラスが生成されます。
mogenerator --v2 --model ToDoList.xcdatamodeld --Swift
生成に成功すると以下のような表示になります。
1 machine files and 1 human files generated.
生成されるファイルにはmachine fileとhuman fileがあります。この2つのファイルをドラッグ&ドロップでXcodeに結びつけます。
これだけでToDoDataエンティティを扱うことができるようになります。次の章では実際にToDoDateエンティティを扱うクラスを作成していきます。
モデルクラスを扱うクラスの作成
データの挿入、削除、取り出し、保存といった機能を一つのクラスにまとめて定義してあげます。
モデルクラスを扱うには、まずはじめに管理オブジェクトであるコンテキストの指定をしてあげる必要があります。
func context() -> NSManagedObjectContext{
return NSManagedObjectContext.MR_defaultContext()
}
上記のようにして取得できるコンテキストは、アプリケーション起動直後(AppDelegate.swift)に最初にセットアップしたコンテキストになります。
データの挿入には、MR_createEntity()を利用します。これによってtitleとtimeStampが空のデータが1つ生成されます。そして、引数として、受け取ったNSStringとNSDateを挿入し、コンテキストを保存することでデータの挿入が完了します。MR_saveToPersistentStoreAndWait()はMagicalRecordの公式サイトでデータを保存するときに推奨しているため、利用しています。
func insertData(title : NSString , timestamp : NSDate){
updateData(ToDoData.MR_createEntity()!, title: title, timestamp: timestamp)
}
func updateData(data : ToDoData , title : NSString , timestamp : NSDate){
data.title = title as String
data.timeStamp = timestamp
context().MR_saveToPersistentStoreAndWait()
}
データの消去にはMR_deleteEntitiy()を使用します。データを一つ受け取りそれを削除します。
func deleteData(data : ToDoData){
data.MR_deleteEntity()
}
データの取り出しにはMR_findAll()を利用します。
作成したToDodataエンティティの全てのデータをNSArray形式で返ってきます、
func MakeData() -> NSArray{
return ToDoData.MR_findAll()!
}
データの保存は、データの挿入の時と同様、MR_saveToPersistentStoreAndWait()を利用します。
func save(){
context().MR_saveToPersistentStoreAndWait()
}
最終的にモデルクラスを扱うクラスのコードは以下のようになります。
class ModelManager{
func context() -> NSManagedObjectContext{
return NSManagedObjectContext.MR_defaultContext()
}
func insertData(title : NSString , timestamp : NSDate){
updateData(ToDoData.MR_createEntity()!, title: title, timestamp: timestamp)
}
func updateData(data : ToDoData , title : NSString , timestamp : NSDate){
data.title = title as String
data.timeStamp = timestamp
context().MR_saveToPersistentStoreAndWait()
}
func deleteData(data : ToDoData){
data.MR_deleteEntity()
}
func MakeData() -> NSArray{
return ToDoData.MR_findAll()!
}
func save(){
context().MR_saveToPersistentStoreAndWait()
}
}
この作成したクラスを使い、後は実際に利用したいViewControllerで利用するだけです。
##データの取り出し
データの取り出しはメイン画面でviewWillAppearが呼ばれるたびに行います。viewWillAppearとは画面が呼ばれ、表示される直前に毎回呼ばれるメソッドです。
つまり、画面が呼ばれるたびに、ToDoDataエンティティの中にある全てのデータをdataという配列に入れるという操作を行います。
以下それを記したコードになります。
var data = [] as! [ToDoData]
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
reloadData()
}
func reloadData(){
data = ToDoData.MR_findAll() as! [ToDoData]
tableView.reloadData()
}
##データの表示
データの表示は、セルのメインテキストにタスクの内容(title)、サブのテキストに時間(timeStamp)が表示されるようにしています。timeStampはDate型であり、そのままテキストとして表示することが出来ないためString型にフォーマットして表示しています。
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: "Cell")
cell.textLabel?.text = data[indexPath.row].title
cell.detailTextLabel?.text = stringFromDate(data[indexPath.row].timeStamp!, format: "yyyy年MM月dd日 HH時mm分")
return cell
}
func stringFromDate(date: NSDate, format: String) -> String {
let formatter: NSDateFormatter = NSDateFormatter()
formatter.dateFormat = format
return formatter.stringFromDate(date)
}
##データの挿入(新しいタスクの追加)
データの挿入は遷移先の画面で行うため、はじめに画面を遷移させます。
遷移させるための方法はいかに記します。今回は特に遷移先にデータを送るといった操作は行いません。
@IBAction func addingButton(sender: AnyObject) {
self.performSegueWithIdentifier("PushData", sender: nil)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "PushData"){
}
}
遷移先の画面では実際にデータの挿入を行います。タスクであるtitleは下図の画面に設定してあるtextFieldから、時刻であるtimeStampは完了ボタンが押されたときの現在時刻をデータとして挿入します。最後にpopViewControllerAnimatedを配置することでデータの挿入後、遷移前の画面に戻るようにしています。
以下、遷移先の画面のコードと実際のStoryBoard上での画面になります。
class NextViewController: UIViewController {
@IBOutlet weak var textField: UITextField!
@IBAction func insertNewObject(sender: AnyObject) {
let model = ModelManager()
let date = NSDate()
model.insertData(textField.text!, timestamp: date)
self.navigationController?.popViewControllerAnimated( true )
}
}
##データの削除(タスクの編集)
データの削除を行うときは、エンティティとして保存されているモデルのデータとTableViewに表示されているデータの2つを削除しなければなりません。以下コードです。Deleteのボタンが押されたとき(editingStyleがDelete状態のとき)に上記の順番でデータの削除を行っています。
TableViewに表示されているデータを先に消去してしまうと、インデックスが1行ずれてしまい、エラーの原因になるので気をつけてください。
override func setEditing(editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
self.tableView.setEditing(editing, animated: animated)
}
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
if(editingStyle == UITableViewCellEditingStyle.Delete){
let model = ModelManager()
model.deleteData(data[indexPath.row])
model.save()
data.removeAtIndex(indexPath.row)
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Fade)
}
}
##終わりに
簡単な説明ではありましたがMagicalRecordとmogeneratorを使ったToDoリストアプリの作成を行いました。
まだまだ未熟者ですので、ご不明な点、ご指摘などありましたらどうぞよろしくお願いします。
今回のソースコードはGitHubにあげときました。ぜひ参考にしていただければと思います。