Objective-C #ifdef DEBUG is TRUE when the app is distributed? - objective-c

In my project, I am sharing events to Firebase Analytics and other analytics platform
When an event occurs I am checking whether the scheme is in debug mode:
#ifdef DEBUG
#define sharedAnalytics NO
#else
#define sharedAnalytics YES
#endif
When debugging in Xcode evetything seems to work as expected:
-When changing the build configuration in scheme to Release, sharedAnalytics is true, and the event is fired,
when changing the build configuration to Debug - sharedAnalytics is false.
But the app in Appstore stopped firing those events somewhere in Jan 2020, both in Firebase Analytics and in AppsFlyer.
In the Android app everything works.
From what I know, when the app is compiled for Appstore (or Testflight) it automatically switches to Release build configuration, but it looks like it's not?
My questions are:
-Do I need to make sure I am in Release mode before uploading an app to Appstore?
-What changed in the beginning of 2020?
Thanks!
EDIT:
This is where I check sharedAnalytics
- (void)logFirebaseEvent:(NSString*)eventName params:(NSDictionary*)params{
if (sharedAnalytics) {
[FIRAnalytics logEventWithName:eventName parameters:params];
}
else {
NSLog(#"%#", sharedAnalytics == nil ? #"sharedAnalytics is nil" : #"sharedAnalytics == NO");
}
}
When in DEBUG prints "sharedAnalytics is nil"
When in RELEASE prints logs the event.

Debug, Release, Appstore is just build configurations.
Xcode by default just create Debug and Release configurations in new projects.
There is two possible ways to define macros:
in code by #define ...
in build settings in GCC_PREPROCESSOR_DEFINITIONS parameter
May be you have copied Appstore from Debug or may be you have #define DEBUG some where
May be you have DEBUG=0
Proper way to check for DEBUG is
#if defined(DEBUG) && DEBUG
#define sharedAnalytics NO
#else
#define sharedAnalytics YES
#endif
May be you also checking sharedAnalytics wrong.
In both cases #ifdef sharedAnalytics gives YES

Ok Vadim, not that this will solve the problem, but when you check this is a bit better
- (void)logFirebaseEvent:(NSString*)eventName params:(NSDictionary*)params{
if (sharedAnalytics) {
[FIRAnalytics logEventWithName:eventName parameters:params];
}
else {
NSLog(#"sharedAnalytics == NO");
}
}
Technically that
sharedAnalytics == nil
evaluates to
sharedAnalytics == NO
which is true and so it reports it as nil.
The problem is this still leaves an NSLog in your production / release code, so maybe you need to do that differently. Again, this is moot relative to your problem, just doing the tests a bit better. Anyway, herewith an idea.
Do the define as follows.
#ifdef DEBUG
#undef sharedAnalytics
#else
#define sharedAnalytics
#endif
To be extremely pure, you should use caps and reduce that to a single branch but lets not go there right now ...
Then when you test do as follows
- (void)logFirebaseEvent:(NSString*)eventName params:(NSDictionary*)params{
#ifdef sharedAnalytics
[FIRAnalytics logEventWithName:eventName parameters:params];
#else
NSLog(#"sharedAnalytics == NO");
#endif
}
While this looks a lot like your original code and does not solve the direct problem you have, it is better since when you compile for DEBUG you get the NSLog bit and when you compile for RELEASE that code is stripped completely from your release version.
What do you get for all this? You should strip all NSLog's from release code as it has performance implications.

Related

Objective C create static library with debug option set-able in launch arguments

Apple library produces extra debug info when compiled with corresponding debug options.
e.g. for sqlite debug info compile with -com.apple.CoreData.SQLDebug to 1 in the Arguments list.
How can we give similar options for our clients when creating a static lib?
I used a DEBUG preprocessor var, but want something more elegant than below,
#ifdef DEBUG
#define DLog(fmt, ...) NSLog((#"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define DLog(...) /* */
#endif
for the above, I have to compile my static lib with DEBUG. But I would like to give my user to set the debug as Apple does.
Question is, how the 'arguments passed on launch' is exposed in the code.
To allow runtime control of the debugging options then you need to allow the user to set a debug option, so provide a function to do that:
YourLibrary.h:
extern void MyLib_SetDebug(BOOL debug);
YourLibrary.m
static BOOL _debug = NO;
void MyLib_SetDebug(BOOL debug)
{
_debug = debug;
}
and create a log function that checks this debug flag and avoids writing if not set:
void DLog(NSString *format, ...)
{
if (!_debug)
return;
va_list va;
va_start(va, format);
NSString *message = [[NSString alloc] initWithFormat:format arguments:va];
va_end(va);
NSLog(#"%#", message);
}
and then delete that DLog macro you are using. You can then compile in Release mode and get debugging behaviour as required at runtime.

Gnustep with Arc and Gui fails INCOMPATIBLE ABIs

Basically, I followed the instructions here: http://wiki.gnustep.org/index.php/GNUstep_under_Ubuntu_Linux
With the extra step of building the gui and back.
If I build and run a non-gui program, all works well. Only when I add something that uses the gui, like the following:
// Created by Tobias Lensing on 2/22/13.
#import <Foundation/Foundation.h>
#import <AppKit/AppKit.h>
#import <dispatch/dispatch.h>
int main(int argc, const char * argv[])
{
#autoreleasepool {
int multiplier = 7;
int (^myBlock)(int) = ^(int num) {
return num * multiplier;
};
NSLog(#"%d", myBlock(3));
dispatch_queue_t queue = dispatch_queue_create(NULL, NULL);
dispatch_sync(queue, ^{
printf("Hello, world from a dispatch queue!\n");
});
// dispatch_release(queue); // ARC takes care of this
}
#autoreleasepool {
[NSApplication sharedApplication];
NSRunAlertPanel(#"Test", #"Wow it works!", #"OK", nil, nil);
}
return 0;
}
I compile this with the following:
clang `gnustep-config --objc-flags` `gnustep-config --objc-libs` -fobjc-arc -fobjc-runtime=gnustep -fblocks -lobjc -ldispatch -lgnustep-gui test.m
It compiles and links with no errors.
When I run it, however, it spits out the following ugly-gram:
Hello, world from a dispatch queue!
Objective-C ABI Error: Loading modules from incompatible ABI's while loading .GSBackend.m
a.out: /home/lloyd/projects/ThirdParty/BuildGnuStep/libobjc2/loader.c:53: void __objc_exec_class(struct objc_module_abi_8 *): Assertion `objc_check_abi_verion(module)' failed.
Aborted (core dumped)
I have assured myself that there is no other version of libobjc (this is on a virtual machine so I can go back redo my steps).
Commenting out the following:
// [NSApplication sharedApplication];
// NSRunAlertPanel(#"Test", #"Wow it works!", #"OK", nil, nil);
and everything compiles and runs, aside from the GUI obviously.
How can I have two ABI's when I build everything from scratch? Do I need to configure the GUI differently? I've been puzzling over this for a couple of weeks.
Sorry, too long for a comment:
Well I am not sure of you exact problem but you have 3 ABI'a to consider rather than just the normal 2 (gnustep, fragile, not-fragile)... I am not an absolute expert on the area, but I believe you can run gnustep against the newer apple non-fragile ABI... so it is possible that isn't the ABI you have, but it is the one that gets selected with: gnustep-config --objc-libs, you could try omitting that...
I always used gnustep-make, but I haven't done much gnustep for a long time, and don't know if that is still preferred.
I did find an interesting thread: http://comments.gmane.org/gmane.comp.lib.gnustep.general/38698
Okay, after much messing around I discovered that, surprise, I was building everything wrong.
I would go into the long, deep, painful process, but this website actually has a nice set up, scripts, and everything.
Sadly I didn't find this website before asking my question. Now I have Objective C, ARC, blocks and GNUstep!

Detecting Mountain Lion from within the iOS Simulator [duplicate]

I'd like to know whether my app is being run on device or simulator at run time. Is there a way to detect this?
Reason being to test bluetooth api with simulator:
http://volcore.limbicsoft.com/2009/09/iphone-os-31-gamekit-pt-1-woooohooo.html
#if TARGET_OS_SIMULATOR
//Simulator
#else
// Device
#endif
Pls refer this previous SO question also What #defines are set up by Xcode when compiling for iPhone
I created a macro in which you can specify which actions you want to perform inside parentheses and these actions will only be performed if the device is being simulated.
#define SIM(x) if ([[[UIDevice currentDevice].model lowercaseString] rangeOfString:#"simulator"].location != NSNotFound){x;}
This is used like this:
SIM(NSLog(#"This will only be logged if the device is simulated"));
TARGET_IPHONE_SIMULATOR is defined on the device (but defined to false). and defined as below
#if TARGET_IPHONE_SIMULATOR
NSString * const DeviceMode = #"Simulator";
#else
NSString * const DeviceMode = #"Device";
#endif
Just use DeviceMode to know between device and simulator
Check if simulator
#if TARGET_IPHONE_SIMULATOR
// Simulator
#endif
Check if device
#if !(TARGET_IPHONE_SIMULATOR)
// Device
#endif
Check for both
#if TARGET_IPHONE_SIMULATOR
// Simulator
#else
// Device
#endif
Please note that you should not ifdef on
TARGET_IPHONE_SIMULATOR because it will always be defined to either 1 or 0.
From XCode 9.3+ , Swift
#if targetEnvironment(simulator)
//Simulator
#else
//Real device
#endif
Helps you to code against device type specific.
You can use the TARGET_IPHONE_SIMULATOR preprocessor macro to distinguish between device and simulator targets.
Use this below code:
#if targetEnvironment(simulator)
// iOS Simulator
#else
// Device
#endif
Works for Swift 4 and Xcode 9.4.1
if anyone is looking for Unity solution i did this, the only way i found how.
using System.Globalization;
public static bool IsArm() {
return CultureInfo.InvariantCulture.CompareInfo.IndexOf(SystemInfo.processorType, "ARM", CompareOptions.IgnoreCase) >= 0;
}

conditional compilation warning for constant

QUESTION
I would like to trigger a warning only if my TESTING is YES. Is this possible? What I have now doesn't work. What should I do?
BOOL const TESTING = NO;
#if TESTING == YES
#warning don't forget to turn testing to NO before upload
#endif
ANSWER
Based on the answer below, here is what worked for me:
#define _TESTING // COMMENT THIS OUT TO DISABLE TESTING MODE
#ifdef _TESTING
BOOL const TESTING = YES;
#warning don't forget to turn testing to NO for production
#else
BOOL const TESTING = NO;
#endif
Try replacing what you have with
#ifdef TESTING
#warning //... warning here
BOOL const testingBool = YES;
#else
BOOL const testingBool = NO;
#endif
Then, you need to add TESTING as a "Preprocessor Macro" in your Target's Build Settings (see this question for more details on how to do that).

How do I temporary disable warnings in my objective c source file?

One project I work on has a build system that enforces no warnings.
But I have some code that needs warnings to work. Here is an example
NSString* title = #"";
if ([view respondsToSelector:#selector(title)]) {
title = [view title];
}
After some googling I tried disable warnings for the code block by wrapping that code area with
#pragma warning disable
// my code
#pragma warning restore
Didn't work :(
Anyone know how to do this in Xcode?
Any help is appreciated.
-CV
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wshadow-ivar"
// your code
#pragma GCC diagnostic pop
You can learn about GCC pragma here and to get the warning code of a warning go to the Log Navigator (Command+7), select the topmost build, expand the log (the '=' button on the right), and scroll to the bottom and there your warning code is within square brackets like this [-Wshadow-ivar]
Edit
For clang you can use
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshadow-ivar"
// your code
#pragma clang diagnostic pop
There are a number of things you could do here, but the simplest would probably to rewrite your code just a tad.
NSString* title = #"";
if ([view respondsToSelector:#selector(title)]) {
title = [(id)view title];
}
Casting the view variable to id before sending the message should ensure that so long as a method named -title exists anywhere, it'll stay silent.
Another option:
NSString* title = #"";
if ([view respondsToSelector:#selector(title)]) {
title = [view performSelector:#selector(title)];
}
This is a little different from the above, in that it doesn't require the file to "see" any method named title; but it's a bit more wordy.
Edit: I'm aware that neither of these approaches actually turn of warnings for any amount of time, but rather suppress them.
Suppression, when done correctly, at least, is usually better than simply ignoring.
The closest way to do what you want is to use the GCC diagnostic pragma. See this question for details.