rastersize / cdevents Goto Github PK
View Code? Open in Web Editor NEWAn Objective-C wrapper for Mac OS X’s FSEvents C API.
Home Page: http://rastersize.github.com/CDEvents
License: MIT License
An Objective-C wrapper for Mac OS X’s FSEvents C API.
Home Page: http://rastersize.github.com/CDEvents
License: MIT License
[[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.)
Possibly use a serial GCD-queue instead of the current callback function.
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?
The usage docs are currently not complete, fix it.
It would be quite nice to be able to pass a block instead of a delegate to CDEvents
.
@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:
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];
Add unit tests, because units tests are just as cool as bow ties and bow ties are coool.
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...
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)
}
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:
@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;
@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;
@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;
/*!
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)
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;
IMPLEMENTATION:
@implementation WatcherFileSystem
@synthesize fileSystemQueue = _fileSystemQueue;
(id) init {
if ( (self = [super init]) ) {
_fileSystemQueue = [[NSOperationQueue alloc] init];
REPORT_ARGS( errReportArgInfoLoaded, @"Watcher (File System)" );
} return self;
}
(void) close { [self.fileSystemQueue cancelAllOperations]; }
(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;
} } }
}
@interface WatcherFSOperation ()
static void FSEventsCallback(
ConstFSEventStreamRef streamRef,
void *callbackInfo,
size_t numEvents,
void *eventPaths,
const FSEventStreamEventFlags eventFlags[],
const FSEventStreamEventId eventIds[]);
@implementation WatcherFSOperation
@synthesize streamRef = _streamRef,
sinceIdentifier = _sinceIdentifier,
watchedURLs = _watchedURLs;
(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] );
}
}
(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);
}
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];
}
}
@implementation WatcherFSEvent
@synthesize identifier = _identifier,
flags = _flags,
url = _url;
(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;
}
(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 ); }
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:
/*!
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.
Use it, should be quite easy to transition.
We should stop using Gestalt
in the test app and move on to something else.
Best and easiest way to do this would be to use Justin Spahr-Summers’ config files in xcconfigs.
I've written a podspec for CDEvents: https://github.com/dwlnetnl/CocoaPodsSpecs/commit/76b2b5e821c995c94f56de399cf4eb88a8413702
Please add and update the spec so we can easily include CDEvents in projects!
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.
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...
Remove them, replace them with normal assertions instead perhaps?
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)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.