Swiftで遊ぼう! on Hatena

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

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

下記のチュートリアルを取り組んでいますがチュートリアルで説明されていないNSFileManagerクラスを使ったファイル操作の勉強をしています。

www.raywenderlich.com

昨日のdefaultManagerを使用する理由がまだはっきりしていませんが先に進みます。

NSFileManagerクラスはファイル操作、NSBundleクラスは存在するファイルの検索操作、のように考えていいかもしれません。この2つのクラスを使いこなして、アプリで管理するファイルを自由自在に扱えるようにします。

数日前の「チュートリアル4」でCoreDataStack.swiftのコードを載せましたが、その中にある1つの遅延格納型プロパティのNSPersistentSotoreCordinatorクラスの「psc」のみ注目します。

  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
    }()

重要なことを思い出す必要があります。Swiftで遊ぼう! - 543 - Core Data シンプルチュートリアル - Swiftで遊ぼう! on Hatena」ここのチュートリアルに説明していますが、プロジェクト作成時に「Use Core Data」のチェックボックスを入れた場合、CoreDataStackはAppDelegateファイル上で初期化が生じるため、自前でCoreDataStackの初期化コードは要らないはずです。しかし、このスタータープロジェクトのAppDelegate.swiftファイルを見ると、CoreDataStackの初期化ステップがありません! という訳で、「Swiftで遊ぼう! - 530 - Core Data - Swiftで遊ぼう! on Hatena」で説明しているように、CoreDataStackクラスを用意しないといけません。私は一瞬勘違いしそうになりました。初心者の皆さん、注意が必要です。

定数「url」をもう一度注目します。

let url =
    self.applicationDocumentsDirectory
      .URLByAppendingPathComponent(self.seedName + ".sqlite")

「applicationDoucumentsDirectory」はDocumentsディレクトリの位置情報を保持する「NSURL」クラスのオブジェクトで、それが持つメソッド「URLByAppendingPathComponent()」を使って、ファイルのディレクトリ情報を「NSURL」クラスオブジェクトとして持たせています。

これが「//2」の「copyItemAtURL()」メソッドで指定されているコピー先です。このメソッドerrorを投げる(thorws)が組み込まれているので、使用する場合「try」が必要になり、url指定先に既にファイルが存在しているとエラーになります。何もファイルが存在していなければ、メソッドは成功するので、「didCopyDatabase」に「true」が指定されます。

もう少し、この「copyItemAtURL()」メソッドを考えます。このコピー元のNSURLデータの「seededDatabaseURL」がどこから持ってくるのか理解している必要があります。「SurfJournalDatabase.sqlite」というファイルのNSURLオブジェクト情報は、NSFileManagerクラスメソッドではなく、NSBundleクラスメソッドを使って習得しています。

Xcodeに組み込んでいる「SurfJournalDatabase.sqlite」はNSBundleクラスを使ってアプリに取りこみます。
f:id:yataiblue:20160229155357j:plain

1度起動したアプリに存在する「SurfJournalDatabase.sqlite」はNSFileManagerを使って操作します。
f:id:yataiblue:20160302151402j:plain

やっと理解できました。