How would I monitor a folder for new files in swift, without polling (which is very inefficient)? I've heard of APIs such as kqueue and FSEvents - but I'm not sure it's possible to implement them in swift?
-
1`FSEventStreamCreate` is missing from Swift, not allowing a pure Swift implementation. However, you could make an Obj-C wrapper class and manipulate that with Swift. I had to do similar with CommonCrypto for hashing. – Erik Jun 11 '14 at 02:30
-
Note: Here's an old discussion about this: http://stackoverflow.com/questions/7720246/monitoring-a-directory-in-cocoa-cocoa-touch – eonil Nov 12 '14 at 02:44
-
I see a Swift version of FSEventStreamCreate which I'm battling to use (due to total lack of Swift knowledge...) https://developer.apple.com/library/mac/documentation/Darwin/Reference/FSEvents_Ref/index.html#//apple_ref/c/func/FSEventStreamCreate – Andrew Mackenzie Jun 07 '15 at 12:02
-
I found http://blog.beecomedigital.com/2015/06/27/developing-a-filesystemwatcher-for-os-x-by-using-fsevents-with-swift-2/ which looks promising but unfortunately doesn't cope well with memory. I just need to know _which_ file has been added but there doesn't seem to be a simple solution. – qwerty_so Jul 13 '15 at 16:46
-
1I found this https://github.com/gurinderhans/SwiftFSWatcher . It even allows creating multiple `monitors` for different places. – someguy234 Apr 11 '16 at 01:58
10 Answers
GCD seems to be the way to go. NSFilePresenter
classes doesn't work properly. They're buggy, broken, and Apple is haven't willing to fix them for last 4 years. Likely to be deprecated.
Here's a very nice posting which describes essentials of this technique.
"Handling Filesystem Events with GCD", by David Hamrick.
Sample code cited from the website. I translated his C code into Swift.
let fildes = open("/path/to/config.plist", O_RDONLY)
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
let source = dispatch_source_create(
DISPATCH_SOURCE_TYPE_VNODE,
UInt(fildes),
DISPATCH_VNODE_DELETE | DISPATCH_VNODE_WRITE | DISPATCH_VNODE_EXTEND | DISPATCH_VNODE_ATTRIB | DISPATCH_VNODE_LINK | DISPATCH_VNODE_RENAME | DISPATCH_VNODE_REVOKE,
queue)
dispatch_source_set_event_handler(source,
{
//Reload the config file
})
dispatch_source_set_cancel_handler(source,
{
//Handle the cancel
})
dispatch_resume(source);
...
// sometime later
dispatch_source_cancel(source);
For reference, here're another QAs posted by the author:
If you're interested in watching directories, here's another posting which describes it.
"Monitoring a Folder with GCD" on Cocoanetics. (unfortunately, I couldn't find the author's name. I am sorry for lacking attribution)
The only noticeable difference is getting a file-descriptor. This makes event-notification-only file descriptor for a directory.
_fileDescriptor = open(path.fileSystemRepresentation(), O_EVTONLY)
Update
Previously I claimed FSEvents
API is not working, but I was wrong. The API is working very well, and if you're interested in watching on deep file tree, than it can be better then GCD by its simplicity.
Anyway, FSEvents cannot be used in pure Swift programs. Because it requires passing of C callback function, and Swift does not support it currently (Xcode 6.1.1). Then I had to fallback to Objective-C and wrap it again.
Also, any of this kind API is all fully asynchronous. That means actual file system state can be different at the time you are receiving the notifications. Then precise or accurate notification is not really helpful, and useful only for marking a dirty flag.
Update 2
I finally ended up with writing a wrapper around FSEvents
for Swift.
Here's my work, and I hope this to be helpful.
-
Nice. I tried to 'listen' to a folder being written to once, but gave up because I couldn't get `FSEvents` to work properly. I was waiting for very large image files to be written by a camera driver, and never quite got to be notified at the right moment when the file was 'ready' to be opened, ended with black images, etc. Also, I saw some inconsistency in the messages being broadcast and had a hard time interpreting them. Perhaps I didn't understand the docs properly and didn't come across good sample code. WILL definitely try GCD. – Nicolas Miari Dec 05 '14 at 03:57
-
Thanks. I just used your Framework by cloning directly from GitHub, and followed your video and Built! Worked first time. So, now I just need to learn some Swift to use it! :-) – Andrew Mackenzie Jun 07 '15 at 12:14
-
Hey @Eonil do you know how can I detect when a file is updated/modified? I saw your approximation and I have tried few more that i have found but none fires when the file is updated. – matiasdim Jul 17 '17 at 20:10
-
`NSFilePresenter` is not a class, it's a protocol. It has no functionality on its own. It's typically used with a `NSFileCoordinator` and this class is not for monitoring directories but for coordinating read/write I/O operations on file systems within a single process as well as across process boundaries and that works perfectly using `NSFileCoordinator`. – Mecki May 04 '18 at 12:31
I adapted Stanislav Smida's code to make it work with Xcode 8 and Swift 3
class DirectoryObserver {
private let fileDescriptor: CInt
private let source: DispatchSourceProtocol
deinit {
self.source.cancel()
close(fileDescriptor)
}
init(URL: URL, block: @escaping ()->Void) {
self.fileDescriptor = open(URL.path, O_EVTONLY)
self.source = DispatchSource.makeFileSystemObjectSource(fileDescriptor: self.fileDescriptor, eventMask: .all, queue: DispatchQueue.global())
self.source.setEventHandler {
block()
}
self.source.resume()
}
}

- 1,049
- 11
- 24
-
-
2021 year. Code works. Great file monitor! looks exactly only one directory without subdirectories. Just the think that I searched for! Thanks a lot! – Andrew_STOP_RU_WAR_IN_UA Apr 07 '21 at 09:10
The simplest solution is to use Apple's DirectoryMonitor.swift https://github.com/Lax/Learn-iOS-Swift-by-Examples/blob/master/Lister/ListerKit/DirectoryMonitor.swift
var dm = DirectoryMonitor(URL: AppDelegate.applicationDocumentsDirectory)
dm.delegate = self
dm.startMonitoring()

- 9,318
- 5
- 65
- 101

- 5,515
- 2
- 37
- 44
-
1
-
-
2Swift 3 version mirrored at: https://github.com/robovm/apple-ios-samples/blob/master/ListerforwatchOSiOSandOSX/Swift/ListerKit/DirectoryMonitor.swift – Daniel Oct 17 '19 at 15:38
Swift 5 Version for Directory Monitor, with GCD, original from Apple
import Foundation
/// A protocol that allows delegates of `DirectoryMonitor` to respond to changes in a directory.
protocol DirectoryMonitorDelegate: class {
func directoryMonitorDidObserveChange(directoryMonitor: DirectoryMonitor)
}
class DirectoryMonitor {
// MARK: Properties
/// The `DirectoryMonitor`'s delegate who is responsible for responding to `DirectoryMonitor` updates.
weak var delegate: DirectoryMonitorDelegate?
/// A file descriptor for the monitored directory.
var monitoredDirectoryFileDescriptor: CInt = -1
/// A dispatch queue used for sending file changes in the directory.
let directoryMonitorQueue = DispatchQueue(label: "directorymonitor", attributes: .concurrent)
/// A dispatch source to monitor a file descriptor created from the directory.
var directoryMonitorSource: DispatchSource?
/// URL for the directory being monitored.
var url: URL
// MARK: Initializers
init(url: URL) {
self.url = url
}
// MARK: Monitoring
func startMonitoring() {
// Listen for changes to the directory (if we are not already).
if directoryMonitorSource == nil && monitoredDirectoryFileDescriptor == -1 {
// Open the directory referenced by URL for monitoring only.
monitoredDirectoryFileDescriptor = open((url as NSURL).fileSystemRepresentation, O_EVTONLY)
// Define a dispatch source monitoring the directory for additions, deletions, and renamings.
directoryMonitorSource = DispatchSource.makeFileSystemObjectSource(fileDescriptor: monitoredDirectoryFileDescriptor, eventMask: DispatchSource.FileSystemEvent.write, queue: directoryMonitorQueue) as? DispatchSource
// Define the block to call when a file change is detected.
directoryMonitorSource?.setEventHandler{
// Call out to the `DirectoryMonitorDelegate` so that it can react appropriately to the change.
self.delegate?.directoryMonitorDidObserveChange(directoryMonitor: self)
}
// Define a cancel handler to ensure the directory is closed when the source is cancelled.
directoryMonitorSource?.setCancelHandler{
close(self.monitoredDirectoryFileDescriptor)
self.monitoredDirectoryFileDescriptor = -1
self.directoryMonitorSource = nil
}
// Start monitoring the directory via the source.
directoryMonitorSource?.resume()
}
}
func stopMonitoring() {
// Stop listening for changes to the directory, if the source has been created.
if directoryMonitorSource != nil {
// Stop monitoring the directory via the source.
directoryMonitorSource?.cancel()
}
}
}

- 41
- 1
SKQueue is a Swift wrapper around kqueue. Here is sample code that watches a directory and notifies of write events.
class SomeClass: SKQueueDelegate {
func receivedNotification(_ notification: SKQueueNotification, path: String, queue: SKQueue) {
print("\(notification.toStrings().map { $0.rawValue }) @ \(path)")
}
}
if let queue = SKQueue() {
let delegate = SomeClass()
queue.delegate = delegate
queue.addPath("/some/file/or/directory")
queue.addPath("/some/other/file/or/directory")
}

- 162
- 6
-
the problem is: how to receive a list of files added to the monitored directory. – Duck Jun 29 '22 at 17:29
I've tried to go with these few lines. So far seems to work.
class DirectoryObserver {
deinit {
dispatch_source_cancel(source)
close(fileDescriptor)
}
init(URL: NSURL, block: dispatch_block_t) {
fileDescriptor = open(URL.path!, O_EVTONLY)
source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, UInt(fileDescriptor), DISPATCH_VNODE_WRITE, dispatch_queue_create(nil, DISPATCH_QUEUE_CONCURRENT))
dispatch_source_set_event_handler(source, { dispatch_async(dispatch_get_main_queue(), block) })
dispatch_resume(source)
}
//
private let fileDescriptor: CInt
private let source: dispatch_source_t
}
Be sure to not get into retain cycle. If you are going to use owner of this instance in block, do it safely. For example:
self.directoryObserver = DirectoryObserver(URL: URL, block: { [weak self] in
self?.doSomething()
})

- 1,565
- 17
- 23
-
Great code. Here is an example monitoring the desktop: let theURL = NSURL("~/Desktop/".stringByExpandingTildeInPath) – Sentry.co Apr 08 '16 at 11:09
-
Your code only works when files are added or deleted. Not file changes. Great anyways. I need to monitor file changes so ill keep looking. – Sentry.co Apr 08 '16 at 11:11
-
Easiest method I've found that I'm currently using is this wonderful library: https://github.com/eonist/FileWatcher
Installation:
- CocoaPods
pod "FileWatcher"
- Carthage
github "eonist/FileWatcher" "master"
- Manual Open
FileWatcherExample.xcodeproj
let filewatcher = FileWatcher([NSString(string: "~/Desktop").expandingTildeInPath])
filewatcher.callback = { event in
print("Something happened here: " + event.path)
}
filewatcher.start() // start monitoring

- 3,293
- 21
- 33
I faced a problem that is not mentioned in any of the answers. As my app is using a UIDocumentBrowserViewController (i.e. Apple's own Files app) to manage its documents, I have no control over my users' habits. I was using SKQueue to monitor all files in order to keep metadata in sync, and at a certain point the app started crashing.
As it turns out, there is an upper limit of 256 file descriptors that can be open by an app simultaneously, even just for monitoring. I ended up combining SKQueue and Apple's Directory Monitor (reference to which you can find in this answer of the current thread) to create a class I named SFSMonitor, which monitors a whole queue of files or directories by using Dispatch Sources.
I detailed my findings and the practices I now use in this SO thread.

- 459
- 2
- 18
-
-
1Every directory and subdirectory is monitored as an individual entity. Let's say you copy a subdirectory into a monitored directory: you'll be notified of that change, but you will not know about anything that happens within that subdirectory. You will have to check what was added, and when you realize it is a subdirectory, add it to your watched list. In other words - watching is not recursive, and a subdirectory is just another entry, as far as the original directory is concerned. – Ron Regev Oct 21 '20 at 14:54
You could add UKKQueue to your project. See http://zathras.de/angelweb/sourcecode.htm it's easy to use. UKKQueue is written in Objective C, but you can use it from swift

- 13,017
- 2
- 34
- 58
-
Note that UKKQueue has been claimed superseded by https://github.com/bdkjones/VDKQueue in 2012, itself also claimed to be superseded by GCD in 2011 (https://www.reddit.com/r/programming/comments/l6j3g/using_kqueue_in_cocoa/c2q74yy) – Cœur Nov 16 '22 at 17:24
Depending on your application needs, you may be able to use a simple solution.
I actually used kqueue in a production product; I wasn't crazy with the performance but it worked, so I didn't think too much of it till I found a nice little trick that worked even better for my needs, plus, it used less resources which can be important for performance intensive programs.
What you can do, again, if your project permits, is that every time you switch to your application, you can just check the folder as part of your logic, instead of having to periodically check the folder using kqueue. This works and uses far less resources.

- 755
- 3
- 13
-
I think Atom.io does this when it asserts if a file has been pushed via git. Its totally annoying to have to switch back and forth between apps to see the change. This is an UI/UX anti-pattern to me. – Sentry.co Apr 08 '16 at 10:55