Swiftで遊ぼう! on Hatena

あしたさぬきblogでやってた初心者オヤジのiOSプログラミング奮闘記がHatenaに来ました

Swiftで遊ぼう! - 599 - Multiple Managed Object Contextsチュートリアル 4

オリジナルアプリに近いアプリのチュートリアに取り組んでいます。

www.raywenderlich.com

今回のチュートリアルは少し複雑なので、ある程度できあがっているスターター・プロジェクトが用意されています

ダウンロードしてプロジェクト・ナビゲータを開くと、数々のファイルが並んでいます。まず、CoreDataStack.swiftファイルと、関連するCoreDataSeedフォルダーに存在するファイルの関連性をみていきます。

f:id:yataiblue:20160229155357j:plain

まずCoreDataStack.swiftファイルのコードをみます。まず全体のコードを眺めてみても何となく理解できるような気がします。やっぱり完全な初心者から脱却できているのかもしれません。チュートリアルで注目している部分から取りかかります。

import CoreData

class CoreDataStack {
  
  let modelName = "SurfJournalModel"
  let seedName = "SurfJournalDatabase"
  
  lazy var applicationDocumentsDirectory: NSURL = {
    let urls = NSFileManager.defaultManager().URLsForDirectory(
      .DocumentDirectory, inDomains: .UserDomainMask)
    return urls[urls.count-1]
    }()
  
  lazy var managedObjectModel: NSManagedObjectModel = {
    let modelURL = NSBundle.mainBundle()
      .URLForResource(self.modelName,
        withExtension: "momd")!
    return NSManagedObjectModel(contentsOfURL: modelURL)!
    }()
  
  lazy var psc: NSPersistentStoreCoordinator = {
    let coordinator = NSPersistentStoreCoordinator(
      managedObjectModel: self.managedObjectModel)
    let url =
    self.applicationDocumentsDirectory
      .URLByAppendingPathComponent(self.seedName + ".sqlite")
    
    // 1
    let bundle = NSBundle.mainBundle()
    let seededDatabaseURL = bundle
      .URLForResource(self.seedName, withExtension: "sqlite")!

    // 2
    let didCopyDatabase: Bool
    do {
      try NSFileManager.defaultManager()
        .copyItemAtURL(seededDatabaseURL, toURL: url)
      didCopyDatabase = true
    } catch {
      didCopyDatabase = false
    }

    // 3
    if didCopyDatabase {
  
      // 4
      let seededSHMURL = bundle
        .URLForResource(self.seedName, withExtension: "sqlite-shm")!
      let shmURL = self.applicationDocumentsDirectory
        .URLByAppendingPathComponent(self.seedName + ".sqlite-shm")
      do {
        try NSFileManager.defaultManager()
          .copyItemAtURL(seededSHMURL, toURL: shmURL)
      } catch {
        let nserror = error as NSError
        print("Error: \(nserror.localizedDescription)")
        abort()
      }

      // 5
      let seededWALURL = bundle
        .URLForResource(self.seedName, withExtension: "sqlite-wal")!
      let walURL = self.applicationDocumentsDirectory
        .URLByAppendingPathComponent(self.seedName + ".sqlite-wal")
      do {
        try NSFileManager.defaultManager()
          .copyItemAtURL(seededWALURL, toURL: walURL)
      } catch {
        let nserror = error as NSError
        print("Error: \(nserror.localizedDescription)")
        abort()
      }
      
      print("Seeded Core Data")
    }
    
    // 6
    do {
      try coordinator.addPersistentStoreWithType(
        NSSQLiteStoreType, configuration: nil, URL: url, options: nil)
    } catch {
      //7
        let nserror = error as NSError
        print("Error: \(nserror.localizedDescription)")
        abort()
    }
    
    return coordinator
    }()
  
  lazy var context: NSManagedObjectContext = {
    var managedObjectContext = NSManagedObjectContext(
      concurrencyType: .MainQueueConcurrencyType)
    managedObjectContext.persistentStoreCoordinator = self.psc
    return managedObjectContext
    }()
  
  func saveContext () {
    if context.hasChanges {
      do {
        try context.save()
      } catch {
        let nserror = error as NSError
        print("Error: \(nserror.localizedDescription)")
        abort()
      }
    }
  }
}

まずこの長ったらしいコードの中でナンバリングされている部分から取りかかります。

// 1
let bundle = NSBundle.mainBundle()
let seededDatabaseURL = bundle
  .URLForResource(self.seedName, withExtension: "sqlite")!
 
// 2
let didCopyDatabase: Bool
do {
  try NSFileManager.defaultManager()
    .copyItemAtURL(seededDatabaseURL, toURL: url)
  didCopyDatabase = true
} catch {
  didCopyDatabase = false
}
  1. 「NSBundle.mainBundle()」は、何度も何度も繰り返して勉強しているので、やっと自然に頭に入ってくるようになりました。まだの人はシングルトン・パターンのページを読みましょう。NSBundleクラスも復習したので、まだの人はこのページを確認しましょう。まずこのデータベースは、立ち上げた時に前もってデータを持たせるステップです。「URLForResourceメソッドは、プロジェクト内に存在するファイルの位置を「NSURL?」クラスで返します。パラメーターで、String型の名前と拡張子を受け取ります。ここで「self.seedName」になっていますが、コードの前半部分で定義されています(ハードコーティング)。「seededDatabaseURL」には、ファイルの位置情報がNSURLクラスとして保持されます。
  2. 「didCopyDatabase」はBoolean型の定数です。私がコーディングするなら、変数にしてしまいそうですが、ベテランプログラマーは、何の躊躇いもなく定数宣言しています。確かに、この定数に値を入れるタイミングは1度だけでいいので変数にする必要もありません。そして、NSFileManagerクラスが出てきます。これも復習したんですが、新しいメソッドが出てきています。「copyItemAtURL」は、2つの引数を取ります。どちらもNSURLクラスのオブジェクトを指定します。最初の引数はコピー元で、2番目の引数はコピー先です。コピー先にオブジェクトが存在すれば、このメソッドerrorを投げる(throws)タイプなので、必ず「try」が要ります。コピーに成功すれば、定数「didCopyDatabase」を「true」にして、失敗すれば「false」にして後の処理に利用します。

copyItemAtURLメソッドの2つめの引数「url」を明日考察します。