No NSLog output in distributed app under iOS 10 - ios

In iOS 10 Apple has made a change that NSLog() output is not emitted in distributed apps (enterprise, app-store).
Note that when running from Xcode, NSLog() works fine.
Is there a way to force debug for all apps (very useful under beta testing phase)?
Some clues appear here: NSLog on devices in iOS 10 / Xcode 8 seems to truncate? Why?
However, can we have a clear implementation for this?

Our solution depends on two questions:
Are we compiling using Xcode 8 and up? If yes, the new os_log is recognized. If not, we must fall back to existing NSLog behavior.
Are we running under iOS-10 and up? If yes, we can use the new logger. If not, we must fall back to existing NSLog behavior.
We will find the answer to question [1] on compile time. For [2] we must test in runtime.
Here is the implementation:
mylog.h
//only used to force its +load() on app initialization
#interface MyLog:NSObject
#end
#if !__has_builtin(__builtin_os_log_format)
//pre Xcode 8. use NSLog
#else
//we need this include:
#import <os/log.h>
#endif
void myLog(NSString *format, ...);
#ifdef DEBUG
#define NSLog(f, ...) myLog(f, ## __VA_ARGS__)
#else
#define NSLog(f, ...) (void)0
#endif
mylog.m
#implementation MyLog
BOOL g_useNewLogger = NO;
+(void)load
{
NSOperatingSystemVersion os_ver = [[NSProcessInfo processInfo] operatingSystemVersion];
if (os_ver.majorVersion >= 10) {
g_useNewLogger = YES;
}
NSLog(#"Use new logger: %#", g_useNewLogger? #"YES":#"NO");
}
#end
void myLog(NSString *format, ...)
{
va_list args;
va_start(args, format);
#if !__has_builtin(__builtin_os_log_format)
//pre Xcode 8. use NSLog
NSLogv(format, args);
#else
//Xcode 8 and up
if (g_useNewLogger) { // >= iOS 10
NSString *nsstr = [[NSString alloc] initWithFormat:format arguments:args];
os_log(OS_LOG_DEFAULT, "%{public}s", [nsstr cStringUsingEncoding:NSUTF8StringEncoding]);
} else { // < iOS 10
NSLogv(format, args);
}
#endif
va_end(args);
}

If you want a drop-in solution to get the logs of your apps, I recommend you to take a look to a tool we have created. It's called Bugfender and what it does is that it send all the app logs to our server so you can check them on our site.
It's very useful while doing beta testing, because your users can test the app and if they find a problem you will get the information of everything they have done in the app.

Related

Capturing NSLog Output with Firebase Crashlytics

I used to use Crashlytics long ago and there was a simple macro that could be used to capture all output from NSLog() to send it along with the crash report to Crashlytics. Very very helpful so that if a user has a crash, I can see the related logging that led up to it. I even had this working after Google gobbled up Crashlytics for awhile.
But a few years (many?) later and I'm trying to use Crashlytics in a new project and none of these macros are working anymore. The best I've come up with is the following :
#define NSLog(...) [[FIRCrashlytics crashlytics] logWithFormat:(__VA_ARGS__)]
However this generates compile warnings for every NSLog call and it does not appear to be compatible with anything either than NSStrings as arguments to the format. For instance the following will not compile...
NSLog (#"My Float Value : %f", myFloat);
I realize this is Objective C so my apologies to the Swift folk out there. Also I'm not sure if switching over to OSLog would actually help either. I'm just looking to capture the logging output so I can get some more information past just the stack trace. Otherwise this is no better than what TestFlight gives me and is therefore useless to add in for my use case.
Remove the parenthesis around the __VA_ARGS__ from the macro:
#define NSLog(...) [[FIRCrashlytics crashlytics] logWithFormat: __VA_ARGS__]
After doing that, I was able to call these NSLogs:
NSLog (#"My Float Value %f", 1.0);
NSLog (#"My Float string %s", "some string");
NSLog (#"My decimal Value %d", 963);
And this is the result I got in the crash report:
In order to resolve this for a call that has both a format string and values for that format string (NSLog (#"My Value %d", myValue);) and the case for just a single string (NSLog (#"Hello");) then the solution I found was this...
#define NSLog(__FORMAT__, ...) { NSString * str = [NSString stringWithFormat:__FORMAT__, ##__VA_ARGS__]; [[FIRCrashlytics crashlytics] log:str];}
However I ended up with this instead.
#define NSLog(__FORMAT__, ...) rrlog(__FORMAT__, ##__VA_ARGS__)
And this Class...
#interface RRLogger : NSObject
OBJC_EXTERN void rrlog(NSString* format, ...) NS_FORMAT_FUNCTION(1, 2);
#end
#implementation RRLogger
void rrlog(NSString* format, ...)
{
if (!format) return;
va_list args;
va_start(args, format);
NSString *msg = [[NSString alloc] initWithFormat:format arguments:args];
NSLogv(format, args);
va_end(args);
[[FIRCrashlytics crashlytics] log:msg];
}
#end
The reason for this is because the Firebase -FIRDebugEnabled launch argument in my launch scheme wasn't outputting everything to the console. I'm not sure why but it was missing some of the log output. Hence the call to NSLogv in the above function.

How do I change a constant based on the build target in Swift? [duplicate]

In Objective-C we can know if an app is being built for device or simulator using macros:
#if TARGET_IPHONE_SIMULATOR
// Simulator
#else
// Device
#endif
These are compile time macros and not available at runtime.
How can I achieve the same in Swift?
Update 30/01/19
While this answer may work, the recommended solution for a static check (as clarified by several Apple engineers) is to define a custom compiler flag targeting iOS Simulators. For detailed instructions on how to do to it, see #mbelsky's answer.
Original answer
If you need a static check (e.g. not a runtime if/else) you can't detect the simulator directly, but you can detect iOS on a desktop architecture like follows
#if (arch(i386) || arch(x86_64)) && os(iOS)
...
#endif
After Swift 4.1 version
Latest use, now directly for all in one condition for all types of simulators need to apply only one condition -
#if targetEnvironment(simulator)
// your simulator code
#else
// your real device code
#endif
For more clarification, you can check Swift proposal SE-0190
For older version -
Clearly, this is false on a device, but it returns true for the iOS Simulator, as specified in the documentation:
The arch(i386) build configuration returns true when the code is compiled for the 32–bit iOS simulator.
If you are developing for a simulator other than iOS, you can simply vary the os parameter: e.g.
Detect the watchOS simulator
#if (arch(i386) || arch(x86_64)) && os(watchOS)
...
#endif
Detect the tvOS simulator
#if (arch(i386) || arch(x86_64)) && os(tvOS)
...
#endif
Or, even, detect any simulator
#if (arch(i386) || arch(x86_64)) && (os(iOS) || os(watchOS) || os(tvOS))
...
#endif
If you instead are ok with a runtime check, you can inspect the TARGET_OS_SIMULATOR variable (or TARGET_IPHONE_SIMULATOR in iOS 8 and below), which is truthy on a simulator.
Please notice that this is different and slightly more limited than using a preprocessor flag. For instance you won't be able to use it in place where a if/else is syntactically invalid (e.g. outside of functions scopes).
Say, for example, that you want to have different imports on the device and on the simulator. This is impossible with a dynamic check, whereas it's trivial with a static check.
#if (arch(i386) || arch(x86_64)) && os(iOS)
import Foo
#else
import Bar
#endif
Also, since the flag is replaced with a 0 or a 1 by the swift preprocessor, if you directly use it in a if/else expression the compiler will raise a warning about unreachable code.
In order to work around this warning, see one of the other answers.
OUTDATED FOR SWIFT 4.1. Use #if targetEnvironment(simulator) instead. Source
To detect simulator in Swift you can use build configuration:
Define this configuration -D IOS_SIMULATOR in Swift Compiler - Custom Flags > Other Swift Flags
Select Any iOS Simulator SDK in this drop down
Now you could use this statement to detect simulator:
#if IOS_SIMULATOR
print("It's an iOS Simulator")
#else
print("It's a device")
#endif
Also you could extend UIDevice class:
extension UIDevice {
var isSimulator: Bool {
#if IOS_SIMULATOR
return true
#else
return false
#endif
}
}
// Example of usage: UIDevice.current.isSimulator
Updated Info as of February 20, 2018
It looks like #russbishop has an authoritative answer that renders this answer "incorrect" - even though it appeared to work for a long time.
Detect if app is being built for device or simulator in Swift
Previous Answer
Based on #WZW's answer and #Pang's comments, I created a simple utility struct. This solution avoids warning produced by #WZW's answer.
import Foundation
struct Platform {
static var isSimulator: Bool {
return TARGET_OS_SIMULATOR != 0
}
}
Example usage:
if Platform.isSimulator {
print("Running on Simulator")
}
Swift 4
You can now use targetEnvironment(simulator) as an argument.
#if targetEnvironment(simulator)
// Simulator
#else
// Device
#endif
Updated for Xcode 9.3
From Xcode 9.3
#if targetEnvironment(simulator)
Swift supports a new platform condition targetEnvironment with a
single valid argument simulator. Conditional compilation of the form
'#if targetEnvironment(simulator)' can now be used to detect when the build target is a simulator. The Swift compiler will attempt to
detect, warn, and suggest the use of targetEnvironment(simulator) when
evaluating platform conditions that appear to be testing for simulator
environments indirectly, via the existing os() and arch() platform
conditions. (SE-0190)
iOS 9+:
extension UIDevice {
static var isSimulator: Bool {
return NSProcessInfo.processInfo().environment["SIMULATOR_DEVICE_NAME"] != nil
}
}
Swift 3:
extension UIDevice {
static var isSimulator: Bool {
return ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nil
}
}
Before iOS 9:
extension UIDevice {
static var isSimulator: Bool {
return UIDevice.currentDevice().model == "iPhone Simulator"
}
}
Objective-C:
#interface UIDevice (Additions)
- (BOOL)isSimulator;
#end
#implementation UIDevice (Additions)
- (BOOL)isSimulator {
if([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){9, 0, 0}]) {
return [NSProcessInfo processInfo].environment[#"SIMULATOR_DEVICE_NAME"] != nil;
} else {
return [[self model] isEqualToString:#"iPhone Simulator"];
}
}
#end
Let me clarify some things here:
TARGET_OS_SIMULATOR is not set in Swift code in many cases; you may be accidentally getting it imported due to a bridging header but this is brittle and not supported. It also isn't even possible in frameworks. This is why some people are confused about whether this works in Swift.
I strongly advise against using architecture as a substitute for simulator.
To perform dynamic checks:
Checking ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nil is perfectly fine.
You can also get the underlying model being simulated by checking SIMULATOR_MODEL_IDENTIFIER which will return strings like iPhone10,3.
To perform static checks:
Xcode 9.2 & earlier: define your own Swift compilation flag (as shown in other answers).
Xcode 9.3+ use the new targetEnvironment condition:
#if targetEnvironment(simulator)
// for sim only
#else
// for device
#endif
Swift 5.2.4
Xcode 11.7
#if targetEnvironment(simulator)
#endif
Runtime, but simpler than most of the other solutions here:
if TARGET_OS_SIMULATOR != 0 {
// target is current running in the simulator
}
Alternatively, you can just call an Objective-C helper function that returns a boolean that uses the preprocessor macro (especially if you're already mixing in your project).
Edit: Not the best solution, especially as of Xcode 9.3. See HotJard's answer
What works for me since Swift 1.0 is checking for an architecture other than arm:
#if arch(i386) || arch(x86_64)
//simulator
#else
//device
#endif
I hope this extension comes handy.
extension UIDevice {
static var isSimulator: Bool = {
#if targetEnvironment(simulator)
return true
#else
return false
#endif
}()
}
Usage:
if UIDevice.isSimulator {
print("running on simulator")
}
In modern systems:
#if targetEnvironment(simulator)
// sim
#else
// device
#endif
It's dat easy.
TARGET_IPHONE_SIMULATOR is deprecated in iOS 9. TARGET_OS_SIMULATOR is the replacement. Also TARGET_OS_EMBEDDED is available.
From TargetConditionals.h :
#if defined(__GNUC__) && ( defined(__APPLE_CPP__) || defined(__APPLE_CC__) || defined(__MACOS_CLASSIC__) )
. . .
#define TARGET_OS_SIMULATOR 0
#define TARGET_OS_EMBEDDED 1
#define TARGET_IPHONE_SIMULATOR TARGET_OS_SIMULATOR /* deprecated */
#define TARGET_OS_NANO TARGET_OS_WATCH /* deprecated */
Xcode 11, Swift 5
#if !targetEnvironment(macCatalyst)
#if targetEnvironment(simulator)
true
#else
false
#endif
#endif
In Xcode 7.2 (and earlier but I haven't tested how much earlier), you can set a platform specific build flag "-D TARGET_IPHONE_SIMULATOR" for "Any iOS Simulator".
Look in the project build settings under "Swift Compiler - Customer Flags" and then set the flag in "Other Swift Flags". You can set a platform specific flag by clicking the 'plus' icon when you hover over a build configuration.
There are a couple of advantages of doing it this way: 1) You can use the same conditional test ("#if TARGET_IPHONE_SIMULATOR") in your Swift and Objective-C code. 2) You can compile out variables that only apply to each build.
Xcode build settings screenshot
All described here Darwin.TargetConditionals:
https://github.com/apple/swift-corelibs-foundation/blob/master/CoreFoundation/Base.subproj/SwiftRuntime/TargetConditionals.h
TARGET_OS_SIMULATOR - Generated code will run under a simulator
Use this below code:
#if targetEnvironment(simulator)
// Simulator
#else
// Device
#endif
Works for Swift 4 and Xcode 9.4.1
Here is an Xcode 11 Swift example based off of HotJard's awesome answer above, this also adds an isDevice Bool and uses SIMULATOR_UDID instead of name. Variable assignments are done on each line so that you can more easily examine them in the debugger if you choose to.
import Foundation
// Extensions to UIDevice based on ProcessInfo.processInfo.environment keys
// to determine if the app is running on an actual device or the Simulator.
#objc extension UIDevice {
static var isSimulator: Bool {
let environment = ProcessInfo.processInfo.environment
let isSimulator = environment["SIMULATOR_UDID"] != nil
return isSimulator
}
static var isDevice: Bool {
let environment = ProcessInfo.processInfo.environment
let isDevice = environment["SIMULATOR_UDID"] == nil
return isDevice
}
}
There also is the dictionary entry of DTPlatformName which should contain simulator.
In addition to other answers.
In Objective-c, Just make sure you included TargetConditionals.
#include <TargetConditionals.h>
before using TARGET_OS_SIMULATOR.
I used this below code in Swift 3
if TARGET_IPHONE_SIMULATOR == 1 {
//simulator
} else {
//device
}
Swift 4:
Currently, I prefer to use the ProcessInfo class to know if the device is a simulator and which kind of device is in use:
if let simModelCode = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] {
print("yes is a simulator :\(simModelCode)")
}
But, as you know, simModelCode isn't a comfortable code to understand immediatly which kind of simulator was launched so, if your need, you can try to see this other SO answer to determine the current iPhone/device model and to have a more human readable string.
I do not know if this will be useful to anyone, but at least the current version of M1 macs do not seem to pass the SIMULATOR_MODEL_IDENTIFIER into NSProcessInfo
I used
BOOL isMobile = [[NSProcessInfo processInfo].environment[#"USER"] isEqual:#"mobile"];
and swift equivalent. This may be fragile but it works.

How to detect if app is being built for device or simulator in Swift

In Objective-C we can know if an app is being built for device or simulator using macros:
#if TARGET_IPHONE_SIMULATOR
// Simulator
#else
// Device
#endif
These are compile time macros and not available at runtime.
How can I achieve the same in Swift?
Update 30/01/19
While this answer may work, the recommended solution for a static check (as clarified by several Apple engineers) is to define a custom compiler flag targeting iOS Simulators. For detailed instructions on how to do to it, see #mbelsky's answer.
Original answer
If you need a static check (e.g. not a runtime if/else) you can't detect the simulator directly, but you can detect iOS on a desktop architecture like follows
#if (arch(i386) || arch(x86_64)) && os(iOS)
...
#endif
After Swift 4.1 version
Latest use, now directly for all in one condition for all types of simulators need to apply only one condition -
#if targetEnvironment(simulator)
// your simulator code
#else
// your real device code
#endif
For more clarification, you can check Swift proposal SE-0190
For older version -
Clearly, this is false on a device, but it returns true for the iOS Simulator, as specified in the documentation:
The arch(i386) build configuration returns true when the code is compiled for the 32–bit iOS simulator.
If you are developing for a simulator other than iOS, you can simply vary the os parameter: e.g.
Detect the watchOS simulator
#if (arch(i386) || arch(x86_64)) && os(watchOS)
...
#endif
Detect the tvOS simulator
#if (arch(i386) || arch(x86_64)) && os(tvOS)
...
#endif
Or, even, detect any simulator
#if (arch(i386) || arch(x86_64)) && (os(iOS) || os(watchOS) || os(tvOS))
...
#endif
If you instead are ok with a runtime check, you can inspect the TARGET_OS_SIMULATOR variable (or TARGET_IPHONE_SIMULATOR in iOS 8 and below), which is truthy on a simulator.
Please notice that this is different and slightly more limited than using a preprocessor flag. For instance you won't be able to use it in place where a if/else is syntactically invalid (e.g. outside of functions scopes).
Say, for example, that you want to have different imports on the device and on the simulator. This is impossible with a dynamic check, whereas it's trivial with a static check.
#if (arch(i386) || arch(x86_64)) && os(iOS)
import Foo
#else
import Bar
#endif
Also, since the flag is replaced with a 0 or a 1 by the swift preprocessor, if you directly use it in a if/else expression the compiler will raise a warning about unreachable code.
In order to work around this warning, see one of the other answers.
OUTDATED FOR SWIFT 4.1. Use #if targetEnvironment(simulator) instead. Source
To detect simulator in Swift you can use build configuration:
Define this configuration -D IOS_SIMULATOR in Swift Compiler - Custom Flags > Other Swift Flags
Select Any iOS Simulator SDK in this drop down
Now you could use this statement to detect simulator:
#if IOS_SIMULATOR
print("It's an iOS Simulator")
#else
print("It's a device")
#endif
Also you could extend UIDevice class:
extension UIDevice {
var isSimulator: Bool {
#if IOS_SIMULATOR
return true
#else
return false
#endif
}
}
// Example of usage: UIDevice.current.isSimulator
Updated Info as of February 20, 2018
It looks like #russbishop has an authoritative answer that renders this answer "incorrect" - even though it appeared to work for a long time.
Detect if app is being built for device or simulator in Swift
Previous Answer
Based on #WZW's answer and #Pang's comments, I created a simple utility struct. This solution avoids warning produced by #WZW's answer.
import Foundation
struct Platform {
static var isSimulator: Bool {
return TARGET_OS_SIMULATOR != 0
}
}
Example usage:
if Platform.isSimulator {
print("Running on Simulator")
}
Swift 4
You can now use targetEnvironment(simulator) as an argument.
#if targetEnvironment(simulator)
// Simulator
#else
// Device
#endif
Updated for Xcode 9.3
From Xcode 9.3
#if targetEnvironment(simulator)
Swift supports a new platform condition targetEnvironment with a
single valid argument simulator. Conditional compilation of the form
'#if targetEnvironment(simulator)' can now be used to detect when the build target is a simulator. The Swift compiler will attempt to
detect, warn, and suggest the use of targetEnvironment(simulator) when
evaluating platform conditions that appear to be testing for simulator
environments indirectly, via the existing os() and arch() platform
conditions. (SE-0190)
iOS 9+:
extension UIDevice {
static var isSimulator: Bool {
return NSProcessInfo.processInfo().environment["SIMULATOR_DEVICE_NAME"] != nil
}
}
Swift 3:
extension UIDevice {
static var isSimulator: Bool {
return ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nil
}
}
Before iOS 9:
extension UIDevice {
static var isSimulator: Bool {
return UIDevice.currentDevice().model == "iPhone Simulator"
}
}
Objective-C:
#interface UIDevice (Additions)
- (BOOL)isSimulator;
#end
#implementation UIDevice (Additions)
- (BOOL)isSimulator {
if([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:(NSOperatingSystemVersion){9, 0, 0}]) {
return [NSProcessInfo processInfo].environment[#"SIMULATOR_DEVICE_NAME"] != nil;
} else {
return [[self model] isEqualToString:#"iPhone Simulator"];
}
}
#end
Let me clarify some things here:
TARGET_OS_SIMULATOR is not set in Swift code in many cases; you may be accidentally getting it imported due to a bridging header but this is brittle and not supported. It also isn't even possible in frameworks. This is why some people are confused about whether this works in Swift.
I strongly advise against using architecture as a substitute for simulator.
To perform dynamic checks:
Checking ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nil is perfectly fine.
You can also get the underlying model being simulated by checking SIMULATOR_MODEL_IDENTIFIER which will return strings like iPhone10,3.
To perform static checks:
Xcode 9.2 & earlier: define your own Swift compilation flag (as shown in other answers).
Xcode 9.3+ use the new targetEnvironment condition:
#if targetEnvironment(simulator)
// for sim only
#else
// for device
#endif
Swift 5.2.4
Xcode 11.7
#if targetEnvironment(simulator)
#endif
Runtime, but simpler than most of the other solutions here:
if TARGET_OS_SIMULATOR != 0 {
// target is current running in the simulator
}
Alternatively, you can just call an Objective-C helper function that returns a boolean that uses the preprocessor macro (especially if you're already mixing in your project).
Edit: Not the best solution, especially as of Xcode 9.3. See HotJard's answer
What works for me since Swift 1.0 is checking for an architecture other than arm:
#if arch(i386) || arch(x86_64)
//simulator
#else
//device
#endif
I hope this extension comes handy.
extension UIDevice {
static var isSimulator: Bool = {
#if targetEnvironment(simulator)
return true
#else
return false
#endif
}()
}
Usage:
if UIDevice.isSimulator {
print("running on simulator")
}
In modern systems:
#if targetEnvironment(simulator)
// sim
#else
// device
#endif
It's dat easy.
TARGET_IPHONE_SIMULATOR is deprecated in iOS 9. TARGET_OS_SIMULATOR is the replacement. Also TARGET_OS_EMBEDDED is available.
From TargetConditionals.h :
#if defined(__GNUC__) && ( defined(__APPLE_CPP__) || defined(__APPLE_CC__) || defined(__MACOS_CLASSIC__) )
. . .
#define TARGET_OS_SIMULATOR 0
#define TARGET_OS_EMBEDDED 1
#define TARGET_IPHONE_SIMULATOR TARGET_OS_SIMULATOR /* deprecated */
#define TARGET_OS_NANO TARGET_OS_WATCH /* deprecated */
Xcode 11, Swift 5
#if !targetEnvironment(macCatalyst)
#if targetEnvironment(simulator)
true
#else
false
#endif
#endif
In Xcode 7.2 (and earlier but I haven't tested how much earlier), you can set a platform specific build flag "-D TARGET_IPHONE_SIMULATOR" for "Any iOS Simulator".
Look in the project build settings under "Swift Compiler - Customer Flags" and then set the flag in "Other Swift Flags". You can set a platform specific flag by clicking the 'plus' icon when you hover over a build configuration.
There are a couple of advantages of doing it this way: 1) You can use the same conditional test ("#if TARGET_IPHONE_SIMULATOR") in your Swift and Objective-C code. 2) You can compile out variables that only apply to each build.
Xcode build settings screenshot
All described here Darwin.TargetConditionals:
https://github.com/apple/swift-corelibs-foundation/blob/master/CoreFoundation/Base.subproj/SwiftRuntime/TargetConditionals.h
TARGET_OS_SIMULATOR - Generated code will run under a simulator
Use this below code:
#if targetEnvironment(simulator)
// Simulator
#else
// Device
#endif
Works for Swift 4 and Xcode 9.4.1
Here is an Xcode 11 Swift example based off of HotJard's awesome answer above, this also adds an isDevice Bool and uses SIMULATOR_UDID instead of name. Variable assignments are done on each line so that you can more easily examine them in the debugger if you choose to.
import Foundation
// Extensions to UIDevice based on ProcessInfo.processInfo.environment keys
// to determine if the app is running on an actual device or the Simulator.
#objc extension UIDevice {
static var isSimulator: Bool {
let environment = ProcessInfo.processInfo.environment
let isSimulator = environment["SIMULATOR_UDID"] != nil
return isSimulator
}
static var isDevice: Bool {
let environment = ProcessInfo.processInfo.environment
let isDevice = environment["SIMULATOR_UDID"] == nil
return isDevice
}
}
There also is the dictionary entry of DTPlatformName which should contain simulator.
In addition to other answers.
In Objective-c, Just make sure you included TargetConditionals.
#include <TargetConditionals.h>
before using TARGET_OS_SIMULATOR.
I used this below code in Swift 3
if TARGET_IPHONE_SIMULATOR == 1 {
//simulator
} else {
//device
}
Swift 4:
Currently, I prefer to use the ProcessInfo class to know if the device is a simulator and which kind of device is in use:
if let simModelCode = ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] {
print("yes is a simulator :\(simModelCode)")
}
But, as you know, simModelCode isn't a comfortable code to understand immediatly which kind of simulator was launched so, if your need, you can try to see this other SO answer to determine the current iPhone/device model and to have a more human readable string.
I do not know if this will be useful to anyone, but at least the current version of M1 macs do not seem to pass the SIMULATOR_MODEL_IDENTIFIER into NSProcessInfo
I used
BOOL isMobile = [[NSProcessInfo processInfo].environment[#"USER"] isEqual:#"mobile"];
and swift equivalent. This may be fragile but it works.

Can NSLog be disabled from appearing in device's console? [duplicate]

This question already has answers here:
How do I disable NSLog?
(15 answers)
Closed 8 years ago.
I have a builded application that is running on device. I open device's console view in XCode's Organizer window. I assume (for the sake of this question) that NSLog(#"Some string") gets called.
Is there any way, may be an option in device, or application's settings, that would disable this log from appearing in console?
Edit: I'm not interested in replacing NSLog by other solution that can achieve this effect. The purpose of this question is to fully understand NSLog's functionality.
(Thanks to #MartinR for encouraging me to pull my finger out on this answer and to correctly identify that you cannot just close stdout/stderr, as the next open() will re-use those file descriptors, but to redirect stdout/stderr to the infamous /dev/null).
logControl.h:
#pragma once
extern void stopLogging();
extern void startLogging();
logControl.c:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
static int loggingStopped = 0;
static int oldStdout = -1;
static int oldStderr = -1;
void stopLogging() {
if (!loggingStopped) {
oldStdout = dup(1);
oldStderr = dup(2);
int devNull = open("/dev/null", O_WRONLY);
dup2(devNull, 1);
dup2(devNull, 2);
close(devNull);
loggingStopped = 1;
}
}
void startLogging() {
if (loggingStopped && oldStdout >= 0 && oldStderr >= 0) {
dup2(oldStdout, 1);
close(oldStdout);
oldStdout = -1;
dup2(oldStderr, 2);
close(oldStderr);
oldStderr = -1;
loggingStopped = 0;
}
}
This works at runtime, not compile time, which I believe is what you are asking. Simply call stopLogging() or startLogging() as required.
NOTE: There is no error-checking to speak of, so that could be improved perhaps.
Add this line given below in your .pch file in Xcode.
#define NSLog(...)
It will disable all NSLogs.
for more alternatives see the link
#if TARGET_IPHONE_SIMULATOR
//Simulator
#else
// Device
#define NSLog
#endif
Add this in your .pch file this will disable NSLog only for device alone not for simulator.
Add below code to .pch file
#ifdef DEBUG
# define NSLog(...) NSLog(__VA_ARGS__)
#else
# define NSLog(...) /* */
#endif
And in Build Settings, search "Preprocessor Macros" and remove "DEBUG=1" written in it.
and Thats it, you will not see any logs in your console now.

Any way to differentiate between code running on a iPhone simulator vs live device? [duplicate]

This question already has answers here:
How can I programmatically determine if my app is running in the iphone simulator?
(21 answers)
Closed 9 years ago.
I have some logic in my iOS application that I would like to execute differently when testing on an the iPhone simulator vs when its running on a live device.
Is there any way to determine in objective C whether the logic is being executed on one or the other?
Currently, I comment out some code before I deploy to my physical iPhone. Not convenient.
The reason behind the (slightly) different execution paths btw, is that my application utilizes data that is time/date dependent. On the simulator i have a static data set loaded so my testing takes that into account (i.e doesnt use current systems dates etc).
On the live device, the data is always fresh so no such issues.
It really should be known at compile time, as per TARGET_IPHONE_SIMULATOR macro. If you need to do a runtime check:
bool is_simulator()
{
NSString *model = [[UIDevice currentDevice] model];
return [model isEqualToString:#"iPhone Simulator"];
}
or without using objective C you could perhaps use sysctl as follows:
static int32_t sysctl_int32(const char* name)
{
int32_t val = 0;
size_t size = sizeof(val);
sysctlbyname(name, &val, &size, NULL, 0);
return val;
}
bool is_simulator()
{
int32_t cpu_type = sysctl_int32("hw.cputype");
return (cpu_type == CPU_TYPE_I386 || cpu_type == CPU_TYPE_X86_64)
}
Try
if (TARGET_IPHONE_SIMULATOR){
//Running on simulator
}else{
//Real one
}
Use
#if TARGET_IPHONE_SIMULATOR
// Using Simulator
#else
// Using device

Resources