#swift #realm
#swift #область
Вопрос:
Я очень новичок в программировании. Я пытаюсь обновить объект в своей базе данных Realm, но всегда получаю сообщение об ошибке. Я пытался найти проблему, но не могу найти никого с подобной проблемой.
Что я пытаюсь сделать, так это: у меня есть приложение для оценки игры. Он должен отображать имена на Tab1 и на Tab2 Я хочу дать пользователю возможность изменять имена игроков. Как только viewDidDisappear я хочу записать изменения в Realm.
Я уже выяснил, как обновить имена в базе данных. И он работает правильно с первого раза. Но как только я перехожу во второй раз к Tab2 и снова возвращаюсь к Tab1, я получаю сообщение «Первичный ключ не может быть изменен после вставки объекта».
Есть идеи?
class Games: Object {
@objc dynamic var game_id = UUID().uuidString
@objc dynamic var gameName: String = ""
var playerNames = List<String>()
override class func primaryKey() -> String? {
return "game_id"
}
}
class FirstPageVC: UIViewController {
@IBOutlet var playerNameLabels: [UILabel]!
@IBOutlet weak var gameNameLabel: UILabel!
let realm = try! Realm()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
let games = realm.objects(Games.self)
gameNameLabel.text = games[0].gameName
for i in 0...playerNameLabels.count - 1 {
playerNameLabels[i].text = games[0].playerNames[i]
}
}
}
class SecondPageVC: UIViewController {
@IBOutlet var playerNameTextbox: [UITextField]!
@IBOutlet weak var gameNameTextbox: UITextField!
@IBOutlet weak var numberOfIndex: UITextField!
let realm = try! Realm()
var playerNames: [String] = []
var gameName: String = ""
var game = Games()
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
if realm.objects(Games.self).count != 0 {
let games = realm.objects(Games.self)
gameNameTextbox.text = games[0].gameName
for i in 0...playerNameTextbox.count - 1 {
playerNameTextbox[i].text = games[0].playerNames[i]
}
}
}
@IBAction func addButton(_ sender: UIButton) {
gameName = gameNameTextbox.text!
for i in 0...playerNameTextbox.count - 1 {
playerNames.append(playerNameTextbox[i].text!)
}
let items = realm.objects(Games.self)
let number = Int(numberOfIndex.text!)
game.game_id = items[number!].game_id
game.gameName = gameName
game.playerNames.append(objectsIn: playerNames)
try! realm.write {
realm.add(game, update: .modified)
}
}
}
Комментарии:
1. Первичный ключ — это то, что однозначно идентифицирует объект в базе данных, поэтому, если вы измените это для объекта, он больше не может быть правильно идентифицирован, и это то, что сообщает вам ошибка. Лучшее решение для вас — добавить другое свойство, возможно, типа Int или UUID, и пусть это свойство будет первичным ключом, чтобы вы могли изменять имя. Я не могу помочь вам с точным способом сделать это, так как я не знаю Realm
Ответ №1:
Проблема заключается в структуре вашего объекта Realm. Все, что может быть изменено, не должно использоваться в качестве первичного ключа.
Также обратите внимание на документацию Realm
После добавления объекта с первичным ключом в область, первичный ключ не может быть изменен.
Чтобы подробнее остановиться на этом, часто рекомендуется отделить ключ объекта (например, первичный ключ) от остальных свойств объекта.
Вот как это сделать
class Games: Object{
@objc dynamic var game_id = UUID().uuidString
@objc dynamic var gameName: String = ""
var playerNames = List<String>()
override class func primaryKey() -> String? {
return "game_id"
}
}
UUID().UUIDString сгенерирует уникальную строку для каждого созданного объекта и будет выглядеть примерно так
CDEA69EA-AC84-4465-ABE3-DDA29D31B925
После создания объекта вы можете использовать его для загрузки этого конкретного объекта или обновления его свойств.
Смотрите объекты с первичными ключами
Вот как изменить название игры
let item = realm.object(ofType: Game.self, forPrimaryKey: "CDEA69EA-AC84-4465-ABE3-DDA29D31B925")!
try! realm.write {
game.gameName = "Pwn You!"
}
Комментарии:
1. Спасибо, Джей, за ваше предложение. Теперь я изменил свой код на основе ваших улучшений, но я по-прежнему получаю ту же проблему, как только хочу обновить новые значения. Вот мой код: game.game_id = «CDEA69EA-AC84-4465-ABE3-DDA29D31B925» game.GameName = GameName game.playerNames.append(objectsIn: имена игроков) попробуйте! realm.write { realm.add(game, update: .modified) }
2. @Flo Если вы получаете то же сообщение «Первичный ключ не может быть изменен после вставки объекта», то вы все еще пытаетесь изменить это, и его нельзя изменить. Убедитесь, что вы обновили свой объект, чтобы иметь другое свойство первичного ключа, а также убедитесь, что вы обновили
override class func primaryKey()
функцию, чтобы указывать на это новое свойство, а не на свойство GameName .3. Извините, я, возможно, слишком глуп, я не понимаю своей вины. Я отредактировал свой первоначальный вопрос с помощью нового кода, который я написал. Я добавил кнопку на свой VC и текстовое поле для добавления и изменения моих входных данных, но, тем не менее, при первом изменении моих данных в базе данных все работает и обновляется с новыми значениями, во второй раз происходит сбой с сообщением об ошибке.
4. @Flo Что происходит, так это то, что при первом переходе к контроллеру представления создается новый игровой объект
var game = Games()
. Затем он заполняется и записывается в realm . На этом этапе игровой объект теперь управляется. В следующий раз, когда вы перейдете к этому ViewController, игровой объект все еще находится в области видимости, поэтому новый не создается. Поэтому, когда вы пытаетесь изменить его вне записи, происходит сбой. Похоже, что нет причины, котораяgame
является переменной класса, поскольку похоже, что целью является создание новой игры внутри функции добавления. Если это так, создайте переменную внутри функции addButton
Ответ №2:
Попробуйте это решение:
Замените код в viewDidDisappear после завершения цикла for следующим кодом:
if let gameInRealm = realm.objects(Game.self).first{
try! realm.write {
gameInRealm.gameName = gameName
gameInRealm.playerNames = playerNames
}
}else{
game.gameName = gameName
game.playerNames.append(objectsIn: playerNames)
realm.add(game)
}
Объяснение (при необходимости):
Код изменяет существующие свойства игры, если игровой объект существует. В противном случае он создает новый с новыми свойствами.
Следовательно, оператор else должен выполняться при первом выходе из SecondPageVC, а затем оператор if будет запускаться каждый раз, когда вы покидаете SecondPageVC.