Last active
August 3, 2021 17:08
-
-
Save sidraval/86f3de0b57472cea7b7ecfd957427ee5 to your computer and use it in GitHub Desktop.
FileWatcher / FileListener
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
fileprivate let queue = DispatchQueue(label: "FileMonitorQueue", target: .main) | |
enum FileWatcherError: Error { | |
case directoryOpenFailed(Int32) | |
case directoryURLInvalid(URL) | |
} | |
final class FileWatcher { | |
let fsSource: DispatchSourceFileSystemObject | |
var readSource: DispatchSourceRead! | |
init(in directory: URL, filename: String, errorHandler: ((Error) -> Void)?) throws { | |
let fileDescriptor: Int32 = try directory.withUnsafeFileSystemRepresentation { path in | |
guard let path = path else { throw FileWatcherError.directoryURLInvalid(directory) } | |
let result = open(path, O_EVTONLY) | |
guard result >= 0 else { throw FileWatcherError.directoryOpenFailed(errno) } | |
return result | |
} | |
fsSource = DispatchSource.makeFileSystemObjectSource( | |
fileDescriptor: fileDescriptor, | |
eventMask: .write, | |
queue: queue | |
) | |
fsSource.setEventHandler { [unowned self] in | |
do { | |
let fileHandle = try FileHandle( | |
forReadingFrom: directory.appendingPathComponent(filename) | |
) | |
self.readSource = DispatchSource.makeReadSource(fileDescriptor: fileHandle.fileDescriptor, queue: queue) | |
self.readSource.setEventHandler { | |
fileHandle.readToEndOfFileInBackgroundAndNotify() | |
} | |
self.readSource.resume() | |
self.readSource.setCancelHandler { | |
fileHandle.closeFile() | |
} | |
} catch { | |
errorHandler?(error) | |
} | |
} | |
fsSource.setCancelHandler { | |
close(fileDescriptor) | |
} | |
fsSource.resume() | |
} | |
deinit { | |
fsSource.cancel() | |
} | |
} | |
final class FileListener { | |
init() { | |
NotificationCenter.default.addObserver( | |
self, | |
selector: #selector(handler), | |
name: .NSFileHandleReadToEndOfFileCompletion, | |
object: nil | |
) | |
} | |
@objc func handler() { | |
/* file is available for reading here */ | |
} | |
} |
Sid,
I solved it myself :)
I needed to change line 26 from:
eventMask: .write,
to
eventMask: .all,
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello Sid,
Thanks for your help, I managed to make it work :)
Actually, I am facing a strange issue now:
as long as the monitored file is being edited and saved by a text editor (e.g. SubEthaEdit) Filewatcher can detect changes.
But when I change the contents of the monitored file with AppleScript (see example below) then Filewatcher does NOT detect the change.
Why? Do you have any idea how to fix it?
Here is my AppleScript used to change the contents of the file:
set documentsPath to POSIX path of (path to documents folder from user domain)
set my_file to (documentsPath & "test_file.txt")
set newContent to "new text"
set fileHandle to open for access my_file with write permission
set eof of fileHandle to 0 -- make sure to overwrite the whole contents in the file
write ((newContent as string) & return) to fileHandle --- starting at eof
close access fileHandle