Edited at

RealmSwiftとUITableViewを使用してToDoアプリを作成

More than 3 years have passed since last update.

初めての投稿になります。

私、まだSwiftを勉強し始めて1か月半程の新米であり、初めて投稿をさせていただきます。

現在、Realmを勉強しています。

勉強している際に、Objectにデータを追加する方法や、削除の方法はサンプルが沢山あり、勉強するのは容易だったのですが、

TableViewでのデータの扱いなど、具体的なサンプルがあまり見つからず、結構ハマってしまいました・・・。

自分で勉強したことの復習の意味も込めて、RealmSwift + UITableView でToDoアプリを制作してみました。

ソースコードはコチラから参照・ダウンロードしていただけます。


Realmの概要

Realmって何?

どうやってインストールするの?

といった疑問は、公式サイトがすごくわかりやすいので、参照してみてください。

※ちなみに今回は、cocoa podsを使ってプロジェクトにRealmSwiftを追加しました。


環境

OSX EI Capitan 10.11.6

Xcode 7.3.1

RealmSwift 1.0.2


まずは画面を作成

スクリーンショット 2016-08-01 21.16.27.png


TableViewController: UITableViewController

・NavigationControllerをEditor → Embed In → NavigationController の手順で追加

・BarButtonItemを追加し、InputViewControllerにドラッグしsegueを作成(Show, segueのIdentifier → toInputVC)


TableViewController.swift

import UIKit

class TableViewController: UITableViewController {

override func viewDidLoad() {
super.viewDidLoad()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int)->Int {
return 1
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexpath: NSIndexPath)->UITableViewCell {

let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")

return cell
}
}



InputViewController: UIViewController

・UILabel

・UITextField → IBOutletをInputViewControllerに追加

・UIButton → IBActionをInputViewControllerに追加


InputViewController.swift

import UIKit

class InputViewController: UIViewController {

@IBOutlet weak var titleTextField: UITextField!

override func viewDidLoad() {
super.viewDidLoad()

titleTextField.frame.size.height = 30
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}

//Buttonが押された時の処理
@IBAction func addButtonTapped(sender: AnyObject) {

}
}



Realmを使用するためにクラス作成

Realmでデータを保存するためには、クラスが必要です。

今回は、Realmのクラスを記述するために、新たにファイルを追加しましたが、既存のファイルにclassを記述することも可能です。


ToDoRealmObject.swift

import RealmSwift

class ToDo: Object {
dynamic var title = ""
}


ToDoのタイトルとなるString型の変数を宣言します。

※通常のSwiftの記述方法と似ていますが、変数の宣言時にdynamicをつけるのを忘れないように注意してください。


データを保存

データを保存する処理を記述していきます。


InputViewController.swift

import UIKit

//追加
import RealmSwift

class InputViewController: UIViewController {



InputViewController.swift

@IBAction func addButtonTapped(sender: AnyObject) {

let newTodo = ToDo()

// textFieldに入力したデータをnewTodoのtitleに代入
newTodo.title = titleTextField.text!

// 上記で代入したテキストデータを永続化するための処理
do{
let realm = try Realm()
try realm.write({ () -> Void in
realm.add(newTodo)
print("ToDo Saved")
})
}catch{
print("Save is Faild")
}
// 最後に、TableViewControllerに表示を切り替える self.navigationController?.popToRootViewControllerAnimated(true)
}


①import UIKitの下に、import RealmSwiftを追記

②InputViewController内の、addButtonTappedを上記のように記述


データの呼び出し

アプリを一度閉じた後でも、データが引き継がれるように保存することはできましたが、これではまだ不十分です。

保存されたデータを表示するためにデータの取り出しを行う必要があります。


TableViewController.swift

import UIKit

// 追加
import RealmSwift

class TableViewController: UITableViewController {


こちらも同様に、import RealmSwiftを追記します。


TableViewController.swift

class TableViewController: UITableViewController {

// 追加
var todoItem: Results<ToDo>!

override func viewDidLoad() {
super.viewDidLoad()

// 追加
// 永続化されているデータを取りだす
do{
let realm = try Realm()
todoItem = realm.objects(ToDo)
tableView.reloadData()
}catch{

}

}

// 追加 画面が表示される際などにtableViewのデータを再読み込みする
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
}


上記が、保存されたデータを取り出す処理になります。

最後に取り出したデータをTableViewに表示するための処理を書き加えていきます。


TableViewController.swift

 override func tableView(tableView: UITableView, numberOfRowsInSection section: Int)->Int {

// todoItemの数 = セルの数
return todoItem.count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexpath: NSIndexPath)->UITableViewCell {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")

// todoItemに代入されたデータをobject:NSArrayに代入
let object = todoItem[indexpath.row]

//cellのtextLabelのtextにobjectのtitleプロパティを代入
cell.textLabel?.text = object.title

return cell
}


これでデータの取り出しの処理も完了です。

しかし、せっかくここまできたら、データを削除する処理も追加したいですね!

ということで・・・。


TableViewController.swift

// TableViewのCellの削除を行った際に、Realmに保存したデータを削除する

override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {

if(editingStyle == UITableViewCellEditingStyle.Delete) {
do{
let realm = try Realm()
try realm.write {
realm.delete(self.todoItem[indexPath.row])
}
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Fade)
}catch{
}
tableView.reloadData()
}
}


TableViewのセルを左にスワイプし、Deleteボタンを押下すると、TableViewからセルが削除されることはもちろん、Realmに保存されていた該当データも削除されています。

より、ToDoアプリらしくなったのではないでしょうか。

最後にソースコードを全て記載しておきます。


TableViewController.swift

import UIKit

import RealmSwift

class TableViewController: UITableViewController {

var todoItem: Results<ToDo>!

override func viewDidLoad() {
super.viewDidLoad()

// 永続化されているデータを取りだす
do{
let realm = try Realm()
todoItem = realm.objects(ToDo)
tableView.reloadData()
}catch{

}

}

override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
tableView.reloadData()
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}

override func tableView(tableView: UITableView, numberOfRowsInSection section: Int)->Int {
return todoItem.count
}

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexpath: NSIndexPath)->UITableViewCell {
let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell")

let object = todoItem[indexpath.row]

cell.textLabel?.text = object.title
return cell
}

// TableViewのCellの削除を行った際に、Realmに保存したデータを削除する
override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {

if(editingStyle == UITableViewCellEditingStyle.Delete) {
do{
let realm = try Realm()
try realm.write {
realm.delete(self.todoItem[indexPath.row])
}
tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: UITableViewRowAnimation.Fade)
}catch{
}
tableView.reloadData()
}
}
}



InputViewController.swift

import UIKit

import RealmSwift

class InputViewController: UIViewController {
@IBOutlet weak var titleTextField: UITextField!

override func viewDidLoad() {
super.viewDidLoad()

titleTextField.frame.size.height = 30
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}

@IBAction func addButtonTapped(sender: AnyObject) {
let newTodo = ToDo()

// textFieldに入力したデータをnewTodoのtitleに代入
newTodo.title = titleTextField.text!

// 上記で代入したテキストデータを永続化
do{
let realm = try Realm()
try realm.write({ () -> Void in
realm.add(newTodo)
print("ToDo Saved")
})
}catch{
print("Save is Faild")
}
self.navigationController?.popToRootViewControllerAnimated(true)
}
}



ToDoRealmObject.swift

import RealmSwift

class ToDo: Object {
dynamic var title = ""
}



完成

スクリーンショット 2016-08-01 22.36.33.png

最後までご覧いただき、ありがとうございます。

初めての投稿だったので、至らない点やわかりにくい点が多かったかと思いますが、お付き合いいただき、誠にありがとうございました。

この投稿がどなたかのお役に立てれば、幸いです。

では、失礼いたします。