No visible #interface for 'CLLocationManager' declares the selector 'requestAlwaysAuthorization' - ios

We are making an app to be compatible with iOS 8, but at the same time, some of our developers do not have Xcode 6 yet, so they are getting this error when trying to call
[self.locationManager requestAlwaysAuthorization];
Even if it is inside an if
if(floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_7_1) {
[self.locationManager requestAlwaysAuthorization];
}
How can we solve this to compile on Xcode 5?

The following is the proper way to deal with this. This assumes that your app has a "Deployment Target" of iOS 7.x or earlier and you need to compile the project with different values for the "Base SDK" (such as iOS 8 under Xcode 6 and iOS 7 under Xcode 5):
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
// Being compiled with a Base SDK of iOS 8 or later
// Now do a runtime check to be sure the method is supported
if ([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[self.locationManager requestAlwaysAuthorization];
} else {
// No such method on this device - do something else as needed
}
#else
// Being compiled with a Base SDK of iOS 7.x or earlier
// No such method - do something else as needed
#endif

Accepted answer didn't work for my particular situation. Due to build enviroment limitations (Phonegap/Cordova) I'm stuck on compiling against the iOS7 SDK only.
I implemented the following (as suggested in comments):
if([self.locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
// Use performSelector: so compiler won't blow up on this
[self.locationManager performSelector:#selector(requestAlwaysAuthorization)];
}
It might show compiler warnings, but atleast it works in that specific case.

Related

Using new iOS 10 API in compiled framework under XCode 7

My company is developing an iOS SDK, which uses the new CallKit APIs. The SDK (.framework) is compiled with XCode 8 / SDK 10.0.
However, some of out customers are still using XCode 7 - When I try to integrate our SDK under a XCode 7 project, I got the following error :
ld: framework not found CallKit for architecture arm64
However, I put some macro directive into the SDK code, to provide a CoreTelephony fallback, but event with this trick, the project's target does not compile with the message above.
Here is the directive example :
#ifdef __IPHONE_10_0
#import CallKit;
#endif
Do you have a trick to use a SDK compiled with iOS 10 API working under XCode 7 ?
EDIT
Find a way to manage that. The idea is to lazy load CallKit (runtime). I had to call performSelector instead of calling directly methods.
Here is some code :
// Lazy load CallKit framework to keep compatibility for XCode 7 SDK integration
if ([[AppKit sharedInstance] systemVersion] >= kIosSystemVersion10) {
NSBundle *b = [NSBundle bundleWithPath:#"/System/Library/Frameworks/CallKit.framework"];
_isCallKitFrameworkLoaded = [b load];
}
if (_isCallKitFrameworkLoaded) {
SEL callObserverDelegate = NSSelectorFromString(#"setDelegate:queue:");
if ([_callObserver respondsToSelector:callObserverDelegate]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[_callObserver performSelector:callObserverDelegate withObject:self withObject:(__bridge id)(_callObserverQueue)];
#pragma clang diagnostic pop
}
} else {
// CoreTelephony fallback
}
CallKit is available from iOS 10 and later. Xcode 7 support upto iOS 9 only. If you need to use this framework, you need to update to latest Xcode(obviously Xcode8) which supports iOS 10.
So users who are having iOS 10 only can use this feature and not iOS 9.

'-[CLLocationManager requestAlwaysAuthorization]: unrecognized selector sent to instance

Can anyone please help in getting fix for fetching location updates in ios8 xcode6 i am getting the crash at [locationManager requestAlwaysAuthorization];
if i place this line of code in
if ([locationManager respondsToSelector:#selector(requestAlwaysAuthorization)]) {
[locationManager requestAlwaysAuthorization];
}
then Locationmanager delegates are not called.
I was having the exactly same problems.
Then I figured that I ran it on the wrong device (wrong iOS)
locationManager requestAlwaysAuthorization
availability iOS 8.0 and later
That's it. Run it on iOS 8 or later. You better use check version to use that method.

Using __IPHONE_7_0 and respondsToSelector

I came across this code and I was wondering if the #ifdef check is redundant.
UIButton *doneButton = [[UIButton alloc] init];
...
#ifdef __IPHONE_7_0
if([self respondsToSelector:#selector(setEdgesForExtendedLayout:)])
[doneButton setContentEdgeInsets:UIEdgeInsetsMake(0, 12, 0, -12)];
#endif
I tried removing it and running it in iOS 6 and it ran fine. Is there some special case I need to be aware of in iOS 6/7 that causes it to not trigger or causes crashes?
The #ifdef is a compile-time directive. It will cause the code between the #ifdef and the #endif to only be compiled if the project is built against the iOS 7 SDK. If you build against the iOS 6 SDK, the code won't be compiled at all.
The #ifdef would prevent compiler errors if the code inside uses symbols that are only defined in the iOS 7 SDK. In the code you posted, I'm not sure what it's doing. The setContentEdgeInsets code is valid for most iOS versions. I would think the code should check to see if self responds to setEdgesForExtendedLayout, and then call setEdgesForExtendedLayout if it does respond.
The #ifdef is redundant. Also, depending on how __IPHONE_7_0 is defined, it may cause your app to stop working on iOS 8. Just remove it.

iOS: Preprocessor for OS version check

In the past I used the following preprocessor code to conditionally execute code for different iOS versions:
#ifdef __IPHONE_OS_VERSION_MIN_REQUIRED
// target is iOS
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 60000
// target is lower than iOS 6.0
#else
// target is at least iOS 6.0
#endif
#endif
However with iOS 7 I have the following problem:
#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
The NSLog message above appears on console under iOS 7. Am I doing something wrong?
EDIT: The following code running under iOS 7 (simulator and device)
NSLog(#"Version %i", __IPHONE_OS_VERSION_MIN_REQUIRED);
gives: Version 60000
That is the Deployment Target of your app (the minimum version where your app can be installed), not the version where the app is running in the device.
In the settings of your project, you can set that field:
If you change it like this, this input:
NSLog(#"Version %i", __IPHONE_OS_VERSION_MIN_REQUIRED);
Returns 7000
If what you want is to check the actual version of the operative system, I refer you to this question:
How to check iOS version?
But, it's done in runtime, not at compile time.
#ifdef __AVAILABILITY_INTERNAL__IPHONE_9_0_DEP__IPHONE_9_0
// you're in Xcode 7.x and can use buggy SDK with ios 9.0 only functionality
CGFloat iOSVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
if (iOSVersion>=9) {
// same good old API that predates brave new post Steve Jobs world of bugs and crashes
}
#else
// you're running Xcode 6.4 or older and should use older API here
#endif
swift:
if #available(iOS 13, *) {
toSearchBar?.isHidden = true
} else {
// a path way to discovering how fast UIKit will rot
// now that there is SwiftUI
}

Conditionally Hide Code from the Compiler

So here's the problem. I'm set to release an update soon for iOS that will address some problems in iOS 7. In order to do this, I need to use some specific iOS 7 functions/types. I've made absolutely certain that iOS 7 code will only be executed on iOS 7 and fallback to different code for pre iOS 7. Of course, I'm not allowed to submit with the current Xcode beta, so I'm trying to compile with the current Xcode release. However, I can't seem to find a way to disable this particular warning:
Use of undeclared identifier '<Redacted>'.
Does anyone know of a way to disable this warning using a #pragma. I've tried a bunch of different ones including
-w, -Weverthing, -Wall
but nothing seems to work.
UPDATE
Answer: You can't, of course, because the compiler can't compile an identifier it knows nothing about. My solutions was to simply create a #define:
#define <redacted> 1
UPDATE 2
The answer below actually made it much easier. I had already created a #define Xcode5Code(code, alt) that allowed me to execute code blocks conditionally. By modifying it using the solution by #maddy:
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000
#define Xcode5Code(code, alt) code
#else
#define Xcode5Code(code, alt) alt
#endif
This allows me to to easily hide blocks of code from the compiler by using:
Xcode5Code({
//Code to be execute only with Xcode 5
}, {
//code to be executed in previous versions of Xcode
})
The main benefit of using the #define Xcode5Code is that Xcode will auto-complete it for you, which is a lot easier than using the full #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000, which Xcode won't auto-complete.
This won't actually distinguish between iOS 7 and pre iOS 7 devices. It only distinguishes what version of iOS the current Xcode can handle. To distinguish between iOS devices versions I use:
NSUInteger DeviceSystemMajorVersion(void) {
static NSUInteger _deviceSystemMajorVersion = -1;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_deviceSystemMajorVersion = [[[[[UIDevice currentDevice] systemVersion] componentsSeparatedByString:#"."] objectAtIndex:0] intValue];
});
return _deviceSystemMajorVersion;
}
The above is Apple's code, by the way. To dance around the NDA a little, I'll say that this helps with laying out a root controller's view, because that depends on both the version of Xcode you're using AND the version of iOS that's on the device. And if you're trying to manage beta's as well as production code, this can help a lot. Once you can submit apps with Xcode 5, the #define Xcode5Code will no longer be necessary.
If you want to compile your app with two difference versions of Xcode or two different Base SDK settings then you should use compiler directives:
#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 // iOS 7.0 supported
// iOS 7 code here
#else
// Pre-iOS 7 code here
#endif
Do not use this for runtime checks. This solution is only to be used when you must compile your code with two different versions. An example would be you have added iOS 7 code but you still need to compile the code with Xcode 4.6. Using the compile directives allows you to "hide" the iOS 7 code from the compiler using the older Base SDK.
See the "SDK Compatibility Guide" in the docs for more on this and proper runtime checks.

Resources