How would you monitor a directory with NSFileManager
?
I would like to able to detect when a file is deleted/added in my documents directory while my app is running.
How would you monitor a directory with NSFileManager
?
I would like to able to detect when a file is deleted/added in my documents directory while my app is running.
Look Kernel Queues: An Alternative to File System Events in Apple documentation.
There is an example for iOS in AVPlayerDemo (look DirectoryWatcher
class).
Also, check Directory Monitor blog post.
Here's my own version of DirectoryWatcher written in Swift using GCD instead of Mach and using a closure instead of a delegate
import Foundation
@objc public class DirectoryWatcher : NSObject {
override public init() {
super.init()
}
deinit {
stop()
}
public typealias Callback = (_ directoryWatcher: DirectoryWatcher) -> Void
@objc public convenience init(withPath path: String, callback: @escaping Callback) {
self.init()
if !watch(path: path, callback: callback) {
assert(false)
}
}
private var dirFD : Int32 = -1 {
didSet {
if oldValue != -1 {
close(oldValue)
}
}
}
private var dispatchSource : DispatchSourceFileSystemObject?
@objc public func watch(path: String, callback: @escaping Callback) -> Bool {
// Open the directory
dirFD = open(path, O_EVTONLY)
if dirFD < 0 {
return false
}
// Create and configure a DispatchSource to monitor it
let dispatchSource = DispatchSource.makeFileSystemObjectSource(fileDescriptor: dirFD, eventMask: .write, queue: DispatchQueue.main)
dispatchSource.setEventHandler {[unowned self] in
callback(self)
}
dispatchSource.setCancelHandler {[unowned self] in
self.dirFD = -1
}
self.dispatchSource = dispatchSource
// Start monitoring
dispatchSource.resume()
// Success
return true
}
@objc public func stop() {
// Leave if not monitoring
guard let dispatchSource = dispatchSource else {
return
}
// Don't listen to more events
dispatchSource.setEventHandler(handler: nil)
// Cancel the source (this will also close the directory)
dispatchSource.cancel()
self.dispatchSource = nil
}
}
Use it like Apple's DirectoryWatcher example, something like this:
let directoryWatcher = DirectoryWatcher(withPath: "/path/to/the/folder/you/want/to/monitor/", callback: {
print("the folder changed")
})
Destroying the object will stop watching, or you can stop it explicitly
directoryWatcher.stop()
It should be compatible with Objective C they way it's written (untested). Using it would be like this:
DirectoryWatcher *directoryWatcher = [DirectoryWatcher.alloc initWithPath: @"/path/to/the/folder/you/want/to/monitor/" callback: ^(DirectoryWatcher *directoryWatcher) {
NSLog(@"the folder changed")
}];
Stopping it is similar
[directoryWatcher stop];