Edited at

Realmに初期データを入れてマスターとして使いたい

More than 1 year has passed since last update.

・モチベーション

 Realmにあらかじめデータを投入した状態で使用したかった

・前提

 id, value程度の単純なモデル


今回の成果物

こちらに置いてあります

negibouze/Realm-Register-Initial-Data


1. プロジェクトにRealmを追加する

省略


2. モデルを作成する


step1. 基底のクラスを作成する


DBBase.swift

import Foundation

import RealmSwift

/* 基底 */
class DBBase: Object {
dynamic var id = 0
dynamic var name = ""

override static func primaryKey() -> String? {
return "id"
}

override static func indexedProperties() -> [String] {
return ["id"]
}
}



step2. モデルを作成する

基本的なパターン


Prefecture.swift

import Foundation

import RealmSwift

/* 都道府県 */
class Prefecture: DBBase {
}


サブモデルを持っているパターン


Owner.swift

import Foundation

import RealmSwift

/* オーナー */
class Owner: DBBase {
var dogs = List<Dog>()
}



3. シード(データ)を作成する


step1. protocolを作成する


RealmSeed.swift

protocol RealmSeed {

associatedtype SeedType: DBBase
static var values: [[Any]] { get }
static func items() -> [SeedType]
}

extension RealmSeed {
static func items() -> [SeedType] {
return values.map { val in
let t = SeedType()
t.id = val[0] as! Int
t.name = val[1] as! String
return t
}
}
}



step2. structを作成する

!この方法だとデータ数が5,000件くらいで結構つらいです。


PrefectureSeed.swift

struct PrefectureSeed: RealmSeed {

typealias SeedType = Prefecture
static var values: [[Any]] {
return PrefectureData.data
}
}

struct PrefectureData {
static let data: [[Any]] = [
[1, "北海道"],
[2, "青森県"],
[3, "岩手県"],
[4, "宮城県"],
中略
[47, "沖縄県"]
]
}



OwnerSeed.swift

import RealmSwift

struct OwnerSeed: RealmSeed {
typealias SeedType = Owner
static var values: [[Any]] {
return OwnerData.data
}
}

extension OwnerSeed {
static func items() -> [SeedType] {
return values.map { val in
let id = val[0] as! Int
let t = SeedType()
t.id = id
t.name = val[1] as! String
let l = { (_ ownerId: Int) -> [Dog] in
let data = DogData.get(ownerId)
return data.map { v in
let d = Dog()
d.id = v[0] as! Int
d.name = v[1] as! String
return d
}
}(id)
t.dogs = List<Dog>(l)
return t
}
}
}

struct OwnerData {
static var data: [[Any]] {
return [
[1, "オーナー1"],
[2, "オーナー2"],
[3, "オーナー3"],
[4, "オーナー4"],
[5, "オーナー5"]
]
}
}

struct DogData {
static let data: [Int: [[Any]]] = [
1: [
[11, "オーナー1の犬1"],
[12, "オーナー1の犬2"],
[13, "オーナー1の犬3"],
[14, "オーナー1の犬4"],
[15, "オーナー1の犬5"]
],
2: [
[21, "オーナー2の犬1"],
[22, "オーナー2の犬2"],
[23, "オーナー2の犬3"]
]
以下略
]

static func get(_ ownerId: Int) -> [[Any]] {
return data[ownerId]!
}
}



4. Initializerを作成する


RealmInitializer.swift

import Foundation

import RealmSwift

struct RealmInitializer {

static func setUp() {
// シードデータ作成
insertSeedData(PrefectureSeed())
insertSeedData(OwnerSeed())
print(Realm.Configuration.defaultConfiguration.fileURL!)
}

private static func delete<T: RealmSeed>(_ seed: T) where T.SeedType: DBBase {
// realm
let realm = try! Realm()
try! realm.write {
realm.delete(realm.objects(T.SeedType.self))
}
}

// Realmファイルを作成する
private static func insertSeedData<T: RealmSeed>(_ seed: T) where T.SeedType: DBBase {
// realm
let realm = try! Realm()
try! realm.write {
T.items().forEach { val in
realm.add(val, update: true)
}
}
}
}



5. ビルドする


AppDelegate.swift

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// ここに追加
RealmInitializer.setUp()
return true
}
中略
}



6. データ確認

コンソールに出力されたパスを開く

open /Users/...

default.realmをRealm Browserで開くと、こんな感じのデータが登録されています。

・prefecture

・owner

・dog


7. 作ったファイルを使う


step1. 対象プロジェクトにdefault.realmをコピーする

作成したデータを使いたいプロジェクトに6.のdefault.realmをコピーする


step2. AppDelegateに処理を追加する


AppDelegate.swift

@UIApplicationMain

class AppDelegate: UIResponder, UIApplicationDelegate {

var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// ここに追加
copyRealm()
return true
}
中略
}

extension AppDelegate {
// Realmファイルをコピーする
fileprivate func copyRealm() {
let defaultRealmPath = Realm.Configuration.defaultConfiguration.fileURL!
// 存在する場合は何もしない
if FileManager.default.fileExists(atPath: defaultRealmPath.path) {
return
}
let bundleRealmPath = Bundle.main.url(forResource: "default", withExtension: "realm")
do {
try FileManager.default.copyItem(at: bundleRealmPath!, to: defaultRealmPath)
} catch let error {
print("error copying realm file: \(error)")
}
}
}



8. おまけ

事前データの用意でサブモデルのデータを紐づける際に(例だとOwnerとDog)、

[1, 11, "オーナー1の犬1"]

[1, 12, "オーナー1の犬2"]
[1, 13, "オーナー1の犬3"]
[1, 14, "オーナー1の犬4"]
[1, 15, "オーナー1の犬5"]
[2, 21, "オーナー2の犬1"]
[2, 22, "オーナー2の犬2"]
[2, 23, "オーナー2の犬3"]

こういうのを

[

1: [
[11, "オーナー1の犬1"],
[12, "オーナー1の犬2"],
[13, "オーナー1の犬3"],
[14, "オーナー1の犬4"],
[15, "オーナー1の犬5"]
],
2: [
[21, "オーナー2の犬1"],
[22, "オーナー2の犬2"],
[23, "オーナー2の犬3"]
]
]

こうしたかったので、こういうのを用意しました。