首页 > 解决方案 > NSManagedObject 的 objectID 保存到变量中,但是从 .sheet 处理程序调用时该变量已变为 nil

问题描述

我正在构建的 iOS 应用程序遍历 Core Data 中定义的 PhraseGroup 对象列表,并显示extraText与每个 PhraseGroup 关联的值。

这是 Xcode 为 PhraseGroup 生成的 NSManagedObject 子类的顶部:

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

我希望用户能够长按显示列表中的任何 extraText 条目,然后在模式表中对其进行编辑。但是,我正在努力寻找一种方法,将对长按 PhraseGroup 的有效引用传递给我的工作表。

下面显示了我如何显示 PhraseGroupsextraText值的列表:

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(即我按下的那个),我正在正确阅读将 PhraseGroupobjectID放入 @State var,然后创建对同一核心数据对象的另一个引用并输出其extraText值。

但是,当 .sheet 关闭之后立即出现时,phraseGroupObjectID 已设置为零,当显示 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)
        }
    }
}
}

第一个 Text 字段中的消息为phraseGroupObjectID is nil,单击第二个 Text 会导致第一个 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上有一些关于故障或唯一性的非常好的文档,但我在那里读到的任何内容都没有让我感到震惊一个明显的解决我的问题的方法。

标签: iosswiftcore-dataswiftui

解决方案


您可以使用可选对象并将数据直接传递到工作表中,而不是使用 Bool 来切换工作表。将您的 phaseGroupObjectID 定义更改为以下内容

@State private var phraseGroupObjectID: NSManagedObjectID? = nil

将您的 .onLongPressGesture 更改为以下内容

.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")
 }

您现在可以删除显示PhraseGroupEditView。

只要 phaseGroupObjectID 不为零,就会显示工作表。您可以通过将 phaseGroupObjectID 再次设置为 nil 来关闭工作表。


推荐阅读