SwiftでReactiveCocoa+Realm を試してみる


ということで、何となく`Swift`で`ReactiveCocoa`+`Realm`を使ったサンプルを作ってみた。

# 使用したライブラリ

|Library|Version|備考|
|-------|-------|---|
|ReactiveCocoa|2.4.4|-|
|Realm  |0.92.2|RealmSwiftを仕様|

## ReactiveCocoaについて

ReactiveCocoaを使うにあたって、以下を参考にした。
http://blog.scottlogic.com/2014/07/24/mvvm-reactivecocoa-swift.html

# サンプルコード

## Model

```
import RealmSwift

class Note: Object {
    dynamic var text = ""
}
```

## ViewModel

```
import UIKit
import RealmSwift

class ViewModel: NSObject {

    var token: NotificationToken?
    var notes = Realm().objects(Note)
    let updateSignal: RACSubject
    
    override init() {
        updateSignal = RACSubject()
        super.init()
        // RealmのNotificationBlockをReactiveCocoaとうまく使うにはどうするのがいいのか
        // 取りあえずRACSubjectを使ってSignalを送る用にしたみが...
        token = Realm().addNotificationBlock { [unowned self] note, realm in
            self.updateSignal.sendNext(nil)
        }
    }
    
    func numberOfSections() -> Int {
        return 1
    }
    
    func numberOfItemsInSection(seciont: Int) -> Int {
        return Int(notes.count)
    }
    
    func deleteAtIndex(index: Int) {
        let realm = Realm()
        realm.write {
            realm.delete(self.notes[index])
        }
    }
        
    func noteAtIndex(index: Int) -> Note? {
        return notes[index]
    }
}
```

## TableViewController

```
import UIKit
import RealmSwift

class ListViewController: UITableViewController {

    var selectedUuid: String?
    var viewModel: ViewModel?
    var tableViewDelegate: AnyObject?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.viewModel = ListViewModel()
        viewModel?.updateSignal.subscribeNext({[weak self](value:AnyObject?) -> Void in
            self!.tableView.reloadData()
        })
        
        // DelegateProxyはこんな感じでいいのだろうか
        // withProtocol: の引数はNSProtocolFromStringを使ってなかば無理矢理に
        tableViewDelegate = RACDelegateProxy(withProtocol:NSProtocolFromString("UITableViewDelegate"))
        tableViewDelegate?.rac_signalForSelector("tableView:didSelectRowAtIndexPath:").subscribeNext({ (RACTuple tuple) -> Void in
            println("\(tuple)")
        })
        tableView.delegate = tableViewDelegate as? UITableViewDelegate

    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    // MARK: - Table view data source

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return viewModel!.numberOfSections()
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return viewModel!.numberOfItemsInSection(section)
    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("BasicCell", forIndexPath: indexPath) as! UITableViewCell
        
        if let note = viewModel!.noteAtIndex(indexPath.row) {
            cell.textLabel?.text = note.text
        }
        return cell
    }

    // Override to support conditional editing of the table view.
    override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool {
        // Return NO if you do not want the specified item to be editable.
        return true
    }
    
    // Override to support editing the table view.
    override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {
        
        if editingStyle == .Delete {
            // Delete the row from the data source
            viewModel?.deleteAtIndex(indexPath.row)
        } else if editingStyle == .Insert {
            // Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
        }    
    }

    
}
```