SwiftUI:核心数据@FetchRequest和显示托管对象的列表-丢失锁的数据

时间:2020-04-24 09:22:37

标签: core-data swiftui fetchrequest

我在丢失核心数据托管对象中的数据时遇到了问题(尽管使用了.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 ??“”),或者如果我使用脱包o像contact.name一样只是崩溃了应用程序!

所以这打破了

将我的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)
    }

Empty list loaded from Core Data

更新

联系人 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情况下会有所帮助,但在更复杂的示例(例如在List中填充了.sheet()中的托管对象的解决方案)中不是理想的解决方案(因此,在这里,我需要停止显示此类工作表)

 @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 个答案:

答案 0 :(得分:0)

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

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

当您提取的结果更新时,这实际上将立即更新。

仅对此稍作扩展,但是FetchRequestFetchedResults知道update(),它们的意思是更新快速ui View的{​​{1 }}。 body不是,它只是一个简单的集合。因此,当发生更改时,您的enumerated()不在乎,它还没有订阅更改。当body发生更改时,您的FetchedResults会执行,它会被 订阅,因此会自动更新并重新提供其内容。