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
    }
]

で、こちらが作成したCoreData。
スクリーンショット 2016-07-17 21.33.09

ちなみにプライマリーキーは必須のようで、「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オブジェクトをいちいち自分でマッピングすると面倒くさくて死ねるので、こういう便利なマッパーを使って工数を削減したいものですね。

 - 技術系(アプリ) , , ,