Only run code if iOS < 6.0 with preprocessing? - ios

I can find many examples on how to only compile/run code then iOS version is > something, but how do I do it the other way around? I tried the following by running iOS 5.0 in the simulator:
#if __IPHONE_OS_VERSION_MAX_ALLOWED < __IPHONE_6_0
// Code for iOS < 6.0 here
#endif
But the code inside the #if - #endif is not run on iOS 5.0 in the simulator. How can I do this?
[EDIT]
Ok so I wasn't sure what I wanted it seems, sorry :) The thing is that I want this code in my UITableViewDelegate to be run only if the device is running iOS < 6.0:
-(UIView*)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
}
This is because I want to do some styling if I am running iOS < 6, but on iOS 6 I can do this styling much much easier. But a version check at runtime inside this method is not really what I want because then it is to late.
Thank you
Søren

There's a difference between
a compile-time check if you're compiling against a specific SDK and
checking which OS your code is running on
Checking for the __IPHONE_6_0 macro will just check which target you're compiling for... is that what you want? If so, you could use #ifndef __IPHONE_6_0 to check if you are not compiling for iOS 6.
If you want to know which OS your code is running on, you can check MSK's answer.

Here is a run time not compile time check.
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
use it like
if(SYSTEM_VERSION_LESS_THAN(#"6.0"))
{
}

Related

Using __IPHONE_7_0 and respondsToSelector

I came across this code and I was wondering if the #ifdef check is redundant.
UIButton *doneButton = [[UIButton alloc] init];
...
#ifdef __IPHONE_7_0
if([self respondsToSelector:#selector(setEdgesForExtendedLayout:)])
[doneButton setContentEdgeInsets:UIEdgeInsetsMake(0, 12, 0, -12)];
#endif
I tried removing it and running it in iOS 6 and it ran fine. Is there some special case I need to be aware of in iOS 6/7 that causes it to not trigger or causes crashes?
The #ifdef is a compile-time directive. It will cause the code between the #ifdef and the #endif to only be compiled if the project is built against the iOS 7 SDK. If you build against the iOS 6 SDK, the code won't be compiled at all.
The #ifdef would prevent compiler errors if the code inside uses symbols that are only defined in the iOS 7 SDK. In the code you posted, I'm not sure what it's doing. The setContentEdgeInsets code is valid for most iOS versions. I would think the code should check to see if self responds to setEdgesForExtendedLayout, and then call setEdgesForExtendedLayout if it does respond.
The #ifdef is redundant. Also, depending on how __IPHONE_7_0 is defined, it may cause your app to stop working on iOS 8. Just remove it.

iOS: Preprocessor for OS version check

In the past I used the following preprocessor code to conditionally execute code for different iOS versions:
#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
// target is iOS
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000
// target is lower than iOS 6.0
#else
// target is at least iOS 6.0
#endif
#endif
However with iOS 7 I have the following problem:
#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
// target is iOS
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 70000
// target is lower than iOS 7.0
NSLog(#"This message should only appear if iOS version is 6.x or lower");
#else
// target is at least iOS 7.0
#endif
#endif
The NSLog message above appears on console under iOS 7. Am I doing something wrong?
EDIT: The following code running under iOS 7 (simulator and device)
NSLog(#"Version %i", __IPHONE_OS_VERSION_MIN_REQUIRED);
gives: Version 60000
That is the Deployment Target of your app (the minimum version where your app can be installed), not the version where the app is running in the device.
In the settings of your project, you can set that field:
If you change it like this, this input:
NSLog(#"Version %i", __IPHONE_OS_VERSION_MIN_REQUIRED);
Returns 7000
If what you want is to check the actual version of the operative system, I refer you to this question:
How to check iOS version?
But, it's done in runtime, not at compile time.
#ifdef __AVAILABILITY_INTERNAL__IPHONE_9_0_DEP__IPHONE_9_0
// you're in Xcode 7.x and can use buggy SDK with ios 9.0 only functionality
CGFloat iOSVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
if (iOSVersion>=9) {
// same good old API that predates brave new post Steve Jobs world of bugs and crashes
}
#else
// you're running Xcode 6.4 or older and should use older API here
#endif
swift:
if #available(iOS 13, *) {
toSearchBar?.isHidden = true
} else {
// a path way to discovering how fast UIKit will rot
// now that there is SwiftUI
}

Conditionally Hide Code from the Compiler

So here's the problem. I'm set to release an update soon for iOS that will address some problems in iOS 7. In order to do this, I need to use some specific iOS 7 functions/types. I've made absolutely certain that iOS 7 code will only be executed on iOS 7 and fallback to different code for pre iOS 7. Of course, I'm not allowed to submit with the current Xcode beta, so I'm trying to compile with the current Xcode release. However, I can't seem to find a way to disable this particular warning:
Use of undeclared identifier '<Redacted>'.
Does anyone know of a way to disable this warning using a #pragma. I've tried a bunch of different ones including
-w, -Weverthing, -Wall
but nothing seems to work.
UPDATE
Answer: You can't, of course, because the compiler can't compile an identifier it knows nothing about. My solutions was to simply create a #define:
#define <redacted> 1
UPDATE 2
The answer below actually made it much easier. I had already created a #define Xcode5Code(code, alt) that allowed me to execute code blocks conditionally. By modifying it using the solution by #maddy:
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
#define Xcode5Code(code, alt) code
#else
#define Xcode5Code(code, alt) alt
#endif
This allows me to to easily hide blocks of code from the compiler by using:
Xcode5Code({
//Code to be execute only with Xcode 5
}, {
//code to be executed in previous versions of Xcode
})
The main benefit of using the #define Xcode5Code is that Xcode will auto-complete it for you, which is a lot easier than using the full #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000, which Xcode won't auto-complete.
This won't actually distinguish between iOS 7 and pre iOS 7 devices. It only distinguishes what version of iOS the current Xcode can handle. To distinguish between iOS devices versions I use:
NSUInteger DeviceSystemMajorVersion(void) {
static NSUInteger _deviceSystemMajorVersion = -1;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_deviceSystemMajorVersion = [[[[[UIDevice currentDevice] systemVersion] componentsSeparatedByString:#"."] objectAtIndex:0] intValue];
});
return _deviceSystemMajorVersion;
}
The above is Apple's code, by the way. To dance around the NDA a little, I'll say that this helps with laying out a root controller's view, because that depends on both the version of Xcode you're using AND the version of iOS that's on the device. And if you're trying to manage beta's as well as production code, this can help a lot. Once you can submit apps with Xcode 5, the #define Xcode5Code will no longer be necessary.
If you want to compile your app with two difference versions of Xcode or two different Base SDK settings then you should use compiler directives:
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 // iOS 7.0 supported
// iOS 7 code here
#else
// Pre-iOS 7 code here
#endif
Do not use this for runtime checks. This solution is only to be used when you must compile your code with two different versions. An example would be you have added iOS 7 code but you still need to compile the code with Xcode 4.6. Using the compile directives allows you to "hide" the iOS 7 code from the compiler using the older Base SDK.
See the "SDK Compatibility Guide" in the docs for more on this and proper runtime checks.

How can I add one app to the App Store and in this app there is one for ios7 and one for the rest?

Hi since there are a lot of things in ios 7 that does´t work properly on iOS 6, and around. I wonder if there is possible to have two versions of an app on the App Store under the same app. Is it possible to make it so that when someone that have iOS 6 download the app they get the app optimized for ios 6 and when someone who have ios 7 download it, they get one that is optimized for iOS 7. Do I have to create two different apps on the appstore?
Please help.
You cannot submit two different versions for an app (single build file), Think of it - it doesn't make any sense.
You can check inside the app the version of the iOS on the device it runs but it's very bad thing to do because that would break someday.
The recommended thing to do is before you are willing to use a new feature to check if this feature is implemented on the version you are running, For example if you want to check if you can use:
[[AVAudioSession sharedInstance] requestRecordPermission];
You should check that this one is true (should return true only if this function is implemented):
[[AVAudioSession sharedInstance] respondsToSelector:
#selector(requestRecordPermission)]
The big advantage of using this is that it works on iOS 7.x and would return true also if it would be supported on iOS 8.x and so on.
Another big advantage is that you would only have small sections that would be specific for the OS support and not a full app for each version (easier to maintain).
Libraries: You should pay attention to the libraries you are linking with. If you are using new libraries that aren't supported on previous iOS versions you should mark them as "optional" instead of "Required" (which is the default).
Last thing, If you do want to create separate app for each OS (if I didn't succeed to explain how wrong it is) you can always add a new target (just duplicate the one you are working on) and set the parameters accordingly. Pay attention that Apple allow you only to limit the oldest version that can download the app and not the newest so it could be that users with iOS 7 would be able to download the app that is dedicated for iOS 6 so it should work also (maybe not with all the great 7 features but must work).
Hope that helps a bit, If there is something missing please comment below.
You can't have two versions of an app on the App Store under the same name.
Check #rmedy comment and #ldan answer for better options to build the application for different versions.
As a last resort, within your app you can check the iOS version to make decisions. Here are few macros that may be helpful:
#define SYSTEM_VERSION_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)

Is there a way to find methods in my code which won't run on previous versions of iOS?

I had a problem with the latest release of my app, where iOS 5 users said it was crashing. I quickly discovered the problem to be with using an iOS 6 method. I didn't realise this method was a new one. I was wondering if there was a way to quickly check my code (without doing it method-by-method) to make sure all code is compatible with previous versions of iOS?
You should test your app on iPhone/iPad 5.0 simulator. I think they are not available by default XCode 4.6 onwards, but you can download them from: Preferences > Downloads > Components.
Edit: It seems like XCode doesn't warn about new APIs in your code. There a workaround described in this answer: Get xcode 4.5 to warn about new API calls -
#define __AVAILABILITY_TOO_NEW __attribute__((deprecated("TOO NEW!"))) __attribute__((weak_import))
#if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_6_0
#undef __AVAILABILITY_INTERNAL__IPHONE_6_0
#define __AVAILABILITY_INTERNAL__IPHONE_6_0 __AVAILABILITY_TOO_NEW
#endif

Resources