При сохранении основного объекта данных в popover в SwiftUI возникает ошибка nilError без повторной передачи .environment в SubView

15

Игра с SwiftUI и Core Data привела меня к любопытной проблеме. Итак, ситуация следующая:

У меня есть основной вид «AppView» и подвид с именем «SubView». Представление SubView будет открыто из представления AppView, если я нажму кнопку «плюс» на панели навигации NavigationTitleBar в виде всплывающего окна или листа.

@Environment(\.managedObjectContext) var managedObjectContext
@State private var modal: Bool = false
...
Button(action: {
        self.modal.toggle()
      }) {
        Image(systemName: "plus")
      }.popover(isPresented: self.$modal){
        SubView()
      }

Представление SubView имеет небольшую форму с двумя объектами TextField для добавления имени и фамилии. Входные данные этих двух объектов обрабатываются двумя отдельными свойствами @State. Третий объект в этой форме - простая кнопка, которая должна сохранить имя и фамилию в прикрепленном объекте клиента для CoreData.

...
@Environment(\.managedObjectContext) var managedObjectContext
...
Button(action: {
  let customerItem = Customer(context: self.managedObjectContext)
  customerItem.foreName = self.forename
  customerItem.surname = self.surname

  do {
    try self.managedObjectContext.save()
  } catch {
    print(error)
  }
}) {
  Text("Speichern")
}

Если я пытаюсь сохранить сущность Customer таким способом, я получаю ошибку: «nilError», в частности: «Неразрешенная ошибка Error Domain = Foundation._GenericObjCError Code = 0» (null) », [:]" из NSError.

Но после выяснения, что когда я добавляю .environment(\.managedObjectContext, context)в вызов SubView () примерно так, SubView().environment(\.managedObjectContext, context)он работает как шарм.

Кто-нибудь знает, почему мне нужно передать managedObjectContext во второй раз? Я подумал, что мне просто нужно передать managedObjectContext один раз, чтобы использовать его во всей иерархии представления, как в SceneDelegate.swift:

    // Get the managed object context from the shared persistent container.
    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

    // Create the SwiftUI view and set the context as the value for the managedObjectContext environment keyPath.
    // Add `@Environment(\.managedObjectContext)` in the views that will need the context.
    let contentView = AppView().environment(\.managedObjectContext, context)

Это потому, что вызов SubView () таким образом, представление не является частью иерархии представления? Я не понимаю это ...

lukas_nitaco
источник
1
Я наблюдал такое же поведение на iOS 13.1. Xcode 11.1
Арун Патра
Вы не первый, кто обнаружил эту проблему, я решил ее, передав контекст в качестве параметра. Надеюсь, Apple исправит это в ближайшее время.
Майкл Салмон
1
Как и ожидалось, это ошибка в компиляторе Swift / SwiftUI. Харлан Хаскинс из Apple дал мне подтверждение этому: bugs.swift.org/browse/SR-11607 - так что я надеюсь, что это будет исправлено в ближайшее время. Для быстрого исправления: Передача .environment (\. ManagedObjectContext, context) во всплывающее окно SubView работает.
lukas_nitaco

Ответы:

24

ВАУ ЭТО ПОБЕЛА МЕНЯ ОРЕХАМИ! Тем более, что ошибки сообщают вам абсолютно никакой информации о том, как исправить.

Вот исправление, пока ошибка в XCode не будет решена:

        .navigationBarItems(trailing:
            Button(action: {
                self.add = true
            }, label: {
                Text("Add Todo List")
            }).sheet(isPresented: $add, content: {
                AddTodoListView().environment(\.managedObjectContext, managedObjectContext)
            })
        )

Просто добавьте .environment(\.managedObjectContext, managedObjectContext)к вашему вторичному представлению (модальный, в этом примере).

kdion4891
источник
8
огромная помощь для всех нас, достаточно смелых, чтобы развиваться в SwiftUI прямо сейчас ...
Apostolos Apostolidis
Решил и мою проблему. Спасибо.
P. Ent
1
Мой друг! Почему SwiftUI делает это необходимым? Окружающая среда должна быть доступна во всем мире.
pulse4life
Но зачем это нужно? Действительно странно, что SwiftUI не делает это автоматически ...
Loris Foe
Это необходимо, потому что это единственное решение проблемы сейчас. Apple, очевидно, работает над исправлением. Помните, что SwiftUI все еще очень новый.
stardust4891