🎦
SwiftUIでRealmを使ってカウンターアプリを作ってみた
Overview
Realmとは、MongoDBの会社が作ったローカルDBです。今回は、こちらを使ってカウンターの値を端末に保存するアプリを作ってみましょう!
こちらを参考にRealmeを追加してください
summary
プロジェクトを作成して、Realmを追加したら、UIを配置するApplicationディレクトリ
と、Entity(モデルの方がわかりやすいか?)とロジックを書いたコードを配置するdomainディレクトリ
を作成します。
.
├── Application
│ └── ContentView.swift
├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
├── CounterRealmeApp.swift
├── Domain
│ ├── Counter.swift
│ └── RealmManager.swift
├── Preview Content
│ └── Preview Assets.xcassets
│ └── Contents.json
こちらが作成した3つのファイルです。
Realmを使ってモデルクラスを作成します。難しい設計の専門用語だと、エンティティって呼んでます。
モデルとなるコード
import Foundation
import RealmSwift
// カウンターのモデルを定義する
class Counter: Object, ObjectKeyIdentifiable {
@Persisted(primaryKey: true) var id: ObjectId
@Persisted var count: Int = 0
}
ライフサイクルの違いを説明すると...
ロジックを書いたコードはViewModelなんでしょうけど、ロジックを持ってるので違う気がする???
状態を扱う変数の@State
がないのは、ObservableObject
があるからです。このプロトコルを使っているから、なくても良い。
@Stateは通常、ビュー自体に固有の単純なデータを管理するために使用されます。このデータはビューの内部状態を表し、ビューが再描画されても保持されます。
@ObservedObjectは、外部の参照型のオブジェクト(この場合はRealmManager)を監視するために使用されます。このオブジェクトはビューの外部に存在し、ビューはオブジェクトの変更を監視して対応します。
ロジック
import Foundation
import RealmSwift
// 状態が変更されたときに、Viewに更新を通知するクラス
class RealmManager: ObservableObject {
private(set) var localRealm: Realm?
@Published var counters: [Counter] = []
// initはRealmManagerが生成されたときに呼ばれる
init() {
// Realmを開くメソッドを実行
openRealm()
// 保存されてるカウンターの情報を取得
getCounters()
}
// Realmとの接続を開くメソッド
func openRealm() {
do {
let config = Realm.Configuration(schemaVersion: 1)
Realm.Configuration.defaultConfiguration = config
localRealm = try Realm()
} catch {
print("Error opening Realm", error)
}
}
// カウンターを追加するメソッド
func addCounter(count: Int) {
// if letでlocalRealmがnilでないことを確認
if let localRealm = localRealm {
// do-catchでエラー処理
do {
// Realmに書き込み
try localRealm.write {
let newCounter = Counter()
newCounter.count = count
localRealm.add(newCounter)
getCounters()
}
} catch {
print("Error adding counter to Realm: \(error)")
}
}
}
// カウンターの情報を取得するメソッド
func getCounters() {
// if letでlocalRealmがnilでないことを確認
if let localRealm = localRealm {
// Realmから全てのカウンターを取得
let allCounters = localRealm.objects(Counter.self)
// countersに全てのカウンターを代入
counters = []
// forEachでallCountersの要素をcounterに代入。配列なので、appendでcountersに追加
allCounters.forEach { counter in
counters.append(counter)
}
}
}
// カウンターをリセットするメソッド
func resetCounter(id: ObjectId) {
// if letでlocalRealmがnilでないことを確認
if let localRealm = localRealm {
// do-catchでエラー処理
do {
// idが一致するカウンターを取得
let counterToReset = localRealm.objects(Counter.self).filter(NSPredicate(format: "id == %@", id))
// guardでcounterToResetが空でないことを確認
guard !counterToReset.isEmpty else { return }
// Realmに書き込み
try localRealm.write {
// countを0にする
counterToReset[0].count = 0
getCounters()
}
} catch {
print("Error resetting counter \(id) to Realm: \(error)")
}
}
}
}
View側のコード
Application/ContentView.swift
import SwiftUI
struct ContentView: View {
// @ObservedObject var realmManagerは、RealmManagerクラスのインスタンスを生成
@ObservedObject var realmManager = RealmManager()
var body: some View {
VStack {
// 現在のカウントを表示
Text("Count: \(realmManager.counters.last?.count ?? 0)")
.font(.largeTitle)
// カウントアップボタン
Button(action: {
// カウントを1増やす
let newCount = (realmManager.counters.last?.count ?? 0) + 1
// カウントを追加
realmManager.addCounter(count: newCount)
}) {
// 追加ボタンのデザイン
Text("カウントアップ")
.foregroundColor(.white)
.padding()
.background(Color.blue)
.cornerRadius(10)
}
// リセットボタンのデザイン
Button(action: {
// if letでlastCounterIdがnilでないことを確認
if let lastCounterId = realmManager.counters.last?.id {
// カウンターをリセット
realmManager.resetCounter(id: lastCounterId)
}
}) {
// リセットボタンのデザイン
Text("リセット")
.foregroundColor(.white)
.padding()
.background(Color.red)
.cornerRadius(10)
}
}
.padding()
}
}
📱ビルドして実験してみる
ボタンを押してカウンターを増やして、アプリを停止して、またビルドしてみましょう。ボタンを押してカウントして、アプリを停止して、ビルドすると先ほどの数値のままになっていれば端末に情報を保存できています。
thoughts
今回は、簡単にと言いたいですが、設定で躓いたので簡単ではなかったです😅
SwiftUIでRealmを使ったアプリを作ってみました。
こちらが完成品です
Discussion