Code Monkey home page Code Monkey logo

cdevents's People

Contributors

dwlnetnl avatar justsid avatar kunalparmar avatar rastersize avatar tonyxiao avatar ttilley avatar uasi avatar vtourraine avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cdevents's Issues

Returning different value of _eventsFlags in CDEvents(FSEvents wrapper )?

I implemented file watcher part using CDEvents
Its giving output like this :

2014-02-11 16:08:38.725 TestCDEvent10-2[2995:403]
CDEvent {
eventId = 3182336,
eventPath = /Users/user1/Desktop,
eventFlags = 131328
}
Value of enum is :

kFSEventStreamEventFlagItemCreated = 0x00000100,
kFSEventStreamEventFlagItemRemoved = 0x00000200,
kFSEventStreamEventFlagItemInodeMetaMod = 0x00000400,
kFSEventStreamEventFlagItemRenamed = 0x00000800,
kFSEventStreamEventFlagItemModified = 0x00001000,
kFSEventStreamEventFlagItemFinderInfoMod = 0x00002000,
kFSEventStreamEventFlagItemChangeOwner = 0x00004000,
kFSEventStreamEventFlagItemXattrMod = 0x00008000,
kFSEventStreamEventFlagItemIsFile = 0x00010000,
kFSEventStreamEventFlagItemIsDir = 0x00020000,
kFSEventStreamEventFlagItemIsSymlink = 0x00040000

It register CDEvents flags with above value, and If we run the application and change the file contents or renamed the file the value of _eventsFlags is different. So i unable to know which file event is occured?

Please provide me direction...

Suggestion: Refactoring just a little

Hi Aron,

I actually wrote a wrapper of my own for kQueue and FSEvents. I dropped kQueue to avoid issues with my sandboxed app, but took the FSEvent code and refactored it. I was inspired by yours and SCEvent in how you handle your event class. So I borrowed a bit from it. Anyways, I am using a simple Category on NSObject that helps make things a little bit easier.

@implementation NSObject (CategoryNSObject)

pragma mark Category Methods:

  • (void) associateValue:(id)value withKey:(void *)aKey {
    objc_setAssociatedObject(self, aKey, value, OBJC_ASSOCIATION_RETAIN);

}

  • (id) associatedValueForKey:(void *)aKey {
    return objc_getAssociatedObject(self, aKey);
    }

Anyways, to share just a bit here is my code where maybe it can give you any ideas for future work or refactoring CDEvents:

HEADER:

pragma mark - Watcher File System Event:

@interface WatcherFSEvent : NSObject {

@Private
WatcherFSEventID _identifier;
WatcherFSFlags _flags;
NSURL * _url;
}

@Property (readonly, nonatomic) WatcherFSEventID identifier;
@Property (readonly, nonatomic) WatcherFSFlags flags;
@Property (readonly, strong, nonatomic) NSURL * url;

  • (id) eventWithID:(WatcherFSEventID)anID
    flags:(WatcherFSFlags)fsFlags
    url:(NSURL *)aURL;
  • (id) initWithID:(WatcherFSEventID)anID
    flags:(WatcherFSFlags)fsFlags
    url:(NSURL *)aURL;
  • (NSString *) description;

@Property (readonly, nonatomic) BOOL isGenericChange;
@Property (readonly, nonatomic) BOOL mustRescanSubDirectories;
@Property (readonly, nonatomic) BOOL isUserDropped;
@Property (readonly, nonatomic) BOOL isKernelDropped;
@Property (readonly, nonatomic) BOOL isEventIdsWrapped;
@Property (readonly, nonatomic) BOOL isHistoryDone;
@Property (readonly, nonatomic) BOOL isRootChanged;
@Property (readonly, nonatomic) BOOL didVolumeMount;
@Property (readonly, nonatomic) BOOL didVolumeUnmount;

@EnD

pragma mark - Watcher File System Operation:

@interface WatcherFSOperation : NSOperation {

@Private
WatcherFSRef _streamRef;
WatcherFSEventID _sinceIdentifier;
NSArray * _watchedURLs;
}

@Property (readwrite, nonatomic) WatcherFSRef streamRef;
@Property (readwrite, nonatomic) WatcherFSEventID sinceIdentifier;
@Property (readwrite, strong, nonatomic) NSArray * watchedURLs;

  • (id) initWithIdentifier:(WatcherFSEventID)eventID;

@EnD

pragma mark - Watcher File System:

/*!

  • @discussion Usage:
  •          Call startWatching: to begin watching
    
  •          a file system level URL (file://)
    
  •          Call stopWatching: to stop the file stream
    
  •          Call resumeWatching: to resume watching
    
  •          from a prior session (e.g. after app restarts)
    
  • @example [[NSApp watcherFS] startWatching:arrayOfURLs
  •                                     block:^(WatcherFSEvent *event){
    
  •              << Do stuff here when event fires   >>
    
  •              << Use WatcherFSEvent for more info >>
    
  •          }];
    
    */

typedef void (^WatcherFSBlock)(WatcherFSEvent *event);

@interface WatcherFileSystem : NSObject {

@Private
NSOperationQueue * _fileSystemQueue;
}

@Property (readonly, strong, nonatomic) NSOperationQueue * fileSystemQueue;

  • (WatcherOPUUID *) startWatching:(NSArray *)urlArray
    block:(WatcherFSBlock)block;
  • (WatcherOPUUID *) resumeWatching:(NSArray *)urlArray
    withEventID:(WatcherFSEventID)eventID
    block:(WatcherFSBlock)block;
  • (void) stopWatching:(WatcherOPUUID *)opUUID;
  • (void) close;

@EnD

IMPLEMENTATION:

import "Watcher.h"

pragma mark Implemination (File System):

@implementation WatcherFileSystem

@synthesize fileSystemQueue = _fileSystemQueue;

pragma mark Initialization:

  • (id) init {

    if ( (self = [super init]) ) {

    _fileSystemQueue = [[NSOperationQueue alloc] init];
    
    REPORT_ARGS( errReportArgInfoLoaded, @"Watcher (File System)" );
    

    } return self;
    }

  • (void) close { [self.fileSystemQueue cancelAllOperations]; }

pragma mark Watching Methods:

  • (WatcherOPUUID *) startWatching:(NSArray *)urlArray
    block:(WatcherFSBlock)block {

    WatcherOPUUID * opUUID = [NSProcessInfo shortUUID];
    WatcherFSOperation * operation = [[WatcherFSOperation alloc] init];
    [operation associateValue:opUUID withKey:kWatcherFileSystemOPUUID];
    [operation associateValue:[block copy] withKey:kWatcherFileSystemOPBlock];

    operation.watchedURLs = urlArray;
    [self.fileSystemQueue addOperation:operation];

    return opUUID;
    }

  • (WatcherOPUUID *) resumeWatching:(NSArray *)urlArray
    withEventID:(WatcherFSEventID)eventID
    block:(WatcherFSBlock)block {

    WatcherOPUUID * opUUID = [NSProcessInfo shortUUID];
    WatcherFSOperation * operation = [[WatcherFSOperation alloc] initWithIdentifier:eventID];
    [operation associateValue:opUUID withKey:kWatcherFileSystemOPUUID];
    [operation associateValue:[block copy] withKey:kWatcherFileSystemOPBlock];

    operation.watchedURLs = urlArray;
    [self.fileSystemQueue addOperation:operation];

    return opUUID;
    }

  • (void) stopWatching:(WatcherOPUUID *)opUUID {

    NSArray * operations = [self.fileSystemQueue operations];

    if ( [operations count] > 0 ) {
    for ( NSOperation * op in operations ) {
    WatcherOPUUID * aUUID = [op associatedValueForKey:kWatcherFileSystemOPUUID];
    if ( [opUUID isEqualToString:aUUID] ) {
    [op cancel]; break;
    } } }
    }

@EnD

pragma mark - FS Operation (Callback):

@interface WatcherFSOperation ()
static void FSEventsCallback(
ConstFSEventStreamRef streamRef,
void *callbackInfo,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[]);

  • (void) disposeStream;
  • (void) handleEvent:(WatcherFSEvent *)event;

@EnD

pragma mark Implemination (FS Operation):

@implementation WatcherFSOperation

@synthesize streamRef = _streamRef,
sinceIdentifier = _sinceIdentifier,
watchedURLs = _watchedURLs;

pragma mark Initialization:

  • (id) init {
    if ( (self = [super init]) ) {
    self.sinceIdentifier = kFSEventStreamEventIdSinceNow;
    } return self;
    }
  • (id) initWithIdentifier:(WatcherFSEventID)eventID {
    if ( (self = [super init]) ) {
    self.sinceIdentifier = eventID;
    } return self;
    }

pragma mark Main Run Loop:

  • (void) main {

    @Try {

    if ( [self.watchedURLs count] == 0 ) {
        [self cancel]; return;   
    }
    
    NSMutableArray * fsArray = [NSMutableArray array];
    for ( NSURL * aURL in self.watchedURLs )
        [fsArray addObject:[aURL path]];
    
    FSEventStreamContext callbackCTX = { 0, (void *)self, nil, nil, nil };
    
    self.streamRef = FSEventStreamCreate( kCFAllocatorDefault, 
                                          &FSEventsCallback, 
                                          &callbackCTX, 
                                          (CFArrayRef)fsArray, 
                                          self.sinceIdentifier, 
                                          (CFTimeInterval)kWatcherEventLatency, 
                                          kWatcherFSCreateFlags );
    
    FSEventStreamScheduleWithRunLoop( self.streamRef, 
                                      CFRunLoopGetCurrent(), 
                                      kCFRunLoopDefaultMode );
    fsArray = nil;
    
    if ( !FSEventStreamStart( self.streamRef ) ) {
    
        REPORT( errReportErrorWatcherFSStart );
        [self disposeStream];
        [self cancel]; return;
    }
    
    while ( !self.isCancelled )
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode 
                                 beforeDate:[[NSDate date] dateByAddingTimeInterval:0.5]];
    
    [self disposeStream];
    

    } @catch ( NSException *exception ) {
    REPORT_ARGS( errReportArgErrorWatcherFSException, [exception description] );
    }
    }

pragma mark Private Methods:

  • (void) disposeStream {

    if ( !self.streamRef ) return;

    FSEventStreamStop ( self.streamRef );
    FSEventStreamInvalidate( self.streamRef );
    FSEventStreamRelease ( self.streamRef );
    self.streamRef = nil;
    }

  • (void) handleEvent:(WatcherFSEvent *)event {

    WatcherFSBlock block = [self associatedValueForKey:kWatcherFileSystemOPBlock];
    block(event);
    }

pragma mark FS Callback:

static void FSEventsCallback(
ConstFSEventStreamRef streamRef,
void *callbackInfo,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[]) {

WatcherFSOperation * watcher = (WatcherFSOperation *)callbackInfo;
NSArray            * paths   = (NSArray *)eventPaths;

for ( NSUInteger i = 0; i < numEvents; i++) {

    NSString * path = [paths objectAtIndex:i];
    path = [path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];

    NSURL * eventURL = [NSURL URLWithString:path];

    WatcherFSEvent * event = [WatcherFSEvent eventWithID:eventIds[i] 
                                                   flags:eventFlags[i] 
                                                     url:eventURL];        
    [watcher handleEvent:event];
}

}

@EnD

pragma mark - Implemination (FS Event):

@implementation WatcherFSEvent

@synthesize identifier = _identifier,
flags = _flags,
url = _url;

pragma mark Initialization:

  • (WatcherFSEvent *) eventWithID:(WatcherFSEventID)anID
    flags:(WatcherFSFlags)fsFlags
    url:(NSURL *)aURL {

    return [[WatcherFSEvent alloc] initWithID:anID
    flags:fsFlags
    url:aURL];
    }

  • (id) initWithID:(WatcherFSEventID)anID
    flags:(WatcherFSFlags)fsFlags
    url:(NSURL *)aURL {

    if ( (self = [super init]) ) {

    _identifier = anID;
    _flags      = fsFlags;
    _url        = aURL;
    

    } return self;
    }

pragma mark Getter Methods:

  • (NSString *) description {

    return [NSString stringWithFormat: @"id = %ld, URL = %@, flags = %ld",
    (unsigned long)self.identifier,
    [self.url path],
    (unsigned long)self.flags];
    }

  • (BOOL) isGenericChange { return ( kFSEventStreamEventFlagNone == _flags ); }

  • (BOOL) mustRescanSubDirectories { return ( ((self.flags) & (kFSEventStreamEventFlagMustScanSubDirs)) ? YES : NO ); }

  • (BOOL) isUserDropped { return ( ((self.flags) & (kFSEventStreamEventFlagUserDropped)) ? YES : NO ); }

  • (BOOL) isKernelDropped { return ( ((self.flags) & (kFSEventStreamEventFlagKernelDropped)) ? YES : NO ); }

  • (BOOL) isEventIdsWrapped { return ( ((self.flags) & (kFSEventStreamEventFlagEventIdsWrapped)) ? YES : NO ); }

  • (BOOL) isHistoryDone { return ( ((self.flags) & (kFSEventStreamEventFlagHistoryDone)) ? YES : NO ); }

  • (BOOL) isRootChanged { return ( ((self.flags) & (kFSEventStreamEventFlagRootChanged)) ? YES : NO ); }

  • (BOOL) didVolumeMount { return ( ((self.flags) & (kFSEventStreamEventFlagMount)) ? YES : NO ); }

  • (BOOL) didVolumeUnmount { return ( ((self.flags) & (kFSEventStreamEventFlagUnmount)) ? YES : NO ); }

@EnD

DEFINITIONS:

import <CoreServices/CoreServices.h>

pragma mark Watcher File System Definitions:

typedef NSString WatcherOPUUID;
typedef FSEventStreamRef WatcherFSRef;
typedef FSEventStreamEventId WatcherFSEventID;
typedef FSEventStreamEventFlags WatcherFSFlags;
typedef FSEventStreamCreateFlags WatcherFSCreate;

static NSString * const kWatcherFileSystemOPUUID = @"WatcherFSOPUUID";
static NSString * const kWatcherFileSystemOPBlock = @"WatcherFSOPBlock";

static NSTimeInterval const kWatcherEventLatency = 3.0;
static WatcherFSCreate const kWatcherFSCreateFlags = ( kFSEventStreamCreateFlagUseCFTypes |
kFSEventStreamCreateFlagWatchRoot );

Note: Though I compile with 10.7 SDK my target is 10.6 or greater. That is why several of the other event flags were not implemented. It also keeps things easier and events to fire less frequently.

One last thing, I have an instance of this class in the singleton of the app, but my macro for a singleton is like so:

/*!

  • @function Singleton GCD Macro
    */
    #ifndef SINGLETON_GCD
    #define SINGLETON_GCD(classname)
    \
    • (classname *)shared##classname {

      static dispatch_once_t pred;
      static classname * shared##classname = nil;
      dispatch_once( &pred, ^{
      shared##classname = [[self alloc] init];
      });
      return shared##classname;
      }
      #endif

Just declare the function in the head and call this macro in the implementation: SINGLETON_GCD(mySingletonClass)

Hope this helps you out, since you helped me out with your code.

Spaces in path

CDEvent URLs come back as null if the path has any spaces. I think you need to run the string through stringByAddingPercentEscapesUsingEncoding before converting to NSURL as below.

NSString *escapedEventPath = [eventPath stringByAddingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
NSURL *eventURL = [NSURL URLWithString:escapedEventPath];

(not sure if NSUTF8StringEncoding is the best encoding)

How to identify rename and removed event?

CDEvent returns 67584 flags for both operation renamed and removed.
So how to identify whether which events is raised? How to get proper created, deleted, renamed, and modified events?

Add unit tests

Add unit tests, because units tests are just as cool as bow ties and bow ties are coool.

path with whiteSpace will be ignore

[[CDEvents alloc]initWithURLs:urlPaths.....
when path has whiteSpace inside, i have to convert it with utf8, and use urlWithString to init a NSURL, and pass it to initWithURLs:... it will be ignored,So i have to modify the CDEvents origin code to make it work. (forgive my poor english.)

Sandbox or CDEvents issue? Parent directories denied access.

First of all, let me commend you on functional, well-packaged and documented code!

I'm considering using CDEvents to monitor directories in a sandboxed app. This seems to work well, provided that you access the correct type of security scoped urls.

However, when the app accesses a directory, the parent directories seem to fire off some access request, which is denied by the sandbox. This doesn't seem to break anything I care about, but it is worrisome to see such logging in the console:

The security-scoped url I am accessing and watching with CDEvents is /Users/steve/Desktop/myFolder.

When I begin the even stream in CDEvents, these lines are logged to the console:

7/10/14 3:29:04.000 PM kernel[0]: Sandbox: Draft Control(67135) deny file-read-data /Users/steve/Desktop
7/10/14 3:29:04.000 PM kernel[0]: Sandbox: Draft Control(67135) deny file-read-data /Users/steve
7/10/14 3:29:04.000 PM kernel[0]: Sandbox: Draft Control(67135) deny file-read-data /Users

The key here is that these parent folders are not being watched and CDEvents should not be attempting to access them.

I haven't looked into the CDEvents code yet, but will do so to try to find the issue.

Use a GCD-queue

Possibly use a serial GCD-queue instead of the current callback function.

Proper file event flags for create, removed and renamed???

@rastersize Hi. As @Bhushan30 already mentioned. CDEvent always returns isRenamed regardless of the action that a file was dropped to the watched folder. When I drop a file to the observed folder isCreated is always NO but isRenamed is always YES!
What's your suggestion to filter file changes like:

  • created file to watched folder
  • removed file from watched folder
  • renamed file in watched folder

How to suppress notification on changes in .DS_Store?

This is what I have so far:

    _events = [[CDEvents alloc] initWithURLs:watchedURLs
                                       block:^(CDEvents *watcher, CDEvent *event) {
                                         NSString *file = event.URL.path;
                                         const std::string fileName([file UTF8String]);
                                         au::arcwork::Handler* handler = au::arcwork::Handler::GetInstance();
                                         if (event.isCreated) { // ALWAYS NO!!!
                                           handler->OnFileNotify(fileName, au::arcwork::DIR_CHANGE_TYPE::ADDED);
                                         }
                                         if (event.isRemoved) { // ALWAYS NO!!!
                                           handler->OnFileNotify(fileName, au::arcwork::DIR_CHANGE_TYPE::DELETED);
                                         }
                                         if (event.isRenamed) { // ALWAYS YES!!!
                                           ...
                                         }
                                       }
                                   onRunLoop:[NSRunLoop currentRunLoop]
                        sinceEventIdentifier:kCDEventsSinceEventNow
                        notificationLantency:CD_EVENTS_DEFAULT_NOTIFICATION_LATENCY
                     ignoreEventsFromSubDirs:CD_EVENTS_DEFAULT_IGNORE_EVENT_FROM_SUB_DIRS
                                 excludeURLs:nil
                         streamCreationFlags:creationFlags];

Blockify

It would be quite nice to be able to pass a block instead of a delegate to CDEvents.

Get all File and Folder notification using CDEvents (FSEvents wrapper )?

I had implemented file watcher part using CDEvents : It is notifying all changes. But I want only file created, deleted, renamed, modified events notification and also folder created,deleted,renamed notification? How to get exact these notification? And How to used it? I don't need all notification.

output I get is this:

2014-02-12 13:09:14.607 TestCDEvents10-2[5189:503] CDEvents { eventId = 3348870, eventPath = /Users/user1/.Trash/untitled folder 1.09.13 PM, eventFlags = 133120 }
Its notifying me changed in the form of event_id, event_path and event_flags. But how should I get information which event is raise. Its not giving any details like File created event occurs, File Renamed event occur etc.

How should i get proper information about which event is raised?
Can we get notification type(like file created, file deleted etc.) using eventFlags? or is there any other way for this?

Please give me direction. Thanks...

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.