Cocoa compatible with different Mac OS - objective-c

I have a Cocoa app which has different features in Mac OS 10.7 and 10.8 (The deployment target is 10.7). For example, in 10.8 I have a button for Sharing Service while in 10.7 the button is hidden.
The problem here is how can I know which kind of Mac OS is there while my app is running. For iOS, I can get it from UIDevice. But for Cocoa, I don't find the similar class.
Currently, I detect the OS using:
- (BOO)isServiceAvalable
{
if (NSClassFromString(#"A_Unique_Class_In_One_OS"))
{
return YES;
}
return NO;
}
I hope there is more elegant way to do it.

StackOverflow: os version checking in cocoa
StackOverflow: How to get the Mac OS X system version?
StackOverflow: How can I determine the running Mac OS X version programmatically?
Cocoa Dev Central: Checking the User's Mac OS X Version

If Gestalt is deprecated as scorpiozj mentions then here's a simple NSApplescript way to do it...
NSString* getSystemVersion() {
NSString* returnString = nil;
NSString* cmd = #"return system version of (get system info)";
NSAppleScript* theScript = [[NSAppleScript alloc] initWithSource:cmd];
NSDictionary* errorDict = nil;
NSAppleEventDescriptor* result = [theScript executeAndReturnError:&errorDict];
[theScript release];
if (errorDict) {
returnString = [NSString stringWithFormat:#"Error:%# %#", [errorDict valueForKey:#"NSAppleScriptErrorNumber"], [errorDict valueForKey:#"NSAppleScriptErrorMessage"]];
} else {
returnString = [result stringValue];
}
return returnString;
}

Related

Troubles getting an actual macOS version

I'm using the following code:
NSOperatingSystemVersion macOsVersion()
{
return [[NSProcessInfo processInfo] operatingSystemVersion];
}
It's working fine when I build it on my machine - it returns 11.5.1 version.
But, we use Jenkins, which is working on a remote macOS machine, and a build from Jenkins shows 10.16.0 version on my machine.
I'm not an experienced macOS developer. Am I doing something wrong? Is there a better API?
You could try this way, which is not dependent on which SDK you are linking to:
NSDictionary* systemVersion = [NSDictionary dictionaryWithContentsOfFile:#"/System/Library/CoreServices/SystemVersion.plist"];
NSString *macOSVersion = [systemVersion objectForKey:#"ProductVersion"];
NSLog (#"productVersion =========== %#", macOSVersion);
Thanks to CTABUYO for the code example, and mikdusan, the solution is to read contents of the /System/Library/CoreServices/.SystemVersionPlatform.plist file. It's not affected by macOS version compatibility stuff (at least for now).
if (FileExists("/System/Library/CoreServices/.SystemVersionPlatform.plist"))
{
NSDictionary* systemVersion = [NSDictionary dictionaryWithContentsOfFile:#"/System/Library/CoreServices/.SystemVersionPlatform.plist"];
NSString *macOSVersion = [systemVersion objectForKey:#"ProductVersion"];
NSLog (#"productVersion =========== %#", macOSVersion);
}
else
{
NSDictionary* systemVersion = [NSDictionary dictionaryWithContentsOfFile:#"/System/Library/CoreServices/SystemVersion.plist"];
NSString *macOSVersion = [systemVersion objectForKey:#"ProductVersion"];
NSLog (#"productVersion =========== %#", macOSVersion);
}

El Capitan - How to read Mac Os X Mail Accounts Informations

I have an Application which uses the Content of the Accounts.plist.
After upgrade to Mac OS El Capitan / 10.11.1 the Accounts.plist isn't supported under the new Mail-Path.
Before:
/Users/[homefolder]/Library/Mail/v2/MailData/Accounts.plist
Now:
/Users/[homefolder]/Library/Mail/v3/MailData/*
How can i get the Mail-Account Details now ?
Stumbled upon a cleaner way to do this. Refer SBSendMail(https://developer.apple.com/library/mac/samplecode/SBSendEmail/Introduction/Intro.html) for Apple's scripting bridge. Then to read smtp servers:
MailApplication *mail = [SBApplication applicationWithBundleIdentifier:#"com.apple.Mail"];
/* set ourself as the delegate to receive any errors */
mail.delegate = self;
SBElementArray *smtpServers = mail.smtpServers;
for (MailSmtpServer *server in smtpServers)
{
NSLog(#"%# %# %# %# %d", server.name, server.password, server.userName, server.serverName, server.port);
}
You can read IMAP & POP account settings similarly.

I try to find a API can instead of AuthorizationExecuteWithPrivileges? [duplicate]

Since updating to OSX 10.7 Lion, Xcode tells me that AuthorizationExecuteWithPrivileges is deprecated.
Can anyone suggest a way my application can write to a directory it doesn't have permission for?
I know it sounds crazy, but this actually works:
NSDictionary *error = [NSDictionary new];
NSString *script = #"do shell script \"whoami > /tmp/me\" with administrator privileges";
NSAppleScript *appleScript = [[NSAppleScript alloc] initWithSource:script];
if ([appleScript executeAndReturnError:&error]) {
NSLog(#"success!");
} else {
NSLog(#"failure!");
}
I'm executing an Applescript from Objective C. The only disadvantage is that you cannot gain permanent root privileges with this. It will ask for the password each time you run this.
In fact, AuthorizationExecuteWithPrivileges() has been deprecated for a very long time, it's only recently that the header file has caught up with this fact.
You can create a privileged helper tool as part of your application. You can use ServiceManagement.framework's SMJobBless() function to have the helper deployed into the system launchd context: then when you need to perform privileged tasks, you just message the privileged helper to do that work.
There's a little bit of hidden complexity, in that the app and the helper must each declare the signing identity of the other before SMJobBless() believes they're supposed to be used together, and you need to get the linker to write the helper tool's Info.plist file into the binary. That's all covered by Apple's Documentation and Apple have provided a sample project, too.
I wrote an example application that uses SMJobBless() to deploy its privileged helper.
Based on a great find by user950473 I've implemented his/her discovery as a method; thought I'd share the code in case it's helpful.
- (BOOL) runProcessAsAdministrator:(NSString*)scriptPath
withArguments:(NSArray *)arguments
output:(NSString **)output
errorDescription:(NSString **)errorDescription {
NSString * allArgs = [arguments componentsJoinedByString:#" "];
NSString * fullScript = [NSString stringWithFormat:#"'%#' %#", scriptPath, allArgs];
NSDictionary *errorInfo = [NSDictionary new];
NSString *script = [NSString stringWithFormat:#"do shell script \"%#\" with administrator privileges", fullScript];
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;
}
}
Usage example:
NSString * output = nil;
NSString * processErrorDescription = nil;
BOOL success = [self runProcessAsAdministrator:#"/usr/bin/id"
withArguments:[NSArray arrayWithObjects:#"-un", nil]
output:&output
errorDescription:&processErrorDescription];
if (!success) // Process failed to run
{
// ...look at errorDescription
}
else
{
// ...process output
}
It's very slightly hacky, but IMHO is a satisfactory solution.
AuthorizationExecuteWithPrivileges is indeed deprecated.
But fortunately, there is a new recommended way to proceed.
As of 10.6 there is the new API and it is recommended to install a helper tool that will perform the privileged operation. Apple provide a code sample that clearly demonstrate how to manage it.
Make sure you check out their readme.txt since contrarily to other code sample there is more to do than just downloading the project and running it.
From The SMJobBless example introduction
SMJobBless demonstrates how to securely install a helper tool that performs a privileged operation and how to associate the tool
with an application that invokes it.
As of Snow Leopard, this is the preferred method of managing privilege
escalation on Mac OS X and should be used instead of earlier
approaches such as BetterAuthorizationSample or directly calling
AuthorizationExecuteWithPrivileges.
SMJobBless uses ServiceManagement.framework that was introduced in Mac
OS X v10.6 Snow Leopard.
Source: Apple SMJobBless code sample

AuthorizationExecuteWithPrivileges is Deprecated. Have to use something else? [duplicate]

Since updating to OSX 10.7 Lion, Xcode tells me that AuthorizationExecuteWithPrivileges is deprecated.
Can anyone suggest a way my application can write to a directory it doesn't have permission for?
I know it sounds crazy, but this actually works:
NSDictionary *error = [NSDictionary new];
NSString *script = #"do shell script \"whoami > /tmp/me\" with administrator privileges";
NSAppleScript *appleScript = [[NSAppleScript alloc] initWithSource:script];
if ([appleScript executeAndReturnError:&error]) {
NSLog(#"success!");
} else {
NSLog(#"failure!");
}
I'm executing an Applescript from Objective C. The only disadvantage is that you cannot gain permanent root privileges with this. It will ask for the password each time you run this.
In fact, AuthorizationExecuteWithPrivileges() has been deprecated for a very long time, it's only recently that the header file has caught up with this fact.
You can create a privileged helper tool as part of your application. You can use ServiceManagement.framework's SMJobBless() function to have the helper deployed into the system launchd context: then when you need to perform privileged tasks, you just message the privileged helper to do that work.
There's a little bit of hidden complexity, in that the app and the helper must each declare the signing identity of the other before SMJobBless() believes they're supposed to be used together, and you need to get the linker to write the helper tool's Info.plist file into the binary. That's all covered by Apple's Documentation and Apple have provided a sample project, too.
I wrote an example application that uses SMJobBless() to deploy its privileged helper.
Based on a great find by user950473 I've implemented his/her discovery as a method; thought I'd share the code in case it's helpful.
- (BOOL) runProcessAsAdministrator:(NSString*)scriptPath
withArguments:(NSArray *)arguments
output:(NSString **)output
errorDescription:(NSString **)errorDescription {
NSString * allArgs = [arguments componentsJoinedByString:#" "];
NSString * fullScript = [NSString stringWithFormat:#"'%#' %#", scriptPath, allArgs];
NSDictionary *errorInfo = [NSDictionary new];
NSString *script = [NSString stringWithFormat:#"do shell script \"%#\" with administrator privileges", fullScript];
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;
}
}
Usage example:
NSString * output = nil;
NSString * processErrorDescription = nil;
BOOL success = [self runProcessAsAdministrator:#"/usr/bin/id"
withArguments:[NSArray arrayWithObjects:#"-un", nil]
output:&output
errorDescription:&processErrorDescription];
if (!success) // Process failed to run
{
// ...look at errorDescription
}
else
{
// ...process output
}
It's very slightly hacky, but IMHO is a satisfactory solution.
AuthorizationExecuteWithPrivileges is indeed deprecated.
But fortunately, there is a new recommended way to proceed.
As of 10.6 there is the new API and it is recommended to install a helper tool that will perform the privileged operation. Apple provide a code sample that clearly demonstrate how to manage it.
Make sure you check out their readme.txt since contrarily to other code sample there is more to do than just downloading the project and running it.
From The SMJobBless example introduction
SMJobBless demonstrates how to securely install a helper tool that performs a privileged operation and how to associate the tool
with an application that invokes it.
As of Snow Leopard, this is the preferred method of managing privilege
escalation on Mac OS X and should be used instead of earlier
approaches such as BetterAuthorizationSample or directly calling
AuthorizationExecuteWithPrivileges.
SMJobBless uses ServiceManagement.framework that was introduced in Mac
OS X v10.6 Snow Leopard.
Source: Apple SMJobBless code sample

Iphone SDK 4 sms composer

Have anyone tried to use the SDK4's SMS composer?
If anyone's got some reference or source code please put in here
Thanks
If you want to support 3.1 devices, you need to do a few things:
In your target's build settings:
set Base SDK to iPhone Device 4.0
set iPhone OS Deployment Target to iPhone OS 3.x (the lowest OS level you want to support)
In your target's general settings, under Linked Libraries, change the "Type" next to MessageUI.framework to Weak.
Don't import <MessageUI/MFMessageComposeViewController.h> or it will crash on launch on 3.1. Just import <MessageUI/MessageUI.h>
To make sure it doesn't crash on 3.1.x, you need to test for the availability of MFMessageComposeViewController:
Class smsClass = (NSClassFromString(#"MFMessageComposeViewController"));
if (smsClass != nil && [MFMessageComposeViewController canSendText]) {
MFMessageComposeViewController *controller = [[MFMessageComposeViewController alloc] init];
controller.body = text;
controller.recipients = [NSArray arrayWithObjects: nil];
controller.messageComposeDelegate = self;
[self presentModalViewController:controller animated:YES];
[controller release];
}
If you've got the 4.0 SDK already, check MFMessageComposeViewController. The usage is similar to MFMailComposeViewController.