##はじめに
SwiftUIでRealmを使ったサンプルアプリを作成しました。
理解するのに時間がかかったので、自分のアウトプットを兼ねて書いています。
↓こんな動きをするアプリです。右の数字は0~100からランダムで取得します。
##環境
Swift 5.3
Xcode 12.0.1
Cocoapods 1.9.3
RealmSwift 5.4.7
##経緯
最近、SwiftでRealmに初めて触れ、SwiftUIではどのように実装できるのか気になったので調べ始めました。
##実装した動作
・追加ボタン
Listに「First Title」と0〜100の乱数の行を追加
・ゴミ箱マーク
対応する行を削除
・全削除ボタン
行を全て削除
・行のタイトルを長押し
「First Title」を「Changed Title」にリネーム、乱数再取得
・Realmで保存
アプリを落としてもデータ保存
##ソースコード
まずはデータの元となるコードを記述します。
import RealmSwift
// Realm用のClass
class ItemDB: Object {
@objc dynamic var id = 0
@objc dynamic var title = ""
@objc dynamic var number = 0
// 主キーを使うと、データの更新や削除に便利
override static func primaryKey() -> String? {
"id"
}
}
import Foundation
struct Item: Identifiable {
let id: Int
let title: String
let number: Int
}
extension Item {
init(itemDB: ItemDB) {
id = itemDB.id
title = itemDB.title
number = itemDB.number
}
}
import RealmSwift
final class ItemStore: ObservableObject {
private var itemResults: Results<ItemDB>
// itemResultsにDBのデータをセット
init(realm: Realm) {
itemResults = realm.objects(ItemDB.self)
}
var items: [Item] {
itemResults.map(Item.init)
}
}
ItemStore.swiftには、DBを操作する以下のメソッドも記述します。
上記の実装した動作を行うためのものです。
- create (データを追加)
- update (データを更新)
- delete (データを削除)
- deleteAll (データを全削除)
extension ItemStore {
// データを追加
func create() {
// これを書かないとDBの変更をViewに伝えることができません。
objectWillChange.send()
do {
let realm = try Realm()
let itemDB = ItemDB()
itemDB.id = UUID().hashValue
itemDB.title = "First Title"
itemDB.number = Int.random(in: 0...100)
try realm.write {
realm.add(itemDB)
}
} catch let error {
print(error.localizedDescription)
}
}
// データを更新
func update(itemID: Int) {
objectWillChange.send()
do {
let realm = try Realm()
try realm.write {
realm.create(ItemDB.self,
value: ["id": itemID,
"title": "Changed Title",
"number": Int.random(in: 0...100)],
update: .modified)
}
} catch let error {
print(error.localizedDescription)
}
}
// データを削除
func delete(itemID: Int) {
objectWillChange.send()
guard let itemDB = itemResults.first(where: { $0.id == itemID})
else {
return
}
do {
let realm = try Realm()
try realm.write {
realm.delete(itemDB)
}
} catch let error {
print(error.localizedDescription)
}
}
// データを全削除
func deleteAll() {
objectWillChange.send()
do {
let realm = try Realm()
try realm.write {
realm.deleteAll()
}
} catch let error {
print(error.localizedDescription)
}
}
}
続いてViewとなるコードを書いていきます。
import SwiftUI
struct ItemRowView: View {
@EnvironmentObject var store: ItemStore
let item: Item
var body: some View {
HStack{
Text(item.title)
// タイトルの長押しでデータの更新
.onLongPressGesture {
store.update(itemID: item.id)
}
Spacer()
Text(String(item.number))
// ゴミ箱マークを設置して、削除を実装
Image(systemName: "trash.circle.fill")
.resizable()
.frame(width: 24, height: 24)
.foregroundColor(.red)
.onTapGesture {
store.delete(itemID: item.id)
}
}
}
}
import SwiftUI
struct ItemListView: View {
@EnvironmentObject var store: ItemStore
let items: [Item]
var body: some View {
List {
Section(header: sectionHeaderView) {
ForEach(items) { item in
HStack{
ItemRowView(item: item)
}
}
}
}
.navigationTitle("RealmDBリスト")
}
// ヘッダーに追加ボタンと全削除ボタンを設置
var sectionHeaderView: some View {
HStack {
Button("追加", action: store.create)
Spacer()
Button("全削除", action: store.deleteAll)
}
}
}
import SwiftUI
struct ContentView: View {
@EnvironmentObject var store: ItemStore
var body: some View {
NavigationView {
ItemListView(items: store.items)
}
}
}
最後に、「SceneDelegate.swift」の一部を書き換えて完了です。
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = scene as? UIWindowScene {
do {
let realm = try Realm()
let window = UIWindow(windowScene: windowScene)
// Realmを最初に読み込むところ
let contentView = ContentView()
.environmentObject(ItemStore(realm: realm))
window.rootViewController = UIHostingController(rootView: contentView)
self.window = window
window.makeKeyAndVisible()
} catch let error {
fatalError("Failed to open Realm. Error: \(error.localizedDescription)")
}
}
}
ソースコード全体はこちら
https://github.com/takuma-2531/SwiftUI_Realm_1To1
##参考にしたサイト
https://www.raywenderlich.com/12235561-realm-with-swiftui-tutorial-getting-started
SwiftUIでのRealmの使い方を調べた中で、このサイトが一番わかりやすかったです。
英語ですが、Google翻訳を駆使して読みました。
このサイトのコードをより単純にしたものが上記サンプルです。
##おわりに
最後まで読んでくださりありがとうございます。
SwiftUIやRealmを使う人の役に立てれば幸いです。