読者です 読者をやめる 読者になる 読者になる

ほげほげ(仮)

仮死状態

Swift + AppExtension + MagicalRecord

Xcode iOS Objective-C Swift

SwiftでコンテナアプリとAppExtensionのデータをMagicalRecordで共有する方法です。

(少しObjective-Cは必要になります)

注意

現状だとちょっと試した動いた状態なので何か問題があとからあるかもしれません。

あと、MagicalRecordはbeta版を使用するのでここも問題があるかもしれません。

環境

プロジェクト作成

サンプルでSingle View Applicationで作ります。下のような感じで設定します。

f:id:STAR_ZERO:20141119170524p:plain

AppExtensionのTarget作成

今回はToday Extensionでやります。他のAppExtensionでもやり方はほぼ同じかと思います。

f:id:STAR_ZERO:20141119170601p:plain

Podfile作成

Podfileを作成します。

$ pod init

次のように追記して、インストールします。

source 'https://github.com/CocoaPods/Specs.git'

pod 'MagicalRecord', git: 'https://github.com/magicalpanda/MagicalRecord.git', tag: 'v2.3.0-beta.5'

target 'SampleToday' do
  pod 'MagicalRecord', git: 'https://github.com/magicalpanda/MagicalRecord.git', tag: 'v2.3.0-beta.5'
end
$ pod install

インストールしたらworkspaceで開き直します。

MagicalRecordのbeta版について

sqliteのファイルを共有するためにAppGroupの設定が必要なんですが、その対応がMaigcalRecordの安定版の2.2には含まれていないので、beta版を使います。2.3には組み込まれるようです。

iOS8 Extension issue. · Issue #858 · magicalpanda/MagicalRecord

AppGroup設定

ここは下記の記事が詳しいので参照してください。

[iOS 8] App Extension #3 – App Group で containing app とデータを共有する | Developers.IO

コンテナアプリとAppExtensionともに設定しておきます。

CoreData設定

mogeneratorも使っていくので、よくわからない人はまず下記の記事を見ることをおすすめします。

Mogenerator + MagicalRecordでCoreData入門 - Qiita

xcdatamodeldファイル作成

Sampleのグループを右クリックしてNew File... -> Core Data -> Data Modelを選択します。

f:id:STAR_ZERO:20141119170618p:plain

とりあえずファイル名はModel.xcdatamodeldにしました。

Entity設定

テキトーにEntitiyを作ります。

とりあえず下のような感じにしました。

f:id:STAR_ZERO:20141119170648p:plain

クラス名も忘れずに設定します。

f:id:STAR_ZERO:20141119170657p:plain

モデルクラス作成

mogeneratorを使います。まだSwiftに対応していないので、ここだけはObjective-Cでいきます。

$ mogenerator --template-var arc=true -m Sample/Model.xcdatamodeld/Model.xcdatamodel -O Sample

これを実行すると合計で4ファイルできると思います。これはまだXcodeに追加されていないのでドラッグ&ドロップで追加します。Test.h, Test.m, _Test.h, _Test.m

SwiftからObjective-Cを使えるようにする

Sample-Bridging-Header.hを作成して、Build SettingのObjective-C Bridging Headerを追加してください。詳しくはググればすぐ出るかともいます。

Sample-Bridging-Header.hを次のようにして、MagicalRecordと作成したモデルクラスをSwiftから使えるようにします。

#import <MagicalRecord/CoreData+MagicalRecord.h>

#import "Test.h"

MagicalRecordセットアップ

ようやくMagicalRecordです。

AppDelegate.swiftに追記していきます。グループ名とSQLiteファイル名を指定して初期化します。

ついでにテスト用のデータ作成も書いておきます。

...

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        
        // MagicalRecordの初期化
        let fileManager = NSFileManager()
        let directory = fileManager.containerURLForSecurityApplicationGroupIdentifier("group.com.zero.star.Sample")
        let pathToStore = directory?.URLByAppendingPathComponent("Sample.sqlite")
        MagicalRecord.setupCoreDataStackWithStoreAtURL(pathToStore)
        
        // テストでデータ追加
        let test = Test.MR_createEntity()
        test.name = "hogehoge"
        test.managedObjectContext?.MR_saveToPersistentStoreAndWait()
        
        return true
    }

...

    func applicationWillTerminate(application: UIApplication) {
        // MagicalRecord終了処理
        MagicalRecord.cleanUp()
    }
}

ここまでできたら一度実行してエラーとか出ないことを確認しておきます。

AppExtensionでCoreDataを参照できるようにする

モデルファイルをターゲットに追加

まずはAppExtensionターゲットに先ほど作ったモデルファイル関連を追加します。

Model.xcdatamodeldTest.m_Test.mのファイルをXcodeのProjectNavigatorで選択して、右側のinspectorのTarget MembershipのAppExtensionターゲットにチェックを入れます。

f:id:STAR_ZERO:20141119170713p:plain

SwiftからObjective-Cを使えるようにする

これは先ほどと同様にAppExtension側のターゲット内にも作成します。

SampleToday-Bridging-Header.hを作成して、Build SettingのObjective-C Bridging Headerを追加してください。

#import <MagicalRecord/CoreData+MagicalRecord.h>

#import "Test.h"

データを参照する

TodayViewController.swiftを下記のようにします

...

    override func viewDidLoad() {
        super.viewDidLoad()
        
        // MagicalRecordの初期化
        let fileManager = NSFileManager()
        let directory = fileManager.containerURLForSecurityApplicationGroupIdentifier("group.com.zero.star.Sample")
        let pathToStore = directory?.URLByAppendingPathComponent("Sample.sqlite")
        MagicalRecord.setupCoreDataStackWithStoreAtURL(pathToStore)
        
        // データ取得
        let tests = Test.MR_findAll()
        for test in tests {
            NSLog("Test: \(test.name)")
        }
        
    }

...

今回のサンプルの場合だと通知バーでTodayExtensionを追加して表示させるとログが出ると思います。

最終的なProjectNavigator

f:id:STAR_ZERO:20141119170803p:plain

まとめ

本当はEmbedded Frameworkを使いたかったんですけど、SwiftだとCocoaPodsがうまくいかなくて断念しました。

かなり手順的には面倒ですが、MagicalRecordの恩恵は受けたいので。

はてなブログってSwiftシンタックスハイライトしてくれないのね…