How to know what Mac OS the app is running on? - objective-c

I've seen in some projects something like:
#if .....
code...
#endif
but i can't find it now...
Let's say, for example, if the app is running on 10.8 the app does 1 thing, if not the app does other thing.
Whats to code to check if it's running on 10.8?
Thanks.

You're probably asking the wrong question. Except in very rare cases, you should not care what system version the user is running. Instead, you should be checking if the specific thing you're interested in is available.
For instance, if Apple introduces a MagicHologram class in Mac OS X 10.9 that you want to use, you don't check if the user is running Mac OS X 10.9. Instead, you check if the MagicHologram class is available. If it is, you can use it. If not, it's not available. It doesn't even matter why. Maybe they're running 10.8. But maybe it's five years later, and Apple's decided to drop the MagicHologram class entirely.
(Also, keep in mind that you'd need to weak link to HologramKit, the library that provides the MagicHologram class.)
Likewise, if they introduce a new method to NSString, instead of checking the OS version you'd check if NSString knows about the new method.
That said, NSApplication.h includes an external constant called NSAppKitVersionNumber. You can compare this to constants like NSAppKitVersionNumber10_7 which (it should be noted) are numbers like 1138, not 10.7. There's only a few places this is appropriate, mostly where classes were private and undocumented but got major changes before being documented and becoming a part of the public parts of the SDK. Also, it might be helpful if you want to avoid a specific bug that's been fixed since.
To recap:
Detect individual classes and methods, which should cover 99.44% of your cases.
Use NSAppKitVersionNumber and NSAppKitVersionNumber10_7 to cover those cases where class or method detection would lie to you.
Those first two points cover all normal cases. You should go no further. But if you must have behaviour based on humane version, look at abarnert's answer below. It's the sanest way to get them.
Don't use operatingSystemVersionString, which is specifically listed as not safe for parsing.
References/more information:
SDK Compatibility Guide "Read this document if you want your application to target a specific version or multiple versions of iOS or Mac OS X."
Using SDK-Based Development Describes how to use weakly linked classes, methods, and functions to support running on multiple versions of an operating system.

A quick way to do it is:
if ( NSAppKitVersionNumber >= NSAppKitVersionNumber10_7 ) {
// Do stuff for Lion or later
}
More here.
You can see all the constants available in NSApplication.h, which you can get to by using Open Quickly... (Cmd-Shift O) in Xcode.
The header files provide for some surprisingly interesting reading material, if you are so inclined.

As others have said above (and I'd pick Steven Fisher's answer), you usually do not actually want to get the version number.
And if you only need to do comparisons against a major OS X version up to the version of the current SDK you're using, NSAppKitVersionNumber (as in Monolo's answer) is the right way to do it.
If you actually do need to get the version number for some reason (e.g., for recording analytics about your users, so you can decide when to stop supporting 10.6.0-10.6.5), here's how to do it:
#import <CoreServices/CoreServices.h>
SInt32 majorVersion, minorVersion, bugFixVersion;
Gestalt(gestaltSystemVersionMajor, &majorVersion);
Gestalt(gestaltSystemVersionMinor, &minorVersion);
Gestalt(gestaltSystemVersionBugFix, &bugFixVersion);
For 10.7.3, this gives majorVersion = 10, minorVersion = 7, bugFixVersion = 3.
The 10.7 documentation removed the paragraph that directly suggested Gestalt as the way to get OS version, but it's still not deprecated or legacy, and there's no other suggestions. In fact, every other way to get this information (parsing -[NSProcessInfo operatingSystemVersionString], calling sysctlbyname on "kern.osrelease" and converting Darwin kernel version to OS X version, etc.) is explicitly counter-indicated somewhere. So, this is the way to do it, if you really want to.
Just keep in mind that, as the release notes for System 6.0.4 said back in 1989, this new API may not be permanent and could be removed in a future version of the OS.

You can get the current release from the uname -r command (that's actually the kernel release, though it's easy to map onto Mac OS X versions), from the sw_vers command which gives you the release name, version and build identifier, or from the Gestalt() function (on current versions of Mac OS X, anyway). The safest way is probably to read the output of sw_vers which is in a stable format that's easy to parse.
Notice that you probably don't want to know what version of the OS you're on, though. What you probably want to do is to test whether a particular feature is available. You can do this by weak-linking frameworks, by weak class references, or by inspecting whether a class responds to selectors appropriate to the feature you're interested in.

As Steven Fisher said, you should not check for the system version but for the availability of the class or method you want to use.
The check if a specific class is available use
if ([NSHologram class]) {
// Create an instance of the class and use it.
} else {
// The Hologram class is not available.
}
To check if a specific method is available use
NSString* hologramText = #"Hologram";
if ([hologramText respondsToSelector:#selector(convertHologram)]) {
[hologramText convertHologram];
}
Yet for the method checking, the method must be available on the system where you build your app, or else you will get a compile error.

Here is code for how I do it. I love it this way, mainly because I don't have to A) Rely on an NSTask or B) Rely on any File I/O that many processes have access to.
static NSString* const kVarSysInfoVersionFormat = #"%#.%#.%# (%#)";
static NSString* const kVarSysInfoKeyOSVersion = #"kern.osrelease";
static NSString* const kVarSysInfoKeyOSBuild = #"kern.osversion";
- (NSString *) _strControlEntry:(NSString *)ctlKey {
size_t size = 0;
if ( sysctlbyname([ctlKey UTF8String], NULL, &size, NULL, 0) == -1 ) return nil;
char *machine = calloc( 1, size );
sysctlbyname([ctlKey UTF8String], machine, &size, NULL, 0);
NSString *ctlValue = [NSString stringWithCString:machine encoding:[NSString defaultCStringEncoding]];
free(machine); return ctlValue;
}
- (NSString *) getOSVersionInfo {
NSString *darwinVer = [self _strControlEntry:kVarSysInfoKeyOSVersion];
NSString *buildNo = [self _strControlEntry:kVarSysInfoKeyOSBuild];
if ( !darwinVer || !buildNo ) return nil;
NSString *majorVer = #"10", *minorVer = #"x", *bugFix = #"x";
NSArray *darwinChunks = [darwinVer componentsSeparatedByCharactersInSet:[NSCharacterSet punctuationCharacterSet]];
if ( [darwinChunks count] > 0 ) {
NSInteger firstChunk = [(NSString *)[darwinChunks objectAtIndex:0] integerValue];
minorVer = [NSString stringWithFormat:#"%ld", (firstChunk - 4)];
bugFix = [darwinChunks objectAtIndex:1];
} return [NSString stringWithFormat:kVarSysInfoVersionFormat, majorVer, minorVer, bugFix, buildNo];
}
Enjoy!

You can get OS version like this:
NSString *version = [[NSProcessInfo processInfo] operatingSystemVersionString];
NSLog(version);
Output:
And I see You want to get just version. It can be done like this:
NSString *version = [[NSProcessInfo processInfo] operatingSystemVersionString];
NSRange range = NSMakeRange(8, 4);
NSString *justVersion = [version substringWithRange: range];
NSLog(#"%#", justVersion);
Result:
And for checking:
if ([justVersion isEqualToString:#"10.7"]) {
code...
}
else {
...
}

Related

Objective-c pointers magic. Type protection

I have a dictionary.
I extract one of its values as follows:
NSString *magicValue= [filterDict valueForKey:[filterDict allKeys][0]];
[SomeClass foo: magicValue];
And foo is:
- (void)foo:(NSString*)magicValue
{
NSLog("magicValue is string:%#",[magic isKindOfClass:[NSString class]] ? #"YES" : #"NO");
NSLog("magicValue is number:%#",[magic isKindOfClass:[NSNumber class]] ? #"YES" : #"NO");
}
If the dictionary value is number magicValue will be NSNumber. So the defined string pointer will be pointing to an NSNumber. The log will return yes for the number check.
I never added protection to such methods, to check what class "magicValue" is. I assumed that when I define a method with string parameter it will be string.
Should I start accounting for such behavior and always add checks, or is it the fault of the guy that assigned that dictionary value to magic in such a way and used my method. I need some best practices advice and how to handle this.
This question could have already been answered but I didn't know how to search for it.
Short answer: No, do not check that, if there is no special reason.
Long answer:
You have to differentiate between two cases:
A. Using id
You have a variable or a return vale of the type id. This is in your example -valueForKey:. The reason for that typing is to keep the method generic. Even it is theoretically possible, in practice a type mismatch in such situation is very rare and detected fast in development. With a different motivation I asked the audience (>200) in a public talk, how many times they had such a typing error in production. For all listeners, all of their apps in all of the app's versions there was 1(in words: one!) case. Simply forget about that risk. It is the anxiety of developers using statically typing languages (Java, C++, Swift).
B. Wrong assignment
If you do not have an id type, it is still possible to do such tricks. (And sometimes you want to do that. That is a strength of dynamic typing.) There are two subcategories:
You can do it implicitly:
NSString *string = [NSNumber numberWithInt:1];
The compiler will warn you about that. So everything is fine, because the developer will see his mistake. You do not have to protect him or your code.
One can do it explicitly:
NSString *string = (NSString*)[NSNumber numberWithInt:1];
In such a case, code can break. But he did it explicitly. If it is wrong, the developer had criminal energy to do so and you do not have to protect him from himself.
Most of the time you should know what class you're referencing, or at least what you intend it to be. On the occasions where you have an uexpected class which can cause a crash depending on what messages you send to it, you can then debug your code and get the correct reference.
There are times, usually when dealing with inheritance, when you need to determine the class at runtime rather than at compile time. This is when, isKindOfClass: can be useful. If you know that a value could be one of many classes, I would extract it as an id and then cast it at the last moment e.g.
id value = [[NSUserDefaults standardUserDefaults] valueForKey:aKey];
if ([value isKindOfClass:[MyClass class]]) {
// Do one thing
}
else {
// Do another
}

How to zeroize an objective-c object under ARC [duplicate]

On iOS, I was wondering, say if I read user provided password value as such:
NSString* strPwd = UITextField.text;
//Check 'strPwd'
...
//How to clear out 'strPwd' from RAM?
I just don't like leaving sensitive data "dangling" in the RAM. Any idea how to zero it out?
Basically you really can't. There are bugs filed with Apple over this exact issue. Additionally there are problems with UITextField and NSString at a minimum.
To reiterate the comment in a now deleted answer by #Leo Natan:
Releasing the enclosing NSString object does not guarantee the string
bytes are zeroes in memory. Also, if a device is jailbroken, all the
sandboxing Apple promises will be of no use. However, in this case,
there is little one can do, as it is possible to swap the entire
runtime in the middle of the application running, this getting the
password from the memory.
Please file another bug with apple requesting this, the more the better.
Apple Bug Reporter
While NSString doesn't have this capability (for reasons of encapsulation mentioned elsewhere), it shouldn't be too hard to have your app use regular old C-strings, which are just pointers to memory. Once you have that pointer, it's fairly easy to clear things out when you're done.
This won't help with user-entered text-fields (which use NSString-s and we can't change them), but you can certainly keep all of your app's sensitive data in pointer-based memory.
I haven't experimented with it (I don't have a current jailbroken device), but it also might be interesting to experiment with NSMutableString -- something like:
// Code typed in browser; may need adjusting
// keep "password" in an NSMutableString
NSInteger passLength = password.length;
NSString *dummy = #"-";
while (dummy.length < passLength)
{
dummy = [dummy stringByAppendingString: #"-"];
}
NSRange fullPass = NSMakeRange(0, passLength);
[password replaceOccurancesOfString: password
withString: dummy
options: 0
range: fullPass];
NOTE: I have no idea if this does what you want; it's just something I thought of while typing my earlier answer. Even if it does work now, I guess it depends on the implementation, which is fragile (meaning: subject to breaking in the future), so shouldn't be used.
Still, might be an interesting exercise! :)

How to dynamically typecast objects to support different versions of an application's ScriptingBridge header files?

Currently I'm trying to implement support for multiple versions of iTunes via ScriptingBridge.
For example the method signature of the property playerPosition changed from (10.7)
#property NSInteger playerPosition; // the player’s position within the currently playing track in seconds.
to (11.0.5)
#property double playerPosition; // the player’s position within the currently playing track in seconds
With the most current header file in my application and an older iTunes version the return value of this property would always be 3. Same thing goes the other way around.
So I went ahead and created three different iTunes header files, 11.0.5, 10.7 and 10.3.1 via
sdef /path/to/application.app | sdp -fh --basename applicationName
For each version of iTunes I adapted the basename to inlcude the version, e.g. iTunes_11_0_5.h.
This results in the interfaces in the header files to be prefixed with their specific version number.
My goal is/was to typecast the objects I'd use with the interfaces of the correct version.
The path to iTunes is fetched via a NSWorkspace method, then I'm creating a NSBundle from it and extract the CFBundleVersion from the infoDictionary.
The three different versions (11.0.5, 10.7, 10.3.1) are also declared as constants which I compare to the iTunes version of the user via
[kiTunes_11_0_5 compare:versionInstalled options:NSNumericSearch]
Then I check if each result equals NSOrderedSame, so I'll know which version of iTunes the user has installed.
Implementing this with if statement got a bit out of hand, as I'd need to do these typecasts at many different places in my class and I then started to realize that this will result in a lot of duplicate code and tinkered around and thought about this to find a different solution, one that is more "best practice".
Generally speaking, I'd need to dynamically typecast the objects I use, but I simply can't find a solution which wouldn't end in loads of duplicated code.
Edit
if ([kiTunes_11_0_5 compare:_versionString options:NSNumericSearch] == NSOrderedSame) {
NSLog(#"%#, %#", kiTunes_11_0_5, _versionString);
playerPosition = [(iTunes_11_0_5_Application*)_iTunes playerPosition];
duration = [(iTunes_11_0_5_Track*)_currentTrack duration];
finish = [(iTunes_11_0_5_Track*)_currentTrack finish];
} else if [... and so on for each version to test and cast]
[All code directly entered into answer.]
You could tackle this with a category, a proxy, or a helper class, here is a sketch of one possible design for the latter.
First create a helper class which takes and instance of your iTunes object and the version string. Also to avoid doing repeated string comparisons do the comparison once in the class setup. You don't give the type of your iTunes application object so we'll randomly call it ITunesAppObj - replace with the correct type:
typedef enum { kEnumiTunes_11_0_5, ... } XYZiTunesVersion;
#implementation XYZiTunesHelper
{
ITunesAppObj *iTunes;
XYZiTunesVersion version;
}
- (id) initWith:(ITunesAppObj *)_iTunes version:(NSString *)_version
{
self = [super self];
if (self)
{
iTunes = _iTunes;
if ([kiTunes_11_0_5 compare:_version options:NSNumericSearch] == NSOrderedSame)
version = kEnumiTunes_11_0_5;
else ...
}
return self;
}
Now add an item to this class for each item which changes type between versions, declaring it with whatever "common" type you pick. E.g. for playerPosition this might be:
#interface XYZiTunesHelper : NSObject
#property double playerPosition;
...
#end
#implementation XYZiTunesHelper
// implement getter for playerPosition
- (double) playerPosition
{
switch (version)
{
case kEnumiTunes_11_0_5:
return [(iTunes_11_0_5_Application*)_iTunes playerPosition];
// other cases - by using an enum it is both fast and the
// compiler will check you cover all cases
}
}
// now implement the setter...
Do something similar for track type. Your code fragment then becomes:
XYZiTunesHelper *_iTunesHelper = [[XYZiTunesHelper alloc] init:_iTunes
v ersion:_versionString];
...
playerPosition = [_iTunesHelper playerPosition];
duration = [_currentTrackHelper duration];
finish = [_currentTrackHelper finish];
The above is dynamic as you requested - at each call there is a switch to invoke the appropriate version. You could of course make the XYZiTunesHelper class abstract (or an interface or a protocol) and write three implementations of it one for each iTunes version, then you do the test once and select the appropriate implementation. This approach is more "object oriented", but it does mean the various implementations of, say, playerPosition are not together. Pick whichever style you feel most comfortable with in this particular case.
HTH
Generating multiple headers and switching them in and out based on the application's version number is a really bad 'solution': aside from being horribly complicated, it is very brittle since it couples your code to specific iTunes versions.
Apple events, like HTTP, were designed by people who understood how to construct large, flexible long-lived distributed systems whose clients and servers could evolve and change over time without breaking each other. Scripting Bridge, like a lot of the modern 'Web', was not.
...
The correct way to retrieve a specific type of value is to specify your required result type in the 'get' event. AppleScript can do this:
tell app "iTunes" to get player position as real
Ditto objc-appscript, which provides convenience methods specifically for getting results as C numbers:
ITApplication *iTunes = [ITApplication applicationWithBundleID: #"com.apple.itunes"];
NSError *error = nil;
double pos = [[iTunes playerPosition] getDoubleWithError: &error];
or, if you'd rather get the result as an NSNumber:
NSNumber *pos = [[iTunes playerPosition] getWithError: &error];
SB, however, automatically sends the 'get' event for you, giving you no what to tell it what type of result you want before it returns it. So if the application decides to return a different type of value for any reason, SB-based ObjC code breaks from sdp headers onwards.
...
In an ideal world you'd just ditch SB and go use objc-appscript which, unlike SB, knows how to speak Apple events correctly. Unfortunately, appscript is no longer maintained thanks to Apple legacying the original Carbon Apple Event Manager APIs without providing viable Cocoa replacements, so isn't recommended for new projects. So you're pretty much stuck with the Apple-supplied options, neither of which is good or pleasant to use. (And then they wonder why programmers hate everything AppleScript so much...)
One solution would be to use AppleScript via the AppleScript-ObjC bridge. AppleScript may be a lousy language, but at least it knows how to speak Apple events correctly. And ASOC, unlike Cocoa's crappy NSAppleScript class, takes most of the pain out of gluing AS and ObjC code together in your app.
For this particular problem though, it is possible to monkey-patch around SB's defective glues by dropping down to SB's low-level methods and raw four-char codes to construct and send the event yourself. It's a bit tedious to write, but once it's done it's done (at least until the next time something changes...).
Here's a category that shows how to do this for the 'player position' property:
#implementation SBApplication (ITHack)
-(double)iTunes_playerPosition {
// Workaround for SB Fail: older versions of iTunes return typeInteger while newer versions
// return typeIEEE64BitFloatingPoint, but SB is too stupid to handle this correctly itself
// Build a reference to the 'player position' property using four-char codes from iTunes.sdef
SBObject *ref = [self propertyWithCode:'pPos'];
// Build and send the 'get' event to iTunes (note: while it is possible to include a
// keyAERequestedType parameter that tells the Apple Event Manager to coerce the returned
// AEDesc to a specific number type, it's not necessary to do so as sendEvent:id:parameters:
// unpacks all numeric AEDescs as NSNumber, which can perform any needed coercions itself)
NSNumber *res = [self sendEvent:'core' id:'getd' parameters: '----', ref, nil];
// The returned value is an NSNumber containing opaque numeric data, so call the appropriate
// method (-integerValue, -doubleValue, etc.) to get the desired representation
return [res doubleValue];
}
#end
Notice I've prefixed the method name as iTunes_playerPosition. Unlike objc-appscript, which uses static .h+.m glues, SB dynamically creates all of its iTunes-specific glue classes at runtime, so you can't add categories or otherwise patch them directly. All you can do is add your category to the root SBObject/SBApplication class, making them visible across all classes in all application glues. Swizzling the method names should avoid any risk of conflict with any other applications' glue methods, though obviously you still need to take care to call them on the right objects otherwise you'll likely get unexpected results or errors.
Obviously, you'll have to repeat this patch for any other properties that have undergone the same enhancement in iTunes 11, but at least once done you won't have to change it again if, say, Apple revert back to integers in a future release or if you've forgotten to include a previous version in your complicated switch block. Plus, of course, you won't have to mess about generating multiple iTunes headers: just create one for the current version and remember to avoid using the original -playerPosition and other broken SB methods in your code and use your own robust iTunes_... methods instead.

Cocoa / Objective-C (Deprecated FMActivateFonts)

I'm trying to update my code so it will compile x86_64, unfortunately the code below is the main culprit (which happens to be roughly ten years old). I know it should be a CTFont type class, although I have no idea how to adapt this old code to the current. Any help would be appreciated. I've also included a reference to this code and some of its predecessors, which also happen to be deprecated. thanks!
- (void)loadLocalFonts
{
NSString *fontsFolder;
if ((fontsFolder = [[NSBundle mainBundle] resourcePath])) {
NSURL *fontsURL;
if ((fontsURL = [NSURL fileURLWithPath:fontsFolder])) {
FSRef fsRef;
FSSpec fsSpec;
(void)CFURLGetFSRef((CFURLRef)fontsURL, &fsRef);
if (FSGetCatalogInfo(&fsRef, kFSCatInfoNone, NULL, NULL, &fsSpec,
NULL) == noErr) {
FMActivateFonts(&fsSpec, NULL, NULL, kFMLocalActivationContext);
}
}
}
}
Reference: Using Custom Fonts In Your Cocoa Applications
Thanks for the comment back. I wasn't sure exactly what you were doing there.
To me it looked like you were loading a selection (or font family) from your resource folder and activating them (whatever that used to be ... Font Manager FM_____ calls feel like 10 years ago at this point :-)
Anyways, check out this potentially useful question that has answers that may help you out. Looks like it might be as easy as making the font available via a ATSApplicationFontsPath key in the info.list file (the higher rated answer there).

Adding string objects to NSMutableArray?

I have a little foundation tool test (Objective-C) that I am playing with and I have a few questions ...
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
int planetLoop;
NSString *tempString;
NSMutableArray *planetArray = [[NSMutableArray alloc] init];
NSLog(#"STRING ARRAY ... Start");
for(planetLoop=0; planetLoop<10; planetLoop++) {
tempString = [NSString stringWithFormat: #"Planet_%03d", planetLoop+1];
NSLog(#"Planet_%03d", planetLoop+1);
[planetArray addObject:tempString];
}
[planetArray release];
[pool drain];
return 0;
}
First, usually I release an object after adding it to an array, but am I right in thinking that what I have currently is right because "tempString" is a string literal, and as such does not need to be allocated or released?
Secondly, when I run this (prior to execution) I get the following eror "unable to read unknown load command 0x80000022" if this a problem with my code? from searching on google it looks like it might be a bug in xCode 3.1.2, anyone have any ideas?
Finally anything I am doing wrong, the idea is to fill an array with 10 string "Planet_001" through to "Planet_010"
EDIT: Ah, I see, thats because of the "= [NSString" bit i.e.
// Autoreleased object
tempString = [NSString stringWithFormat: #"Planet_%03d", planetLoop+1];
// String literal
tempString = #"Planet_";
many thanks, much appreciated -gary-
tempString isn't actually a string literal. #"Planet_%03d" is a string literal. tempString is an autoreleased object, meaning that it will be released when the NSAutoreleasePool is drained. Basically, the memory is already managed and you don't have to do anything.
The rule is: If you new, alloc, copy or retain an object, then you have to release it. Otherwise, the memory is already managed, probably by an autorelease.
Also, you forgot to release pool. Other than that, it looks fine.
One possible reason for the "unable to read unknown load command 0x80000022" error appears to be that I've upgraded to Snow Leopard without upgrading the developers tools at the same time. It looks like the error might be caused by trying to use the 10.5 version to XCode to compile in a 10.6 environment. I will look into this tomorrow.
Xcode 3.2 is now available in the Snow Leopard (Mac OS X 10.6) release. After installing Snow Leopard, upgrade to Xcode 3.2 by installing it separately from the Xcode Tools disk image. You can install it over prior versions of Xcode, or move them aside prior to installing.
PS: When I got the "unable to read unknown load command 0x80000022" error I was running OSX 10.6.1 with xCode 3.1.2
cheers -gary-
That "load command" error is due to the fact that the executable format has changed from iPhone OS 3.0 to iPhone OS 3.1.
http://networkpx.blogspot.com/2009/09/about-lcdyldinfoonly-command.html