Monitoring Files Using Dispatch Sources

If you need to know when a file has been modified, renamed, deleted... there's an easy way to do it using dispatch sources. In this post, we'll create a simple library that will notify you when a file has been changed.If you need to know when a file has been modified, renamed, deleted… there’s an easy way to do it using dispatch sources. In this post, we’ll create a simple library that will notify you when a file has been changed. I know I say this every time, but I’ll try and keep this post short (under 1000 words) ๐Ÿ™‚

Dispatch Sources

Think of dispatch sources as a feature built on top of GCD. In a nutshell, with dispatch sources, you have the ability to receive low-level system events. You can register to be notified when one of these system events occur.

GCD supports a handful of events you can register to receive:

  • Timer sources
  • Signal sources
  • Descriptor sources
  • Process sources
  • Mach port sources
  • Custom sources

In this article, we’ll focus on descriptor sources. Descriptor sources notify you of file and socket operations, so these are perfect for our purpose.

Show Me The Code

We’ll be using a special type of a dispatch source called a vnodeย and it allows us to monitor file descriptors (among other things). When we create the dispatch source we pass in the event flags that we want to register for (like file deletion, modification…). Let’s see this on an example:

We have to check if the file exists because if we try and use a non-existing file, we’ll crash and burn. We open a descriptor to the file and request to receive notifications only (the O_EVTONLY flag). Then at the end, we create our dispatch source by calling ‘makeFileSystemObjectSource’. Apart from the file descriptor, this method accepts the event mask, here we’ll register to receive write events. We can also specify a dispatch queue as a parameter. This will be the queue the event handler and the cancel handler will get called.

Every time a file is written to, the dispatch source we created will call the event handler. We have to set that up next:

In our event handler, we’re just calling a closure that we have set as our instance variable, but you can do a bit more here if you wanted. For example, you can fetch the number of bytes written to the file.

One important thing we have to do in our example is to close the file descriptor. The best place to do this is in the cancel handler:

Dispatch source will continue to receive events in the event handler until you cancel it. Once you cancel it, that’s it, it’s gone. But before it’s gone, it will call the cancel handler, here you have to close your file descriptors (or sockets if you’re using them), basically, clean up after yourself.

Dispatch source will not start dispatching events automatically. It will allow you to set everything up first. This means you have to tell it when it’s OK to start dispatching:

Couldn’t be simpler ๐Ÿ™‚ Now you should be seeing events coming in every time you change a file.

DAFileMonitor

I created a simple wrapper around dispatch sources that will allow you to easily monitor your file changes, you can find the library on my GitHub account.

The library has a closure that gets called from the event handler to notify the receiver that the file has been modified:

We’re doing two things when setting the closure: we’re cancelling the event source just before setting the closure and we’re creating the event source after setting the closure (if the closure is not nil).

And, of course, in the deinit method we have to cancel the dispatch source:

Usage

This class is really simple to use. If you’re happy with the default parameters, all you need to do is provide the path to the file you’re monitoring and you’ll get a callback when the file changes:

There we go, our simple file monitor is finished ๐Ÿ™‚

Conclusion

If you have to monitor your files for changes there’s an easy way to do it and with this little wrapper, it’s even easier. To be honest with you, I was planning on writing about something else this week, but I needed a library like this one. After a bit of digging through Apple documentation, I wrote my own. Hopefully, in one of the future posts, you’ll see how this piece of the puzzle fits into the picture (I won’t spoil the surprise for you ๐Ÿ™‚ ). You can get all the code on my GitHub account.

As promised, under 1000 words ๐Ÿ™‚

Have a nice day ๐Ÿ™‚

Dejan.

More resources

1+

Leave a Reply