SwiftUI: Core Data @FetchRequest и List, отображающие управляемые объекты - потеря данных при блокировке

У меня проблема с потерей данных в управляемых объектах Core Data (все свойства, несмотря на.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)
      }
    }
  }
}

Приведенный выше упрощает код, он работает нормально. Вы можете заблокировать экран и вернуться, и строки будут перезагружены с помощью управляемых объектов с ошибочными основными данными.

Но простое добавление ZStack, VStack вокруг ContactRow(contact: contact) нарушает это, и переход на экран блокировки, а затем снова приводит к перезагрузке этого списка с пустыми управляемыми объектами (со свойствами 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)
    }

ОБНОВИТЬ

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, но не является желательным решением в более сложных примерах, таких как 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 ответ

Просто предположение, но, возможно, потому, что вы используете enumerated() в вашем ForEach. Если вас интересует индекс, попробуйте вместо этого использовать это:

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

Это будет фактически обновляться сейчас, когда ваши полученные результаты обновятся.

Просто чтобы немного расширить это, но FetchRequest и поэтому FetchedResults являются update() известно, они предназначены для обновления быстрого пользовательского интерфейса Viewс body. enumerated()нет, это просто сборник. Итак, когда это изменится, вашbodyвсе равно, он не подписался на его изменения. когдаFetchedResults изменения, ваш body делает уход, он будет подписан, и поэтому он обновляет себя и повторно выводит его содержимое.

Другие вопросы по тегам