SwiftUI: Core Data @FetchRequest и List отображают управляемые объекты - теряют данные на lockscreemn - PullRequest
0 голосов
/ 24 апреля 2020

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

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

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

UPDATE

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 Ответ

0 голосов
/ 24 апреля 2020

Просто предположение, но, возможно, потому что вы используете 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 заботится о , он подписан на , и поэтому он обновляется и перерисовывает свое содержимое.

...