We have an app that uses file sharing. UIFileSharingEnable is set etc. and it all seems to work fine, but I'm looking for some sort of notification of when files have been added/deleted on the iPhone side. Can anyone advise?
Cheers in advance.
This thread on the Apple Developer Forums may be of interest, in which it is suggested that you run a kqueue in its own thread, tracking the app's Documents folder.
An Apple tech followed up with some sample code here:
- (void)kqueueFired
{
int kq;
struct kevent event;
struct timespec timeout = { 0, 0 };
int eventCount;
kq = CFFileDescriptorGetNativeDescriptor(self->_kqRef);
assert(kq >= 0);
eventCount = kevent(kq, NULL, 0, &event, 1, &timeout);
assert( (eventCount >= 0) && (eventCount < 2) );
if (eventCount == 1) {
NSLog(@"dir changed");
}
CFFileDescriptorEnableCallBacks(self->_kqRef, kCFFileDescriptorReadCallBack);
}
static void KQCallback(CFFileDescriptorRef kqRef, CFOptionFlags callBackTypes, void *info)
{
ViewController * obj;
obj = (ViewController *) info;
assert([obj isKindOfClass:[ViewController class]]);
assert(kqRef == obj->_kqRef);
assert(callBackTypes == kCFFileDescriptorReadCallBack);
[obj kqueueFired];
}
- (IBAction)testAction:(id)sender
{
#pragma unused(sender)
NSString * docPath;
int dirFD;
int kq;
int retVal;
struct kevent eventToAdd;
CFFileDescriptorContext context = { 0, self, NULL, NULL, NULL };
CFRunLoopSourceRef rls;
docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
assert(docPath != 0);
NSLog(@"%@", docPath);
dirFD = open([docPath fileSystemRepresentation], O_EVTONLY);
assert(dirFD >= 0);
kq = kqueue();
assert(kq >= 0);
eventToAdd.ident = dirFD;
eventToAdd.filter = EVFILT_VNODE;
eventToAdd.flags = EV_ADD | EV_CLEAR;
eventToAdd.fflags = NOTE_WRITE;
eventToAdd.data = 0;
eventToAdd.udata = NULL;
retVal = kevent(kq, &eventToAdd, 1, NULL, 0, NULL);
assert(retVal == 0);
assert(self->_kqRef == NULL);
self->_kqRef = CFFileDescriptorCreate(NULL, kq, true, KQCallback, &context);
assert(self->_kqRef != NULL);
rls = CFFileDescriptorCreateRunLoopSource(NULL, self->_kqRef, 0);
assert(rls != NULL);
CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
CFRelease(rls);
CFFileDescriptorEnableCallBacks(self->_kqRef, kCFFileDescriptorReadCallBack);
}
Here's an alternative solution using Grand Central Dispatch (GCD) that allows you to receive file change notifications from NSNotificationCenter:
Add these variables to the class's interface:
// Dispatch queue
dispatch_queue_t _dispatchQueue;
// A source of potential notifications
dispatch_source_t _source;
Add the following code to the implementation:
#define fileChangedNotification @"fileChangedNotification"
// Get the path to the home directory
NSString * homeDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
// Create a new file descriptor - we need to convert the NSString to a char * i.e. C style string
int filedes = open([homeDirectory cStringUsingEncoding:NSASCIIStringEncoding], O_EVTONLY);
// Create a dispatch queue - when a file changes the event will be sent to this queue
_dispatchQueue = dispatch_queue_create("FileMonitorQueue", 0);
// Create a GCD source. This will monitor the file descriptor to see if a write command is detected
// The following options are available
/*!
* @typedef dispatch_source_vnode_flags_t
* Type of dispatch_source_vnode flags
*
* @constant DISPATCH_VNODE_DELETE
* The filesystem object was deleted from the namespace.
*
* @constant DISPATCH_VNODE_WRITE
* The filesystem object data changed.
*
* @constant DISPATCH_VNODE_EXTEND
* The filesystem object changed in size.
*
* @constant DISPATCH_VNODE_ATTRIB
* The filesystem object metadata changed.
*
* @constant DISPATCH_VNODE_LINK
* The filesystem object link count changed.
*
* @constant DISPATCH_VNODE_RENAME
* The filesystem object was renamed in the namespace.
*
* @constant DISPATCH_VNODE_REVOKE
* The filesystem object was revoked.
*/
// Write covers - adding a file, renaming a file and deleting a file...
_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE,filedes,
DISPATCH_VNODE_WRITE,
_dispatchQueue);
// This block will be called when teh file changes
dispatch_source_set_event_handler(_source, ^(){
// We call an NSNotification so the file can change can be detected anywhere
[[NSNotificationCenter defaultCenter] postNotificationName:fileChangedNotification object:Nil];
});
// When we stop monitoring the file this will be called and it will close the file descriptor
dispatch_source_set_cancel_handler(_source, ^() {
close(filedes);
});
// Start monitoring the file...
dispatch_resume(_source);
//...
// When we want to stop monitoring the file we call this
//dispatch_source_cancel(source);
// To recieve a notification about the file change we can use the NSNotificationCenter
[[NSNotificationCenter defaultCenter] addObserverForName:fileChangedNotification object:Nil queue:Nil usingBlock:^(NSNotification * notification) {
NSLog(@"File change detected!");
}];
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With