回答編集履歴
1
回答追記
test
CHANGED
@@ -57,3 +57,96 @@
|
|
57
57
|
|
58
58
|
そして、このように @ObservedResultsを使ってappend/removeを実施する処理にすれば
|
59
59
|
Userクラスにextensionで追加した `findAll`、`add`、`delete`のstaticメソッドは不要になります。
|
60
|
+
|
61
|
+
---
|
62
|
+
(6/15追記)
|
63
|
+
|
64
|
+
> @ObservedResultsをについてもう少しお伺いたいのですが、 ViewにViewModelからusersデータを持ってくるのと(下記のコード)、 Modelからusersデータを持ってくる(今回のコード)やり方では どちらがいいなどはあるのでしょうか? ※最終的にはどちらも同じになると思うのですが...
|
65
|
+
|
66
|
+
``` swift
|
67
|
+
@ObservedObject var viewModel = UserViewModel() ForEach(viewModel.users.sorted { $0.postedDate > $1.postedDate }, id: \.self)
|
68
|
+
```
|
69
|
+
|
70
|
+
このコードは、viewModelからUserデータを持ってくるのはいいのですが、 `viewModel.users` がデータベース内に格納されているデータを `Array()` で `[User]` (Userの配列)に変換して全てのUserデータをメモリ上の配列に読み込んでいるのが問題です。実際にはUserデータは1000件もないのだろうと思うので現実的に問題が発生することはないと思いますが、もし10万件存在するデータを同じ方法で処理していたら、10万件のデータを全てメモリ上の配列に読み込もうとしてメモリ不足のエラーになります。データベースから取得した型(Results<User>型や@ObservedResults)を使って処理すれば、画面に表示しているデータしかメモリに読み込まないので、100万件のデータが保存されていてもメモリ不足になることはありません。それが、データベースのメリットをあまり活かせていない問題のあるやり方であり、@ObservedResultsを使ったやり方とは大きく異るということです。
|
71
|
+
|
72
|
+
|
73
|
+
そこで、Arrayを使用せず、@ObservedResultsを使用した上で、ViewModelからUserデータを取得できるようにする方法を考えるとよさそうですが、
|
74
|
+
普通に考えるとUserViewModelの中で
|
75
|
+
``` swift
|
76
|
+
@ObservedResults(User.self, sortDescriptor: SortDescriptor(keyPath: "create_date", ascending: false)) var users
|
77
|
+
```
|
78
|
+
を宣言し、UserViewの中で
|
79
|
+
``` swift
|
80
|
+
ForEach(viewModel.users)
|
81
|
+
```
|
82
|
+
のようにしてviewModelのusersを使用すれば、Arrayに変換しないデータをviewModelから提供できるので一番よさそうです。
|
83
|
+
|
84
|
+
しかし、実際にやってみると、この場合データの追加/削除がうまく画面に反映されませんでした。調べてみると、
|
85
|
+
|
86
|
+
https://github.com/realm/realm-swift/issues/7712
|
87
|
+
|
88
|
+
にある通り、 `@ObservedResults` は、View以外の場所で宣言すると、データの変更が検知されなくなるので、うまく動作しないようです。
|
89
|
+
|
90
|
+
そのため、 `@ObservedResults` を使ってデータの一覧を表示するには、今回の質問でやっているようにViewの中で宣言する(Realmの機能を直接使う)しかないようです。(ViewModelの中で宣言した `@ObservedResults` は、6/13の回答のようにappend/removeメソッドを使うことはできるようです。)
|
91
|
+
|
92
|
+
表示するデータを(Arrayに変換せずに)viewModelで宣言し、Viewに表示するデータをviewModelから提供するよう統一したいなら、
|
93
|
+
|
94
|
+
https://software.small-desk.com/development/2022/01/28/swiftui-realm-create-todoapp/
|
95
|
+
|
96
|
+
で解説されているように `@ObservedResults` を使用せず、データベースから取得した一覧を@Publishedの変数で宣言して、データの追加/削除をした時にobjectWillChange.send()で通知し、UserViewでfreezeするしかないようです。
|
97
|
+
|
98
|
+
具体的には、
|
99
|
+
UserView.swift
|
100
|
+
``` swift
|
101
|
+
@ObservedResults(User.self, sortDescriptor: SortDescriptor(keyPath: "create_date", ascending: false)) var users
|
102
|
+
↓
|
103
|
+
@ObservedObject var viewModel = UserViewModel()
|
104
|
+
```
|
105
|
+
``` swift
|
106
|
+
ForEach(users) { user in
|
107
|
+
↓
|
108
|
+
ForEach(viewModel.users.freeze()) { user in
|
109
|
+
```
|
110
|
+
UserViewModel.swift
|
111
|
+
``` swift
|
112
|
+
@ObservedResults(User.self) var users
|
113
|
+
↓
|
114
|
+
@Published var users = User.findAll().sorted(byKeyPath: "create_date")
|
115
|
+
```
|
116
|
+
``` swift
|
117
|
+
self.$users.append(user)
|
118
|
+
↓
|
119
|
+
User.add(user)
|
120
|
+
self.objectWillChange.send()
|
121
|
+
```
|
122
|
+
``` swift
|
123
|
+
self.$users.remove(user)
|
124
|
+
↓
|
125
|
+
User.delete(user)
|
126
|
+
self.objectWillChange.send()
|
127
|
+
```
|
128
|
+
User.swift
|
129
|
+
``` swift
|
130
|
+
以下のメソッドを復活
|
131
|
+
static func findAll() -> Results<User> {
|
132
|
+
realm.objects(self)
|
133
|
+
}
|
134
|
+
|
135
|
+
static func add(_ user: User) {
|
136
|
+
try! realm.write {
|
137
|
+
realm.add(user, update: .all)
|
138
|
+
}
|
139
|
+
}
|
140
|
+
|
141
|
+
static func delete(_ user: User) {
|
142
|
+
let actualUser = realm.object(ofType: User.self, forPrimaryKey: user.id)!
|
143
|
+
try! realm.write {
|
144
|
+
realm.delete(actualUser)
|
145
|
+
|
146
|
+
}
|
147
|
+
}
|
148
|
+
```
|
149
|
+
とすることでうまく処理できることを確認しました。
|
150
|
+
|
151
|
+
結局、MVVMアーキテクチャを厳密に守り、Viewに提供するデータを全てViewModelが管理する構造にすることを優先するか、Realmが新しく提供した `@ObservedResults` を活かして、Viewに表示するデータを直接Modelから取得できる部分は直接使用することを許容してコードを簡潔にすることを優先するか、どちらを選択するかの問題だと思います。(自分の考え次第だと思います。)
|
152
|
+
RealmもSwiftUIに対応した機能を少しずつ拡張しているようなので、そのうちMVVMに対応した形でもっとスマートに書ける機能をRealmが提供するかもしれません。
|