I am using SKQueue to monitor some folders in the mac filesystem. As per the documentation, I have added the directory paths to the queues but I noticed that while adding the path, the following line of code in SKQueue is returning -1 and hence it is unable to monitor my folder.
This is the SKQueue Documentation.
The following is code from the documentation, written in the controller class.
import SKQueue
class SomeClass: SKQueueDelegate {
func receivedNotification(_ notification: SKQueueNotification, path: String, queue: SKQueue) {
print("\(notification.toStrings().map { $0.rawValue }) @ \(path)")
}
}
let delegate = SomeClass()
let queue = SKQueue(delegate: delegate)!
queue.addPath("/Users/steve/Documents")
queue.addPath("/Users/steve/Documents/dog.jpg")
The following is code inside SKQueue dependency.
public func addPath(_ path: String, notifyingAbout notification: SKQueueNotification = SKQueueNotification.Default) {
var fileDescriptor: Int32! = watchedPaths[path]
if fileDescriptor == nil {
fileDescriptor = open(FileManager.default.fileSystemRepresentation(withPath: path), O_EVTONLY)
guard fileDescriptor >= 0 else { return }
watchedPaths[path] = fileDescriptor
}
fileDescriptor = open(FileManager.default.fileSystemRepresentation(withPath: path), O_EVTONLY)
The above code is returning -1 and hence it is failing.
I was getting the same -1 return code and couldn't understand why. Whilst looking for a solution I stumbled upon SwiftFolderMonitor at https://github.com/MartinJNash/SwiftFolderMonitor. This class worked so I knew it wasn't a permission problem.
SwiftFolderMonitor uses DispatchSource.makeFileSystemObjectSource rather than kevent, but it also takes a URL parameter rather than a String path. I amended SKQueue to take a URL instead of a String and it works.
Here's my amended addPath:
public func addPath(url: URL, notifyingAbout notification: SKQueueNotification = SKQueueNotification.Default) {
let path = url.absoluteString
var fileDescriptor: Int32! = watchedPaths[path]
if fileDescriptor == nil {
fileDescriptor = open((url as NSURL).fileSystemRepresentation, O_EVTONLY)
guard fileDescriptor >= 0 else { return }
watchedPaths[path] = fileDescriptor
}
var edit = kevent(
ident: UInt(fileDescriptor),
filter: Int16(EVFILT_VNODE),
flags: UInt16(EV_ADD | EV_CLEAR),
fflags: notification.rawValue,
data: 0,
udata: nil
)
kevent(kqueueId, &edit, 1, nil, 0, nil)
if !keepWatcherThreadRunning {
keepWatcherThreadRunning = true
DispatchQueue.global().async(execute: watcherThread)
}
}
I don't know why this works, perhaps someone else can shed some light on this.
I'm still playing with both solutions but it looks like SwiftFolderMonitor does all I need (I just need to know when a specific file has changed) and it's code is clean and minimal so I think I'll use it over SKQueue.
I hope this helps.