我之前没有广泛使用过 RealmSwift,所以也许我的问题的解决方案可能相当微不足道。我一直在尝试配置下面的
GroupView
,使其反映其 group
ObservedObject 的状态。请注意,视图绑定到 AsyncImage 初始值设定项中的 photo._url
,目的是在修改照片的 url 时重新评估视图。
import SwiftUI
import RealmSwift
struct GroupView: View {
@Environment(\.realmUser) var user //custom environment key
@ObservedRealmObject var group: Group
var body: some View {
HStack(spacing: 20){
ForEach((group.groupContent?.tableContent?.photos)!,id: \.id){ photo in
AsyncImage(url: URL(string: photo._url)){ phase in
switch phase{
case .success(let image):
image
.resizable()
.scaledToFit()
case .failure:
Text("Image not found").task {
if let user = user{
await photo.refreshURLIfNeeded(user: user)
}
}
case .empty:
ProgressView()
}
}.task{
if let user = user{
let _ = await photo.refreshURLIfNeeded(user:user)
}
}
}
}
}
}
出现问题的原因是,即使在
group.groupContents?.tableContent?.photosEmbeddedObject 中的照片的
_url
属性通过调用 photo.refreshPhotoURL(user:User)
更新后,AsyncImageView 也没有被重新评估(我已经验证了 url)正在使用 Atlas 控制台中的有效照片链接进行更新,因此空/无效的 url 不是罪魁祸首)。由于绑定$group
在refreshPhotoURL的主体中不直接可用,因此解冻后通过realm!.write{}事务执行更新,遵循realm的执行显式写入的示例。
extension Photo{
func refreshURLIfNeeded(user: User) async{
//Call serverless Atlas function for new URL
let urlString = try? await user.functions.refreshURL([AnyBSON(self.id!)]).stringValue
let realm = self.realm!.thaw()
try! realm.write{
self.thaw()!._url = urlString ?? ""
}
}
}
final class Group: Object,ObjectKeyIdentifiable{
@Persisted(primaryKey: true) var _id: ObjectId
@Persisted var groupContent: GroupContent?
}
final class GroupContent: EmbeddedObject{
@Persisted var tableContent: TableContent?
}
final class tableContent:EmbeddedObject{
@Persisted var photos: List<Photo>
}
在 Atlas Console 中手动更改
_url
属性也会导致活动 GroupView
中无响应/失效。即使修改了观察到的嵌入属性的值,也可能导致 AsyncImageViews 保持不变 group
?
该问题似乎与嵌入无关,因为将
TableContent
设置为对象而不是嵌入对象并将其传递到 GroupView 中代替 group
仍然会导致视图不更新。
正如我编辑的问题中提到的,实例是顶级
Objects
或 EmbeddedObject
与问题的根本原因关系不大。相反,上面面临的问题根源于 RealmSwift中
@ObservedRealmObject
属性包装器的实现,它本身依赖于 SwiftUI 的 @ObservedObject
属性包装器来提供其持久/无效功能。
为了捕获
@ObservedRealmObject
发生的问题,我生成了以下包含嵌套 ObservableObjects 的示例:
class ObserveMe:ObservableObject{
@Published var observeMeOnceMore = ObserveMeOnceMore()
}
class ObserveMeOnceMore:ObservableObject{
@Published var counter = 0
}
struct ContentView: View {
@ObservedObject var observeMe = ObserveMe()
var body: some View {
Button("Does not publish: \(observeMe.observeMeOnceMore.counter)"){
observeMe.observeMeOnceMore.counter += 1 //Change not published to view since observation does not occur at the level of @ObserveMeOnceMore
print(observeMe.observeMeOnceMore.counter)
}
CounterView(observeMeOnceMore: observeMe.observeMeOnceMore)
}
}
struct CounterView:View{
@ObservedObject var observeMeOnceMore: ObserveMeOnceMore
var body:some View{
Button("Does publish: \(observeMeOnceMore.counter)"){
observeMeOnceMore.counter += 1
print(observeMeOnceMore.counter) //Works as expected
}
}
}
在写这个答案的过程中,我还发现了@lorem-ipsum的这个答案,这似乎反映了包含ObservableObject实例的Views
中发生的不适当的观察
级别的常见问题。
将
@ObservedObject
的观察级别原则应用于 @ObservedRealmObject
,创建一个单独的 PhotoView
,它直接接受 EmbeddedObject
作为参数,以实现 GroupView 所需的精确观察的级别。唯一棘手的细节是,EmbeddedObject(或对象)必须符合 ObjectKeyIdentifiable
才能由 @ObservedRealmObject
属性包装器绑定(编译器警告,No exact matches in call to initializer
。我已提交了 功能请求 来改进编译器警告,但暂时不是主要问题。