-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathSingleFileWatcher.swift
More file actions
81 lines (69 loc) · 2.42 KB
/
SingleFileWatcher.swift
File metadata and controls
81 lines (69 loc) · 2.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import Foundation
import Logger
class SingleFileWatcher: FileWatcherProtocol {
private var fileDescriptor: CInt = -1
private var dispatchSource: DispatchSourceFileSystemObject?
private let fileURL: URL
private let dispatchQueue: DispatchQueue?
// Callbacks for file events
private let onFileModified: (() -> Void)?
private let onFileDeleted: (() -> Void)?
private let onFileRenamed: (() -> Void)?
init(
fileURL: URL,
dispatchQueue: DispatchQueue? = nil,
onFileModified: (() -> Void)? = nil,
onFileDeleted: (() -> Void)? = nil,
onFileRenamed: (() -> Void)? = nil
) {
self.fileURL = fileURL
self.dispatchQueue = dispatchQueue
self.onFileModified = onFileModified
self.onFileDeleted = onFileDeleted
self.onFileRenamed = onFileRenamed
}
func startWatching() -> Bool {
// Open the file for event-only monitoring
fileDescriptor = open(fileURL.path, O_EVTONLY)
guard fileDescriptor != -1 else {
Logger.client.info("[FileWatcher] Failed to open file \(fileURL.path).")
return false
}
// Create DispatchSource to monitor the file descriptor
dispatchSource = DispatchSource.makeFileSystemObjectSource(
fileDescriptor: fileDescriptor,
eventMask: [.write, .delete, .rename],
queue: self.dispatchQueue ?? DispatchQueue.global()
)
dispatchSource?.setEventHandler { [weak self] in
guard let self = self else { return }
let flags = self.dispatchSource?.data ?? []
if flags.contains(.write) {
self.onFileModified?()
}
if flags.contains(.delete) {
self.onFileDeleted?()
self.stopWatching()
}
if flags.contains(.rename) {
self.onFileRenamed?()
self.stopWatching()
}
}
dispatchSource?.setCancelHandler { [weak self] in
guard let self = self else { return }
close(self.fileDescriptor)
self.fileDescriptor = -1
}
dispatchSource?.resume()
Logger.client.info("[FileWatcher] Started watching file: \(fileURL.path)")
return true
}
func stopWatching() {
dispatchSource?.cancel()
dispatchSource = nil
}
deinit {
stopWatching()
}
}