こんにちは。スマートフォンエンジニアのまさです。
最近サーバとやりとりをするアプリを作成していますが、APIをなんども叩くアプリでは、
どうしてもローディングが多くなりがちで、ユーザーにストレスを与えてしまいます。
何度も叩く必要のないAPIはレスポンスをRealmに保存しておいて、より快適なアプリを実現したいですね。
今回は、Realmで多対多のモデルを作る方法をご紹介します。
Realmのインストール
まずは、podでrealmを入れます。
$ pod init
$ vi Podfile
pod 'RealmSwift' # 追加
$ pod install
Realmのモデルを作成する
今回は複数の子供が複数のグループに所属しているモデルを作ってみようと思います。
基本的には、多対多は、中間テーブルを用意し、それぞれのテーブルに一対多にすることで実現します。
import RealmSwift
class Kid: Object {
dynamic var id = 0
dynamic var name = ""
var kidGroups = List<KidGroup>()
override static func primaryKey() -> String? {
return "id"
}
}
class KidGroup: Object {
dynamic var group: Group?
dynamic var kid: Kid?
let ownerKid = LinkingObjects(fromType: Kid.self, property: "kidGroups")
let ownerGroup = LinkingObjects(fromType: Group.self, property: "kidGroups")
}
class Group: Object {
dynamic var id = 0
dynamic var name = ""
var kidGroups = List<KidGroup>()
override static func primaryKey() -> String? {
return "id"
}
}
Realmのモデルに実際にデータを入れてみる
モデルクラスを用意したら、データを入れてみます。
テストなので、viewDidLoadに入れてログを出して確認しています。
override func viewDidLoad() {
super.viewDidLoad()
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let path = paths[0] + "/test.realm"
let url = URL(fileURLWithPath: path)
let fileManager = FileManager.default
if fileManager.fileExists(atPath: path) {
try! FileManager.default.removeItem(atPath: path)
try! FileManager.default.removeItem(atPath: path + ".lock")
try! FileManager.default.removeItem(atPath: path + ".management")
}
let realm = try! Realm(fileURL: url)
let kid = Kid()
kid.id = 1
kid.name = "たろう"
let kid2 = Kid()
kid2.id = 2
kid2.name = "じろう"
let group = Group()
group.id = 101
group.name = "たぬき組"
let group2 = Group()
group2.id = 102
group2.name = "きつね組"
let kidGroup = KidGroup()
kidGroup.group = group
kidGroup.kid = kid
kid.kidGroups.append(kidGroup)
group.kidGroups.append(kidGroup)
let kidGroup2 = KidGroup()
kidGroup2.group = group2
kidGroup2.kid = kid2
kid2.kidGroups.append(kidGroup2)
group2.kidGroups.append(kidGroup2)
let kidGroup3 = KidGroup()
kidGroup3.group = group
kidGroup3.kid = kid2
kid2.kidGroups.append(kidGroup3)
group.kidGroups.append(kidGroup3)
try! realm.write() {
realm.add(kid)
realm.add(kid2)
}
}
正しく保存されているか確認してみる
それでは、正しく参照できるのか、また中間テーブルが削除されれば
きちんと関連がきれるのか、テストしてみようと思います。下記のコードをviewDidLoadの下側に追記してみます。
print("Kidのオブジェクトをすべて取得する")
let kids = realm.objects(Kid.self)
print(kids.count)
print(kids[0].kidGroups.count)
print(kids[1].kidGroups.count)
print("Kidのidが2のオブジェクトを取得する")
let kidsId_2 = realm.objects(Kid.self).filter("id = 2")
print(kidsId_2[0].kidGroups.count)
print(kidsId_2[0].kidGroups[0].group?.name ?? "")
print(kidsId_2[0].kidGroups[1].group?.name ?? "")
print("Groupがきつね組に所属しているkidを取得する")
let groupsId_102 = realm.objects(Group.self).filter("id = 102")
print(groupsId_102[0].kidGroups.count)
print(groupsId_102[0].kidGroups[0].kid?.name ?? "")
print("Kidじろうのgroupきつね組との関連を切ってみる")
try! realm.write {
realm.delete(groupsId_102[0].kidGroups[0])
}
print("きちんと関連が切れているか確認する groupから参照した場合")
let updateGroupsId_102 = realm.objects(Group.self).filter("id = 102")
print(updateGroupsId_102[0].kidGroups.count)
print("きちんと関連が切れているか確認する kidから参照した場合")
let updateKidsId_2 = realm.objects(Kid.self).filter("id = 2")
print(updateKidsId_2[0].kidGroups.count)
print(updateKidsId_2[0].kidGroups[0].group?.name ?? "")
正しく参照できているようです。中間テーブルを削除して関連を切れば、kidから参照しても、groupから参照しても正しい結果と
なっているのがわかります。多対多を実現させることができました。
まだまだRealm初心者なので、今後もいろいろと試してみようと思います。
もしここがおかしいとか、こういう書き方もあるよなどありましたら、ご指摘頂けると幸いです。