【Xcode/SwiftUI】Realmを使ってみよう

StoryboardではRealmを使った経験がありますがSwiftUIでは無かったのでやってみました。


全体の構成

ひとくちメモ

プロジェクトを作成した初期状態ではSwiftUIファイルがContentView.swiftとなっているのでHomeView.swiftに変更しておきましょう。それに伴ってWindowGroup内のContentView()HomeView()に変更する必要があります。


各ファイルのコード実装

RealmはPod Installしておいてください。

pod 'RealmSwift'

わからない場合は以下の記事を参考に

【Xcode/Swift】Realmを使ってみよう

*本番コードで使用するときはオプショナルは使用せずdo-catch構文等に修正しておきましょう

Model

import RealmSwift
import Foundation

class User: Object {
    @objc dynamic var userName: String = "" // Realmで管理したいもの
}

View

import SwiftUI
import RealmSwift

struct HomeView: View {
    @State private var user: [User] = []
    @ObservedObject var homeViewModel = HomeViewModel()

    var body: some View {
        VStack {
            List {
                ForEach(homeViewModel.users, id: \.self) { user in
                    Text(user.userName) // RealmのDBに存在するuserNameを表示させる
                }
                .onDelete { indexSet in // リストアイテムの削除
                    let deletedItem = self.homeViewModel.users[indexSet.first!]
                    self.homeViewModel.deleteUserName(deletedItem)
                }
            }
            Button { // AddItemViewにモーダル遷移
                homeViewModel.showAddItemView = true
            } label: {
                Text("Add New User")
                    .font(.largeTitle)
            }
        }
        .sheet(isPresented: $homeViewModel.showAddItemView) {
            AddItemView(homeViewModel: homeViewModel)
        }
    }
}

struct HomeView_Previews: PreviewProvider {
    static var previews: some View {
        HomeView()
    }
}
import SwiftUI

struct AddItemView: View {
    @ObservedObject var homeViewModel: HomeViewModel // HomeViewでインスタンス化したHomeViewModelをバインディング
    @ObservedObject var addItemViewModel = AddItemViewModel()

    var body: some View {
        VStack {
            Form {
                TextField("User name", text: $addItemViewModel.userName) // 入力値はAddItemViewModelで管理
            }
            Button {
                if addItemViewModel.userName == "" { // TextFieldが空の場合はアラートを出す
                    addItemViewModel.showAlert = true
                } else { // それ以外はRealmにTextFieldの入力値を登録する(realm.write)
                    addItemViewModel.addNewUserName(homeViewModel)
                    homeViewModel.showAddItemView = false
                }
            } label: {
                Text("Add New User")
                    .font(.largeTitle)
            }
        }
        .alert("入力が不十分です", isPresented: $addItemViewModel.showAlert) {
            Button("OK", role: .cancel) { }
        }
    }
}

struct AddItemView_Previews: PreviewProvider {
    @State static var homeViewModel = HomeViewModel()
    static var previews: some View {
        AddItemView(homeViewModel: homeViewModel)
    }
}

ViewModel

import RealmSwift
import SwiftUI

class HomeViewModel: ObservableObject {
    @Published var users: [User] = []
    @Published var showAddItemView = false
    private let realm = try! Realm()

    init() {
        users = Array(realm.objects(User.self))
    }

    // リストアイテムの削除処理
    func deleteUserName(_ user: User) {
        withAnimation(.easeInOut(duration: 0.5)) { // 削除にアニメーションをつけてみる (必須ではない)
            try! realm.write {
                realm.delete(user)
            }
            users = Array(realm.objects(User.self))
        }
        users = Array(realm.objects(User.self))
    }
}
import RealmSwift
import SwiftUI

class AddItemViewModel: ObservableObject {
    @Published var userName = ""
    @Published var showAlert = false
    private var realm = try! Realm()

    // アイテム追加処理
    func addNewUserName(_ homeViewModel: HomeViewModel) {
        realm = try! Realm()
        try! realm.write {
            let user = User()
            user.userName = userName
            realm.add(user)
        }
        homeViewModel.users = Array(realm.objects(User.self))
    }
}

まとめ

SwiftUIの楽しさが少しずつ分かってきた感じがする、、、