Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSFileManager watch directory

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.

like image 657
Gavy Avatar asked Oct 23 '11 17:10

Gavy


2 Answers

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];
like image 147
John Stephen Avatar answered Oct 16 '22 14:10

John Stephen


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.

like image 39
djromero Avatar answered Oct 16 '22 14:10

djromero