はじめに
ちょっとディレクトリの監視をしたかったのでNSFileCoordinator/NSFilePresenterについて少し調べました。
ファイルシステムプログラミングガイドを読んだところ、ディレクトリを監視するには以下の二つの方法があるそうです。
- FSEvents
- ファイルコーディネータ/ファイルプレゼンタ
その中でFSEventは、CベースのAPIなので使うのがダルそうです。
swiftラッパーのソースも見つけたのですが、あんまりメンテされてなさそうだったので、ファイルコーディネータ/ファイルプレゼンタを使ってみることにしました。
ちょっとファイル監視するくらいなら簡単にできます。
実装
ここでは、NSFileCoordinator/NSFilePresenterを使って指定したディレクトリ内のファイルの追加、更新、削除を監視してみます。
流れ
- ファイル/ディレクトリを管理するオブジェクトにNSFilePresenterプロトコルを指定する。
- NSFileCoordinatorのaddFilePresenter:クラスを呼び出してオブジェクトを登録する。
- NSFilePresenterのメソッド内にそれぞれの処理を書く
- 管理が必要なくなるタイミングで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)")
}
}
参考