Ad

Our DNA is written in Swift
Jump

Monitoring a Folder with GCD

There is no notification to be gotten if the user adds or removes files to your app’s documents folder. The only way to update your list of files in that case is to monitor the folder for changes. There are several different approaches to achieve this, the traditional one being the File System Events API.

But since iOS 4 – together with GCD – Apple added a simpler method for monitoring a vnode, dispatch sources.

A dispatch source can be many different things, according to the Concurrency Programming Guide:

dispatch source is a fundamental data type that coordinates the processing of specific low-level system events. Grand Central Dispatch supports the following types of dispatch sources:

  • Timer dispatch sources generate periodic notifications.
  • Signal dispatch sources notify you when a UNIX signal arrives.
  • Descriptor sources notify you of various file- and socket-based operations, such as:
    • When data is available for reading
    • When it is possible to write data
    • When files are deleted, moved, or renamed in the file system
    • When file meta information changes
  • Process dispatch sources notify you of process-related events, such as:
    • When a process exits
    • When a process issues a fork or exec type of call
    • When a signal is delivered to the process
  • Mach port dispatch sources notify you of Mach-related events.
  • Custom dispatch sources are ones you define and trigger yourself.

Basically a dispatch source has a block at the ready and will dispatch that onto a specified GCD queue whenever certain events occur. Today we are specifically interested in dispatch sources which are trigged by changes to file descriptors as marked in yellow above.

A file descriptor is basically a handle for a vnode in the file system. Such a vnode can be any entry, like a folder or a file. This is the first thing we need to create:

_fileDescriptor = open([path fileSystemRepresentation], O_EVTONLY);

The O_EVTONLY means that we are getting a file descriptor “for event notifications only”, according to the open() man page.

As also want to have our own dispatch queue for the event blocks, so that we see this queue in debugger.

_queue = dispatch_queue_create("DTFolderMonitor Queue", 0);

Those are the basic ingredients, now we can create the dispatch source, set the event handler and the cancel handler. The event handler is the block to execute, the cancel handler gets executed if you cancel the dispatch source. We want “a dispatch source that monitors a file descriptor for events” (DISPATCH_SOURCE_TYPE_VNODE) and of the various kinds of VNODE events we are only interested in writes to the descriptor because this occurs when files are added or removed from the folder.

// watch the file descriptor for writes
_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, _fileDescriptor, DISPATCH_VNODE_WRITE, _queue);
 
// call the passed block if the source is modified
dispatch_source_set_event_handler(_source, _block);
 
// close the file descriptor when the dispatch source is cancelled
dispatch_source_set_cancel_handler(_source, ^{
 
	close(_fileDescriptor);
});
 
// at this point the dispatch source is paused, so start watching
dispatch_resume(_source);

The cancel handler is also the best place to close the file descriptor. Dispatch sources come into play suspended, so the final statement here is to resume it so that it starts watching our folder.

There will be a time when we want to cancel the monitoring, this is done by cancelling the dispatch source and cleaning up. Note that depending on your deployment target GCD objects might be covered by ARC, so this is why we use the OS_OBJECT_USE_OBJC define to only release them if we may.

dispatch_source_cancel(_source);
 
#if !OS_OBJECT_USE_OBJC
dispatch_release(_source);
#endif
 
_source = nil;
 
#if OS_OBJECT_USE_OBJC
	dispatch_release(_queue);
#endif

Those code snippets are from DTFolderMonitor which I am adding to DTFoundation, with a bit nice documentation and also facilities to start and stop monitoring. So if you don’t want to copy/paste the code from here into your own folder monitor class, then just use my implementation.

Conclusion

Understanding all of the available dispatch sources takes quite a bit of reading. This is the first concrete example I was able to come up with to demonstrate a use case for it.

Once you are down to the bare bones necessary to get it working you quickly see that it is actually relatively simple to understand, don’t let yourself be confused by the C-syntax of GCD.


Categories: Recipes

18 Comments »

  1. Great article!
    The GCD works great for monitoring file changes. However, I find the information that can be obtained about the actual change very limited. Is there a way to identify the process that triggered the change? Another useful information would be to find out which attribute changed, in case of a metadata change.

  2. Somehow, I am not able to monitor the changes in the Documents directory of my iOS app. My application is a file sharing enabled app. I try to add a file to the documents directory from iTunes but still my block of code is not being called [meaning, the dispatch is not firing] Any clue?

    Thanks,