Septeni Engineer's Blog

セプテーニエンジニアが綴る技術ブログ

【Swift】CoreDataをメモリ上で取り扱う

どーも。寺坂です

最近、CoreDataのテストを書いている時に、

ローカルのファイルを汚したくないなー
と思ったので、Swiftメモリ上でデータ管理を行う方法 を紹介します。

前提:CoreDataとは

CoreDataは、データを永続化するためのフレームワークの一つです。

多くの場合、SQLiteを利用して使用され、
端末内に.sqliteファイルを作成して、そこにデータを保存します。

このままだと、ファイルが汚れてゴミが残ったり、
読み書きが発生して遅くなったりするので、

今回は、このCoreDataの保存先を
メモリ上に変更する方法を紹介したいと思います。

※ 使用するコードは、Xcodeでデフォルト生成されるものを元にします。

実装1.NSManagedObjectModel

NSManagedObjectModelは、CoreDataのEntityを定義したものです。

これは .momdで保存されるファイルに書かれているので、
まずはそのファイルパスを取得します。

class CoreDataInMemory {

  let myModuleName = "モジュール名を記述"

  lazy var managedObjectModel: NSManagedObjectModel = {
     let modelURL = NSBundle(forClass: CoreDataInMemory.self).URLForResource(myModuleName, withExtension: "momd")!
     return NSManagedObjectModel(contentsOfURL: self.modelURL)!
     }()

}

実装2.NSPersistentStoreCoordinator

managedObjectModelを呼んで、
NSPersistentStoreCoordinatorを生成します。

ここでNSInMemoryStoreType を指定することによって、
メモリ上で動作するようになります。

参考:CoreData Framework Reference NSPersistentStoreCoordinator Class Reference - Store Types

class CoreDataInMemory {...

    lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
        var coordinator:NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
        if coordinator?.addPersistentStoreWithType( NSInMemoryStoreType, configuration: nil, URL: nil, options: nil, error: nil) == nil {
            coordinator = nil
        }
        return coordinator
        }()

...
}

実装3.NSManagedObjectContext

NSManagedObjectContextの生成。

ここで先ほどのcoordinatorを使用します。

let coordinator = self.persistentStoreCoordinator
managedObjectContext.persistentStoreCoordinator = coordinator
を忘れると動かないので、ご注意を。

class CoreDataInMemory {...

    lazy var managedObjectContext: NSManagedObjectContext? = {
        let coordinator = self.persistentStoreCoordinator
        var managedObjectContext = NSManagedObjectContext()
        managedObjectContext.persistentStoreCoordinator = coordinator
        return managedObjectContext
        }()

...
}

実装4.saveContext()

データ保存はそのままです。

class CoreDataInMemory {...

    func saveContext() {
        var error:NSError? = nil
        managedObjectContext?.save(&error)
        if let e = error {
            log.error( CoreDataError(cause: e).description )
        }
    }

...
}

実装5.解放

nilを入れましょう。

class CoreDataInMemory {...

    func releaseMemory(){
        persistentStoreCoordinator = nil
        managedObjectContext = nil
    }

...
}

まとめ

以下のようなクラスができました。

細かい部分はプロダクトに応じて変えてください。

テストの時はこちらを使うようにすると、
ストレスレステストができますね。

元記事はこちら

class CoreDataInMemory {
    
    let myModuleName = "モジュール名を記述"
    
    lazy var managedObjecar: NSManagedObjectModel = {
        let modelURL = NSBundle(forClass: CoreDataInMemory.self).URLForResource(myModuleName, withExtension: "momd")!
        return NSManagedObjectModel(contentsOfURL: self.modelURL)!
        }()
    
    lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
        var coordinator:NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
        if coordinator?.addPersistentStoreWithType( NSInMemoryStoreType, configuration: nil, URL: nil, options: nil, error: nil) == nil {
            coordinator = nil
        }
        return coordinator
        }()
    
    
    lazy var managedObjectContext: NSManagedObjectContext? = {
        let coordinator = self.persistentStoreCoordinator
        var managedObjectContext = NSManagedObjectContext()
        managedObjectContext.persistentStoreCoordinator = coordinator
        return managedObjectContext
        }()
    
    
    func saveContext() {
        var error:NSError? = nil
        managedObjectContext?.save(&error)
        if let e = error {
            log.error( CoreDataError(cause: e).description )
        }
    }
    
    func releaseMemory(){
        persistentStoreCoordinator = nil
        managedObjectContext = nil
    }
    
}