[Realm][Swift4対応 完全保存版] 2.モデルの定義

はじめに

第2回目となる今回は、Realmにおけるモデルの定義について説明します。

過去記事は以下を参照ください。

環境

Xcode:9.4.1
Swift:4.1.2
Realm:3.7.4

目次

モデルを定義する

モデル定義をするには RealmSwiftをimportした上でObjectを継承したクラスを作成する必要があります。

またモデルに定義するデータは、「@objc dynamic var」を付けたプロパティとして宣言をします。

例として、スポーツのチームをモデル定義する例を示します。

import Foundation
import RealmSwift  // 必須

/// チームモデル
class Team: Object {  // Objectを継承してクラスを作成
    // 各プロパティは@objc dynamic varを付けて宣言
    // チーム名
    @objc dynamic var name : String = ""
    // 作成日時
    @objc dynamic var createdDate : Date = NSDate() as Date
    // 更新日時
    @objc dynamic var updatedDate : Date = NSDate() as Date
}

上記のTeamクラスは、チーム名、作成日時、更新日時の3つのデータを持つことができます。

1対1の関係を持つモデルを定義する

先ほど定義したTeamモデルに、1対1の関係を持つモデルを定義する方法を説明します。

Teamモデルはスポーツのチームを表しますので、ここでは「コーチ」のモデルを定義して1対1の関係を持たせる例を示します。

/// チームモデル
class Team: Object {
    // チーム名
    @objc dynamic var name : String = ""
    // コーチ(★★★ 追加したプロパティ ★★★)
    @objc dynamic var coach: Coach? //Coachモデルと1対1の関係
    // 作成日時
    @objc dynamic var createdDate : Date = NSDate() as Date
    // 更新日時
    @objc dynamic var updatedDate : Date = NSDate() as Date
}

/// ★★★ コーチモデル ★★★ 
class Coach: Object {
    // コーチ名
    @objc dynamic var name : String = ""
    // コーチ歴(単位:年)
    @objc dynamic var year : Int = 1
}

上記のコードでは、新しくCoachモデルを定義しています。Coachモデルではコーチ名とコーチ歴をデータとして持つことができるようにプロパティを定義しています。

TeamとCoachに1対1の関係を持たせるために、Teamモデルにcoachプロパティを定義しています。このときのデータ型は新しく定義したCoachとし、?を付けオプショナル型とします。

※オプショナル型とはnilを許容するデータ型です。

1対Nの関係を持つモデルを定義する

次にTeamモデルに、1対Nの関係を持つモデルを定義する方法を説明します。

1つのチームには複数人の選手を持たせたいので、ここでは「選手」のモデルを定義して1対Nの関係を持たせる例を示します。

/// チームモデル
class Team: Object {
    // チーム名
    @objc dynamic var name : String = ""
    // コーチ
    @objc dynamic var coach: Coach? //Coachモデルと1対1の関係
    // 選手(★★★ 追加したプロパティ ★★★)
    let players = List<Player>()    //Playerモデルと1対Nの関係
    // 作成日時
    @objc dynamic var createdDate : Date = NSDate() as Date
    // 更新日時
    @objc dynamic var updatedDate : Date = NSDate() as Date
}

///★★★ 選手モデル ★★★ 
class Player: Object {
    // 選手名
    @objc dynamic var name : String = ""
    // 身長(単位:m)
    @objc dynamic var  height : Double = 0.0
    // 体重(単位:kg)
    @objc dynamic var  weight : Double = 0.0
    // メモ
    @objc dynamic var memo : String = ""
}

上記のコードでは、新しくPlayerモデルを定義しています。Playerモデルでは選手名と身長、体重、メモをデータとして持つことができるようにプロパティを定義しています。

TeamとPlayerに1対Nの関係を持たせるために、Teamモデルにplayersプロパティを定義しています。このときのデータ型はList型のPlayerとします。

ポイント

1対1のモデルは、オプショナル型のプロパティとして宣言
1対Nのモデルは、List型のプロパティとして宣言

使用可能なデータ型について

プロパティで使用可能なデータ型には次のものがあります。

  • Bool
  • Int, Int8, Int16, 1nt32, Int64
  • Double, Float
  • String
  • Date
  • Data

String, Date, DataはSwift標準のオプショナル型として宣言することも可能です。

数値型でオプショナルにしたい場合は専用のRealmOptional型を使用します。

それぞれの例を以下に示します。

/// オプショナルプロパティの使用例
class OptionalSample : Object {
    // Swift標準のオプショナルの例
    @objc dynamic var name : String? = nil
    
    // RealmSwiftが提供するオプショナルの例
    let age = RealmOptional<Int>()
}

 ※RealmOptional型にする場合は「let」で宣言する必要があるので注意してください。

各データ型におけるオプショナルと非オプショナルのプロパティ宣言例を以下に示します(本家Realmのサイトからの引用で、一部日本語に訳しています)。

データ型 非オプショナル オプショナル
Bool @objc dynamic var value = false let value = RealmOptional<Bool>()
Int @objc dynamic var value = 0 let value = RealmOptional<Int>()
Float @objc dynamic var value: Float = 0.0 let value = RealmOptional<Float>()
Double @objc dynamic var value: Double = 0.0 let value = RealmOptional<Double>()
String @objc dynamic var value = "" @objc dynamic var value: String? = nil
Data @objc dynamic var value = Data() @objc dynamic var value: Data? = nil
Date @objc dynamic var value = Date() @objc dynamic var value: Date? = nil
Object n/a: must be optional @objc dynamic var value: Class?
List let value = List<Type>() 非オプショナルのみ
LinkingObjects let value = LinkingObjects(fromType: Class.self, property: "property") 非オプショナルのみ

主キーを定義する

モデル定義元になっているObjectクラスにはprimaryKeyメソッドがあります。

主キーを作成するには、このメソッドをオーバーライドし、主キーの名称をを返却するように実装します。

例えば、すでに定義したTeamクラスにIdプロパティを定義し、主キーに設定する場合は以下のように実装します。

/// チームクラス
class Team: Object {
    // id(★★★ 主キーにするプロパティ ★★★)
    @objc dynamic var id : Int = 0
    // チーム名
    @objc dynamic var name : String = ""
    // コーチ
    @objc dynamic var coach: Coach? //Coachモデルと1対1の関係
    // 選手
    let players = List<Player>()    //Playerモデルと1対Nの関係
    // 作成日時
    @objc dynamic var createdDate : Date = NSDate() as Date
    // 更新日時
    @objc dynamic var updatedDate : Date = NSDate() as Date
    
    //★★★ 主キーの作成 ★★★ 
    override static func primaryKey() -> String? {
        return "id"
    }
}

インデックスを作成する

インデックスを作成することで、比較演算子を使用したクエリを高速化することができます。

モデル定義元になっているObjectクラスにはindexedPropertiesメソッドがあります。

任意のプロパティをインデックス化するには、Objectクラスが持つindexedPropertiesメソッドをオーバーライドします。

例えば、すでに定義したTeamクラスのnameプロパティをインデックス化するには以下のように実装します。

/// チームクラス
class Team: Object {
    // id
    @objc dynamic var id : Int = 0
    // チーム名(★★★ インデックス化するプロパティ ★★★)
    @objc dynamic var name : String = ""
    // コーチ
    @objc dynamic var coach: Coach? //Coachモデルと1対1の関係
    // 選手
    let players = List<Player>()    //Playerモデルと1対Nの関係
    // 作成日時
    @objc dynamic var createdDate : Date = NSDate() as Date
    // 更新日時
    @objc dynamic var updatedDate : Date = NSDate() as Date
    
    // 主キーの作成
    override static func primaryKey() -> String? {
        return "id"
    }
    
    // ★★★ インデックスの作成 ★★★ 
    override static func indexedProperties() -> [String] {
        return ["name"]
    }
}

インデックスとして定義できるデータ型は String型, 整数型(Int, Int8など), Bool型, Date型です。

データを保存しないプロパティを定義する

モデルとしてのプロパティではなく、単にクラス内のプロパティとして定義したい場合があります。

このような場合はObjectクラスが持 ignoredProperties メソッドをオーバーライドします。

例えば、tempDataというプロパティをモデルデータ保存用ではなく、通常のプロパティとして定義したい場合は、以下のように実装します。

/// チームクラス
class Team: Object {
    // 通常のプロパティ
     @objc dynamic var tmpData = 0
    
    // 通常のプロパティとして使用できるように ignoredProperties をオーバーライド
    override static func ignoredProperties() -> [String] {
        return ["tmpData"]
    }
}

子モデルから親モデルへの関係を作成する

1対1や1対Nのモデルの関係は、親から子への一方通行です。

本記事で示した、TeamとCoachの場合は、Team→Coachの関係は成り立っていますが、Coach→Teamという関係は成り立っていません。

この問題を解決するために、逆関係を表す「リンキング・オブジェクト・プロパティ」というものを作成することができます。リンキング・オブジェクト・プロパティは、LinkingObjectsクラスを使用して作成します。

LinkingObjectsを使用する際の書式を以下に示します

let プロパティ名 = LinkingObjects(fromType: 親クラス.self, property: "親クラスが持つ子クラスのプロパティ名")

実際のコード例を以下に示します。この例では、子クラス「Coach」から親クラス「Team」への関係をLinkingObjectsを使用して作成しています。

class Team: Object {
    // id
    @objc dynamic var id : Int = 0
    // チーム名
    @objc dynamic var name : String = ""

    // コーチ
    @objc dynamic var coach: Coach? //Coachモデルと1対1の関係
}

class Coach: Object {
    // コーチ名
    @objc dynamic var name : String = ""
    // コーチ歴(単位:年)
    @objc dynamic var year : Int = 1
    
    // ★★★ 親(Team)へのリレーション(関係)を作成する ★★★
    let owner = LinkingObjects(fromType: Team.self, property: "coach")
}

次回

モデルの作成方法について説明します。

One Reply to “[Realm][Swift4対応 完全保存版] 2.モデルの定義”

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください