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


皆さん、Swiftでガリガリ開発してますか？
Objective-Cから変わりすぎて涙目になりながら開発してますが、
そんな中データ管理にSwiftでもMagicalRecord使おうかと思ってましたが
言語も新しくなった事だし、何か代わりがないかなぁと探していたところ
[Realm](http://realm.io/)に出会いました。
実際に使ってみたので、その使用感をお伝えできればと思います。

## 環境
* x-code 6.0
* swift
* os:ios
* realm 0.83

## Realmとは
CoreDataやSQLiteに変わる次世代Mobileデータベースみたい。
Androidでも使えるので移植するときにインタフェースも統一されてて移植しやすいよね！
詳しく知りたい方は[ここ](http://realm.io/news/introducing-realm/)を見ればいいと思います。

## なぜRealmを選んだのか
iOSアプリを作ってると、まだAndroidアプリがなかったら必ずと言っていいほど
Androidアプリへの移植が話しにあがってきます。
そのときになるべく移植コストを減らしたいのでAndroid/iOSに対応しているライブラリを選びたいところです。
Realmは両対応してたのでとりあえず使ってみようと思いました。

## インストール

公式がみればよほどの事がない限りインストールできるので
[ここ](http://realm.io/docs/cocoa/0.86.3/#swift)を参照して頑張ってインストールしましょう。

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


```swift
import Realm

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

```

## オブジェクトの保存
今回の例では新しくBookオブジェクトを作って保存してみます。
Realmで扱うためにはRLMObjectを継承して、プロパティはdynamicで宣言します。
プロパティにdynamicがついてないと値が保存されません。

```swift:Book.swift
import Foundation

import Realm

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

実際に保存してみます。
保存の仕方は明示的にTransactionで保存する方式とBlockで保存するがあります。
単一での保存と複数での保存どちらも対応しています。

```swift
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です。

```swift
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)を使いましょう！

```swift: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に関しては別途記述します。

```swift
// 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)
}
```

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

```swift
let realm = RLMRealm.defaultRealm()

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

## オブジェクトの更新
追加・削除できるなら当然更新もやりたい！
ただ今のままだとprimary keyの指定がないので更新できません。
ですので、primary keyの指定を行います。
class funcで定義されてますので、そちらをoverrideしましょう。

```swift: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のオブジェクトが存在しない場合は追加、存在する場合は更新になります。

```swift
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-](http://qiita.com/jtemplej/items/0443571fdbffbcea3e7f)に続きます。