HyperSyncでJSONデータをCoreDataにマッピング
今日は初めてのSwiftの記事になります。
JSONで取得してきたデータをCoreDataにマッピングして保存するなんていうのは、割とよくやると思うのですが、意外と参考になる記事があまり見当たらなかったので自分で書いてみることにします。
今回採用したのは「HyperSync」というpodライブラリです。
https://github.com/hyperoslo/Sync
まずは、Podfileに以下を追加してpod installします。
pod 'Sync'
また、予めCoreDataでJSONデータに対応するEntityを作っておきます。
こちらは元になるJSON。
[ { "channel_dimens": 0, "created_at": 1458096256, "desc": "とっても面白い番組のつまったチャンネルでーす", "genre_id": 0, "id": 1, "published_flg": 1, "title": "MachidaらじCUBE", "updated_at": 1464908686 } ]
ちなみにプライマリーキーは必須のようで、「id」という名前のプロパティがあると自動的にプライマリーキーとして認識してくれるようです。
さらに、Swift上でもCoreDataに対応するクラスを作成しておきましょう。
import Foundation import CoreData @objc(Channel) class Channel: NSManagedObject { @NSManaged var createdAt: Int @NSManaged var desc: String @NSManaged var genreId: Int @NSManaged var id: Int @NSManaged var publishedFlg: Int @NSManaged var title: String @NSManaged var updatedAt: Int }
では、実際に利用する際のサンプルは以下になります。
import UIKit import DATAStack import Sync import CoreData class ViewController: UIViewController { var dataStack = DATAStack(modelName: "TestModel") // CoreDataのモデル名を指定(TestModel.xcdatamodeldとかのファイル名から) override func viewDidLoad() { super.viewDidLoad() // このへんはAPIに接続するための準備 let str = "name=hoge&pw=password" // POSTしたいデータがあれば let strData = str.dataUsingEncoding(NSUTF8StringEncoding) let url = NSURL(string: "https://api.hogehoge.jp/api/hogehoge") // APIのURL let request = NSMutableURLRequest(URL: url!) request.HTTPMethod = "POST" request.HTTPBody = strData do { // requestを実行してjsonオブジェクトを取得 let data = try NSURLConnection.sendSynchronousRequest(request, returningResponse: nil) let json = try! NSJSONSerialization.JSONObjectWithData(data, options: []) as! [String: AnyObject] // CoreDataへの同期開始 Sync.changes( json as! Array, // jsonオブジェクトを引数に渡す inEntityNamed: "Channel", // 対応するCoreDataのEntity名を指定 dataStack: self.dataStack, completion: { error in // 同期後のコールバック処理 print(error) // エラーがなければnil let fetchRequest = NSFetchRequest(entityName: "Channel") // 対応するCoreDataのEntity名を指定 do { let channels = try self.dataStack.mainContext.executeFetchRequest(fetchRequest) as! [Channel] // CoreDataからクラスにフェッチ for channel in channels { print("data: \(channel.id) \(channel.title) \(channel.desc)") // パースされたデータを表示 } } catch let error as NSError { // CoreDataからフェッチ時の例外をキャッチ } } ) } catch let error as NSError { // リクエスト時の例外をキャッチ } }
Sync.changesにて、JSONオブジェクトのデーターをCoreDataに同期しています。
新しいデータ(レコード)があれば追加しますし、同じデータがあれば更新、存在しないデータは自動で削除します。
同一性の判断にはプライマリーキーを利用しています。
また、CoreDataからクラスにフェッチする際に、
let channels = try appDelegate.managedObjectContext.executeFetchRequest(fetchRequest) as! [Channel]
みたいな感じに普通に取ろうとすると何もフェッチ出来なくてハマるので、必ずDATAStackのインスタンスを使ってフェッチを行ってください
var dataStack = DATAStack(modelName: "TestModel")
JSONオブジェクトをいちいち自分でマッピングすると面倒くさくて死ねるので、こういう便利なマッパーを使って工数を削減したいものですね。