( Swift2 ) Alamofire + ObjectMapper + Realm でリクエストを Realm に自動的に保存する
Swift2.2, Xcode, Realm1.0.0
Alamofire と ObjectMapper を利用して API 通信を行っている場合、リクエストが成功した際のレスポンスをそのまま Realm に突っ込んで保存したいケースがあったりします。前回作成した API クライアント(Swift2 Alamofire + ObjectMapper で API クライアントを作成する)を拡張して、リクエストが成功したら結果を自動的に Realm に保存する方法を紹介したいと思います。
- Realm への保存を行うメソッドを定義した RealmStorable プロトコルを作成する
- ObjectMapper の Mappable を採用した Entity に RealmStorable も採用する
- API のリクエスト成功時の formJson メソッドを拡張して、レスポンスを Realm に保存する
1. Realm への保存を行うメソッドを定義した RealmStorable プロトコルを作成する
RealmStorable を採用した Entity は API のリクエストを自動的に Realm に保存するといった作戦です。
- RealmStorable.swift
import RealmSwift /** API のレスポンスを自動的に Realm に保存したいエンティティに設定するプロトコル */ protocol RealmStorable { static func store(object: Object) } extension RealmStorable { // store メソッドは APIのリクエスト成功時の fromJson メソッド内で呼び出されます static func store(object: Object) { let realm = try! Realm() try! realm.write { realm.add(object, update: true) } } }
2. ObjectMapper の Mappable を採用した Entity に RealmStoreble も採用する
今回は、 id で本を検索したら、タイトルと著者をレスポンスで返す API を例にしたいと思います。
API.call(Endpoint.Book.Find(1))
といった形でリクエストを行う- レスポンスは
{ id: 1, title: "炊きたてのご飯", author: "山田太郎" }
といった形式で返却される
上記の場合、本のデータとなる Entity は ObjectMapper を利用して以下のように定義できます。
- BookEntity.swift
import ObjectMapper class BookEntity: Mappable { var id: Int? var title = "" var author = "" required init?(_ map: Map) {} func mapping(map: Map) { self.id <- map["id"] self.title <- map["title"] self.author <- map["author"] } }
BookEntity を Realm に保存したいので BookEntity に Realm の Object を採用します。ObjectMapper の公式サイトに記載がありますが ObjectMapper と Realm を共存させたい時は以下のように書きます。
import RealmSwift import ObjectMapper class BookEntity: Object, Mappable { dynamic var id: Int? dynamic var title = "" dynamic var author = "" override static func primaryKey() -> String { return "id" } required convenience init?(_ map: Map) { self.init() } func mapping(map: Map) { self.id <- map["id"] self.title <- map["title"] self.author <- map["author"] } }
これで ObjectMapper でマッピングされた Entity を Realm に保存できるようになりました。後は Realm に保存したい Entity なので RealmStoreble を採用してあげます。
import RealmSwift import ObjectMapper class BookEntity: Object, Mappable, RealmStoreble { dynamic var id: Int? dynamic var title = "" dynamic var author = "" override static func primaryKey() -> String { return "id" } required convenience init?(_ map: Map) { self.init() } func mapping(map: Map) { self.id <- map["id"] self.title <- map["title"] self.author <- map["author"] } }
3. API のリクエスト成功時の formJson メソッドを拡張して、レスポンスを Realm に保存する
最後に、前回の記事で作成した APIProtocol の fromJson メソッドを RealmStoreble 用に拡張します。
- APIProtocol.swift
import Alamofire import ObjectMapper protocol RequestProtocol: URLRequestConvertible { ... 省略 func fromJson(json: AnyObject) -> Result<ResponseType, NSError> } extension RequestProtocol { ... 省略 func fromJson(json: AnyObject) -> Result<ResponseType, NSError> { ... 省略 } } extension RequestProtocol where ResponseType: Mappable { func fromJson(json: AnyObject) -> Result<ResponseType, NSError> { ... 省略 } } extension APIUsable where ResponseType: protocol<RealmStorable, Mappable> { func fromJson(json: AnyObject) -> Result<ResponseType, NSError> { guard let value = Mapper<ResponseType>().map(json) else { let errorInfo = [ NSLocalizedDescriptionKey: "Mapping object failed" , NSLocalizedRecoverySuggestionErrorKey: "Rainy days never stay." ] let error = NSError(domain: "com.example.app", code: 0, userInfo: errorInfo) return .Failure(error) } guard let object = value as? Object else { let errorInfo = [ NSLocalizedDescriptionKey: "Realm object failed" , NSLocalizedRecoverySuggestionErrorKey: "Rainy days never stay." ] let error = NSError(domain: "com.example.app", code: 0, userInfo: errorInfo) return .Failure(error) } // ResponseType は RealmStorable を採用しているので store メソッドを呼び出すことができる ResponseType.store(object) return .Success(value) } }
これで API.call(Endpoint.Book.Find(1))
とリクエストを投げ、処理が正常終了したら ResponseType.store(object)
の実行により Realm に自動的に保存されるようになりました。