Objective-C
iOS
Swift

Realmを使ってデータ管理【Swift編】-その1-

More than 3 years have passed since last update.

皆さん、Swiftでガリガリ開発してますか?

Objective-Cから変わりすぎて涙目になりながら開発してますが、

そんな中データ管理にSwiftでもMagicalRecord使おうかと思ってましたが

言語も新しくなった事だし、何か代わりがないかなぁと探していたところ

Realmに出会いました。

実際に使ってみたので、その使用感をお伝えできればと思います。


環境


  • x-code 6.0

  • swift

  • os:ios

  • realm 0.83


Realmとは

CoreDataやSQLiteに変わる次世代Mobileデータベースみたい。

Androidでも使えるので移植するときにインタフェースも統一されてて移植しやすいよね!

詳しく知りたい方はここを見ればいいと思います。


なぜRealmを選んだのか

iOSアプリを作ってると、まだAndroidアプリがなかったら必ずと言っていいほど

Androidアプリへの移植が話しにあがってきます。

そのときになるべく移植コストを減らしたいのでAndroid/iOSに対応しているライブラリを選びたいところです。

Realmは両対応してたのでとりあえず使ってみようと思いました。


インストール

公式がみればよほどの事がない限りインストールできるので

ここを参照して頑張ってインストールしましょう。


保存される場所

標準ではDocumets/default.realmに保存されます。

import Realm

// xxxx/Documents/default.realm
println(RLMRealm.defaultRealmPath())


オブジェクトの保存

今回の例では新しくBookオブジェクトを作って保存してみます。

Realmで扱うためにはRLMObjectを継承して、プロパティはdynamicで宣言します。

プロパティにdynamicがついてないと値が保存されません。


Book.swift

import Foundation

import Realm

class Book : RLMObject {
dynamic var isbn = ""
dynamic var name = ""
dynamic var price = 0
}


実際に保存してみます。

保存の仕方は明示的にTransactionで保存する方式とBlockで保存するがあります。

単一での保存と複数での保存どちらも対応しています。

import Realm

let realm = RLMRealm.defaultRealm()

// Bookオブジェクト生成.
let book = Book()
book.isbn = "999999"
book.name = "realm sample"
book.price = 100

// Bookオブジェクトを保存.
realm.beginWriteTransaction()
realm.addObject(book)
realm.commitWriteTransaction()

// 先ほどのBookオブジェクトを取得
// Class.allObjectsで全オブジェクト取得.
for realmBook in Book.allObjects() {
// book name:realm sample
println("book name:\((realmBook as Book).name)")
}

let book2 = Book()
book2.isbn = "999998"
book2.name = "realm tutorial 1"
book2.price = 1000

// Blockでの保存の仕方.
realm.transactionWithBlock() {
realm.addObject(book2)
}

for realmBook in Book.allObjects() {
// book name:realm sample
// book name:realm tutorial 1
println("book name:\((realmBook as Book).name)")
}

という感じで簡単に保存できます。


オブジェクトの検索

先ほどの保存したBookオブジェクトを検索してみましょう。

検索方法は文字列とNSPredicateの2パターンに対応しています。

どちらの方法でも戻り値はRLMResultsです。

let realm = RLMRealm.defaultRealm()

// 文字列検索.
// 戻り値はRLMArray!
let results = Book.objectsWhere("isbn = '999999'")
for realmBook in results {
// book name:realm sample
println("book name:\((realmBook as Book).name)")
}

// NSPredicate検索.
let results2 = Book.objectsWithPredicate(NSPredicate(format: "isbn = %@", "999998"))
for realmBook in results2 {
// book name:realm tutorial 1
println("book name:\((realmBook as Book).name)")
}

Objective-C版ではobjectForPrimaryKey:primaryKey:というメソッドで

一つだけとってくることが可能ですが、Swift版にはないみたいなので

ちょっと使いやすくするためにBookオブジェクトを拡張してみましょう。

追記 2015/03/30

コメントで


Swiftの規則により、objectForPrimaryKey:primaryKey:というクラスメソッドはファクトリメソッド扱いになる>ので、convenience init!(forPrimaryKey primaryKey: AnyObject!)というコンストラクタにマッピングされています。

そのため、プライマリキーでオブジェクトを取ってくる場合は、let book = Book(forPrimaryKey: "999999")のように書きます。


との貴重な情報を頂いたのでむしろ拡張しなくてObject(forPrimaryKey:value)を使いましょう!


Book.swift

import Foundation

import Realm

class Book : RLMObject {
dynamic var isbn = ""
dynamic var name = ""
dynamic var price = 0

// isbnを指定して結果を返す.
class func find(isbn:String) -> Book? {
let result = Book.objectsWithPredicate(NSPredicate(format: "isbn = %@", isbn))
if let books = result {
return books.firstObject() as? Book
}
return nil
}
}


定義したメソッドを実行したいですが、この通り実行すると

migration errorが発生するので、使い方だけ感じ取ってください。

Migrationに関しては別途記述します。

// Migration Errorが発生する!

let realm = RLMRealm.defaultRealm()

// 実装したメソッドで検索.
// すっきり!
// 標準で実装されているので下記が正しい
//if let book = Book.find("999998") {
// // book name:realm tutorial 1
// println(book.name)
//}

// こっちがBest!
if let book = Book(forPrimaryKey: "999998") {
// book name:realm tutorial 1
println(book.name)
}


オブジェクトの削除

先ほど定義したオブジェクトを削除してみましょう。

let realm = RLMRealm.defaultRealm()

// 先ほど保存したisbn=999999を削除.
realm.beginWriteTransaction()
realm.deleteObjects(Book.objectsWhere("isbn = '999999'"))
realm.commitWriteTransaction()


オブジェクトの更新

追加・削除できるなら当然更新もやりたい!

ただ今のままだとprimary keyの指定がないので更新できません。

ですので、primary keyの指定を行います。

class funcで定義されてますので、そちらをoverrideしましょう。


Book.swift

import Foundation

import Realm

class Book : RLMObject {
dynamic var isbn = ""
dynamic var name = ""
dynamic var price = 0

// primaryKeyの指定.
override class func primaryKey() -> String {
return "isbn"
}

// isbnを指定して結果を返す.
class func find(isbn:String) -> Book? {
let result = Book.objectsWithPredicate(NSPredicate(format: "isbn = %@", isbn))
if let books = result {
return books.firstObject() as? Book
}
return nil
}
}


今回はisbnをprimaryKeyにしています。

更新する際にはaddOrUpdateObjectメソッドを使います。

primaryKeyのオブジェクトが存在しない場合は追加、存在する場合は更新になります。

let realm = RLMRealm.defaultRealm()

let book3 = Book()
book3.isbn = "999997"
book3.name = "swift or objective-c"
book3.price = 1500

realm.beginWriteTransaction()
// PrimaryKeyの指定がない場合はException発生!.
realm.addOrUpdateObject(book3)
realm.commitWriteTransaction()

let results = Book.objectsWhere("isbn = '999997'")
for realmBook in results {
// book name:swift or objective-c
// book price:1500
println("book name:\((realmBook as Book).name)")
println("book price:\((realmBook as Book).price)")
}

let book3_v2 = Book()
book3_v2.isbn = "999997"
book3_v2.name = "swift1.0 or swift1.1"
book3_v2.price = 2500

realm.beginWriteTransaction()
realm.addOrUpdateObject(book3_v2)
realm.commitWriteTransaction()

let results_v2 = Book.objectsWhere("isbn = '999997'")
for realmBook in results_v2 {
// 更新されてる!
// book name:swift1.0 or swift1.1
// book price:2500
println("book name:\((realmBook as Book).name)")
println("book price:\((realmBook as Book).price)")
}

たいした事書いてないになぜかめちゃくちゃ長くなったのでRealmを使ってデータ管理【Swift編】-その2-に続きます。