Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Warhaven

macrumors newbie
Original poster
Mar 3, 2010
19
1
Hey everyone. I have an NSTimer which checks every X number of seconds whether each of the iWork apps is running, then asks them to perform a save if it has any documents that've been changed. The problem is that if it's a fresh document, a save dialog will drop down and stall the main thread (beachballs my menulet) until it's been dealt with. I'm guessing the best way to deal with this is create a new thread when a save in requested. Just now sure the best way of implementing it. Here's the basic setup of my code as it is right now...

I have my timer instantiated in -init:

Code:
myTimer = [NSTimer scheduledTimerWithTimeInterval: secondsBetweenSaves target: self selector: @selector(timerWorker:) userInfo: nil repeats: YES];

My -timerWorker selector:

Code:
- (void) timerWorker:(NSTimer*)myTimer {
	PGApplication *pagesApp = [[PGApplication alloc] initWithBundleID: @"com.apple.iWork.Pages"];
	NMApplication *numbersApp = [[NMApplication alloc] initWithBundleID: @"com.apple.iWork.Numbers"];
	KYApplication *keynoteApp = [[KYApplication alloc] initWithBundleID: @"com.apple.iWork.Keynote"];
	//NSError *myError = nil;
	//id result;
	
	// Save Pages Docs
	if ([pagesApp isRunning] && savePages) {
		PGReference *realRef = [pagesApp documents];
		NSNumber *numDocs = [[[pagesApp count] each: [PGConstant document]] send];
		[self doSaves: realRef andNumDocs: numDocs];
	} // end pagesApp if
	
	// Save Numbers Docs
	if ([numbersApp isRunning] && saveNumbers) {
		NMReference *realRef = [numbersApp documents];
		NSNumber *numDocs = [[[numbersApp count] each: [NMConstant document]] send];
		[self doSaves:realRef andNumDocs:numDocs];
	} // End if

	// Save Keynote Docs
	if ([keynoteApp isRunning] && saveKeynote) {
		KYReference *realRef = [keynoteApp documents];
		NSNumber *numDocs = [[[keynoteApp count] each: [KYConstant document]] send];
		[self doSaves:realRef andNumDocs:numDocs];
	}
	
	// Dealloc
	[pagesApp release];
	[numbersApp release];
	[keynoteApp release];
}

And here's the method that potentially stalls the thread if it gets a save dialog:

Code:
- (void) doSaves:(ASReference*)realRef andNumDocs:(NSNumber*)numDocs {
	SEApplication *systemApp = [SEApplication applicationWithName: @"System Events"];
	// Protect method.  Anything but these three might bork program...
	if ([realRef isKindOfClass: [PGReference class]] || [realRef isKindOfClass: [KYReference class]] || [realRef isKindOfClass: [NMReference class]]) {
		// The methods I'm using here are all shared by PGReference, KYReference, and NMReference.
		PGReference *myRef =  (PGReference *)realRef;
		for (int i = 1; i <= [numDocs intValue]; i++) {
			BOOL isModified = [[[[myRef at: i] modified] getItem] boolValue];
			NSString *docPath;
			// Request save
			/************************************/
			/* This is what stalls the menulet. */
			/************************************/
			if (isModified) {
				[[[myRef at: i] save] send];
				docPath = [[[myRef at: i] path] getItem];
			}
			/****************************/
			/*     End stalling code    */
			/****************************/
			// Archive saves?
			if (archiveSaves && isModified && ![docPath isKindOfClass:[NSNull class]]) {
				// Some setup
				NSString *timeStamp = [[NSDate date] descriptionWithCalendarFormat: @"_%Y-%m-%d_%H-%M-%S_" timeZone: nil locale: nil];
				// The document's name
				NSString *docName = [[[myRef at: i] name] getItem];
				// POSIX path sans document name
				SEReference *docRef = [[[systemApp files] byName: docPath] container];
				NSString *docContainer = [NSString stringWithFormat: @"%@/", [[docRef POSIXPath] getItem]];
				// Target path for zip archive with timestamp.
				NSString *targetPath = [NSString stringWithFormat:@"%@%@%@.zip", docContainer, timeStamp, docName];
				// The actual archiving
				NSTask *archiveTask = [[NSTask alloc] init];
				[archiveTask setLaunchPath: @"/usr/bin/ditto"];
				NSArray *arguments = [NSArray arrayWithObjects: @"-ck", docPath, targetPath, nil];
				[archiveTask setArguments: arguments];
				[archiveTask launch];
				[archiveTask release];
			} // End Archive Saves
		} // End for
	}
}

I would expect that the -doSaves method as a whole would be spun off into its own thread, since the archiving bits at the end of that method are tied to the completion of the save dialog, and should wait for the user to finish with the save dialog anyway.

Suggestions?

Thanks,
-Rob
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.