Is there a way to detect what iOS simulator you are running. i.e. The difference between running 5.1 vs. 6.1.
Using [[UIDevice currentDevice] systemVersion] return x86 so i can't determine the difference between the 6.1 simulator and the 7.0 simulator.
You should take a look at the Availability
The relevant declarations are all in
Availability.h
AvailabilityInternal.h
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_6_0
// Here we go with iOS 6++
#else
// This is the old way
#endif
[[UIDevice currentDevice] systemVersion] should give you the information you need. However, that is not something that should be used for logic. It is designed to be a user-presentable string.
If you want to make logic decisions, you should do it based on capabilities. For example, check for the existence of potentially-weak symbols at runtime before using them rather than making decisions based on the system version.
That's easy!
Swift version:
if #available(iOS 11.0, *) {
} else {
}
Objective-C version:
if (#available(iOS 11.0, *)) {
} else {
}
Related
Trying to add a block in the AppDelegate.m file
#if compiler(>=5.0)
if (#available(iOS 13.0, *)) {
// do something...
}
#endif
The error showed is:
Function-like macro 'compiler' is not defined
The following is a Swift compile-time directive:
#if compiler(>=5.0)
...
#endif
It makes no sense to use this in an Objective-C file, AppDelegate.m. In an Objective-C source, it will result in the “Function-like macro 'compiler' is not defined” error.
So, if you want a runtime check for iOS 13, just remove the #if, e.g.:
if (#available(iOS 13.0, *)) {
...
}
See Marking API Availability in Objective-C.
A couple of observations (given that you tagged this with swift):
In Swift, the compile-time pattern is:
#if swift(>=5.0)
...
#endif
Or
#if compiler(>=5.0)
...
#endif
This pattern is generally only used when writing third-party libraries where you need to offer backward support to developers who are compiling your library with earlier versions of Swift.
See The Swift Programming Language: Compiler Control Statements
The following is an Objective-C runtime check for OS version:
if (#available(iOS 13.0, *)) {
// do something...
}
The equivalent Swift pattern is:
if #available(iOS 13.0, *) {
...
}
These are useful when writing code that is targeting multiple iOS versions, but you want to conditionally run code using iOS 13+ API.
To make it confusing, Swift does have an #available construct, but it is for marking a class, extension, or method as being available only for devices running a particular OS version, but is not used in conjunction with an if test. E.g. let’s say you are defining some function that should only be available to iOS 13 and later:
#available(iOS 13.0, *)
func someMethod() {
...
}
This is compile-time construct is to tell the compiler to not complain about iOS 13 API within this function if your app happens to support earlier iOS versions. You are effectively telling the compiler “hey, I know this has code inside it that only works with iOS 13 and later, but don’t worry, because I’ll only call it when the device in question is running iOS 13 or later.”
There are many things wrong that you did in your code example:
#if is a preprocessor macro. You can only use it outside the scope of anything, just like the import statement. If you are using it the way it should be used, then you can't use that if statement there.
#available cannot be used inside of an if statement. It can only be used to represent if a particular class/struct/func is available for said OS version and up, but not fragments of code. For that, use this instead:
if #available(iOS 13.0, *) {
// Do something.
}
If you want to check the Swift compiler version specifically, you can use #if swift. To check for iOS version during runtime, you need to use #available, the # version if used to mark types/functions only available on said versions.
#if swift(>=5.0)
if #available(iOS 13,*) {
//do something
}
#endif
I am working on an app that is close to launch but uses the ABAddressBook framework. With the deprecation of ABAddressBook in iOS9, do I need to check the user's iOS version, and use ABAddressBook for pre iOS9 users, and CNContact for iOS9 users?
How is everyone else handling this? I haven't been in a situation like this before.
I have also been dealing-with and researching this issue, what I've opted to do is as you suggest; check the users iOS version doing something like the following:
NSString *version = [[UIDevice currentDevice] systemVersion];
BOOL isVersion8 = [version hasPrefix:#"8."];
BOOL isVersion7 = [version hasPrefix:#"7."];
//...
...continuing based on the versions you've decided to support for your app.
Then I do a check to either use the Addressbook framework for earlier than iOS 9, and Contacts framework for iOS 9 and beyond.
if(isVersion7 || isVersion8){
//Use AddressBook
}
else{
//Use Contacts
}
That's the best way I could think to deal with this deprecation business...
Deprecated doesn't mean removed. Just make linking to both frameworks as optional and start to design data workflow that can handle both frameworks. Also please mind that CNContact is new and full of bugs.
Once you think your app is refactored and iOS evolved to 9.1 give it a green light
How to know if system supports functionality
1) Check if the class exists
if(NSClassFromString(#"CNContact")) {
// Do something
}
For weakly linked classes, it is safe to message the class, directly. Notably, this works for frameworks that aren't explicitly linked as "Required". For missing classes, the expression evaluates to nil.
2)
#ifned NSFoundationVersionNumber_iOS_9
#def NSFoundationVersionNumber_iOS_9 NUMBER
#endif
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9) {
// Use address book
} else {
// Use contact framework
}
Run the app in simulator to find the NSFoundationVersionNumber constant
if #available(iOS 9, *)
{
// iOS 9 - CNContact
}
else
{
// iOS 8 - ABAddressBook
}
This is the right way to check.
I have an app that must work for iOS versions >= 5.1.
Apple docs say that "The AudioSession API has been completely deprecated in iOS 7.0". And one should use AVAudioSession class instead.
But the method that I need (- (BOOL)setCategory:(NSString *)category withOptions:(AVAudioSessionCategoryOptions)options error:(NSError **)outError) is only available starting from iOS 6.0.
So it seems that I have to continue usage of deprecated AudioSessionSetProperty to support iOS 5.1.
What is a correct way to handle such situations (my question is general, not only about this particular problem with audio)? Should I write something like
if ([[[UIDevice currentDevice] systemVersion] compare:#"6.0" options:NSNumericSearch] == NSOrderedAscending)
{
// use deprecated methods (AudioSessionSetProperty)
}
else
{
// use brand-new methods (AVAudioSession)
}
or maybe it's ok to just use AudioSessionSetProperty until it stops to compile?
Deprecated methods doesn't stops you from using it, but yes if its deprecated from your current selected iOS version then it'll give you warning. An example, if your app still supporting iOS < 6.0 and you're need to change alignment of UILabel then you can do it like this,
if ([[[UIDevice currentDevice] systemVersion] compare:#"6.0" options:NSNumericSearch] == NSOrderedDescending)
{
lbl.textAlignment = UITextAlignmentCenter;
//for iOS < 6.0, warning when deployment target is iOS 5.0
}
else
{
lbl.textAlignment = NSTextAlignmentCenter; //for iOS >= 6.0
}
OR
lbl.textAlignment = UITextAlignmentCenter; //with warning in iOS 6.0
OR
lbl.textAlignment = NSTextAlignmentCenter; //will crash in iOS 5.0
PS. better to support iOS 7.0 and higher (not even iOS 6.0 as Apple wouldn't allow apps with iOS6.0 from coming Feb as commented by #ahwulf.), as latest iOS 8 was released with lots of good features and most of the people updated their phone with this. This will help you in two cases, you'll not need to check for iOS version compatibility and your code looks nice without warnings :) but still its all depends your needs.
Update:
OR
as #rmaddy suggested one should check for existence of method using respondsToSelector:
UILabel *label;
if([label respondsToSelector:#selector(textAlignment)]) {
label.textAlignment = NSTextAlignmentCenter;
}
In above example, we're checking (but in real we're already know this) whether UILabel has textAlignment property or not.
With the release of iOS6, I am going to update my application using the new feature of iOS6, such as UICollectionView.
When I will deploy it on App Store, my application will be only available for devices on iOS6.
Question: How can I let the devices on iOS 5.1 using the older release which do not use UICollectionView?
I could go more deeper by asking: What is the strategy to upgrade an application without loosing the users which have not yet upgrade their OS?
Thank you for your advice.
You could check for availability of new iOS6 classes with Class tmp = NSClassFromString(#"UIActivityViewController");
if (tmp) {//use iOS6 class}else{//do something else}. Also, you could use some macros, like __IPHONE_5_0, put some code between #ifndef or #if defined(...). Good Luck!
This one will work perfectly:
NSString * systemVersion = [[UIDevice currentDevice] systemVersion];
if ([self.systemVersion compare:#"5.0" options:NSNumericSearch] != NSOrderedAscending)
{
osHigherThaniOS5 = TRUE;
}
if ([self.systemVersion compare:#"6.0" options:NSNumericSearch] != NSOrderedAscending)
{
osHigherThaniOS6 = TRUE;
}
Just determine the system version and set your conditions accordingly :)
Is there a specific Xcode compiler flag that gets set when compiling for iPad?
I want to conditionally compile iPad vs iPhone/iPod Touch code for example:
#ifdef TARGET_IPAD
code for iPad
#else
code for iPhone
#endif
I know there is already TARGET_OS_IPHONE and TARGET_CPU_ARM in TargetConditionals.h but anything that easily and specifically targets iPad?
-Rei
The correct API to use for run-time checking of iPad vs. iPhone/iPad Touch is:
BOOL deviceIsPad = ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad);
The UIDevice header filer also includes a convenient macro, UI_USER_INTERFACE_IDIOM(), which will be helpful if your deployment target is < iPhone 3.2.
#define UI_USER_INTERFACE_IDIOM() ([[UIDevice currentDevice] respondsToSelector:#selector(userInterfaceIdiom)] ? [[UIDevice currentDevice] userInterfaceIdiom] : UIUserInterfaceIdiomPhone)
So you could just say, negatively:
BOOL deviceIsPad = (UI_USER_INTERFACE_IDIOM() != UIUserInterfaceIdiomPhone);
Instead of using compile-time flags, you should use run-time check e.g. use NSClassFromString to see if a class exists because the same app can be installed on both devices.
And because of the possibility of running the app in both devices, there isn't a built-in compile-time check whether it targets iPad or not.
Currently I didn’t find anything that would let you check if you are on an iPad, but I’m also not sure if Apple recommends runtime checks. Here’s an excerpt from Apple:
In addition to your view controllers, any classes that are shared between iPhone and iPad devices need to include conditional compilation macros to isolate device-specific code. Although you could also use runtime checks to determine if specific classes or methods were available, doing so would only increase the size of your executable by adding code paths that would not be followed on one device or the other. Letting the compiler remove this code helps keep your code cleaner.
However, there is no place I could find more specific information about conditional compilation macros.
For multiple targets sharing the same project/code, I'm doing this by editing the C flags for my iPad target.
With the [myapp]-iPad target chosen as the active target, pick Project -> Edit Active Target [myapp]-iPad. Search for "c flags" and double-click. Add a flag for "-D TARGET_IPAD". Now the symbol TARGET_IPAD will be defined only for your iPad target.
Of course, this only works if you're using separate targets for iPad and iPhone. If you're running the same binary on both, obviously there's nothing the compiler can do for you. (However, the 3.2 SDK as of the end of January doesn't even support Universal apps yet.)
(Edited; I was confused about the terminology of "Universal" apps etc.)
Or -> just to be sure
-(BOOL)isDeviceAniPad
{
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 30200
BOOL deviceIsPad = ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad);
return deviceIsPad;
#endif
return NO;
}
I think this will do
-(BOOL)isDeviceAniPad
{
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 30200
return ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad);
#endif
return NO;
}