SwiftUI:Core Data @FetchRequest 和 List 显示托管对象 - 在 lockscreemn 上丢失数据

Posted

技术标签:

【中文标题】SwiftUI:Core Data @FetchRequest 和 List 显示托管对象 - 在 lockscreemn 上丢失数据【英文标题】:SwiftUI: Core Data @FetchRequest and List displaying managed objects - losing data on lockscreemn 【发布时间】:2020-04-24 09:22:37 【问题描述】:

我有丢失核心数据托管对象中的数据的问题(所有属性,尽管 .objectID)在进入锁屏和返回后变得为零。

我一直在调试这个并发现了这样的问题。

struct SimpleContactsList: View 

  @FetchRequest var contacts: FetchedResults<Contact>

  let companyId: String

  init(companyId: String) 
    self.companyId = companyId

    self._contacts = FetchRequest(
      entity: Contact.entity(),
      sortDescriptors: [
        NSSortDescriptor(key: "name", ascending: true, selector: #selector(NSString.caseInsensitiveCompare(_:))),
        NSSortDescriptor(keyPath: \Contact.createdAt, ascending: true)
      ],
      predicate: NSPredicate(format: "company.id == %@", companyId)
    )
  


  var body: some View 

    List 
      ForEach(Array(self.contacts.enumerated()), id: \.1.objectID)  (i, contact) in

        ContactRow(contact: contact)
      
    
  

上面简化了代码,它工作正常。您可以锁定屏幕并返回,并使用有故障的核心数据管理对象重新加载行。

但只是在 ContactRow(contact:contact) 周围添加 ZStack、VStack 会破坏此操作并进入锁屏状态,然后再次返回会导致列表重新加载空托管对象(具有 nil 属性)并且列表为空(如果我使用可选展开contact.name ?? "") 或者如果我使用像contact.name这样的前展开o只会使应用程序崩溃!

所以这打破了

将我的 SimpleContactLists 更改为此会破坏代码,例如添加 ZStack 会阻止从数据库中刷新列表元素?

List 
      ForEach(Array(self.contacts.enumerated()), id: \.1.objectID)  (i, contact) in

        ZStack 
          ContactRow(contact: contact)
        
        .listRowBackground( (i%2 == 0) ? Color("DarkRowBackground") : .white)
        .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
      
      .onDelete(perform: self.delete)
    

更新

contact NSManagedObject 为空,但引用 self.contacts[i] 不是!为什么?

List 

                ForEach(Array(self.contacts.enumerated()), id: \.1.objectID)  (i, contact) in
                    Button(action: 
                        self.selectedId = self.contacts[i].id!
                        self.showDetails = true
                    ) 
                        Contact(contact: contact)
                        //ContactRow(contact: self.contacts[i])
                        .background(Color.white.opacity(0.01))
                    
                    .buttonStyle(ListButtonStyle())

                    .listRowBackground( (i%2 == 0) ? Color("DarkRowBackground") : .white)
                    .listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
                
                .onDelete(perform: self.delete)
            

更新 2

我有这样的解决方案。它在某些 List-from-FetchResults 案例中有所帮助,但在更复杂的示例中不是所需的解决方案,例如在 .sheet() 中填充托管对象的 List (所以在这里我需要停止展示此类工作表)

 @State var theId = UUID()

 List  
     //rows 
 
 .id(theId)
 .onReceive(appBecomeActivePublisher, perform:  _ in
        self.theId = UUID()
  

extension UIApplication  
   static var didBecomeActivePublisher: AnyPublisher<UIKit.Notification, Never> 

        NotificationCenter.default.publisher(for: UIApplication.didBecomeActiveNotification)
            .eraseToAnyPublisher()
    

【问题讨论】:

【参考方案1】:

只是猜测,但可能是因为您在 ForEach 中使用了 enumerated()。如果您关心的是索引,请尝试使用它:

ForEach(0..<contacts.count, id: \.self)  i in
    // rest appears to be the same

当您获取的结果更新时,这实际上会更新。

只是稍微扩展一下,但 FetchRequestFetchedResultsupdate() 感知的,它们旨在更新 swift ui Viewbodyenumerated() 不是,它只是一个简单的集合。因此,当这种情况发生变化时,您的 body 不在乎,它没有订阅它的更改。当FetchedResults 发生变化时,您的body 确实关心它,它订阅了,因此它会自我更新并重新呈现其内容。

【讨论】:

我认为这与此无关,因为我在没有 enumareted() 的情况下也对其进行了测试。有点奇怪的是 self.contacts[i] 已设置,而 contact 未设置。但是只有在这个屏幕上,我有类似的 FetchRequest > List > Row 代码的其他屏幕上,self.objects [i] 中也有空对象。我发现我可以观察到 UIApplication.didBecomeActiveNotification 然后更新@State var theId。并将其设置为 List 上的 .id(theId)。但这就像强制刷新整个列表(UITableView 上的 reloadData()),但这也会失去滚动偏移。但我认为 List 应该自动刷新

以上是关于SwiftUI:Core Data @FetchRequest 和 List 显示托管对象 - 在 lockscreemn 上丢失数据的主要内容,如果未能解决你的问题,请参考以下文章

Core Data 更改时更新 Swiftui 视图

Core Data - 数据传输 SwiftUI

SwiftUI 将 Core Data 实体读入 []

SwiftUI + Core Data - 更新对象(Detail -> DetailEdit)

SwiftUI Core Data 在 DetailView 中绑定 TextFields

Swift之深入解析如何结合Core Data和SwiftUI