Sane C++ Libraries
C++ Platform Abstraction Libraries
File System Watcher

🟩 Notifications {add, remove, rename, modified} for files and directories

SC::FileSystemWatcher allows watching directories for changes that happen to them.

Features

  • Get notified about modified files or directories
  • Get notified about added / removed / renamed files or directories

Status

🟩 Usable
Library does have basic capabilities and it can be used just fine.

Description

Caller can specify a callback for receiving notifications the SC::FileSystemWatcher::watch method.

Changes are grouped in two categories:

  • Added, removed and renamed files and directories
  • Modified files

There are two modes in which FileSystemWatcher can be initialized, defining how notifications are delivered:

Mode Description
SC::FileSystemWatcher::ThreadRunner Delivers notifications on a background thread.
SC::FileSystemWatcher::EventLoopRunner Delivers notifications using Async (SC::AsyncEventLoop).

Example using SC::FileSystemWatcher::EventLoopRunner:

// Initialize the FileSystemWatcher
FileSystemWatcher fileSystemWatcher;
FileSystemWatcher::EventLoopRunner eventLoopRunner;
SC_TRY(fileSystemWatcher.init(eventLoopRunner, eventLoop));
// Setup notification callback
auto onFileModified = [&](const FileSystemWatcher::Notification& notification)
{
// This callback will be called from the thread calling AsyncEventLoop::run
SmallString<1024> buffer;
StringView fullPath;
if (notification.getFullPath(buffer, fullPath))
{
switch (notification.operation)
{
case FileSystemWatcher::Operation::Modified: // File has been modified
console.print("Modified {} {}\n", notification.relativePath, fullPath);
break;
case FileSystemWatcher::Operation::AddRemoveRename: // File was added / removed
console.print("AddRemoveRename {} {}\n", notification.relativePath, fullPath);
break;
}
}
};
// Start watching a specific folder
FileSystemWatcher::FolderWatcher folderWatcher;
folderWatcher.notifyCallback = onFileModified;
SC_TRY(fileSystemWatcher.watch(folderWatcher, "/path/to/dir"));
// ...
// At a later point when there is no more need of watching the folder
SC_TRY(folderWatcher.stopWatching());
// ...
// When all watchers have been unwatched and to dispose all system resources
SC_TRY(fileSystemWatcher.close());
#define SC_TRY(expression)
Checks the value of the given expression and if failed, returns this value to caller.
Definition: Result.h:48

Example using SC::FileSystemWatcher::ThreadRunner:

// Initialize the FileSystemWatcher
FileSystemWatcher::ThreadRunner threadRunner; // <--- The thread runner
FileSystemWatcher fileSystemWatcher;
SC_TRY(fileSystemWatcher.init(threadRunner));
// Setup notification callback
auto onFileModified = [&](const FileSystemWatcher::Notification& notification)
{
// Warning! This callback is called from a background thread!
// Make sure to do proper synchronization!
SmallString<1024> buffer;
StringView fullPath;
if (notification.getFullPath(buffer, fullPath))
{
switch (notification.operation)
{
case FileSystemWatcher::Operation::Modified: // File has been modified
console.print("Modified {} {}\n", notification.relativePath, fullPath);
break;
case FileSystemWatcher::Operation::AddRemoveRename: // File was added / removed
console.print("AddRemoveRename {} {}\n", notification.relativePath, fullPath);
break;
}
}
};
// Start watching a specific folder
FileSystemWatcher::FolderWatcher folderWatcher;
folderWatcher.notifyCallback = onFileModified;
SC_TRY(fileSystemWatcher.watch(folderWatcher, "/path/to/dir"));
// ...
// At a later point when there is no more need of watching the folder
SC_TRY(folderWatcher.stopWatching());
// ...
// When all watchers have been unwatched and to dispose all system resources
SC_TRY(fileSystemWatcher.close());

Videos

This is the list of videos that have been recorded showing some of the internal thoughts that have been going into this library:

Blog

Some relevant blog posts are:

Details

The class tries to unify differences between OS specific API to deliver folder change notifications

  • On macOS and iOS FSEvents by CoreServices is used.
  • On Windows ReadDirectoryChangesW is used.

The behavior between these different system also depends on the file system where the watched directory resides.

Note
On iOS FSEvents api is private so using SC::FileSystemWatcher will be very likely causing your app to be rejected from the app store.

Examples

Roadmap

🟦 Complete Features:

  • Not sure what else could be useful here

💡 Unplanned Features:

  • Having a thread based polling stat watcher that checks file modifications on intervals as fallback
  • Allow users to provide their own thread instead of creating it behind the scenes