Realm × Swift2 でidでデータを管理する


## 目的
前回の記事:[Realm × Swift2 でデータを保存してみる](http://qiita.com/_ha1f/items/08bbed8b3dc6f9f6cadd)の続き。
primaryKeyとしてidを用いて、RealmのDB(データベース)を作成すること
autoIncrement(に見える実装)を目指す。

続き → [Realm × Swift2 でシームレスに画像を保存する](http://qiita.com/_ha1f/items/593ca4f9c97ae697fc75)

## primaryKey
Realmでは、primaryKeyとして特定のプロパティを指定することができる。今回はidを用いる。
primaryKeyは以下に示すstaticメソッドをoverrideして、文字列としてプロパティ名を返すことによって実現できる。

```
dynamic private var id = 0
override static func primaryKey() -> String? {
    return "id"
}
```

もちろんidもDBに保存され、これをプライマリキーとして、操作を行うことができる。

## primaryKeyによるupdate
primaryKeyは重複を許さない。すなわち、今まで使っていたメソッド

```swift
realm.add (object)
```

では、もし同じidを持った別のオブジェクトがaddに渡された時には、例外が発生してしまう。
そこで、別のメソッド、

```swift
User.realm.add (object: Object, update: Bool)
```

のupdateをtrueにして呼び出すと、idが重複した時は上書きをすることができる。

今回はidを自動生成しているが、間違えて自分でinitしてしまった時に、大切なデータを消してしまわないように、add(object)のままにしておく。


## autoIncrement

```
static func lastId() -> Int {
    if let user = realm.objects(User).last {
        return user.id + 1
    } else {
        return 1
    }
}
```

そう、見ての通り、一度読みだした後、一番後ろのidを加算してreturnしている。

Realmは仕組み上、値を読みだした後利用して初めてメモリに値がセットされるので、realm.objects(User).lastと書いても、objects(User)の結果が全てメモリにロードされるようなことはない。

一度ロードした後は、lastIdを持っておいてもいいかもしれませんね。

## プロパティのupdate
アップデート処理は、トランザクションの中で行う。
すなわち、

```
let users = User.loadAll()
print(users)
        
for user in users {
    try! User.realm.write {
        user.name = "全部はるふ"
    }
}
print(User.loadAll())
```

のようにする。

たとえaというインスタンスを保存したとしても、一度保存すると変換されてしまうので、updateするにはトランザクションの中で行う必要がある。
トランザクションの中で行うと、自動的にDBも変更される。

## 完成品

これまでのことを含めてできたコードを以下に示す。

```swift:User.swift
import RealmSwift

class User: Object {
    static let realm = try! Realm()
    
    dynamic private var id = 0
    dynamic var name = ""
    
    override static func primaryKey() -> String? {
        return "id"
    }
    
    static func create() -> User {
        let user = User()
        user.id = lastId()
        return user
    }
    
    static func loadAll() -> [User] {
        let users = realm.objects(User).sorted("id", ascending: false)
        var ret: [User] = []
        for user in users {
            ret.append(user)
        }
        return ret
    }
    
    static func lastId() -> Int {
        if let user = realm.objects(User).last {
            return user.id + 1
        } else {
            return 1
        }
    }
    
    // addのみ
    func save() {
        try! User.realm.write {
            User.realm.add(self)
        }
    }
    
    func update(method: (() -> Void)) {
        try! User.realm.write {
            method()
        }
    }
}


```

```swift:ViewController.swift
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let a = User.create()
        a.name = "はるふ"
        a.save()
        
        let b = User.create()
        b.name = "はるふす300"
        b.save()
        
        let users = User.loadAll()
        print(users)
        
        // 全てのnameを変更してしまう
        for user in users {
            user.update {
                user.name = "全部はるふ"
            }
        } 
        
        // 再度loadして表示(loadし直す必要はないが・・・)
        print(User.loadAll())
    }
}

```

ViewControllerからはidを意識することなく、実装されている。

エラーハンドリングは、今回もまた、何もしてない。。。

## 続き
1つめの記事 → [Realm × Swift2 でデータを保存してみる](http://qiita.com/_ha1f/items/08bbed8b3dc6f9f6cadd)
2つめの記事 → [Realm × Swift2 でidでデータを管理する](http://qiita.com/_ha1f/items/f6318e326434dbf83037)(この記事)
3つめの記事 → [Realm × Swift2 でシームレスに画像を保存する](http://qiita.com/_ha1f/items/593ca4f9c97ae697fc75)