Home > Back-end >  NSInvalidArgumentException: nil is not a valid object ID
NSInvalidArgumentException: nil is not a valid object ID

Time:02-14

My app currently uses a NavigationLink to open a view like this:

LazyVGrid(columns: gridLayout, spacing: 10) {
   ForEach(Array(person.project! as! Set<Project>).sorted { $0.date! > $1.date! }, id: \.self) { (project: Project) in
      NavigationLink(destination: ProjectDetailView(project: project, rootIsActive: self.$isActive)) {
         Image(uiImage: UIImage(data: project.image1 ?? self.projectImage1)! )
    ...

However to improve the flow of the UI, I'd like to use a Sheet instead of NavigationLink to show ProjectDetailView

So I changed it like this:

LazyVGrid(columns: gridLayout, spacing: 10) {
   ForEach(Array(person.project! as! Set<Project>).sorted { $0.date! > $1.date! }, id: \.self) { (project: Project) in
      Image(uiImage: UIImage(data: project.image1 ?? self.projectImage1)! )
         .onTapGesture(count: 1) { sheet = .imageTap(project) }
}

where imageTap is added to my sheet enum at the top:

enum Sheet { case imageTap(Project) }
@State var sheet: Sheet? = nil

and my sheet modifier passes the objectID of project to the view:

.sheet(using: $sheet) { sheet in
   switch sheet {
      case .imageTap:
         ProjectDetailView(id: project.objectID, in: viewContext)

In my ProjectDetailView I've defined my @ObservedObject and initialized like this:

    @Environment(\.managedObjectContext) private var viewContext
    @ObservedObject var project: Project
    init(id objectID: NSManagedObjectID, in context: NSManagedObjectContext) {
        if let project = try? context.existingObject(with: objectID) as? Project {
            self.project = project
            self._image1 = State(wrappedValue: project.image1!)
            self._image2 = State(wrappedValue: project.image2 ?? self.image2)
            self._image3 = State(wrappedValue: project.image3 ?? self.image3)
            self._image4 = State(wrappedValue: project.image4 ?? self.image4)
            self._fav = State(wrappedValue: project.favorite)
        } else {
            self.project = Project(context: context)
            try? context.save()
        }
    }

I'm expecting the objectID to be passed into this view when I invoke the onTapGesture on the image. However Xcode errors at this point with *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'nil is not a valid object ID'

I'm using this same method of passing objectID to edit a Person entity (my app has two core data entities: Person and Project), though it doesn't use a ForEach. It's just a .toolbar button and it works as expected. So I had it print the person.ObjectID and it looked like this: 0x8ae3a33c4ba2adf9 <x-coredata://468DD15D-71D2-4990-BD8B-01CAA9786420/Person/p5>

So I went and had it print the project.ObjectID upon tapping the image, and it prints a similar string that looks valid to me: 0x8ae3a33c49e2add9 <x-coredata://468DD15D-71D2-4990-BD8B-01CAA9786420/Project/p23>

So I'm pretty confused as to why ProjectDetailView is receiving a nil value when printing the objectID upon invoking imageTap produces a valid looking objectID

Any ideas? Thanks!!

CodePudding user response:

(As discussed in the comments above)

I'm not sure which instance of project you're referencing on the line ProjectDetailView(id: project.objectID, in: viewContext) or in what scope it exists, but instead, you should be using the associated value in the enum that you set when you do sheet = .imageTap(project).

In that case, the prior line should be: case let .imageTap(project): instead of case .imageTap.

  • Related