Skip to content

A high-performance, Swift-native file system watcher for macOS and iOS that provides intelligent monitoring of directory changes with minimal system resource usage.

License

Notifications You must be signed in to change notification settings

okooo5km/FSWatcher

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Using my apps is also a way to support me:

Zipic Orchard TimeGo Clock KeygenGo HiPixel


FSWatcher

Swift Platform License

A high-performance, Swift-native file system watcher for macOS and iOS that provides intelligent monitoring of directory changes with minimal system resource usage.

Features

Event-Driven Architecture - Uses DispatchSource for efficient file system monitoring
🎯 Smart Filtering - Advanced filter chains with support for file types, sizes, and patterns
🔍 Predictive Ignoring - Avoid monitoring self-generated files
📁 Recursive Monitoring - Watch entire directory trees with configurable depth
Modern Swift - Full support for Combine, Swift Concurrency, and structured concurrency
🛡️ Thread-Safe - Designed for concurrent use across multiple threads
📊 Low Resource Usage - Minimal CPU and memory footprint

Installation

Swift Package Manager

Add FSWatcher to your project through Xcode or by adding it to your Package.swift:

dependencies: [
    .package(url: "https://github.com/okooo5km/FSWatcher.git", from: "1.0.0")
]

Quick Start

Basic Usage

import FSWatcher

// Create a watcher for a directory
let watcher = try DirectoryWatcher(url: URL(fileURLWithPath: "/Users/user/Documents"))

// Set up event handler
watcher.onDirectoryChange = { url in
    print("Directory changed: \\(url.path)")
}

// Start watching
watcher.start()

Filtered Watching

// Watch only image files larger than 1KB
var config = DirectoryWatcher.Configuration()
config.filterChain.add(.imageFiles)
config.filterChain.add(.fileSize(1024...))

let watcher = try DirectoryWatcher(url: watchURL, configuration: config)

watcher.onFilteredChange = { imageFiles in
    print("New images: \\(imageFiles.map { $0.lastPathComponent })")
}

Multiple Directories

let multiWatcher = MultiDirectoryWatcher()
multiWatcher.onDirectoryChange = { url in
    print("Change in: \\(url.path)")
}

multiWatcher.startWatching(directories: [documentsURL, downloadsURL])

Recursive Monitoring

var options = RecursiveWatchOptions()
options.maxDepth = 5
options.excludePatterns = ["node_modules", ".git", "*.tmp"]

let recursiveWatcher = try RecursiveDirectoryWatcher(
    url: projectURL, 
    options: options
)

Advanced Features

Smart Filtering System

FSWatcher provides a powerful filtering system that can be chained together:

// Combine multiple filters
watcher.addFilter(
    .fileExtensions(["swift", "m"])
        .and(.fileSize(1000...))
        .and(.modifiedWithin(3600))
)

// Pre-built filter types
config.filterChain.add(.imageFiles)      // Images
config.filterChain.add(.videoFiles)      // Videos  
config.filterChain.add(.documentFiles)   // Documents
config.filterChain.add(.directoriesOnly) // Directories only

Predictive Ignoring

Prevent monitoring your own output files:

// Set up a transform predictor
let predictor = FileTransformPredictor.imageCompression(suffix: "_compressed")
config.transformPredictor = predictor

// The watcher will automatically ignore predicted output files
watcher.onFilteredChange = { newImages in
    for image in newImages {
        compressImage(image) // Output will be automatically ignored
    }
}

Combine Integration

import Combine

watcher.directoryChangePublisher
    .debounce(for: .seconds(1), scheduler: RunLoop.main)
    .sink { url in
        print("Debounced change: \\(url.path)")
    }
    .store(in: &cancellables)

Swift Concurrency

watcher.start()

for await url in watcher.directoryChanges {
    await processChange(at: url)
}

Configuration Options

DirectoryWatcher.Configuration

var config = DirectoryWatcher.Configuration()

// Debounce interval (default: 0.5 seconds)
config.debounceInterval = 1.0

// File system events to monitor
config.eventMask = [.write, .extend, .delete, .rename]

// Processing queue
config.queue = .global(qos: .userInitiated)

// Filter chain
config.filterChain.add(.imageFiles)

// Ignore list management
config.ignoreList.addIgnorePattern("*.tmp")

// Transform prediction
config.transformPredictor = FileTransformPredictor.imageCompression()

Error Handling

watcher.onError = { error in
    switch error {
    case .cannotOpenDirectory(let url):
        print("Cannot open: \\(url.path)")
    case .insufficientPermissions(let url):
        print("Permission denied: \\(url.path)")
    case .directoryNotFound(let url):
        print("Not found: \\(url.path)")
    default:
        print("Error: \\(error)")
    }
}

Use Cases

Image Processing Pipeline

Perfect for building image compression tools like Zipic:

let pipeline = try ImageCompressionPipeline(
    watchDirectory: URL(fileURLWithPath: "/Users/user/ToCompress"),
    compressionQuality: 0.8
)

pipeline.start()

Development Tool Hot Reload

Monitor source code changes:

let projectWatcher = try RecursiveDirectoryWatcher(url: projectURL)
projectWatcher.addFilter(.fileExtensions(["swift", "js", "css"]))

projectWatcher.onFilteredChange = { changedFiles in
    triggerHotReload(for: changedFiles)
}

Automatic Backup System

let backupWatcher = try DirectoryWatcher(url: documentsURL)
backupWatcher.addFilter(.modifiedWithin(300)) // Last 5 minutes

backupWatcher.onFilteredChange = { recentFiles in
    performIncrementalBackup(files: recentFiles)
}

Performance Considerations

  • Event-driven: Only processes actual file system events, no polling
  • Debounced: Prevents excessive event handling during rapid changes
  • Filtered: Process only relevant files using efficient filter chains
  • Resource management: Automatic cleanup of file descriptors and resources

Thread Safety

FSWatcher is designed to be thread-safe:

  • All public APIs can be called from any thread
  • Internal state is protected with appropriate synchronization
  • Event handlers are called on the configured dispatch queue

System Requirements

  • macOS: 12.0+
  • iOS: 15.0+
  • Swift: 5.9+
  • Xcode: 14.0+

Documentation

Examples

The Examples/ directory contains complete, runnable examples:

  • BasicUsage.swift - Fundamental usage patterns
  • ImageCompression.swift - Complete image processing pipeline
  • HotReload.swift - Development tool integration

Contributing

Contributions are welcome! Please read our Contributing Guide for details.

License

FSWatcher is available under the MIT license. See the LICENSE file for more info.

Acknowledgments

FSWatcher was inspired by the successful file monitoring implementation in Zipic, a popular image compression tool for macOS. The design focuses on performance, reliability, and developer experience learned from real-world usage.


Made with ❤️ for the Swift community

About

A high-performance, Swift-native file system watcher for macOS and iOS that provides intelligent monitoring of directory changes with minimal system resource usage.

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages