iOS7 specific code in iOS6 - ios

I need to use iOS7 specific code in certain places, usually this has not caused much problem until now. I tried some different approaches for the first if statement, the one below seams to be the recommended way. None works. The error i get is this:
dyld: Symbol not found: _UITransitionContextToViewControllerKey
Referenced from: /Users/pese/Library/Application Support/iPhone Simulator/6.1/Applications/0A4B5156-84D8-41DE-C9D1-2E4C9DB38983/aaaa.app/aaaa
Expected in: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator6.1.sdk/System/Library/Frameworks/UIKit.framework/UIKit
in /Users/pese/Library/Application Support/iPhone Simulator/6.1/Applications/0A4B5156-84D8-41DE-C9D1-2E4C9DB38983/aaaa.app/aaaa
Program ended with exit code: 0
And my code:
if ( &UITransitionContextToViewControllerKey != nil )
{
id<UIViewControllerTransitionCoordinator> tc = self.topViewController.transitionCoordinator;
[tc animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext> context) {
CGRect newRect = _inRect;
if ([context viewControllerForKey:UITransitionContextToViewControllerKey] == [self.viewControllers objectAtIndex:0])
{
newRect = _outRect;
}
_backButton.frame = newRect;
} completion:^(id<UIViewControllerTransitionCoordinatorContext> context) {
BOOL enableSwipeToGoBack = YES;
if ([context viewControllerForKey:UITransitionContextToViewControllerKey] == [self.viewControllers objectAtIndex:0] && ![context isCancelled])
{
enableSwipeToGoBack = NO;
}
self.interactivePopGestureRecognizer.enabled = enableSwipeToGoBack;
}];
}
If i just put NO in the if statement it works but i guess that the compiler removes the code during compilation. If i replace the two UITransitionContextToViewControllerKey with nil it also works. Also the symbol causing the error is defined in UIKit/UIViewControllerTransitioning.h and look like this:
UIKIT_EXTERN NSString *const UITransitionContextToViewControllerKey NS_AVAILABLE_IOS(7_0);
Any help would be greatly appreciated.
SOLUTION:
Make UIKit framework optional and change if test to:
NSString * const *exists = &UITransitionContextToViewControllerKey;
if ( exists != NULL )
....

As others have mentioned, testing the features is typically better, but you can use a compiler directive to conditionally compile based on the OS version.
#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
// target is iOS
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 70000
// target is lower than iOS 7.0
NSLog(#"This message should only appear if iOS version is 6.x or lower");
#else
// target is at least iOS 7.0
#endif
#endif

Related

Skobbler didFinishRouteCalculationWithInfo array exception

Getting an exception when using Skobbler Maps in iOS. Seems like a concurrency exception resulting in an invalid array access, but since it's a problem with the provided code from Skobbler, not sure how the best way to fix their broken code.
See the line marked "HERE!!!" in the code below:
File: SKTNavigationManager.m
- (void)routingService:(SKRoutingService *)routingService didFinishRouteCalculationWithInfo:(SKRouteInformation *)routeInformation {
dispatch_async(dispatch_get_main_queue(), ^{
__block BOOL routeExists = NO;
[_calculatedRoutes enumerateObjectsUsingBlock:^(SKRouteInformation *obj, NSUInteger idx, BOOL *stop) {
if (routeInformation.routeID == obj.routeID) {
routeExists = YES;
*stop = YES;
}
}];
if (!routeInformation.corridorIsDownloaded || routeExists || _calculatedRoutes.count == _configuration.numberOfRoutes) {
return;
}
//are we still calculating the routes?
if ([self hasState:SKTNavigationStateCalculatingRoute]) {
[_calculatedRoutes addObject:routeInformation];
//stop progress for the calculated route
SKTRouteProgressView *progressVIew = _mainView.calculatingRouteView.progressViews[_calculatedRoutes.count - 1]; // HERE!!!
[progressVIew startProgress];
//show the info for the calculated route
[_mainView.calculatingRouteView showInfoViewAtIndex:_calculatedRoutes.count - 1];
[self updateCalculatedRouteInformationAtIndex:_calculatedRoutes.count - 1];
//start progress for next route if needed
if (_calculatedRoutes.count < _mainView.calculatingRouteView.numberOfRoutes) {
SKTRouteProgressView *progressVIew = _mainView.calculatingRouteView.progressViews[_calculatedRoutes.count];
[progressVIew startProgress];
}
if (!_selectedRoute) {
_selectedRoute = routeInformation;
[SKRoutingService sharedInstance].mainRouteId = _selectedRoute.routeID;
[self zoomOnSelectedRoute];
_mainView.calculatingRouteView.selectedInfoIndex = 0;
_mainView.calculatingRouteView.startButton.hidden = NO;
}
} else if ([self hasState:SKTNavigationStateRerouting]) { //nope, we're being rerouted
_selectedRoute = routeInformation;
_navigationInfo.currentDTA = _selectedRoute.distance;
[self updateDTA];
_navigationInfo.currentETA = _selectedRoute.estimatedTime;
[self updateETA];
[SKRoutingService sharedInstance].mainRouteId = _selectedRoute.routeID;
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(removeReroutingState) object:nil];
[self performSelector:#selector(removeReroutingState) withObject:nil afterDelay:kMinReroutingDisplayTime];
}
});
}
The exception reported is ...
8 libobjc.A.dylib - objc_exception_throw + 332 (objc-exception.mm:568)
9 CoreFoundation - [__NSArrayM objectAtIndex:] + 240 (NSArray.m:410)
10 MyApp -[SKTNavigationManager routingService:didFinishRouteCalculationWithInfo:]_block_invoke + 676 (SKTNavigationManager.m:463)
Any ideas? I could do a check on the size of _calculatedRoutes before reading from the progressViews array, but the problem I have is there is additional code after the line that accesses both. In other words, I can avoid the line, but how do I fix the method to work correctly?
The code inside SKTNavigationManager (and inside the whole SDKTools project) is offered as open code - it is not part of the SDK itself but constitutes some demo code/ helper classes that should help you address common scenarios (building a simple navigation UI, dealing with offline maps, etc.).
Some developers use this code as documentation, others use it "as is" in their projects, others start from it and customize it to their liking.
In you particular case I'm not sure how you did arrive at this inconsistent state (with the vanilla code, in the demo project I cannot replicate this) - if you believe that you have a concurrency issue feel free to insert additional checks or synchronization mechanisms.

compiling google calendar API - iOS in swift [duplicate]

I am using the google calendar api and I am getting two errors.
GTMGatherInputStream.m:25:13: Multiple methods named 'initWithArray:' found
#import "GTMGatherInputStream.h"
#implementation GTMGatherInputStream
+ (NSInputStream *)streamWithArray:(NSArray *)dataArray {
return [[[self alloc] initWithArray:dataArray] autorelease]; //error on this line
}
GTMOAuth2Authentication.h:31:11: 'GTMSessionFetcher.h' file not found
#if GTM_USE_SESSION_FETCHER
#import "GTMSessionFetcher.h" //GTMSessionFetcher.h file not found error
#else
#import "GTMHTTPFetcher.h"
#endif // GTM_USE_SESSION_FETCHER
I have researched the error everywhere online and I have found nothing. I am running GM El capitan with GM Xcode 7.0. I Have tried multiple different ways on solving it and nothing has worked. My code will not compile. How do I fix this?
I assume Google is going to implement a fix for this in the near future; in the meantime, we can do a couple of hacks to get around those issues:
change return [[[self alloc] initWithArray:dataArray] autorelease];
to
return [[(GTMGatherInputStream*)[self alloc] initWithArray:dataArray] autorelease];
change
#ifndef GTM_USE_SESSION_FETCHER
#define GTM_USE_SESSION_FETCHER 1
#endif
to
#ifndef GTM_USE_SESSION_FETCHER
#define GTM_USE_SESSION_FETCHER 0
#endif
I had to do this in two places where GTM_USE_SESSION_FETCHER was defined.
One final thing, was to go to the GTL project build settings, and set Apple LLVM 7.0 warnings Deprecated Functions to NO. With these 3 steps the Calendar API compiles successfully on iOS9.
I also had to deal with an error Comparison of address of ... not equal to null pointer is always true
This was causing the app not to build. Had to modify lines 340 and 1088 of GTMOAuth2ViewControllerTouch.m
E.g.,
// CGP; 9/30/15; took out "&" before kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly
//if (accessibility == NULL
// && &kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly != NULL) {
if (accessibility == NULL
&& kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly != NULL) {
accessibility = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly;
}
Change self in [[[self alloc] initWithArray:dataArray] autorelease] to GTMGatherInputStream. It's working for me:
#import "GTMGatherInputStream.h"
#implementation GTMGatherInputStream
+ (NSInputStream *)streamWithArray:(NSArray *)dataArray {
return [[[GTMGatherInputStream alloc] initWithArray:dataArray] autorelease];
}

GPUImageiOSBlurFilter imageByFilteringImage: returns nil

spent some time and can't really understand where is a problem.
I'm using GPUImage 0.1.3 (from CocoaPods) and have very straightforward code:
GPUImageiOSBlurFilter *iosBlur = [[GPUImageiOSBlurFilter alloc] init];
UIImage *splashScreenImage = [UIImage imageNamed:#"Splash"];
UIImage *bluredImage = [iosBlur imageByFilteringImage:splashScreenImage];
bluredImage is nil.
I stepped through the code, iosBlur isn't nil, splashScreenImage isn't nil and contains proper image (checked in debugger's quick view). So I don't have any idea where is a problem.
This is how I worked around it - crude, but works.
BTW - this still happens with the latest code of GPUImage, although I mostly see the issue in the simulator.
int errorCount = 0;
UIImage *blurImage;
//Loop workaround for some unknown GPUImage issue where sometimes the result is nil.
while (! (blurImage = [imageFilter imageByFilteringImage:image]) ) {
++errorCount;
}
if (errorCount > 0) {
NSLog(#"GPUImageiOSBlurFilter imageByFilteringImage error count: %i", errorCount);
}
From the GPUImageOutput.h
// Platform-specific image output methods
// If you're trying to use these methods, remember that you need to set -useNextFrameForImageCapture
// before running -processImage or running video and calling any of these methods,
// or you will get a nil image
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
- (UIImage *)imageByFilteringImage:(UIImage *)imageToFilter;

How to #define based on iOS version?

I have a Constants.h file in my app, where I #define app-wide things for easy access later. I'm having a hard time, though, #defineing based on iOS version. Here's what I've tried:
#ifdef __IPHONE_7_0
#define kHamburgerImage [UIImage imageNamed:#"reveal_menu_icon_portrait_ios7.png"];
#else
#define kHamburgerImage [UIImage imageNamed:#"reveal_menu_icon_portrait.png"];
#endif
Just because it says iOS 7 in there doesn't mean this is under NDA, O closers!
Which works fine - for iOS 7. When I run my app on iOS 6, however, the #define is still the iOS 7 one - it seems as though the #ifdef is never taken into account.
What can I do to fix this?
Instead of using compile-time checks, you need runtime checks. This means you can't use #define. I suggest using a static variable that is initialized at runtime based on the version of iOS. Below is an example if you only need the value in a single file.
Some .m file:
static UIImage *kHamburgerImage = nil;
+ (void)initialize {
// This assumes you only support iOS 6 and later - adjust as needed
if ([[UIDevice currentDevice].systemVersion hasPrefix:#"6"]) {
kHamburgerImage = [UIImage imageNamed:#"reveal_menu_icon_portrait.png"];
} else {
kHamburgerImage = [UIImage imageNamed:#"reveal_menu_icon_portrait_ios7.png"];
}
}
Edit: Since these need to be globals, you should do this:
Constants.h:
extern UIImage *kHamburgerImage;
#interface Constants
#end
Constants.m:
UIImage *kHamburgerImage = nil;
#implementation Constants
+ (void)initialize {
// This assumes you only support iOS 6 and later - adjust as needed
if ([[UIDevice currentDevice].systemVersion hasPrefix:#"6"]) {
kHamburgerImage = [UIImage imageNamed:#"reveal_menu_icon_portrait.png"];
} else {
kHamburgerImage = [UIImage imageNamed:#"reveal_menu_icon_portrait_ios7.png"];
}
}
#end
But this suffers from a problem. Unless you take specific steps, accessing these globals could result in nil pointers. They only get initialized if the class is actually referenced. I suggest that as the first line of your application:didFinishLaunchingWithOptions: you do:
[Constants class];
This ensures the initializer is called and the constants are setup before you use them anywhere else in your code.
You can at least shorten your code by defining the iOS check as a macro.
#define IS_IOS7 [[UIDevice currentDevice].systemVersion hasPrefix:#"7"]
And then your new code is way more readable,
if (IS_IOS7) {
kHamburgerImage = [UIImage imageNamed:#"reveal_menu_icon_portrait_ios7.png"];
} else {
kHamburgerImage = [UIImage imageNamed:#"reveal_menu_icon_portrait.png"];
}
You should use #ifndef instead of #ifdef. Here is the code . I hope it will help you.
#ifndef __IPHONE_7_0
#define kHamburgerImage [UIImage imageNamed:#"reveal_menu_icon_portrait_ios7.png"];
#else
#define kHamburgerImage [UIImage imageNamed:#"reveal_menu_icon_portrait.png"];
#endif

How can I programmatically determine if my app is running in the iphone simulator?

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.

Resources