如何使用获取的数据刷新视图 - Firestore 和 SwiftUI

Ril*_*Dev 7 firebase google-cloud-firestore swiftui combine

简短:我认为的图像在第一次加载后没有更新。URL 保持与之前加载的视图相同,但是不从存储中获取 URL 或数据的视图的其余部分被更新。

完整:我有两个视图, aListView和 a DetailView

ListViewI 中显示一个类型列表List。详细视图应该显示每个Profile来自List.profiles. 为此,我将每个字符串存储uid在其中List.profiles并调用model.fetchProfiles以获取所选每个列表的配置文件。

在第一个选择List model.fetchProfiles返回文档并model.profilesDetailView.

当第一次装载DetailViewProfileRow上出现被称为并记录配置文件获取。然后ProfileRow加载imageURLimagePath,并用它喜欢来获取图像。

控制台:加载列表 1

CARD DID APPEAR: Profiles []
CARD DID APPEAR: SortedProfiles [] CARD ROW
CARD ROW DID APPEAR: Profiles profiles/XXXXXX/Profile/profile.png
CARD ROW DID APPEAR: SortedProfiles profiles/XXXXXX/Profile/profile.png
从图片获取网址路径:profiles/XXXXXX/Profile/profile.png
图片网址:https : //firebasestorage.googleapis.com/APPNAME/profiles%XXXXXXX

当选择所述第二ListListView所述ProfileRow didAppear不因调用;

if model.profiles.count > 0 {
  print("CARD ROW DID APPEAR: Profiles \(model.profiles[0]. imgPath)")     
  print("CARD ROW DID APPEAR: Sorted  \(model.sortedProfiles[0].imgPath)")
}
Run Code Online (Sandbox Code Playgroud)

并且在选择Listin时不会再次出现ListView,但是显示了 中的其余配置文件数据,ProfileRow例如名称,因此必须获取数据。

ImagePath与加载完全相同图像的第一个视图相同。Profile名称等的所有其他属性均已正确加载。

控制台:加载列表2

CARD DID APPEAR: Profiles []
CARD DID APPEAR: SortedProfiles [] CARD ROW
从图片路径获取网址:profiles/XXXXXX/Profile/profile.png 图片网址: https ://firebasestorage.googleapis.com/APPNAME/profiles%XXXXXXX

如果我然后导航到List1然后图像List2出现,如果我重新选择List2图像显示正常。The image show is correct on first load, and when selecting another list it always the one from before.

谁能帮我吗 ?

第一次观看

struct ListViw: View {
  @EnvironmentObject var model: Model

  var body: some View {
    VStack {
        
        ForEach(model.lists.indices, id: \.self) { index in
            NavigationLink(
                destination: DetailView()
                            .environmentObject(model)
                            .onAppear() {
                                model.fetchProfiles()
                            }
            ) {
                 ListRow(home:model.lists[index])
                    .environmentObject(model)
            }
            .isDetailLink(false)
        }
    }
  }

}
Run Code Online (Sandbox Code Playgroud)

DetailView卡片

struct ProfilesCard: View {

@EnvironmentObject var model: Model

var body: some View {
    VStack(alignment: .trailing, spacing: 16) {                
            if !model.sortedProfiles.isEmpty {
                VStack(alignment: .leading, spacing: 16) {
                    ForEach(model.sortedProfiles.indices, id: \.self) { index in
                        ProfileRow(
                            name: "\(model.sortedProfiles[index].firstName) \(model.sortedProfiles[index].lastName)",
                            imgPath: model.sortedProfiles[index].imgPath,
                            index: index)
                            .environmentObject(model)
                    }
                }
                .padding(.top, 16)
            }
        
    }//End of Card
    .modifier(Card())
    .onAppear() {
        print("CARD DID APPEAR: Profiles \(model.profiles)")
        print("CARD DID APPEAR: SORTED \(model.sortedTenants)")
    }
}
}



struct ProfileRow: View {

@EnvironmentObject var model: Model

@State var imageURL = URL(string: "")

var name: String
var imgPath: String
var index: Int

private func loadImage() {
    print("load image: \(imgPath)")
    DispatchQueue.main.async {
        fm.getURLFromFirestore(path: imgPath,  success: { (imgURL) in
                   print("Image URL: \(imgURL)")
            imageURL = imgURL
               }) { (error) in
                   print(error)
        }
    }
}

var body: some View {
    VStack(alignment: .leading, spacing: 12) {
        HStack(alignment: .center, spacing: 12) {

                   KFImage(imageURL,options: [.transition(.fade(0.2)), .forceRefresh])
                       .placeholder {
                           Rectangle().foregroundColor(.gray)
                       }
                       .resizable()
                       .aspectRatio(contentMode: .fill)
                       .frame(width: 32, height: 32)
                       .cornerRadius(16)


                // Profile text is always displayed correctly
            Text(name)
                    .modifier(BodyText())
                    .frame(maxWidth: .infinity, alignment: .leading)
        }
    }
    .onAppear() {
        print("CARD ROW")

        // Crashes if check is not there
        if model.profiles.count > 0 {

            print("CARD ROW DID APPEAR: Profiles \(model.profiles[0]. imgPath)")

            print("CARD ROW DID APPEAR: Sorted  \(model.sortedProfiles[0].imgPath)")
        }
        
       loadImage()
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

模型

class Model: ObservableObject {

  init() {
      fetchData()
  }

  @Published var profiles: [Profile] = []
  var sortedProfiles: [Profile] {return profiles.removeDuplicates } 

  @Published var list: List? {
      didSet {
          fetchProfiles()
      }
  }

  func fetchData() {
    if let currentUser = Auth.auth().currentUser {
        
        email = currentUser.email!

            db.collection("lists")
                .whereField("createdBy", isEqualTo: currentUser.uid)
                .addSnapshotListener { (querySnapshot, error) in
                guard let documents = querySnapshot?.documents else {
                    return
                }

                self.lists = documents.compactMap { queryDocumentSnapshot -> List? in
                    return try? queryDocumentSnapshot.data(as: List.self)
                }
            }

    }
 }

  func fetchProfiles() {
    profiles.removeAll()
    
    for p in list!.profiles {
        firestoreManager.fetchProfile(uid: t, completion: { [self] profile in
            profiles.append(profile)
        })
    }
  }

}
Run Code Online (Sandbox Code Playgroud)

更新

到目前为止,我尝试过的是didSet用于ImgPathImgURL但仍然不是运气。也试过model.profiles直接使用。

Asp*_*eri 1

在使用 Firestore API 的所有回调中,为主队列上的已发布或状态属性进行分配,因为可能会在后台队列上调用回调。

因此,假设数据被正确返回并解析,这里应该是这样的

for p in list!.profiles {
    firestoreManager.fetchProfile(uid: t, completion: { [self] profile in
        DispatchQueue.main.async {
           profiles.append(profile)
        }
    })
}
Run Code Online (Sandbox Code Playgroud)

另外,我建议避免使用 SDK 类型对自定义类型进行相同的命名 - 可能会出现非常令人困惑的非明显错误

// List model below might conflict with SwiftUI List
return try? queryDocumentSnapshot.data(as: List.self)
Run Code Online (Sandbox Code Playgroud)