When Swift was announced at WWDC 2014, I was very disappointed to not be able to use some of powerful C APIs such as FSEvents, to write applications in pure Swift language.
A lot of C APIs needs a C function pointer to pass a callback function when you use the API. From Swift 1.0 to Swift 1.2, it was not possible to use C function pointer in pure Swift language.
It was possible to use FSEvents by wrapping it in an Objective-C class, and by calling this class in Swift by using the bridging features offered by the language, but it was not what I attempted to accomplish.
But things change and at WWDC 2015, Apple announced the possibility to use C function pointers with Swift 2.0.
Today I’m very pleased to give you a sample code which is a good starting point to create your own FileSystemWatcher using the FSEvents API written with Swift 2.0.
import Foundation public class FileSystemWatcher { // MARK: - Initialization / Deinitialization public init(pathsToWatch: [String], sinceWhen: FSEventStreamEventId) { self.lastEventId = sinceWhen self.pathsToWatch = pathsToWatch } convenience public init(pathsToWatch: [String]) { self.init(pathsToWatch: pathsToWatch, sinceWhen: FSEventStreamEventId(kFSEventStreamEventIdSinceNow)) } deinit { stop() } // MARK: - Private Properties private let eventCallback: FSEventStreamCallback = { (stream: ConstFSEventStreamRef, contextInfo: UnsafeMutablePointer<Void>, numEvents: Int, eventPaths: UnsafeMutablePointer<Void>, eventFlags: UnsafePointer<FSEventStreamEventFlags>, eventIds: UnsafePointer<FSEventStreamEventId>) in print("***** FSEventCallback Fired *****", appendNewline: true) let fileSystemWatcher: FileSystemWatcher = unsafeBitCast(contextInfo, FileSystemWatcher.self) let paths = unsafeBitCast(eventPaths, NSArray.self) as! [String] for index in 0..<numEvents { fileSystemWatcher.processEvent(eventIds[index], eventPath: paths[index], eventFlags: eventFlags[index]) } fileSystemWatcher.lastEventId = eventIds[numEvents - 1] } private let pathsToWatch: [String] private var started = false private var streamRef: FSEventStreamRef! // MARK: - Private Methods private func processEvent(eventId: FSEventStreamEventId, eventPath: String, eventFlags: FSEventStreamEventFlags) { print("\t\(eventId) - \(eventFlags) - \(eventPath)", appendNewline: true) } // MARK: - Pubic Properties public private(set) var lastEventId: FSEventStreamEventId // MARK: - Pubic Methods public func start() { guard started == false else { return } var context = FSEventStreamContext(version: 0, info: nil, retain: nil, release: nil, copyDescription: nil) context.info = UnsafeMutablePointer<Void>(unsafeAddressOf(self)) let flags = UInt32(kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagFileEvents) streamRef = FSEventStreamCreate(kCFAllocatorDefault, eventCallback, &context, pathsToWatch, lastEventId, 0, flags) FSEventStreamScheduleWithRunLoop(streamRef, CFRunLoopGetMain(), kCFRunLoopDefaultMode) FSEventStreamStart(streamRef) started = true } public func stop() { guard started == true else { return } FSEventStreamStop(streamRef) FSEventStreamInvalidate(streamRef) FSEventStreamRelease(streamRef) streamRef = nil started = false } }
I hope this will help you in your future applications and feel free to share this article if you found it helpful 😉