Objective-C: How to keep an application in dock? - objective-c

I am trying to keep my application in dock, however I am not able to do this.
How I am trying to do?
#interface UserDefaultsHelper : NSUserDefaults
- (BOOL)addApplicationToDock:(NSString *)path;
- (BOOL)removeApplicationFromDock:(NSString *)name;
#end
#implementation UserDefaultsHelper
- (BOOL)addApplicationToDock:(NSString *)path {
NSDictionary *domain = [self persistentDomainForName:#"com.apple.dock"];
NSArray *apps = [domain objectForKey:#"persistent-apps"];
NSArray *matchingApps = [apps filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"%K CONTAINS %#", #"tile-data.file-data._CFURLString", path]];
if ([matchingApps count] == 0) {
NSMutableDictionary *newDomain = [domain mutableCopy];
NSMutableArray *newApps = [apps mutableCopy];
NSDictionary *app = [NSDictionary dictionaryWithObject:[NSDictionary dictionaryWithObject:[NSDictionary dictionaryWithObjectsAndKeys:path, #"_CFURLString", [NSNumber numberWithInt:0], #"_CFURLStringType", nil] forKey:#"file-data"] forKey:#"tile-data"];
[newApps addObject:app];
[newDomain setObject:newApps forKey:#"persistent-apps"];
[self setPersistentDomain:newDomain forName:#"com.apple.dock"];
return [self synchronize];
}
return NO;
}
- (BOOL)removeApplicationFromDock:(NSString *)name {
NSDictionary *domain = [self persistentDomainForName:#"com.apple.dock"];
NSArray *apps = [domain objectForKey:#"persistent-apps"];
NSArray *newApps = [apps filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"not %K CONTAINS %#", #"tile-data.file-data._CFURLString", name]];
if (![apps isEqualToArray:newApps]) {
NSMutableDictionary *newDomain = [domain mutableCopy];
[newDomain setObject:newApps forKey:#"persistent-apps"];
[self setPersistentDomain:newDomain forName:#"com.apple.dock"];
return [self synchronize];
}
return NO;
}
#end
I found the above code from here. Code Source
the above code is getting compiled and running, however it's adding the application in the application's dock.
I am a newbie here. Please be lenient towards me as I do not have OS level of knowledge.
Many thanks in advance for your help and attention.

The code provided in question is correct the only problem was that the dock was not getting refreshed. So we just need to refresh the DOCK
In order to refresh dock I am calling a terminal command from obj-c
Terminal Command killall dock
....code...
[self runCommand:#"killall Dock"];
}
-(NSString*)runCommand:(NSString*)commandToRun;
{
NSTask *task;
task = [[NSTask alloc] init];
[task setLaunchPath: #"/bin/sh"];
NSArray *arguments = [NSArray arrayWithObjects:
#"-c" ,
[NSString stringWithFormat:#"%#", commandToRun],
nil];
NSLog(#"run command: %#",commandToRun);
[task setArguments: arguments];
NSPipe *pipe;
pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
NSFileHandle *file;
file = [pipe fileHandleForReading];
[task launch];
NSData *data;
data = [file readDataToEndOfFile];
NSString *output;
output = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
return output;
}

Related

How to start services like Apache and MYSQL from cocoa app on Mac OS 10.8?

I am new developing with Objective-C and Cocoa.
I need to run/stop/restart Apache and Mysql from a Xcode/Cocoa App and I don't know which is the best practise: Using NSTask, using NSAppleScript or what should be used?
Anyway I think that I need admin privileges for that.
Maybe somebody can help me how or where to get starting with this using best practices.
Below it's little of what I already tried:
I also saw this tutorial: http://www.michaelvobrien.com/blog/2009/07/authorizationexecutewithprivileges-a-simple-example/ but the AuthorizationExecuteWithPrivileges method is deprecated from OS 10.6.
I tried several methods that I find on stackoverflow.com but without any result.
One of the ways I tried is with the following method that I have in a TasksController class:
- (BOOL) runProcessAsAdministrator:(NSString*)scriptPath
withArguments:(NSArray *)arguments
output:(NSString **)output
errorDescription:(NSString **)errorDescription {
NSLog(#"runProcessAsAdministrator called!");
NSString * allArgs = [arguments componentsJoinedByString:#" "];
NSString * fullScript = [NSString stringWithFormat:#"%# %#", scriptPath, allArgs];
NSDictionary *errorInfo = [NSDictionary new];
NSString *script = [NSString stringWithFormat:#"do shell script \"sudo %#\" with administrator privileges", fullScript];
NSLog(#"shell script path: %#",script);
NSAppleScript *appleScript = [[NSAppleScript new] initWithSource:script];
NSAppleEventDescriptor * eventResult = [appleScript executeAndReturnError:&errorInfo];
// Check errorInfo
if (! eventResult)
{
// Describe common errors
*errorDescription = nil;
if ([errorInfo valueForKey:NSAppleScriptErrorNumber])
{
NSNumber * errorNumber = (NSNumber *)[errorInfo valueForKey:NSAppleScriptErrorNumber];
if ([errorNumber intValue] == -128)
*errorDescription = #"The administrator password is required to do this.";
}
// Set error message from provided message
if (*errorDescription == nil)
{
if ([errorInfo valueForKey:NSAppleScriptErrorMessage])
*errorDescription = (NSString *)[errorInfo valueForKey:NSAppleScriptErrorMessage];
}
return NO;
}
else
{
// Set output to the AppleScript's output
*output = [eventResult stringValue];
return YES;
}
}
Called as following:
TasksController *tasks = [[TasksController alloc] init];
NSString * output = nil;
NSString * processErrorDescription = nil;
BOOL success = [tasks runProcessAsAdministrator:#"/Applications/MyAPP/Library/bin/httpd"
withArguments:[NSArray arrayWithObjects:#"-f /Applications/MyAPP/conf/apache/MyAPP-httpd.conf", #"-k start", nil]
output:&output
errorDescription:&processErrorDescription
];
if (!success) // Process failed to run
{
// ...look at errorDescription
[_lblStatus setStringValue:#" Apache could not be started!"];
[_txtLogs setStringValue:processErrorDescription];
NSLog(#"%#",output);
NSLog(#"%#",processErrorDescription);
}
else
{
// ...process output
[_lblStatus setStringValue:#" Hurray Apache running!!"];
[_txtLogs setStringValue:output];
}
When I tried the above method the app is popup a small window asking for admin password but after introducing pass it's try to run and I get the following message in console:
dyld: DYLD_ environment variables being ignored because main executable (/usr/libexec/security_authtrampoline) is setuid or setgid
I also tried using the following methods but without success:
-(NSString*)runCommand:(NSString*)commandToRun
{
NSTask *task;
task = [[NSTask alloc] init];
[task setLaunchPath: #"/bin/sh"];
NSArray *arguments = [NSArray arrayWithObjects:
#"-c" ,
[NSString stringWithFormat:#"%#", commandToRun],
nil];
NSLog(#"run command: %#",commandToRun);
[task setArguments: arguments];
NSPipe *pipe;
pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
NSFileHandle *file;
file = [pipe fileHandleForReading];
[task launch];
NSData *data;
data = [file readDataToEndOfFile];
NSString *output;
output = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
return output;
}
-(void) runScript:(NSString*)scriptName
{
NSTask *task;
task = [[NSTask alloc] init];
[task setLaunchPath: #"/bin/sh"];
NSArray *arguments;
NSString* newpath = [NSString stringWithFormat:#"'%#'/%#",[[NSBundle mainBundle] resourcePath], scriptName];
NSLog(#"shell script path: %#",newpath);
arguments = [NSArray arrayWithObjects:newpath, nil];
[task setArguments: arguments];
NSPipe *pipe;
pipe = [NSPipe pipe];
[task setStandardOutput: pipe];
NSFileHandle *file;
file = [pipe fileHandleForReading];
[task launch];
NSData *data;
data = [file readDataToEndOfFile];
NSString *string;
string = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
NSLog (#"script returned:\n%#", string);
}
For this last method I have ShellScript folder in my Supporting Files folder that contain scripts like startMysql.sh, startApache.sh ...
startMysql.sh example:
# /bin/sh
/Applications/MyAPP/Library/bin/mysqld_safe --port=8889 --socket=/Applications/MyAPP/tmp/mysql/mysql.sock --lower_case_table_names=0 --pid-file=/Applications/MyAPP/tmp/mysql/mysql.pid --log-error=/Applications/MyAPP/logs/mysql_error_log &
Thanks in advance!

NSTask data split by newline \n

I'm new to objective-C, so please forgive me if I'm missing something. But we all have to start somewhere :)
I have a snippet of code I got from another open source project that executes a command and passes the result to another method. What I need to do is listen for each new line printed to stdout and do something with each line.
The code snippet I'm working with is the following:
NSMutableArray *args = [NSMutableArray array];
NSString *input = [details valueForKey:#"input"];
for (NSString *i in [input componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]) {
[args addObject:i];
}
NSTask *scriptTask = [NSTask new];
NSPipe *outputPipe = [NSPipe pipe];
if ([_NSFileManager() isExecutableFileAtPath:scriptPath] == NO) {
NSArray *chmodArguments = #[#"+x", scriptPath];
NSTask *chmod = [NSTask launchedTaskWithLaunchPath:#"/bin/chmod" arguments:chmodArguments];
[chmod waitUntilExit];
}
[scriptTask setStandardOutput:outputPipe];
[scriptTask setLaunchPath:scriptPath];
[scriptTask setArguments:args];
NSFileHandle *filehandle = [outputPipe fileHandleForReading];
[scriptTask launch];
[scriptTask waitUntilExit];
NSData *outputData = [filehandle readDataToEndOfFile];
NSString *outputString = [NSString stringWithData:outputData encoding:NSUTF8StringEncoding];
if (NSObjectIsNotEmpty(outputString)) {
[self.world.iomt inputText:outputString command:IRCPrivateCommandIndex("privmsg")];
}
So, rather than waiting for the process to complete then doing something with the result, I need to wait for each new line that gets printed by the command to stdout.
My background is mostly in web dev, so I guess if you were using Node.js and event emitters my aim would look similar to the following:
task = new Task("ls");
task.addListener("newline", function(data) {
somethingElse("sendmsg", data);
});
task.run();
Hopefully you understand what I'm tying to achieve. Thanks!
What you can do is add an observer to the NSFileHandle to notify you when it reads something.
example:
[fileHandler readInBackgroundAndNotify];
//Need to set NSTask output pipes
[self.task setStandardOutput: outputPipe];
[self.task setStandardError: outputPipe];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(readFromTask:) name:NSFileHandleReadCompletionNotification object:fileHandler];
-(void)readFromTask:(NSNotification *)aNotifcation;
{
NSData *data = [[aNotifcation userInfo] objectForKey: NSFileHandleNotificationDataItem];
if (data != 0) {
NSString *text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// Do something with the text
[text release];
[[aNotifcation object] readInBackgroundAndNotify];
}
}
Then there's a notififcation you can add to the task to indicate when its completed so you can do clean up.
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(taskEnded:) name:NSTaskDidTerminateNotification object:task];
- (void)taskEnded:(NSNotification *) aNotifcation
{
NSData *data = [[aNotifcation userInfo] objectForKey: NSFileHandleNotificationDataItem];
if (data != 0) {
NSString *text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[text release];
}
[self.task release];
}
So from your code, you could remove:
[scriptTask waitUntilExit];
And move the data handling into the notifications.
NSData *outputData = [filehandle readDataToEndOfFile];
NSString *outputString = [NSString stringWithData:outputData encoding:NSUTF8StringEncoding];
if (NSObjectIsNotEmpty(outputString)) {
[self.world.iomt inputText:outputString command:IRCPrivateCommandIndex("privmsg")];
}
That's a rough idea, there's a good post at
http://www.cocoabuilder.com/archive/cocoa/306145-nstask-oddity-with-getting-stdout.html

NSTask only returning standardError in release build

First of all, when debugging and running in Xcode everything works as expected.
But when I try to "share" my app, i.e. make a release build, my NSTask won't output any standardOutput while standardErrors ARE put out. How is that possible?
My code
- (id)initWithWindow:(NSWindow *)window {
self = [super initWithWindow:window];
if (self) {
// Initialization code here.
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(readPipe:) name:NSFileHandleReadCompletionNotification object:nil];
return self;
}
-(void) watchFile:(NSNotification *)notification {
NSString *path = [[notification userInfo] valueForKey:#"path"];
task = [[NSTask alloc] init];
[task setLaunchPath:#"/usr/bin/compass"];
[task setCurrentDirectoryPath:path];
NSArray *arguments;
arguments = [NSArray arrayWithObjects: #"watch",#"--boring", nil];
[task setArguments: arguments];
NSPipe *outPipe, *errPipe;
outPipe = [NSPipe pipe];
errPipe = [NSPipe pipe];
[task setStandardOutput: outPipe];
[task setStandardError: errPipe];
[task setStandardInput: [NSPipe pipe]];
standardHandle = [outPipe fileHandleForReading];
[standardHandle readInBackgroundAndNotify];
errorHandle = [errPipe fileHandleForReading];
[errorHandle readInBackgroundAndNotify];
[self setSplitterPosition:0.0f];
[task launch];
}
-(void)readPipe:(NSNotification *)notification {
NSLog(#"reading pipe");
NSData *data;
NSString *text;
if(!([notification object] == standardHandle) && !([notification object] == errorHandle)) {
return;
}
data = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem];
text = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
if ([data length] == 0) {
//error
[self setSplitterPosition:150.0f];
return;
}
[terminalViewController updateTerminal:text];
if(![text isEqualToString:#"\n"]) [self growlAlert:text title:#"Compapp"];
[text release];
if(task) [[notification object] readInBackgroundAndNotify];
}
/usr/bin/compass is not a binary installed in the standard installation of OSX (I don't have any binary named compass in my /usr/bin on my Mac)
So it seems quite logical that when your app is running on another Mac -- that does not have /usr/bin/compass installed -- and you try to run this task, it can't find it and only output some error on stderr.
If you mean after release you are trying to debug, you need to create a file called Entitlements.plist and set Can be debugged before you build and archive.
if this is still an open issue for you, then checkout the answer I posted at this link: How to use a determinate NSProgressIndicator to check on the progress of NSTask? - Cocoa
it provides a good template for creating an async NSTask and for reading from standard output or standard error.

Clang LLVM 1.0 Error objective-c

I'm a beginner developer. I'and stopped with this error about:
Clang LLVM 1.0 Error
Expected ':'
line: [pipe fileHandleForReading availableData]
Can anyone help me? Thanks in advance.
- (NSInteger)sizeOfItemAtPath:(NSString*)path {
BOOL isdir;
[[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isdir];
if (isdir) {
NSPipe *pipe = [NSPipe pipe];
NSTask *t = [[[NSTask alloc] init] autorelease];
[t setLaunchPath:#"/usr/bin/du"];
[t setArguments:[NSArray arrayWithObjects:#"-k", #"-d", #"0", path, nil]];
[t setStandardOutput:pipe];
[t setStandardError:[NSPipe pipe]];
[t launch];
[t waitUntilExit];
NSString *sizeString = [[[NSString alloc] initWithData:[[pipe fileHandleForReading availableData] encoding:NSASCIIStringEncoding] autorelease];
sizeString = [[sizeString componentsSeparatedByString:#" "] objectAtIndex:0];
BOOL bytes;
bytes = [sizeString longLongValue]*1024;
}
else {
BOOL bytes;
bytes = [[[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil] fileSize];
}
BOOL bytes;
return bytes;
}
You are missing a ]: it must be
[[pipe fileHandleForReading] availableData]
The whole line needs to look like this:
NSString *sizeString = [[[NSString alloc] initWithData:[[pipe fileHandleForReading] availableData] encoding:NSASCIIStringEncoding] autorelease];
Also, your method will return garbage. That is because you've defined bytes three times: once in the if branch, once in the else branch and once in the enclosing method body. The return value will be taken from the last one, but this one is initialized. Not only that, but you're using the wrong type: it must be a NSInteger bytes;, not BOOL bytes;. You need to put the definition at the start of the method and remove all other definitions, the variable may exist only once.
Try this:
[[pipe fileHandleForReading] availableData]

NSFileHandleReadCompletionNotification not posting

I'm having an issue with NSFileHandleReadCompletionNotification not posting. I'm piping the results of one NSTask into another one. For whatever reason my getData method just will not be called. Any ideas? Both tasks are working properly because I'm getting the expected files written to my hard drive. Just no notification.
- (void) encodeVideo:(NSString*)path
{
NSString* ffmpegPath = [[NSBundle mainBundle] pathForResource:#"ffmpeg" ofType:#""];
NSString* presetFile = [[NSBundle mainBundle] pathForResource:#"128" ofType:#"ffpreset"];
NSTask* encodeTask = [[NSTask alloc] init];
NSMutableArray* arguments = [[NSMutableArray alloc] init];
[arguments addObject:#"-y"];
[arguments addObject:#"-i"];
[arguments addObject:path];
[arguments addObject:#"-f"];
[arguments addObject:#"mpegts"];
[arguments addObject:#"-ac"];
[arguments addObject:#"2"];
[arguments addObject:#"-fpre"];
[arguments addObject:presetFile];
[arguments addObject:#"-s"];
[arguments addObject:#"320x240"];
[arguments addObject:#"-aspect"];
[arguments addObject:#"320:240"];
[arguments addObject:#"-async"];
[arguments addObject:#"2"];
[arguments addObject:#"-"];
[encodeTask setLaunchPath: ffmpegPath];
[encodeTask setArguments: arguments];
[encodeTask setStandardOutput:[NSPipe pipe]];
NSMutableArray* segmentArguments = [[NSMutableArray alloc] init];
[segmentArguments addObject:#"-t"];
[segmentArguments addObject:#"10"];
[segmentArguments addObject:#"-f"];
[segmentArguments addObject:#"/Users/Morgan/Desktop/LiveStreamServer/video"];
[segmentArguments addObject:#"-S"];
[segmentArguments addObject:#"1"];
NSString* segmenterPath = [[NSBundle mainBundle] pathForResource:#"mediastreamsegmenter" ofType:#""];
NSLog(#"%#", segmenterPath);
NSTask* segmentTask = [[NSTask alloc] init];
[segmentTask setLaunchPath: segmenterPath];
[segmentTask setArguments: segmentArguments];
[segmentTask setStandardInput:[encodeTask standardOutput]];
[segmentTask setStandardOutput:[NSPipe pipe]];
[segmentTask setStandardError:[NSPipe pipe]];
NSFileHandle *file = [[segmentTask standardOutput] fileHandleForReading];
[file readInBackgroundAndNotify];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(getData:) name:NSFileHandleReadCompletionNotification object:file];
[segmentTask launch];
[encodeTask launch];
[arguments release];
[segmentArguments release];
}
- (void) getData: (NSNotification *)aNotification
{
NSData *data = [[aNotification userInfo] objectForKey:#"NSFileHandleNotificationDataItem"];
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"%#", string);
[[aNotification object] readInBackgroundAndNotify];
}
Needed to read the stdError in the background as well. Had it outputting into a pipe, but wasn't reading it so the buffer got filled up.