Separate code between different versions of Mac OS X? - objective-c

I want some code tobe compiled under 10.6 and below but not 10.7 and above.
For example:
#ifdef current version is MAC_OS_X_VERSION_10_6 or below
// do this
elif current version is MAC_OS_X_VERSION_10_7 or above
//do that
#endif
Can someone help me ge proper macros for it? I looked into AvailabilityMacros.h but was not able to figure out proper ifdef.
I have a dynamic library, and it cannot be loaded under 10.7 and above but loads properly under 10.6. This is due to private symbol _CGContextAppendPath. I want to keep it using under 10.6 and below but avoid its use in 10.7 and below. This is because _CGContextAppendPath symbol is not present on 10.7 and above.
I used,
SInt32 version = 0;
Gestalt( gestaltSystemVersion, &version );
bool lionabove = ( version >= 0x1070 );
did not work.

For Objective C, the go-to route would be to check the availability of specific APIs via e.g. [object respondsToSelector:].
For the C library you are using, weak-link to CoreGraphics.framework (so the loading doesn't fail when some functions aren't present) and check for availability of the function in question via &_CGContextAppendPath != NULL.
Details in http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/cross_development/Configuring/configuring.html#//apple_ref/doc/uid/10000163i-CH1-107837 and http://developer.apple.com/library/mac/#documentation/DeveloperTools/Conceptual/cross_development/Using/using.html#//apple_ref/doc/uid/20002000-1114537-BABHHJBC.

Apple introduced a new Availability.h macro file for iOS and Mac OS 10.6 and above (located in <SDK>/usr/include/Availability.h). You can do what you are asking like this:
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 1070
// code to run up through 10.6
#else
// code to run on 10.7 or higher
#endif
Note that this macro is also available (which might be preferable depending on your logic):
#if __MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
// code to run on 10.7 or above
#else
// code to run below 10.7
#endif
The documentation for this is here: Conditionally Compiling for Different SDKs, particularly worth looking at if you are mixing iOS and Mac OS X code. I also highly recommend reading the header file mentioned above.

Related

Write SDK version to binary when compiling from command line (macOS)

I am trying to detect whether the system is in dark mode.
I have already tried reading AppleInterfaceStyle from the user defaults i.e.
NSString *interfaceStyle = [[NSUserDefaults standardUserDefaults] stringForKey:#"AppleInterfaceStyle"];
BOOL isDark = [#"dark" caseInsensitiveCompare:interfaceStyle] == NSOrderedSame;
which works most of the time but has issues in Auto mode on Catalina.
Now from what I have read is that the more robust approach is to check the effectiveAppearance of NSApplication which looks like this:
NSApplication *app = [NSApplication sharedApplication];
NSAppearance *appearance = app.effectiveAppearance;
NSAppearanceName appearanceName = [appearance bestMatchFromAppearancesWithNames:#[NSAppearanceNameAqua, NSAppearanceNameDarkAqua]];
BOOL isDark = [appearanceName isEqualToString:NSAppearanceNameDarkAqua];
The problem with this approach is that the application I am writing this for manually sets its appearance property, which prevents the effectiveAppearance from using the system appearance.
I tried settings app.appearance = nil before checking the effectiveAppearance but it didn't help.
Now there also is [NSAppearance currentAppearance] which uses the appearance of the current thread. I'm not quite sure what this value resolves to if the thread hasn't set the value explicitly.
My big problem here is that I have no access to a machine running macOS to check my code, so I would highly appreciate if someone knows what to do here.
Edit: It looks like the issue is that the library isn’t compile against the correct version of the SDK. Or at least that version isn’t written to the library information.
From the documentation:
If you build your app against an earlier SDK but still want to support Dark Mode, include the NSRequiresAquaSystemAppearance key (with a value of NO) in your app's Info.plist file. Do so only if your app's appearance looks correct when running in macOS 10.14 and later with Dark Mode enabled.
I am already specifying the version through -mmacosx-version-min=10.14. From what I have found this issue is basically the same that I have, but I don’t quite understand what the solution is from the commit.
I guess it has something to do with the -isysroot and -platform_version. But I didn’t find any good reference for what they do and how they work.
My updated question would be:
How do -isysroot and -platform_version work and how do I use them to enable SDK specific functionality with my binaries?
The solution is quite simple. When manually compiling from the command line -mmacosx-version-min=10.14 needs to get passed to the compiler and the linker.

is there a compatibility checker at XCode?

Can XCode check for the code compatibility against a specific OS X version? or an external tool?
I have a project that's using a function exists at 10.9 and newer, though I set xcode deployment target to 10.7, it builds without errors but when trying to run the application on 10.8, it doesn't work!!
how can I get functions minimum OS version required?
Xcode does not provide a tool to check whether a method is available for the deployment target. But if you know that some method is only available from a specific version you can check whether the method is available:
if ([self respondsToSelector:#selector(newMethodNotAlwaysAvailable:withParameters:)]) {
[self newMethodNotAlwaysAvailable:#"1" withParameters:YES];
}
else {
// Call some other method to just don't do anything.
}
You can also do ut for classes:
if ([SomeNewClass Class] ) {
// Class is available and you can use it
}

Xcode 5.0 XCTest: ambiguous expansion of macro 'NULL'

I'm using Xcode 5. I have a OS X Framework / iOS static library project with mixed ObjC/C++ code and language dialects set to C11/C++11.
I added a Cocoa Unit Testing Bundle target which uses XCTest, I created one for both the iOS static library and the OS X framework.
This is the code I have and the error I get in both:
-(void) testExample
{
NSObject* world = [NSObject new];
world = nil; <--- Ambiguous expansion of macro 'NULL'
}
Running the iOS test the compiler tells me it is using the definition of NULL from stddef.h (line 74, ((void*)0)) and the other definition being in usr/include/stdlib.h (line 115, __DARWIN_NULL).
The OS X test fails points to usr/include/MacTypes.h (line 90, __DARWIN_NULL) with this time the alternative being in stddef.h (line 74, ((void*)0)).
Google has nothing on this issue. I'm just like "Well, I guess it's just one of these days...".
Any clues as to what may be going on here, and how to fix it?
UPDATE:
The iOS tests not running were due to creating the iOS unit test bundle with the OS X "Cocoa Unit Testing Bundle". /me slap
The OS X testing issue persists though. But I have a hunch that being on OS X 10.9 (Mavericks) this may be related to a missing 10.9 SDK (Base SDK is set to 10.8).
In your Build Settings for your test target, try setting "Enable Modules" to NO.
I've been having various kinds of trouble with test targets, all around preprocessor expansion. Doing this fixed things for me.
Try this: Add -Wno-ambiguous-macro to Xcode -> Build Settings -> Other C Flags

When to use "#if __IPHONE_OS_VERSION_MIN_REQUIRED > x"?

This question addresses how to conditionally include code based on iOS version. But how does it work?
Suppose I set iOS Deployment Target to 3.2 in Xcode 4.5.2. In my code I put in some #ifdef statements:
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_0
// Some iOS 4+ code
#endif
If I run the code on a 3.2 device, this code won't be there, but if I run it on a 4.3 device, it will, right? How does that happen? Or am I misunderstanding what's going on here?
That is a compile time check so it will create the same behavior on any iOS version. Since the Deployment Target is less than 4.0 the code inside the if statement will not run on any device.
If you want the behavior you described you need to do a runtime check. You can see an example of how to do this in the thread that you linked.

How to check if my IOS App support IOS 3+? And something about anonymous function

I'm new to IOS development, sometimes I use a function but ignore its Doc.
So, I may use some functions are only supported on IOS 4 or even IOS 5, but I want to support IOS 3+.
Does it has any way to check if my app support IOS 3+?
I don't want to check line by line, thx.
And BTW, anonymous function like void (^ funcName)(NSString *) is objective-c feature, right? So it is supported on all IOS version, right?
Change your 'Deployment Target' to 3.x to see if any methods you're using aren't supported on that version.
That however is not a substitute for testing on 3.x; so either find a 3.x device or drop support for that version. Also, blocks (the 'anonymous function' you describe) are only available on iOS 4 and above.
If you want to write different sets of code for different version targets, you can use preprocessor directives:
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 50000
... 5.x code here ...
#elif __IPHONE_OS_VERSION_MAX_ALLOWED >= 40000
... 4.x code here ...
#else
... 3.x code here ...
#endif
Try running your app on a device running 3.x
by anonymous functions do you mean blocks like:
[self performSomeBlock:^(NSString *smth) {
NSLog(#"%#", smth);
}];
or do you mean just c like definitions (its late so i forgot the legit name)
void doSomething(void *(*func)(NSString *)) {
...
}