概要
Realmについての概要と他のサービスとの比較に加え、Realmを使用した簡易的なiOSアプリケーションの作成方法について、解説します!
この記事を読むことで作成できるTODOアプリ
上記のようなアプリの実装方法を確認したい方は、「実装方法」までスキップしてください!
この記事を読みながら写経すればある程度基礎的なTODOアプリを作成することが可能になるはずなので、ぜひ試してみてください!
面白そうだなと思ったら、この記事をお気に入りしていただけると嬉しいです!
この記事はこんな方におすすめです
- 自力でポートフォリオを作成して、未経験でiOSエンジニア転職を狙う方やSwiftで簡単なアプリを作りたい方
- RealmのCRUDを理解したいエンジニアの方
- RealmとFirebaseのどちらを使用したらいいかわからない方
その他
この記事で説明すること
- ソースコードを用いたRealmの基本的な使用方法について
- Realmについての簡単な基本情報の説明
- Realmを用いた簡単なアプリケーション作成方法について
この記事で説明しないこと
- UIKit等の他ライブラリの使用方法について(EX:TableViewの実装方法やボタンの実装方法等)
- アーキテクチャ(実装の際の作法的なもの)の詳細な解説
開発環境
Mac
macOS Monterey 12.5
MacBook Air(M1, 2020) メモリ8G
XCode
Version 14.0
iPhone
iOS 16.1
iPhone13
前提条件
まずそもそも、「Realmってなんやねん?」「勉強してなんかいいことあるんか?」とお思いの方、いらっしゃると思いますが
結論
iOSエンジニアの方であれば、確実に学んだ方がいい
では、その「Realm」についてですが、めっちゃくちゃ簡単に特徴を説明すると以下の3点が挙げられます。
- iPhone内にデータを保持する仕組みを無料で作成することができる
- 超高速でデータを取得することができ、インターネット通信がない場所でも、データの「登録・取得・更新・削除」が行える。
- 実装がめちゃくちゃ簡単で、すぐに理解できる。その割に、使い勝手もいい。
以上が大きな理由です。
まず1点目の「iOSアプリの場合iPhone内にデータを保持する仕組みを無料で作成することができる」ですが、もともとiOSを開発する際にデバイス内にデータを保持しておける仕組みUserDefaultというものがあります。以下で特徴を比較してみました。
UserDefault
- プロジェクト作成時からデフォルトで使用できる
- 「String」や「Bool」「Int」「Date」「Data」等のあらかじめ存在する限られた型でしかデータを保存することができない。(User型等をそのまま保存できない)
- データを保存しすぎると、アプリの動きがもっさりしたり、最悪の場合バグが生まれる
Realm
- Pod等でライブラリの管理は必要になる
- 任意の型を指定してデータを保存できる
- ある程度データを保存してももっさりすることはない。
おまけ
Cloud Firestore(Firebase)
- 個人開発で、クラウドにデータを保存する機能を使用するにはもってこい
- Firebaseの他のサービスである、Authentificationでアカウント管理が容易にできたり、Storageを使用することで画像の保存もできる万能サービス
- 実装コストがRealmよりも高く、すぐにアプリを出したい場合にはあまり向かない
2点目の「超高速でデータを取得することができ、インターネット通信がない場所でも、データの「登録・取得・更新・削除」が行える。」です。通常、データの取得や更新は、サーバーを通してデータの登録等をしますが、Realmはデータを端末に保存するのでオフライン環境下でも全く影響がありません。
3点目の「実装がめちゃくちゃ簡単で、すぐに理解できる。その割に、使い勝手もいい。」ですが、続きの実装方法で解説します!
上記3つは間違いなくメリットなのですが、強いていうならばデメリットが1つあります。それは、
他のユーザーとのデータのやり取りができない
上記のように、「他ユーザーとデータのやり取りをしたい」 であったり、そもそも、デバイス内にはデータを持たせたくないという場合、にはFirebaseというGoogleが提供してくれている機能を使用することを考えてみるのも1つの手です!
ただ、どちらも使用したことがある身からすると、Firebaseは今回学習するRealmよりも学習コストが高いことと今回作成するTODOアプリのように他ユーザーとのデータのやり取りを行わないアプリを作成する場合は一旦、Realmを使用してみましょう!
(※Firebaseの記事に関しても、今後書きたいと思います。)
実装方法
今回作成するサンプルアプリ要件としては、以下の通りです。
TodoModel
- TodoModel型を作成
RealmDBConverter
- Results型から任意の型(今回はTodoModel)に変換できる
TodoListViewController
- 名前をつけてTodoを投稿することができる
- 投稿時間も保存ができる
- 投稿したTodoは、即座に画面(TableView)に反映して確認できる
- 投稿したTodoをタップすると、編集画面に遷移できる
- 登録したTodoを全て削除することができる
- 名前で登録済みのTodoを検索できる
TodoEditlViewController
- 表示中のTodoのみ削除することができる
- 表示中のTodoの名前を変更することができる
また今回は、アーキテクチャ等の説明は割愛するためViewControllerにベタばりのソースコードにします。
それぞれ、Xibファイルのコードは共有しませんので各々よしなに作成してください。
import Foundation
import RealmSwift
final class TodoModel: Object {
// ポイント①
// TodoModelで保存したい任意の型を作成しています。
// 今回であれば、保存しておきたい「title」と「regDate」をTodoの値として作成していきます。
// Realmの使用仕様上、@objc dynamicというのを変数につけてあげないといけないみたいなので、まじない程度に考えてください。
@objc dynamic var title: String?
@objc dynamic var regDate: Date?
}
おまけファイル
import Foundation
import RealmSwift
struct RealmDBConverter {
/// RealmのResults型で取得することになっているデータを任意の型に形成する処理
/// - Parameter object: RealmのResults型で返却される値
/// - Returns: RealmのResults型から形成されたされた任意の型の配列
static func convertToModelFrom<T>(object: Results<T>) -> [T] {
var list: [T] = []
for i in 0..<object.count {
list.insert(object[i], at: 0)
}
return list
}
// 参考
// データベースから値取得の際には、以下のような型で結果が返却されます
// この型を扱いやすいようにコンバートするのが上記の関数です。
Results<TodoModel> <0z188f01fd0> (
[0] User {
title = サウナに行く;
regDate = 20230401;
}
}
import UIKit
import RealmSwift
final class TodoListViewController: UIViewController {
// TODOを表示するデーブルビュー
@IBOutlet private weak var todoListTableView: UITableView! {
didSet {
todoListTableView.delegate = self
todoListTableView.dataSource = self
// CustomCellをテーブルビューに紐付けてください
}
}
// インプットバー
@IBOutlet private weak var inputTodoTextField: UITextField!
// MARK:@IBAction
// Todoをデータベースに登録する関数(画面上では、青ボタン🟦)
@IBAction private func tappedRegisterTodoButton() {
let inputText = inputTodoTextField.text ?? ""
// 登録時間を保存するためのインスタンス
let currentTime = Date()
// ポイント②
// データベースに値を保存する際は、任意の型をインスタンス化してあげて、
// 保存する値を代入します。
let todoModel: TodoModel = TodoModel()
todoModel.title = inputText
todoModel.regDate = currentTime
// Realm()をインスタンス化してあげて、realmDb.add()でデータベースに値を保存してあげます
let realmDb = try! Realm()
try! realmDb.write({
realmDb.add(todoModel)
// これが実際に完了したら、以下の関数でデータを取得します。
self.getTodoListFromRealmDBForPresentingTableView()
})
}
// Todoをデータベースから全件削除する関数(画面上では、赤ボタン🟥)
@IBAction private func tappedDeleteAllTodosButton() {
// ポイント③
let realmDb = try! Realm()
// 今回は、保存した値を問答無用で全て削除するので、
// 削除する値を保存した型(今回は、TodoModel)を以下のように削除します
try! realmDb.write{
let todoModelTable = realmDb.objects(TodoModel.self)
realmDb.delete(todoModelTable)
self.getTodoListFromRealmDBForPresentingTableView()
}
}
// タイトルが一致するTodoをデータベースから検索する関数(画面上では、緑ボタン🟩)
@IBAction private func tappedSearchTodosButton() {
let inputText = inputTodoTextField.text ?? ""
// ポイント④
// ポイント⑤と同じような要領です。
var todosList: Results<TodoModel>
let realmDb = try! Realm()
// 「Todoのタイトルが入力された文字と一致する」という条件をfilterを用いで実装しています。
todosList = realmDb.objects(TodoModel.self).filter("title == '\(inputText)'")
self.todosList = RealmDBConverter.convertToModelFrom(object: todosList)
todoListTableView.reloadData()
}
var todosList: [TodoModel] = []
private func getTodoListFromRealmDBForPresentingTableView() {
// Todoをデータベースから取得する関数
// ポイント⑤
// 取得したい型を指定してあげて、Realmデータベースから値を取得しています。
var todosList: Results<TodoModel>
let realmDb = try! Realm()
todosList = realmDb.objects(TodoModel.self)
// データを取得してきたら、画面に表示してあげます。
self.todosList = RealmDBConverter.convertToModelFrom(object: todosList)
todoListTableView.reloadData()
}
}
import UIKit
import RealmSwift
final class TodoEditlViewController: UIViewController {
// MARK: - Proparty
private let todo: TodoModel
// MARK: - Initialize
init(todo: TodoModel) {
self.todo = todo
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK:@IBOutlet
// インプットバー
@IBOutlet private weak var inputTodoTextField: UITextField!
// MARK:@IBAction
// 登録したTodoの名前を変更する関数
@IBAction private func tappedEditTodosButton() {
let inputText = inputTodoTextField.text ?? ""
let currentTime = Date()
let realmDb = try! Realm()
// ポイント⑥
// 登録時とほとんど同じです。
let todoModel = realmDb.objects(TodoModel.self).first!
try! realmDb.write{
todoModel.title = inputText
todoModel.regDate = currentTime
}
}
// 登録した特定のTodoを削除する関数
@IBAction private func tappedDeleteTodoButton() {
let realmDb = try! Realm()
// ポイント⑦
// 保存したタイトルの名前と一致しているものをRealmデータベース上で検索し、
// タイトルが一致するもののみを削除する処理にしています。(本来ならば、idなどを保存させるべき...)
let todoTable = realmDb.objects(TodoModel.self)
let result = todoTable.where({ $0.title == title }).first!
try! realmDb.write{
realmDb.delete(result)
}
}
}
さいごに
長くなりましたが、Realmについての解説はここまでです!
僕自身もFirebaseや自社のサーバー関連を扱うようになった後にRealmを触ったので、Realmはめちゃくちゃ簡単に実装できると感じました!
初学者の方や個人開発でアプリをささっと作りたい方には、もってこいなのでぜひ活用していただきたいです!
おまけのファイルに「20230401サウナに行く」と書いていますが、本当にサウナ行ってきます。
参考
Realm公式リファレンス
Firebase公式リファレンス