как изменить атрибуты моих основных данных объекта, но для всех элементов моей базы данных

#swift

#быстрый

Вопрос:

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

Здесь, в этой функции, я могу только изменить имя, а затем я получаю следующую ошибку: строка:

 let objectUpdate = test[0] : Thread 1: Fatal error: Index out of range
 

 func updateData() {

    var newName = ""
    var newPrenom = ""

    newName = name.text!
    newPrenom = prenom.text!

    let managedContext =  AppDelegate.viewContext
    let fetchRequest : NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "Person")
    fetchRequest.predicate = NSPredicate(format: "name = %@", newName)
    do {
        fetchRequest.predicate = NSPredicate(format: "prenom = %@", newPrenom)

        let test = try! managedContext.fetch(fetchRequest) as! [NSManagedObject]
        let objectUpdate = test[0]

        objectUpdate.setValue(newName,forKey: "name")
        objectUpdate.setValue(newPrenom, forKey: "prenom")
        do {
            try managedContext.save()
        }
        catch {
            print(error)
        }
    } catch {
        print(error)
    }
}
 

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

1. Ошибка означает, что ваш запрос на выборку не вернул никаких значений. Вы должны сделать что-то вроде if test.isEmpty { return } перед строкой, которая выдает вам ошибку

2. Можете ли вы опубликовать код своих Person свойств CoreDataProperties?

Ответ №1:

Существует несколько способов избежать этой ошибки.

Разворачивание необязательного .first значения

Коллекция Swift дает нам безопасный способ получить первый элемент, просто обратившись к first свойству в данной коллекции. Он вернет Optional<Element> значение, поэтому нам нужно сначала развернуть его либо if let с помощью guard let

 if let object = test.first {
    // do something with object
}
 

или

 guard let object = test.first else { return }
// do something with object
 

Проверка наличия значения в индексе

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

 if test.indices.contains(0) {
    let object = test[0]
    // do something with object
}
 

Эти подсказки должны предотвратить повторный сбой вашего кода.

Другие предложения

Это не совсем безопасно или чисто:

 var newName = ""
var newPrenom = ""

newName = name.text!
newPrenom = prenom.text!
 

Мы можем сделать его намного чище и, самое главное, безопаснее, используя guard инструкцию

 guard let newName = name.text, let newPrenom = prenom.text else { return }
 

Здесь произошли две важные вещи:

  1. Больше не нужно принудительно разворачивать необязательные значения text [, которые могут привести к сбою]
  2. Свойства теперь неизменяемы, что означает, что мы можем быть уверены, что то, что мы сохраняем в CoreDate, — это то, что было восстановлено в начале функции

С тех пор, как линия:

 let test = try! managedContext.fetch(fetchRequest) as! [NSManagedObject]
 

уже заключен в do-catch предложение, вы можете безопасно удалить forced try! и заменить его try .

 let test = try managedContext.fetch(fetchRequest) as! [NSManagedObject]
 

Давайте использовать типы! В этой строке вы создаете NSFetchRequest объект для некоторой сущности с именем "Person" .

 let fetchRequest : NSFetchRequest<NSFetchRequestResult> = NSFetchRequest.init(entityName: "Person")
 

Я предполагаю, что CoreData сгенерировал для вас NSManagedObject подкласс с именем Person . Если это правда, вы могли бы переписать его следующим образом:

 let fetchRequest = NSFetchRequest<Person>(entityName: "Person")
 

Реализовав предыдущий совет, теперь мы можем избавиться as! [NSManagedObject] от этой строки:

 let test = try managedContext.fetch(fetchRequest) as! [NSManagedObject]
 

Поскольку NSFetchRequest объект теперь хорошо типизирован, мы можем воспользоваться этим, переписав его следующим образом:

 let test: [Person] = try managedContext.fetch(fetchRequest)
 

Итак, теперь мы используем правильные типы? круто! Теперь давайте улучшим это:

 objectUpdate.setValue(newName,forKey: "name")
objectUpdate.setValue(newPrenom, forKey: "prenom")
 

переписав это и используя свойства Person объекта

 objectUpdate.name = newName
objectUpdate.prenom = newPrenom
 

Нет необходимости вводить do-catch пункт второго уровня, поскольку мы уже находимся в одном из них!

 do {
    try managedContext.save()
}
catch {
    print(error)
}
 

вы можете легко заменить его просто save() вызовом, вот так:

 try managedContext.save()
 

Вы уверены, что эти предикаты — это то, что вам нужно?

 fetchRequest.predicate = NSPredicate(format: "name = %@", newName)
fetchRequest.predicate = NSPredicate(format: "prenom = %@", newPrenom)
 

Что я могу прочитать из них, так это то, что вы извлекаете Person объект, где есть имя newName и prenom newPrenom , а затем обновляете его с теми же точными значениями? Используете ли вы какую-то идентификацию пользователей? нравится id: Int или id: UUID ? Было бы гораздо разумнее написать что-то вроде этого

 let id: Int = // ID of the user you are currently editing 
fetchRequest.predicate = NSPredicate(format: "id == (id)")
 

если вы не используете какие-либо идентификаторы, вы можете попробовать сохранить начальные значения name и prenom

 // in cell declaration - set when you configure your cell
var initialName: String?
var initialPrenom: String? 
// then in your function:
fetchRequest.predicate = NSPredicate(format: "name = %@", initialName)
fetchRequest.predicate = NSPredicate(format: "prenom = %@", initialPrenom)
 

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

 fetchRequest.predicate = NSCompoundPredicate(
    type: .and, subpredicates: [
        NSPredicate(format: "name = %@", initialName),
        NSPredicate(format: "prenom = %@", initialPrenom)
    ]
)
 

Предлагаемая версия

 func updateData() {
    guard let newName = name.text, let newPrenom = prenom.text else { return }
    let managedContext = AppDelegate.viewContext
    let fetchRequest = NSFetchRequest<Person>(entityName: "Person")

    fetchRequest.predicate = NSCompoundPredicate(
        type: .and, subpredicates: [
            NSPredicate(format: "name = %@", initialName),
            NSPredicate(format: "prenom = %@", initialPrenom)
        ]
    )
    do {
        let objects: [Person] = try managedContext.fetch(fetchRequest)
        guard let object = objects.first else { return }
        object.name = newName
        object.prenom = newPrenom
        try managedContext.save()
    } catch {
        print(error)
    }
}
 

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

1. Спасибо за вашу помощь, я внес небольшие изменения, но я могу изменить только имя и имя. Для всех моих элементов, сохраненных в моем TableView, я могу изменить только имя каждой ячейки

2. Я только что обновил ответ, ознакомьтесь с той частью, где я говорю о ваших предикатах. Для меня они не имеют особого смысла

3. Для идентификатора я не понят, но в остальном я попробовал, как вы мне точно указали, и в результате я могу просто изменить имя каждого человека для регистрации пример: я записал в: Имя: Копе Имя: Брайан Когда я запускаю обновление, я могу редактировать только Копе, а неБрайан. Поэтому я могу изменить только все имена каждого человека, а не все элементы

4. Давайте пока забудем об идентификаторе. Вы должны использовать старые имена в предикате, а не новые. Новые еще не сохранены, поэтому вы не сможете получить нужный вам объект таким образом. Попробуйте сохранить старое имя и prenom при настройке представления, а затем использовать эти значения для предиката.

5. Здравствуйте, извините, что снова возвращаюсь к вам, я попробовал несколько вещей. Вы правы в отношении предиката, из-за которого возникает ошибка. в итоге я не могу изменить имя не по имени. пример, если в моем представлении таблиц 15 человек. Я мог бы изменить имя всех этих людей, но я хочу изменить имя и фамилию каждого человека

Ответ №2:

Если индекс 0 находится вне диапазона, это означает, что массив пуст. Перед доступом к нему добавьте

 if test.isEmpty{
   return //the fetch request didn't return any values
}