質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Realm

RealmとはSQLiteやCore Dataに代わるモバイルデータベースです。iOSとAndroidの両方でサポートされています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

7232閲覧

Realm Swift でのプライマリキー重複エラーについて

Riscait

総合スコア35

Realm

RealmとはSQLiteやCore Dataに代わるモバイルデータベースです。iOSとAndroidの両方でサポートされています。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

1クリップ

投稿2017/07/26 13:08

編集2017/07/27 09:20

###前提・実現したいこと
SwiftにてiPhoneアプリを作成しました。

確実な再現ができないので、根本的な原因がわからないのですが、

①Realmにていくつかのデータを追加する
②テーブルビューのセルスワイプで削除を行う
③新しくデータを追加しようとする、
のステップでエラーが出ました。(何回試みてもエラーが出ない時もあります)

###発生している問題・エラーメッセージ

Terminating app due to uncaught exception 'RLMException', reason: 'Attempting to create an object of type 'RegisteredData' with an existing primary key value '4'.'

###該当のソースコード
####オブジェクト

swift

1import RealmSwift 2 3class RegisteredData: Object { 4 5 dynamic var id = 0 6 dynamic var useDate = NSDate() 7 dynamic var usedFor = "" 8 dynamic var usePeriod = 0.0 9 10 override static func primaryKey() -> String? { 11 return "id" 12 } 13}

####追加

swift

1 // DBに追加するデータを用意 2let registeredData = RegisteredData() 3registeredData.id = self.newId(model: registeredData)! 4registeredData.useDate = self.useDate as NSDate 5registeredData.usedFor = self.usedFor 6registeredData.usePeriod = self.usePeriod 7 8// DBに書き込み 9try! self.realm.write { 10 self.realm.add(registeredData) 11} 12 13 func newId<T: Object>(model: T) -> Int? { 14 guard let key = T.primaryKey() else { return nil } 15 16 if let last = realm.objects(T.self).last, 17 let lastId = last[key] as? Int { 18 return lastId + 1 19 } else { 20 return 0 21 } 22 }

####削除

swift

1 // Delete ボタンが押された時に呼ばれるメソッド 2 func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) { 3 4 if editingStyle == UITableViewCellEditingStyle.delete { 5 6 // データベースから削除する 7 try! realm.write { 8 self.realm.delete(self.registeredData[indexPath.row]) 9 tableView.deleteRows(at: [indexPath as IndexPath], with: UITableViewRowAnimation.fade) 10 } 11 } 12 }

###試したこと
データを追加するたびに最後のid+1をIDにしているので、重複は通常しなそうですが、削除した際に何らから要因でズレてしまうのでしょうか?

ご教示いただければ嬉しいです。

###補足情報(言語/FW/ツール等のバージョンなど)
Xcode 8.3.3 Swift 3.1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

一般的なデータベースと同様に、Realmは保存されたオブジェクトの順番を保持していません(List型を除く)。ですので

if let last = realm.objects(T.self).last,

このコードは最後に追加したオブジェクト、つまりキーの値が最大のものを取得しようとしていると思いますが、どのオブジェクトが返るかはわかりません。

オブジェクトを削除した後にエラーが起こるのは内部の実装の都合によります。基本的に追加していくだけなら順番に記録されるためです(あくまで現在の実装の都合で順序を保証しているわけではありません)。

対処法としては他のデータベースのときと同様に、常に結果をソートして取得することになります。
今回はプライマリキーの順序が問題なので、idプロパティの昇順でソートすると解決すると思います。

投稿2017/07/27 09:00

退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Riscait

2017/07/27 09:24

ご回答ありがとうございます。 `if let last = realm.objects(T.self).last,` では、最後に追加したデータを正しく取得できない可能性があるため、この処理の前に `id` の昇順でソートすることが対処法ということで解釈間違い無いでしょうか? 早速、実装してみます。 ありがとうございました。 (エラーが起こるタイミングが違っていたので訂正しました。 正しくは、セルの削除後に新しくデータを追加しようとした時、です。)
Riscait

2017/07/27 11:24

if let last = realm.objects(T.self).sorted(byKeyPath: "id", ascending: true).last としたところ、問題なく削除と追加ができております。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問