File System Events How to Use Event Stream

I have been trying to observe a folder to see if files are created/modified/deleted etc. I have read that what I want to use is the File System Events, but I can't seem to get my code to work, and there doesn't seem to be much information about it on the internet. Could anyone give me a pointer in how to get it to work?


class ImageObserver: NSObject {
    init(path: String) {
        let allocator: CFAllocator? = kCFAllocatorDefault
       
        // Create FSEventStream and return valid FSEventStreamRef
        // Alias FSEventStreamCallback - CFunction
       
        typealias FSEventStreamCallback = @convention(c) (ConstFSEventStreamRef, UnsafeMutableRawPointer?, Int, UnsafeMutableRawPointer, UnsafePointer, UnsafePointer) -> Void

       
        let callback: FSEventStreamCallback = {
            (streamRef, clientCallBackInfo, numEvents, eventPaths, eventFlags, eventIds) -> Void in
            print ("changed")
            // handle file event
        }
    
        let context: UnsafeMutablePointer? = nil
        let pathsToWatch: CFArray = [NSHomeDirectory() + path] as CFArray
        let sinceWhen: FSEventStreamEventId = UInt64(kFSEventStreamEventIdSinceNow)
        let latency: CFTimeInterval = 1.0
        let flags: FSEventStreamCreateFlags = UInt32(kFSEventStreamCreateFlagNone)
        let eventStream = FSEventStreamCreate(
            allocator,
            callback,
            context,
            pathsToWatch,
            sinceWhen,
            latency,
            flags
        )

        FSEventStreamScheduleWithRunLoop(eventStream, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode as! CFString)
        FSEventStreamStart(eventStream!)
    }
 }

The class is fairly simple, it just takes in a string to the path and tries to make a file system event stream, adds it to FSEventStreamScheduleWithRunLoop and then starts it with FSEventStreamStart.


The errors i get is either on build

Value of optional type 'FSEventStreamRef?' (aka 'Optional') must be unwrapped to a value of type 'FSEventStreamRef' (aka 'OpaquePointer')

or on runtime

Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)

if i add a '!' to eventStream

Accepted Reply

The most critical part of you code is in this line:

FSEventStreamScheduleWithRunLoop(eventStream, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode as! CFString)


Generally, you should better avoid forced-casting using `as!`, which causes EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) as you see.


And some other points:

- `FSEventStreamCallback` is already defined. You have no need to decalre of your own.

- You should better avoid using forced-unwrapping `!`.


class ImageObserver: NSObject {
    init(path: String) {
        let allocator: CFAllocator? = kCFAllocatorDefault
        
        let callback: FSEventStreamCallback = {
            (streamRef, clientCallBackInfo, numEvents, eventPaths, eventFlags, eventIds) -> Void in
            print("changed")
            // handle file event
        }
        
        let context: UnsafeMutablePointer<FSEventStreamContext>? = nil
        let pathsToWatch: CFArray = [NSHomeDirectory() + path] as CFArray
        let sinceWhen: FSEventStreamEventId = UInt64(kFSEventStreamEventIdSinceNow)
        let latency: CFTimeInterval = 1.0
        let flags: FSEventStreamCreateFlags = UInt32(kFSEventStreamCreateFlagNone)
        if let eventStream = FSEventStreamCreate(
            allocator,
            callback,
            context,
            pathsToWatch,
            sinceWhen,
            latency,
            flags
        )
        {
            FSEventStreamScheduleWithRunLoop(eventStream, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode.rawValue)
            FSEventStreamStart(eventStream)
        } else {
            print("Failed to create FSEventStream")
        }
        
    }
}

Please do not miss `CFRunLoopMode.defaultMode.rawValue` in line 26.

Replies

The most critical part of you code is in this line:

FSEventStreamScheduleWithRunLoop(eventStream, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode as! CFString)


Generally, you should better avoid forced-casting using `as!`, which causes EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) as you see.


And some other points:

- `FSEventStreamCallback` is already defined. You have no need to decalre of your own.

- You should better avoid using forced-unwrapping `!`.


class ImageObserver: NSObject {
    init(path: String) {
        let allocator: CFAllocator? = kCFAllocatorDefault
        
        let callback: FSEventStreamCallback = {
            (streamRef, clientCallBackInfo, numEvents, eventPaths, eventFlags, eventIds) -> Void in
            print("changed")
            // handle file event
        }
        
        let context: UnsafeMutablePointer<FSEventStreamContext>? = nil
        let pathsToWatch: CFArray = [NSHomeDirectory() + path] as CFArray
        let sinceWhen: FSEventStreamEventId = UInt64(kFSEventStreamEventIdSinceNow)
        let latency: CFTimeInterval = 1.0
        let flags: FSEventStreamCreateFlags = UInt32(kFSEventStreamCreateFlagNone)
        if let eventStream = FSEventStreamCreate(
            allocator,
            callback,
            context,
            pathsToWatch,
            sinceWhen,
            latency,
            flags
        )
        {
            FSEventStreamScheduleWithRunLoop(eventStream, CFRunLoopGetCurrent(), CFRunLoopMode.defaultMode.rawValue)
            FSEventStreamStart(eventStream)
        } else {
            print("Failed to create FSEventStream")
        }
        
    }
}

Please do not miss `CFRunLoopMode.defaultMode.rawValue` in line 26.