Is it possible to use the new features of iOS9 such as NSUserActivity and CoreSpotlight, but still set my Development Target to 8.2 so that users with iOS8 can still use the app?
I assume I would just need to do a iOS version number check or use respondsToSelector:.
Is this correct?
Yes, I do it in one of my apps (actually have a deployment target of iOS 7). It's trivial to do. Just make sure the CSSearchableIndex class exists, make the CoreSpotlight framework optional, and write your code properly to prevent the newer APIs from being run on devices with earlier versions of iOS.
You can even guard the code so it compiles under Xcode 6 if you had some reason to do so.
Example:
// Ensure it only compiles with the Base SDK of iOS 9 or later
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000
// Make sure the class is available and the device supports CoreSpotlight
if ([CSSearchableIndex class] && [CSSearchableIndex isIndexingAvailable]) {
dispatch_async(_someBGQueue, ^{
NSString *someName = #"Some Name";
CSSearchableIndex *index = [[CSSearchableIndex alloc] initWithName:someName];
// rest of needed code to index with Core Spotlight
});
}
#endif
In your app delegate:
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 90000
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void(^)(NSArray *restorableObjects))restorationHandler {
if ([[userActivity activityType] isEqualToString:CSSearchableItemActionType]) {
// This activity represents an item indexed using Core Spotlight, so restore the context related to the unique identifier.
// The unique identifier of the Core Spotlight item is set in the activity’s userInfo for the key CSSearchableItemActivityIdentifier.
NSString *uniqueIdentifier = [userActivity.userInfo objectForKey:CSSearchableItemActivityIdentifier];
if (uniqueIdentifier) {
// process the identifier as needed
}
}
return NO;
}
#endif
I am developing an app that makes use of a third party library that presents its own view controller. All that is available to me is a .a library and a header file. My app only runs in portrait mode but when I have the phone in a landscape orientation and present the view controller from the library, the app crashes with an error stating that:
"No supported orientation matches that of the application."
My guess is that they have written the following code:
- (BOOL)shouldAutorotate {
return YES;
}
- (NSUInteger)supportedInterfaceOrientations {
// ATTENTION! Only return orientation MASK values
// return UIInterfaceOrientationPortrait;
return UIInterfaceOrientationMaskPortrait | UIInterfaceOrientationMaskLandscapeLeft | UIInterfaceOrientationMaskLandscapeLeft;
}
If that is the case, I will likely need to override those methods to tell the OS not to rotate and that only portrait is supported. How can I go about doing this?
The only possibility I can think of is to swizzle the method for that view controller but this seems like a dangerous approach according to a couple of SO posts.
You can always do the following (as an example of locking a ViewController in Portrait mode):
1) Setup a property on your application delegate header file ... #property (nonatomic) BOOL lockScreenPortraitOnly;
2) In the application delegate implementation file add the following method:
-(NSUInteger)application:(UIApplication *)application supportedInterfaceOrientationsForWindows:(UIWindow *)window
{
if (!self.lockScreenPortraitOnly)
return UIInterfaceOrientationMaskAll; // or, whatever you wish to support
else
return UIInterfaceOrientationMaskPortrait;
}
3) From within your ViewController add the following:
-(void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
((EliotsApplicationDelegateType *)[UIApplication sharedApplication].delegate).lockScreenPortraitOnly = YES;
}
-(void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
((EliotsApplicationDelegateType *)[UIApplication sharedApplication].delegate).lockScreenPortraitOnly = NO;
}
Now, it doesn't matter what's in Plist or what a 3rd party library is doing as the "window" is the most root, and as such, it controls everything else in the hierarchy. Obviously in my example it is assumed that your VC is invoked before your 3rd-party library code gets spun up (adjust, season, and bake as needed).
Hope this helps.
I'd say go ahead and swizzle it. You'll need to be careful when upgrading the library to test this particular item. The danger in swizzling usually comes with swizzling Apple's APIs as they can be deprecated or their behaviour changed at any release (which could subsequently break your app for users).
I've been searching round for a while to find a way to determine if an iOS external screen is cable connected OR over the air and can't find any obvious way.
I've seen the unofficial AirPlay specs HERE, but can't see any obvious way of detecting it.
Does anybody know if this can be done using legit / 'public' API.
Yes, there actually is a way.
Somewhere in your app, create an instance of MPVolumeView. Hold on to in in some instance variable. You don't have to add it as a subview to anything, it simply has to exist.
Then subscribe to the MPVolumeViewWirelessRouteActiveDidChangeNotification like so:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleWirelessRouteActiveDidChangeNotification:)
name:MPVolumeViewWirelessRouteActiveDidChangeNotification
object:nil];
Add these methods to find out about the state of external displays:
- (BOOL)isAirPlayConnected
{
return _volumeView.isWirelessRouteActive;
}
- (BOOL)isAirPlayMirroringActive
{
if ([self isAirPlayConnected]) {
NSArray *screens = [UIScreen screens];
if ([screens count] > 1) {
return [screens[1] mirroredScreen] == [UIScreen mainScreen];
}
}
return NO;
}
- (BOOL)isAirPlayPlaybackActive
{
return [self isAirPlayConnected] && ![self isAirPlayMirroringActive];
}
- (BOOL)isExternalPlaybackActive
{
if ([self isAirPlayPlaybackActive]) {
return YES;
} else {
NSArray *screens = [UIScreen screens];
if ([screens count] > 1) {
return [screens[1] mirroredScreen] != [UIScreen mainScreen];
}
}
return NO;
}
Additionally you can check for the UIScreenDidConnectNotification and UIScreenDidDisconnectNotification notifications. Armed with all of this, you can tell if you are connected to AirPlay, if AirPlay Mirroring is active, if you AirPlay playback (not mirroring) is active or if you are using any external screen without mirroring.
I don't believe there is any public API for this. I would guess that, in Apple's view, this is not your app's concern. It's up to the user what they do with your app's screen: they can screenshot it and email it to everyone, or just plug a wire into a projector and show it on the side of a building. Trying to prevent these from within an app isn't likely to be possible.
You can achieve some of this, however, with Apple's Configurator tool. It allows you to configure, say, a company-owned iOS device to allow AirPlay only to certain hosts. It can also prevent screenshots and other things that might be helpful. I don't know if you can get exactly what you're looking for, but it might be something to look in to if you have some level of control over the devices this app is going to be installed on.
I'm a little bit lost in the bitwise ;)
My goal is to retrieve the whole set of orientations supported by an application and to test each result value to update a custom variable.
My problem is that I don't know how to make the comparison (I got a conversion/test problem...)
First I read this article : Testing for bitwise Enum values
But it doesn't bring me the light...
Let say I have the following orientation declare for my application (following is the log output for my variable supportedOrientations) :
supported orientations = (
UIInterfaceOrientationPortrait
)
So my first attempt was to try some test on integer values but it does not work (even if the application is declared to be in portrait mode the test return 'false') :
NSArray *supportedOrientations = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"UISupportedInterfaceOrientations"];
NSLog(#"[supported orientations = %#", supportedOrientations);
// for clarity just make a test on the first orientation we found
if ((NSInteger)supportedOrientations[0] == UIInterfaceOrientationPortrait) {
NSLog(#"We detect Portrait mode!");
}
My second attempt was to try the bitwise thing but this time it always return 'true' (even if the supported orientation is not UIInterfaceOrientationPortrait). :
NSArray *supportedOrientations = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"UISupportedInterfaceOrientations"];
NSLog(#"[supported orientations = %#", supportedOrientations);
// for clarity just make a test on the first orientation we found
if ((NSInteger)supportedOrientations[0] | UIInterfaceOrientationPortrait) { // <-- I also test with UIInterfaceOrientationMaskPortrait but no more success
NSLog(#"We detect Portrait mode!");
}
So my question is :
How to test the orientation in my case?
Is it a way to use a test by using a bitwise thing (using | operand)?
The official doc says that UISupportedInterfaceOrientations is an array of strings.
https://developer.apple.com/library/ios/documentation/general/Reference/InfoPlistKeyReference/Articles/iPhoneOSKeys.html#//apple_ref/doc/uid/TP40009252-SW10
So the solution is to use NSString comparison for each element found in the array.
NSArray *supportedOrientations = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"UISupportedInterfaceOrientations"];
for (NSString *orientation in supportedOrientations) {
if ([orientation isEqualToString:#"UIInterfaceOrientationPortrait"] ||
[orientation isEqualToString:#"UIInterfaceOrientationPortraitUpsideDown"]) {
NSLog(#"*** We detect Portrait mode!");
} else if ([orientation isEqualToString:#"UIInterfaceOrientationLandscapeLeft"] ||
[orientation isEqualToString:#"UIInterfaceOrientationLandscapeRight"]) {
NSLog(#"*** We detect Landscape mode!");
}
}
Note that doing like this we didn't take advantage of the enum values (of type UIInterfaceOrientation), but it works!
As the question states, I would mainly like to know whether or not my code is running in the simulator, but would also be interested in knowing the specific iphone version that is running or being simulated.
EDIT: I added the word 'programmatically' to the question name. The point of my question is to be able to dynamically include / exclude code depending on which version / simulator is running, so I'd really be looking for something like a pre-processor directive that can provide me this info.
Already asked, but with a very different title.
What #defines are set up by Xcode when compiling for iPhone
I'll repeat my answer from there:
It's in the SDK docs under "Compiling source code conditionally"
The relevant definition is TARGET_OS_SIMULATOR, which is defined in /usr/include/TargetConditionals.h within the iOS framework. On earlier versions of the toolchain, you had to write:
#include "TargetConditionals.h"
but this is no longer necessary on the current (Xcode 6/iOS8) toolchain.
So, for example, if you want to check that you are running on device, you should do
#if TARGET_OS_SIMULATOR
// Simulator-specific code
#else
// Device-specific code
#endif
depending on which is appropriate for your use-case.
Updated code:
This is purported to work officially.
#if TARGET_IPHONE_SIMULATOR
NSString *hello = #"Hello, iPhone simulator!";
#elif TARGET_OS_IPHONE
NSString *hello = #"Hello, device!";
#else
NSString *hello = #"Hello, unknown target!";
#endif
Original post (since deprecated)
This code will tell you if you are running in a simulator.
#ifdef __i386__
NSLog(#"Running in the simulator");
#else
NSLog(#"Running on a device");
#endif
Not pre-processor directive, but this was what I was looking for when i came to this question;
NSString *model = [[UIDevice currentDevice] model];
if ([model isEqualToString:#"iPhone Simulator"]) {
//device is simulator
}
There is a better way now in Swift.
As of Xcode 9.3 and newer, you can use #if targetEnvironment(simulator) to check.
#if targetEnvironment(simulator)
//Your simulator code
#endif
The best way to do this is:
#if TARGET_IPHONE_SIMULATOR
and not
#ifdef TARGET_IPHONE_SIMULATOR
since its always defined: 0 or 1
In case of Swift we can implement following
We can create struct which allows you to create a structured data
struct Platform {
static var isSimulator: Bool {
#if targetEnvironment(simulator)
// We're on the simulator
return true
#else
// We're on a device
return false
#endif
}
}
Then If we wanted to Detect if app is being built for device or simulator in Swift then .
if Platform.isSimulator {
// Do one thing
} else {
// Do the other
}
Works for Swift 4.1 and newer and Xcode 9.3 and newer
Use this code:
#if targetEnvironment(simulator)
// Simulator
#else
// Device
#endif
All those answer are good, but it somehow confuses newbie like me as it does not clarify compile check and runtime check. Preprocessor are before compile time, but we should make it clearer
This blog article shows How to detect the iPhone simulator? clearly
Runtime
First of all, let’s shortly discuss. UIDevice provides you already information about the device
[[UIDevice currentDevice] model]
will return you “iPhone Simulator” or “iPhone” according to where the app is running.
Compile time
However what you want is to use compile time defines. Why? Because you compile your app strictly to be run either inside the Simulator or on the device. Apple makes a define called TARGET_IPHONE_SIMULATOR. So let’s look at the code :
#if TARGET_IPHONE_SIMULATOR
NSLog(#"Running in Simulator - no app store or giro");
#endif
For Swift 4.2 / Xcode 10
I created an extension on UIDevice, so I can easily ask for if the simulator is running.
// UIDevice+CheckSimulator.swift
import UIKit
extension UIDevice {
/// Checks if the current device that runs the app is xCode's simulator
static func isSimulator() -> Bool {
#if targetEnvironment(simulator)
return true
#else
return false
#endif
}
}
In my AppDelegate for example I use this method to decide wether registering for remote notification is necessary, which is not possible for the simulator.
// CHECK FOR REAL DEVICE / OR SIMULATOR
if UIDevice.isSimulator() == false {
// REGISTER FOR SILENT REMOTE NOTIFICATION
application.registerForRemoteNotifications()
}
The previous answers are a little dated. I found that all you need to do is query the TARGET_IPHONE_SIMULATOR macro (no need to include any other header files [assuming you are coding for iOS]).
I attempted TARGET_OS_IPHONE but it returned the same value (1) when running on an actual device and simulator, that's why I recommend using TARGET_IPHONE_SIMULATOR instead.
In swift :
#if (arch(i386) || arch(x86_64))
...
#endif
From Detect if app is being built for device or simulator in Swift
Has anyone considered the answer provided here?
I suppose the objective-c equivalent would be
+ (BOOL)isSimulator {
NSOperatingSystemVersion ios9 = {9, 0, 0};
NSProcessInfo *processInfo = [NSProcessInfo processInfo];
if ([processInfo isOperatingSystemAtLeastVersion:ios9]) {
NSDictionary<NSString *, NSString *> *environment = [processInfo environment];
NSString *simulator = [environment objectForKey:#"SIMULATOR_DEVICE_NAME"];
return simulator != nil;
} else {
UIDevice *currentDevice = [UIDevice currentDevice];
return ([currentDevice.model rangeOfString:#"Simulator"].location != NSNotFound);
}
}
I had the same problem, both TARGET_IPHONE_SIMULATOR and TARGET_OS_IPHONE are always defined, and are set to 1. Pete's solution works, of course, but if you ever happen to build on something other than intel (unlikely, but who knows), here's something that's safe as long as the iphone hardware doesn't change (so your code will always work for the iphones currently out there):
#if defined __arm__ || defined __thumb__
#undef TARGET_IPHONE_SIMULATOR
#define TARGET_OS_IPHONE
#else
#define TARGET_IPHONE_SIMULATOR 1
#undef TARGET_OS_IPHONE
#endif
Put that somewhere convenient, and then pretend that the TARGET_* constants were defined correctly.
To include all types of "simulators"
NSString *model = [[UIDevice currentDevice] model];
if([model rangeOfString:#"Simulator" options:NSCaseInsensitiveSearch].location !=NSNotFound)
{
// we are running in a simulator
}
With Swift 4.2 (Xcode 10), we can do this
#if targetEnvironment(simulator)
//simulator code
#else
#warning("Not compiling for simulator")
#endif
My answer is based on #Daniel Magnusson answer and comments of #Nuthatch and #n.Drake. and I write it to save some time for swift users working on iOS9 and onwards.
This is what worked for me:
if UIDevice.currentDevice().name.hasSuffix("Simulator"){
//Code executing on Simulator
} else{
//Code executing on Device
}
/// Returns true if its simulator and not a device
public static var isSimulator: Bool {
#if (arch(i386) || arch(x86_64)) && os(iOS)
return true
#else
return false
#endif
}
Apple has added support for checking the app is targeted for the simulator with the following:
#if targetEnvironment(simulator)
let DEVICE_IS_SIMULATOR = true
#else
let DEVICE_IS_SIMULATOR = false
#endif
if nothing worked, try this
public struct Platform {
public static var isSimulator: Bool {
return TARGET_OS_SIMULATOR != 0 // Use this line in Xcode 7 or newer
}
}
This worked for me best
NSString *name = [[UIDevice currentDevice] name];
if ([name isEqualToString:#"iPhone Simulator"]) {
}
In my opinion, the answer (presented above and repeated below):
NSString *model = [[UIDevice currentDevice] model];
if ([model isEqualToString:#"iPhone Simulator"]) {
//device is simulator
}
is the best answer because it is obviously executed at RUNTIME versus being a COMPILE DIRECTIVE.