Using FSEvents with Swift 2.0

Hi,


If you're interested on how to use the FSEvents API with Swift 2.0, I've just posted a sample code on my blog.


http://blog.beecomedigital.com/2015/06/27/developing-a-filesystemwatcher-for-os-x-by-using-fsevents-with-swift-2/


You can see how to use a C function pointer (written as a Swift closure) with an old C API which requires a callback function.


I hope this will help some people 😉


Kind regards

My code, is very similar to yours, but the line is causing an EXC_BAD_ACCESS errpr:

let paths = unsafeBitCast(eventPaths, NSArray.self) as! [String]


Here's the full code for my subscribe implementation:


    func subscribeToEvents()
    {
        var context:FSEventStreamContext = FSEventStreamContext()
        context.info = nil;
        context.version = 0;
        context.retain = nil;
        context.release = nil;
        context.copyDescription = nil;
  
        let fileSystemObserverCallback:FSEventStreamCallback = { (stream: ConstFSEventStreamRef, contextInfo: UnsafeMutablePointer<Void>, numEvents: Int, eventPaths: UnsafeMutablePointer<Void>, eventFlags: UnsafePointer<FSEventStreamEventFlags>, eventIds: UnsafePointer<FSEventStreamEventId>) in
          
            print("File System Event Observed")
            print(numEvents)
            let paths = unsafeBitCast(eventPaths, NSArray.self) as! [String]
            print( paths )
            print("-------------")
        }
      
        let paths = [self.rootPath.path!];
        let pathsToWatch = paths.map({$0 as NSString}) as [AnyObject];
      
        self.stream = FSEventStreamCreate(nil, fileSystemObserverCallback, &context, pathsToWatch, FSEventStreamEventId(kFSEventStreamEventIdSinceNow), CFTimeInterval(1.0), FSEventStreamCreateFlags(kFSEventStreamCreateFlagWatchRoot) )
      
        FSEventStreamScheduleWithRunLoop(self.stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)
        FSEventStreamStart(self.stream)
      
        print("Subscribed to FileStream Events");
    }


The instance of this watcher class is retained by the constructing class as a member variable. Any thoughts? Maybe the version of swift changed this from working? I do notice that it says "UnsafeBitCast" is available on 10.11 and I'm trying to run this on 10.10.....


It does compile, maybe the version discrpency is causing a problem... was hoping to hold off. Maybe I'll look into GCD for this kind of watching in the mean time.

FSEventStreamCallback


eventPaths


An array of paths to the directories in which event(s) occurred.

The type of this parameter depends on the flags passed to FSEventStreamCreate...(). If kFSEventStreamCreateFlagUseCFTypes was set, then this will be a CFArrayRef containing CFStringRef objects (per CFStringCreateWithFileSystemRepresentation()). Ownership follows the Get rule, and they will be released by the framework after your callback returns. If kFSEventStreamCreateFlagUseCFTypes was not set, then the framework will pass your callback a raw C array of raw C strings that will be deallocated by the framework after your callback returns.


In your code, you don't set kFSEventStreamCreateFlagUseCFTypes, so, eventPaths in the callback is NOT a CFArrayRef, which cannot be converted to NSArray with unsafeBitCast.

Hi Stephane


I'm getting the old "A C function pointer cannot be formed from a closure that captures context".


I know only global Swift functions or nested or anonymous functions with no captured data can be passed as @convention(c) parameters.


Do you have any suggestions on how to pass data to your processEvent method?

At the moment I am passing in data to my class init, but then I'm not able to access this globally in the class. Is that why you init lastEventId and pathsToWatch, referring to the local variable?

Hi almccann,


I don't really understand what's your problem (maybe it will be easier with an extract of your code).


What do you mean by "Do you have any suggestions on how to pass data to your processEvent method" ?

Actually in my code, some values are passed to the method so you can add additional parameters and pass relevant values as you want.


Using FSEvents with Swift 2.0
 
 
Q