Идентификатор объекта NSManagedObject сохраняется в переменную, но эта переменная становится нулевой при вызове из обработчика .sheet

#ios #swift #core-data #swiftui

#iOS #swift #основные данные #swiftui

Вопрос:

Приложение iOS, которое я создаю, перебирает список объектов PhraseGroup, определенных в Core Data, и отображает extraText значение, связанное с каждой PhraseGroup .

Это верхняя часть сгенерированного Xcode подкласса NSManagedObject для PhraseGroup:

 extension PhraseGroup {

@nonobjc public class func fetchRequest() -> NSFetchRequest<PhraseGroup> {
    return NSFetchRequest<PhraseGroup>(entityName: "PhraseGroup")
}

@NSManaged public var extraText: String?
@NSManaged public var phraseGroupID: UUID
@NSManaged public var text: String?
@NSManaged public var phrase: NSSet?
@NSManaged public var piece: Piece
  

Я бы хотел, чтобы пользователи могли долго нажимать любую внетекстовую запись в моем отображаемом списке, а затем редактировать ее в модальном листе. Тем не менее, я изо всех сил пытаюсь найти способ передачи действительной ссылки на давно нажатую PhraseGroup на мой лист.

Ниже показано, как я отображаю список extraText значений PhraseGroups:

 import SwiftUI
import CoreData

struct PhraseGroupView: View {

@Environment(.managedObjectContext) var moc
@Binding var phraseGroupViewAlertItem: AlertItem?
@State private var isEditMode: EditMode = .inactive
@State private var showingPhraseGroupEditView = false
@State private var phraseGroupObjectID: NSManagedObjectID!

private var fetchRequest: FetchRequest<PhraseGroup>
private var phraseGroups: FetchedResults<PhraseGroup> { fetchRequest.wrappedValue }


var body: some View {
    NavigationView {
        VStack(spacing: 20){
            Section {
                List {
                    ForEach (phraseGroups, id: (PhraseGroup.phraseGroupID)) { phraseGroup in
                        HStack {
                            Text("(phraseGroup.wrappedExtraText)")
                        }
                        .onLongPressGesture {
                            phraseGroupObjectID = phraseGroup.objectID
                            showingPhraseGroupEditView = true
                            print(phraseGroup.extraText!)  // Sanity check to prove I've got hold of the right row
                            let phraseGroupForEditing = moc.object(with: phraseGroupObjectID) as! PhraseGroup
                            dump(phraseGroupForEditing)
                            print(phraseGroupForEditing.extraText!)
                        }
                    }
                    .onDelete(perform: delete)
                }
                .sheet(isPresented: $showingPhraseGroupEditView) {
                    self.phraseGroupObjectID == nil
                        ?
                        TestPhraseGroupEditView(phraseGroupObjectID: phraseGroupObjectID, message: "phraseGroupObjectID is nil")
                        :
                        TestPhraseGroupEditView(phraseGroupObjectID: phraseGroupObjectID, message: "phraseGroupObjectID is not nil")
                }
            }
        }
        .navigationBarTitle("This is phraseGroup navBarTitle", displayMode: .inline)
        .navigationBarItems(leading:
                                HStack {
                                    Button(action: {
                                        // yet to come
                                    }) {
                                        Image(systemName: "plus").resizable()
                                            .frame(width: 16, height: 16)
                                            .aspectRatio(contentMode: .fit)
                                            .foregroundColor(.myKeyColor)
                                    }
                                }, trailing:
                                    HStack {
                                        EditButton()
                                            .frame(width: 60, height: 20)
                                            .aspectRatio(contentMode: .fit)
                                            .foregroundColor(.myKeyColor)
                                    })
        .environment(.editMode, self.$isEditMode)
    }
}
  

Я использую следующую функцию инициализации для настройки этого представления:

 init (phraseGroupViewAlertItem: Binding<AlertItem?>, piece: Piece) {
    self._phraseGroupViewAlertItem = phraseGroupViewAlertItem
    fetchRequest = FetchRequest<PhraseGroup>(
        entity: PhraseGroup.entity(),
        sortDescriptors: [
            NSSortDescriptor(key: "extraText", ascending: true)
        ],
        predicate: NSPredicate(format: "piece == %@", piece)
        // 'piece' in the predicate above is the name of a Piece <- PhraseGroup relationship defined in Core Data
    )
}
  

Когда я долго нажимаю строку в своем списке, я вижу в своем отладчике, проходя через onLongPressGesture закрытие, что я нашел правильную PhraseGroup (то есть ту, которую я нажал), что я правильно считываю эту PhraseGroup objectID в @State var, а затем создаю другую ссылкув тот же объект Core Data и выводит его extraText значение.

Однако к тому времени, когда сразу после этого отображается закрытие .sheet, для идентификатора phraseGroupObjectID установлено значение nil, что я ясно вижу, когда отображается TestPhraseGroupEditView .

 import SwiftUI
import CoreData

struct TestPhraseGroupEditView: View {

@Environment(.managedObjectContext) var moc
var phraseGroupObjectID: NSManagedObjectID!
var message: String

var body: some View {
    VStack (spacing: 20){
        Text(message)
        Text("Tap to create phraseGroup for debugger").onTapGesture {
            dump(phraseGroupObjectID)
            let phraseGroupForEditing = moc.object(with: phraseGroupObjectID) as! PhraseGroup
            dump(phraseGroupForEditing)
        }
    }
}
}
  

Сообщение в первом текстовом поле читается phraseGroupObjectID is nil , и щелчок по второму тексту приводит к первой печати dump() -nil , за которой следует Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value: file AK6/TestPhraseGroupEditView.swift, line 22

Что я здесь не понимаю? Есть ли какой-то незначительный сбой в моей реализации, который я упустил из виду, или я делаю это совершенно неправильно? У Apple есть довольно хороший документ об ошибках или уникальности в https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/FaultingandUniquing.html , но ничто из того, что я там прочитал, не бросается мне в глаза как очевидное решение моей проблемы здесь.

Ответ №1:

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

 @State private var phraseGroupObjectID: NSManagedObjectID? = nil
  

Измените свой .onLongПредоСтавьте следующее

 .onLongPressGesture {
                        phraseGroupObjectID = phraseGroup.objectID
                        print(phraseGroup.extraText!)  // Sanity check to prove I've got hold of the right row
                        let phraseGroupForEditing = moc.object(with: phraseGroupObjectID) as! PhraseGroup
                        dump(phraseGroupForEditing)
                        print(phraseGroupForEditing.extraText!)
                    }
  

Измените свой лист на следующий

 .sheet(item: $self.phraseGroupObjectID) { objID in
    TestPhraseGroupEditView(phraseGroupObjectID: objID, message: "phraseGroupObjectID is not nil")
 }
  

Теперь вы можете удалить showingPhraseGroupEditView .

Лист отображается всякий раз, когда идентификатор phaseGroupObjectID не равен нулю. Вы можете отклонить лист, снова установив phaseGroupObjectID равным нулю.

Комментарии:

1. браво. Пришлось реализовать extension NSManagedObjectID: Identifiable {} , чтобы избежать повторного несоответствия ошибок Identifiable . В TestPhraseGroupEditView теперь let phraseGroupForEditing = moc.registeredObject(for: phraseGroupObjectID! as NSManagedObjectID) as! PhraseGroup нужно захватить NSManagedObject . Изменено $self.phraseGroupObjectID на self.$phraseGroupObjectID . Не удалось найти способ привязки к phraseGroupObjectID в листе, чтобы я мог установить для него значение nil и закрыть лист с помощью кнопки, но если пользователь закрывает лист, перетаскивая его вниз, эффект эквивалентен.