How to use preprocessors in objective c - ios

Hi in one of my applications I have to support that app for IOS6 & IOS7.Inorder to accomplish that first I have to know the current device version. For that I had defined one macro and I am trying to using that macro as a reference to accomplish my task. The code which I wrote is as such below.
In .h file I defined IPhoneOSVersion as 50000.
This code is in .m file
if([[[UIDevice currentDevice] systemVersion] isEqualToString:#"7.0"])
{
#undef IPhoneOSVersion
#define IPhoneOSVersion 70000
NSLog(#"_IPHONE_OS_VERSION_MIN_REQUIRED after is %d",IPhoneOSVersion);
}
else
{
#undef IPhoneOSVersion
#define IPhoneOSVersion 60000
NSLog(#"_IPHONE_OS_VERSION_MIN_REQUIRED after is %d",IPhoneOSVersion);
}
NSLog(#"_IPHONE_OS_VERSION_MIN_REQUIRED after is %d",IPhoneOSVersion);
And if i run this code in IOS7. In console the data have to print like this _IPHONE_OS_VERSION_MIN_REQUIRED after is 70000 but unfortunately I am getting _IPHONE_OS_VERSION_MIN_REQUIRED after is 60000. Even I put a break points at else condition also but that is not executing but the macro value is changing.Can anyone please let me know why the macro value changing like this.

You shouldn't be hardcoding against the OS version, Apple recommended way of supporting multiple OS versions is to check for some specific class, API, protocol or function, this allows for greater flexibility as some of that stuff is sometimes backwards compatible.
Here's a pretty decent tutorial on how to check for existence of specific resources in code http://www.raywenderlich.com/42591/supporting-multiple-ios-versions-and-devices and the docs from Apple https://developer.apple.com/library/ios/documentation/developertools/conceptual/cross_development/Using/using.html
EDIT: To answer your question on why the macro is changed, the compiler goes over both branches of the if-else, thus the last declaration of the macro is used. You can't use a macro like that and change it during runtime, macros are meant to be define before compilation.

You use the preprocessor in Objective-C in exactly the same way as in C or C++. The preprocessor doesn't care about your if/else statements. It sees a sequence of #undef, #define, #undef, #define and performs them one after the other, so in your last line, the last #define is in effect. You cannot influence these #defines with anything happening at runtime.
There are always three OS versions in play: The deployment target (that is the lowest OS version where you allow your app to run), the SDK version, and the actual version at runtime. The first two you set in Xcode; the actual version is obviously out of your control except that you know it is the same or higher than the deployment target.
__IPHONE_OS_VERSION_MIN_REQUIRED = Deployment target
__IPHONE_OS_VERSION_MAX_ALLOWED = SDK version

Try with
if([[[UIDevice currentDevice] systemVersion] floatValue] == 7.0)

Related

swift #pragma replacement to stub out code in production vs dev (ie #if TEST_CODE == 1)?

I have test code that I use for GPS testing of my app that I want
to stub out entirely so that its not even compiled into the binary.
Its a simple way to turn on/off testing throughout the codebase for
various things I wish to test.
in Objective-C I would do:
#define TEST_CODE == 1
and use it like this for example:
- (void) viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
#if TEST_CODE == 1
addressTextField.text = #"My Real Address";
#endif
...
}
Since binaries can be searched with things like the 'strings' command,
I don't want any of my test stuff to reach production, however AFAIK
there is no way to do that in swift.
Does anyone have any solutions that would do this?
It seems like a deficiency of swift not to have some type of mechanism
to do so. I can't be the only one who uses #pragma's in this way.
The equivalent in Swift is conditional compilation.
You can set your conditional flag as -DTEST_CODE in the OTHER_SWIFT_FLAGS build setting for your Debug configuration. Then, you can refer to it from Swift files as such:
#if TEST_CODE
// conditionally do something
#endif
Note that there is no support for flag values, so you cannot pass the value 1.
For more information: Using Swift with Cocoa and Objective-C (Swift 4): Preprocessor Directives
Original source of the information kitefaster site

Preprocessor macro for Apple Watch?

I was looking at Apple's Lister (for Apple Watch, iOS, and OS X) sample. The sample performs a test for iOS and OS X:
#import <TargetConditionals.h>
#if (TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR)
#import ListerKit;
#elif TARGET_OS_MAC
#import ListerKitOSX;
#endif
However, there is no test for TARGET_OS_WATCH or similar. Grepping for watch in TargetConditionals.h delivers no hits:
$ cat /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer
/SDKs/iPhoneOS7.1.sdk/usr/include/TargetConditionals.h | grep -i watch
$
From TargetConditionals.h, I know there are:
These conditionals specify in which Operating System the generated code will
run. The MAC/WIN32/UNIX conditionals are mutually exclusive. The EMBEDDED/IPHONE
conditionals are variants of TARGET_OS_MAC.
TARGET_OS_MAC - Generate code will run under Mac OS
TARGET_OS_WIN32 - Generate code will run under 32-bit Windows
TARGET_OS_UNIX - Generate code will run under some non Mac OS X unix
TARGET_OS_EMBEDDED - Generate code will run under an embedded OS variant
of TARGET_OS_MAC
TARGET_OS_IPHONE - Generate code will run under iPhone OS which
is a variant of TARGET_OS_MAC.
TARGET_IPHONE_SIMULATOR - Generate code for running under iPhone Simulator
Question: Is there a preprocessor for Apple's watch?
I'm tagging with ios, but I'm not sure that's the correct OS for this question.
The list below was compiled from iPhone's TargetConditionals.h. The Simulator and OS X are similar (they just have different bits set to 1):
#define TARGET_OS_MAC 1
#define TARGET_OS_WIN32 0
#define TARGET_OS_UNIX 0
#define TARGET_OS_EMBEDDED 1
#define TARGET_OS_IPHONE 1
#define TARGET_IPHONE_SIMULATOR 0
Questions: Does the watch use TARGET_OS_EMBEDDED? Does the watch omit TARGET_OS_IPHONE?
You can find all kind of target conditionals in the TargetConditionals.h (cmd + shift + o and type TargetConditionals.h).
In this list you can find a list like this and many more useful defines.
Currently it does contain TARGET_OS_WATCH since WatchOS 2. For WatchOS 1 it was not possible to run custom code on the watch so it was not needed back then since everything ran on the phone itself.
#define TARGET_OS_MAC 1
#define TARGET_OS_WIN32 0
#define TARGET_OS_UNIX 0
#define TARGET_OS_IPHONE 1
#define TARGET_OS_IOS 0
#define TARGET_OS_WATCH 1
#define TARGET_OS_TV 0
#define TARGET_OS_SIMULATOR 0
#define TARGET_OS_EMBEDDED 1
Swift Addition
#if os(watchOS)
[Watch code]
#else
[Code for iOS, appleTV, or any else clause]
#endif
Some other valid values: iOS, OSX, tvOS
A small explanation about this and more http://nshipster.com/swift-system-version-checking/
At the bottom of this document
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithCAPIs.html#//apple_ref/doc/uid/TP40014216-CH8-XID_15#Build Configurations
Under the section 'Build Configurations' you can find a (hopefully) up to date list with all these values that are currently available
As of watchOS 2.0, you can run native code on the watch, so this is a more relevant question.
I'm using the first early beta of watchOS 2, so this may change, but right now, TARGET_OS_WATCH is set to 1 on watchOS.
(Also, be careful: TARGET_OS_IPHONE is also set to 1 on watchOS, though TARGET_OS_IOS is 0.)
There is no WatchKit or app extension target conditional. But you can create your own per-target conditionals that you use in the same way.
If you look in the "Build Settings" section for any target, there's a section called "Other C Flags". Add an entry for the WatchKit target. If you add something like -DMY_WATCHKIT_FLAG=1, you can then do #if MY_WATCHKIT_FLAG in code.
Make your custom flag, well, custom. It's not impossible that Apple might add a flag in the future called something like TARGET_WATCH_APP or whatever. Use a prefix on the flag name to make it specific to you.
With the current WatchKit SDK, all code in a Watch application runs on the phone it’s paired with, so there’s no point at which your preprocessor is going to encounter code that’s going to run on the Watch and thus not much use for a macro to tell it what to do when it does. The code in the ListerWatch target of the sample you linked to will run as an extension on the iPhone and talk to its watch UI via WatchKit.

Operator 'defined' requires an identifier ios

I've below code in my project dlog should print the values in console if isConsoleLogActive is YES.
It gives error like Operator 'defined' requires an identifier
#if defined ([Util isConsoleLogActive])// Operator 'defined' requires an identifier in this line
#define DLog(...) NSLog(__VA_ARGS__)
#define DTrace() NSLog(#"%s", __PRETTY_FUNCTION__)
#else
#define DLog(...) /* */
#define DTrace() /* */
#endif
if I use the same code([Util isConsoleLogActive]) in my .m it works perfectly fine. I face this issue only in #define
What could be the issue. Please give me some idea.
The various commands that start with # are preprocessor directives. These get executed before the compilation phase at build time, before your application actually executes. You should use the preprocessor directives to conditionally include different code in your application based on build configuration. The preprocessor, however, is the wrong way to handle conditional execution on a specific platform at runtime; for that, you want your standard "if...else" logic.
If your goal with that statement is to determine if the given selector exists, try respondsToSelector, instead.
Result of
[Util isConsoleLogActive]
is not known at compile-time. So you can not use it with '#if defined'.

How to set an enum not present in current iOS version?

I am trying to set linebreakmode for textLabel in UIButton.
self.scheduleButton.titleLabel.lineBreakMode = NSLineBreakByWordWrapping;
NSLineBreakByWordWrapping won't be available prior to iOS 6.0.
self.scheduleButton.titleLabel.lineBreakMode = UILineBreakModeWordWrap;
will work prior to iOS6 but XCode is throwing an error saying, it doesn't recognize UILineBreakModeWordWrap.
How do I make sure that UILineBreakModeWordWrap code is completely ignored prior to iOS 6.
I have made a macro to check OS version, I would have used respondsToSelector, had it been a case with a method but this is an enum type.
SYSTEM_VERSION_LESS_THAN(#"6.0")
I am using this here but in an if-else scenario but it wont still work as XCode will still tell me that UILineBreakModeWordWrap is not recognizable.
Is there a way I can know OS version at pre-processing level ?
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
This macro that I have made will just add Objective C code to determine OS type which means the check will happen at runtime.
but because of this XCode will not ignore UILineBreakModeWordWrap.
Both NSLineBreakByWordWrapping and UILineBreakModeWordWrap have the same value. As long as your Base SDK is 6.0 or later then using NSLineBreakByWordWrapping will make the compiler happy and it will work for any version of iOS at runtime.
Option:1
You cane create your own macros to handle this:
#ifdef __IPHONE_6_0
# define LINE_BREAK_MODE_WORD_WRAP NSLineBreakByWordWrapping
#else
# define LINE_BREAK_MODE_WORD_WRAP UILineBreakModeWordWrap
#endif
Now in code you can use, LINE_BREAK_MODE_WORD_WRAP
Option:2
Directly use enum value instead of reference name, both NSLineBreakByWordWrapping and UILineBreakModeWordWrap are having same value - 0
Hence you can directly write this way as well -
self.scheduleButton.titleLabel.lineBreakMode = 0;
It will automatically picks up 0th value from iOS SDK enumeration reference.
Hope this helps.

Is there a specific Xcode compiler flag that gets set when compiling for iPad?

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;
}

Resources