22
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

NSFileCoordinator/NSFilePresenterを使ってディレクトリ監視

Last updated at Posted at 2016-02-02

はじめに

ちょっとディレクトリの監視をしたかったのでNSFileCoordinator/NSFilePresenterについて少し調べました。

ファイルシステムプログラミングガイドを読んだところ、ディレクトリを監視するには以下の二つの方法があるそうです。

  • FSEvents
  • ファイルコーディネータ/ファイルプレゼンタ

その中でFSEventは、CベースのAPIなので使うのがダルそうです。
swiftラッパーのソースも見つけたのですが、あんまりメンテされてなさそうだったので、ファイルコーディネータ/ファイルプレゼンタを使ってみることにしました。

ちょっとファイル監視するくらいなら簡単にできます。

実装

ここでは、NSFileCoordinator/NSFilePresenterを使って指定したディレクトリ内のファイルの追加、更新、削除を監視してみます。

流れ

  1. ファイル/ディレクトリを管理するオブジェクトにNSFilePresenterプロトコルを指定する。
  2. NSFileCoordinatorのaddFilePresenter:クラスを呼び出してオブジェクトを登録する。
  3. NSFilePresenterのメソッド内にそれぞれの処理を書く
  4. 管理が必要なくなるタイミングでNSFileCoordinatorのremoveFilePresenterを呼び出してファイルプレゼンタの登録を解除する。

ソース

ちょっと動作させてみてわからなかったところもあるのですが、サンプルを書いてみました。
デスクトップ上のファイルの監視をしてみます。

追記: 長らく手付かずでしたがswift4で書き直していだだきました。どうもありがとうございます!!

ViewController.swift

import Cocoa

class ViewController: NSViewController, NSFilePresenter {
  
  var isPresenting = false
  var presentedItemURL: URL? {
    return FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent("Desktop", isDirectory: true)
  }
  
  let presentedItemOperationQueue = OperationQueue()
  
  override func viewDidLoad() {
    super.viewDidLoad()
    addFilePresenterIfNeeded()
    
    // Do any additional setup after loading the view.
  }
  
  deinit {
    removeFilePresenterIfNeeded()
  }
  
  // ファイルプレゼンタをシステムに登録
  func addFilePresenterIfNeeded() {
    if !isPresenting {
      isPresenting = true
      NSFileCoordinator.addFilePresenter(self)
    }
  }
  
  // ファイルプレゼンタをシステムの登録から解除
  func removeFilePresenterIfNeeded() {
    if isPresenting {
      isPresenting = false
      NSFileCoordinator.removeFilePresenter(self)
    }
  }
  
  // 提示された項目の内容または属性が変更されたことを伝える。
  func presentedItemDidChange() {
    print("Change item.")
  }
  
  // ファイルまたはファイルパッケージの新しいバージョンが追加されたことをデリゲートに通知する
  func presentedItemDidGainVersion(version: NSFileVersion) {
    print("Update file at \(version.modificationDate!).")
  }
  
  // ファイルまたはファイルパッケージのバージョンが消えたことをデリゲートに通知する
  func presentedItemDidLoseVersion(version: NSFileVersion) {
    print("Lose file version at \(version.modificationDate!).")
  }
  
  // ディレクトリ内のアイテムが新しいバージョンになった(更新された)時の通知
  func presentedSubitem(at url: URL, didGain version: NSFileVersion) {
    
    var isDir = ObjCBool(false)
    if FileManager.default.fileExists(atPath: url.path, isDirectory: &isDir) {
      if Bool(isDir.boolValue) {
        print("Update directory (\(url.path)) at \(version.modificationDate!).")
      } else {
        print("Update file (\(url.path)) at \(version.modificationDate!).")
      }
    }
  }
  
  // ディレクトリ内のアイテムが削除された時の通知
  func presentedSubitem(at url: URL, didLose version: NSFileVersion) {
    print("looooooooooooose")
    var isDir = ObjCBool(false)
    if FileManager.default.fileExists(atPath: url.path, isDirectory: &isDir) {
      if Bool(isDir.boolValue) {
        print("Lose directory version (\(url.path)) at \(version.modificationDate!).")
      } else {
        print("Lose file version (\(url.path)) at \(version.modificationDate!).")
      }
    }
  }
  
  // ファイル/ディレクトリの内容変更の通知
  func presentedSubitemDidChange(at url: URL) {
    if FileManager.default.fileExists(atPath: url.path) {
      print("Add subitem (\(url.path)).")
    } else {
      print("Remove subitem (\(url.path)).")
    }
    
  }
  
  // ファイル/ディレクトリが移動した時の通知
  func presentedSubitemAtURL(oldURL: NSURL, didMoveToURL newURL: NSURL) {
    var isDir = ObjCBool(false)
    if FileManager.default.fileExists(atPath: newURL.path!, isDirectory: &isDir) {
      if Bool(isDir.boolValue) {
        print("Move directory from (\(oldURL.path!)) to (\(newURL.path!).")
      } else {
        print("Move file from (\(oldURL.path!)) to (\(newURL.path!)).")
      }
    }
  }
  
  // MARK: 何したら呼ばれるのか
  
  // 何したら呼ばれるのか
  func accommodatePresentedItemDeletionWithCompletionHandler(completionHandler: (NSError?) -> Void) {
    print("accommodatePresentedItemDeletionWithCompletionHandler")
  }
  
  // 何したら呼ばれるのか
  private func accommodatePresentedSubitemDeletionAtURL(url: URL, completionHandler: @escaping (NSError?) -> Void) {
    print("accommodatePresentedSubitemDeletionAtURL")
    print("url: \(url.path)")
  }
  
  // 何したら呼ばれるのか
  func presentedSubitemDidAppear(at url: URL) {
    print("presentedSubitemDidAppearAtURL")
    print("url: \(url.path)")
  }
}

参考

22
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?