SwiftUI 2.0 iOS 14 Core data удаляет экземпляр при нажатии кнопки

#swift #core-data #swiftui #ios14 #xcode12

#swift #основные данные #swiftui #ios14 #xcode12

Вопрос:

Я пытаюсь выяснить, как удалить экземпляр объекта core data, вызвав метод в моей ViewModel. Мне удалось получить идентификатор экземпляра, чтобы мой метод мог просто бороться с синтаксисом. Я использую Xcode 12.3 iOS 14.3, SwiftUI 2.0, больше нет SceneDelegate или AppDelegate

Вот как выглядит моя модель представления:

 import SwiftUI
import CoreData

class ViewModel : ObservableObject{
@Environment(.managedObjectContext) private var viewContext
@FetchRequest(entity: Counter.entity(), sortDescriptors: [
        
        NSSortDescriptor(
            keyPath: Counter.id,
            ascending: true),
        NSSortDescriptor(
            keyPath:Counter.windowType,
            ascending: true )
    ]) var counters: FetchedResults<Counter>

    //.....




    
 

Ниже приведено то, что я пробовал и получил эту ошибку

entityForName: nil не является законным NSPersistentStoreCoordinator для поиска имени объекта «Счетчик»

После пошагового выполнения кода в отладчике это то, что я получаю, когда распечатываю CounterRequested

<NSFetchRequest: 0x282d70380> (сущность: счетчик; предикат: (id == «C4BE9AFF-8A8E-4413-AFA3-AC90850C482E»); Сортировочные дескрипторы: ((null)); тип: NSManagedObjectResultType; )

идентификаторы действительно совпадают

Затем он выходит из строя в первой строке в блоке do

 func deleteCounter(id: UUID)  {
    let counterRequested: NSFetchRequest<Counter> = Counter.fetchRequest()
    counterRequested.predicate = NSPredicate(format: "id=%@", id.uuidString)
  
    do {
        let savedCounters = try  self.viewContext.fetch(counterRequested)
        
        for counter in savedCounters {
            self.viewContext.delete(counter)
        }
        try self.viewContext.save()
    } catch {
      
        print(error)
    }
 }
}
 

Вот откуда я получаю свое удостоверение личности. Могу ли я пройти весь прилавок здесь? Не уверен, на какой тип ссылаться в моем представлении счетчика?

 List {
       ForEach(counters, id: .self){counter in
                    
          CounterCell(id: counter.id!, windowType: counter.windowType!, location: counter.location!, pickedImg: counter.pickedImg!, price: counter.price!, qty: counter.qty!, subtotal: counter.subtotal!) .listRowInsets(.init(top: 0,
                                                                                                                                                                                                                            leading: 0,
                                                                                                                                                                                                                                          bottom: 20,
                                                                                                                                                                                                                                          trailing: 0))
 

}

Вот мое представление, в котором я хочу вызвать метод удаления

 struct CounterCell: View {
@Environment(.colorScheme) var colorScheme
@State private var stepperValue = 0
@StateObject var estimatorData = EstimatorViewModel()

var id: UUID = UUID()
var windowType: String = "Window Type"
var location: String = "Location"
var pickedImg: String = "defaultImg"
var price: String = "0.0"
var qty: String = "0"
var subtotal: String = "0.00"

var body: some View {
   //.....
 

}

Вот пример, который я нашел:

 import Foundation
import UIKit
import CoreData

class DataManager {
    
    static let shared = DataManager(moc: NSManagedObjectContext.current)
    
    var managedContext: NSManagedObjectContext
    
    private init(moc: NSManagedObjectContext) {
        self.managedContext = moc
    }
    
    
    // Delete method
    // remove birthday
    func removeBirthday(id: UUID) {
        let fetchRequest: NSFetchRequest<Birthday> = Birthday.fetchRequest()
        fetchRequest.predicate = NSPredicate.init(format: "id=%@", id.uuidString)
        do {
            let bdays = try self.managedContext.fetch(fetchRequest)
            for bday in bdays {
                self.managedContext.delete(bday)
            }
            try self.managedContext.save()
        } catch {
          print(error)
        }
    }
    
}

extension NSManagedObjectContext {
    static var current: NSManagedObjectContext {
        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        return appDelegate.persistentContainer.viewContext
    }
}
 

Ответ №1:

Создайте метод в вашей модели представления, который удаляет данный объект.

Что — то вроде

 func delete(counter: Counter) {
    do {
        viewContext.delete(counter)
        viewContext.save()
    } catch {
        // handle error
    }
}
 

Я бы немного обновил код представления и позволил CounterCell использовать свойство Counter

 struct CounterCell: View {
    @Environment(.colorScheme) var colorScheme
    @State private var stepperValue = 0
    @StateObject var estimatorData = EstimatorViewModel()

    var counter: Counter

    var body: some View {
   //.....
 

и измените ForEach соответствующим образом

 List {
   ForEach(counters, id: .self) { counter in
      CounterCell(counter: counter) 
      //...
   }
 

}

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

1. Привет, Йоаким, спасибо за твой ответ. На данный момент я передаю только идентификатор, а не весь объект счетчика. Я полагаю. Я мог бы провести рефакторинг и отправить весь объект целиком, но предпочел бы ссылаться по идентификатору. Сегодня я собираюсь сделать это еще раз.

2. У меня есть forEach с представлением счетчика, я хочу вызвать метод в представлении счетчика

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

4. Я снова отредактировал, чтобы показать, что я пробовал до сих пор.

5. Йоаким, еще раз спасибо за помощь, я разобрался

Ответ №2:

После моей попытки, описанной выше, я обратился к старшему разработчику, и оказалось, что именно так я получал доступ к managedObjectContext в моей модели представления. И в процессе я узнал о доступе к общему persistenceController, который объявлен в файле сохранения, который теперь включен в приложение biolerplate SwiftUI Core Data.

Вот правильный способ в моем случае.

  var viewContext: NSManagedObjectContext { PersistenceController.shared.container.viewContext }
 

Вот мой метод удаления

 func deleteCounter(id: UUID)  {
    let counterRequested: NSFetchRequest<Counter> = Counter.fetchRequest()
    counterRequested.predicate = NSPredicate.init(format: "id=%@", id.uuidString)

    do {
        let savedCounters = try  self.viewContext.fetch(counterRequested)
        
        for counter in savedCounters {
            self.viewContext.delete(counter)
        }
        try self.viewContext.save()
    } catch {
        print(error)
    }
}
 

Рефакторинг

 func deleteWindow(id: UUID)  {
    if let counter = existingCounter {
        viewContext.delete(counter)
        do {
            try viewContext.save()
        } catch {
            print(error)
        }
    }

}
 

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

1. Хотя хорошо, что вы нашли решение, я нахожу немного странным, что вам нужно извлекать объект, который у вас, скорее всего, уже есть в памяти и к которому у вас есть доступ в вашем представлении и / или модели представления.

2. Как бы вы прошли весь прилавок?

3. Йоаким еще раз спасибо за ваш вклад

4. Имейте объект счетчика вместо идентификатора (и некоторых других свойств) в качестве свойства в CounterCell. Затем вы можете передать объект вместо этого методу удаления. Смотрите мой обновленный ответ

5. Йоаким Спасибо, я попробую!