Expected Declaration when Checking iOS Version in Swift 5 - ios

I want to add iOS version checking on my code, however, I always got this error
if #available(iOS 13.4, *) {
var tagSession: NFCTagReaderSession!
} else {
}
I can't use #available(iOS 13.0, *) because I still want to use this scene for below iOS 13.
Does anyone know how to solve the problem? Thank you in advance!

Related

Swift iOS version availability - check from * to iOS 13

I have tried to use such check to apply some code that should works only on previous versions of iOS before iOS 13 and it doesn't work correctly i.e. it is executed on iOS 13
if #available(*, iOS 12) { }
I make a workaround like this
if #available(iOS 13, *) {
/// Do nothing here
} else {
}
But i have additional curly braces block
You can use guard statement
guard #available(iOS 13.0, *) else {
// Code for earlier iOS versions
return
}
You can get the currentVersion of your OS using:
UIDevice.current.systemVersion
Using this you can easily create your own method.
func SYSTEM_VERSION_LESS_THAN(version: String) -> Bool {
return UIDevice.current.systemVersion.compare(version,
options: NSString.CompareOptions.numeric) == ComparisonResult.orderedAscending
}
In Swift 5.6 (Xcode 13.3+), you can use #unavailable condition.
if #unavailable(iOS 13) {
// This code will run on iOS 12.* and earlier
}
This is the proposal for reference: SE-0290.

Deprecation warning in Mac Catalyst but only in Objective-C, not in Swift

I'm using Xcode 11 on the GM build of Catalina (10.15). I'm working on building my iOS app for Mac Catalyst. My iOS app has a deployment target of iOS 11.
I have a simple line in a view controller such as:
self.modalInPopover = YES;
Compiles clean in iOS. When I switch to the "My Mac" destination, I get a deprecation warning:
'modalInPopover' is deprecated: first deprecated in macCatalyst 13.0
OK, fine. I can switch to the new method added in iOS 13:
if (#available(iOS 13.0, *)) {
self.modalInPresentation = YES;
} else {
self.modalInPopover = YES;
}
That should fix it but I still get the same deprecation warning on the use of modalInPopover in the else block.
What's odd is that the corresponding Swift code does not give any warnings. Only the Objective-C code continues to give the warning.
if #available(iOS 13, *) {
self.isModalInPresentation = true
} else {
self.isModalInPopover = true
}
I even tried updating the #available to:
if (#available(iOS 13.0, macCatalyst 13.0, *)) {
but that didn't change anything.
The following disaster solves the problem but it shouldn't be needed:
#if TARGET_OS_MACCATALYST
self.modalInPresentation = YES;
#else
if (#available(iOS 13.0, *)) {
self.modalInPresentation = YES;
} else {
self.modalInPopover = YES;
}
#endif
Am I missing something or is this an Xcode bug? How can I eliminate the deprecation warning in Objective-C without duplicating code using #if TARGET_OS_MACCATALYST which isn't need in Swift.
My iOS app has a deployment target of iOS 11.
That’s why. To see the deprecation warning in Swift you would need to say isModalInPopover not in an available clause with a deployment target of iOS 13.
For the Catalyst build, you're not backward compatible (there is no backward) so it's as if this were an iOS 13 deployment target, and you see the warning.
You can use this to check whenever you running it on different platforms:
#if targetEnvironment(macCatalyst)
print("UIKit running on macOS")
#elseif os(watchOS)
print("Running on watchOS")
#else
print("Your regular code")
#endif
also it should remove the warning.
More details can be found here: https://www.hackingwithswift.com/example-code/catalyst/how-to-detect-your-ios-app-is-running-on-macos-catalyst

[LAContext biometryType]: unrecognized selector on iOS 11.0.0

I see dozens of crashes in fabric
Fatal Exception: NSInvalidArgumentException
-[LAContext biometryType]: unrecognized selector sent to instance 0x1c066aa00
And it's strange because I do call biometryType on LAContext only for iOS 11+.
The code:
private static var biometryType: BiometryType? {
let context = LAContext()
guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil),
context.evaluatedPolicyDomainState == BiometryManager.savedPolicyDomainState else { return nil }
if #available(iOS 11.0, *) {
switch context.biometryType {
case .typeFaceID: return .typeFaceID
case .typeTouchID: return .typeTouchID
case .none: return nil
}
}
return .typeTouchID
}
Any suggestions?
The only clue I have is that all crashes related to 11.0.0. So maybe Apple added biometryType not in 11.0.0 but a bit later.
Links:
https://developer.apple.com/documentation/localauthentication/lacontext/2867583-biometrytype
http://www.codeprocedures.com/question/nsinvalidargumentexception-unrecognized-selector-sent-to-instance-on-specific-phone-with-ios-11/
As #stonesam92 said, it is probably a bug in ios 11.0.0. The below code safeguards me against the crash.
if #available(iOS 11.0, *), authenticationContext.responds(to: #selector(getter: LAContext.biometryType))
This works as well:
if #available(iOS 11.0.1, *) {...}
The iPhone X first release was on 11.0.1
From crash reporting, this definitely works.
It's not much of a solution, but this appears to be a bug in iOS 11.0.
I've seen multiple reports of this crash and all have been resolved when the user upgraded to a more recent version of iOS.
This code works for Objective-C(on iOS 11.0 & below versions)
if (#available(iOS 11.0, *) && [context respondsToSelector:#selector(biometryType)])
{}
credits to #Sonu VR

Checking for quick actions availability

I'm using home screen quick actions that's only supported in IOS9.
Using the constant UIApplicationLaunchOptionsShortcutItemKey will crash if used in IOS8.
What is the correct way to check if quick actions is supported?
One way is to check for IOS9 through systemVersion but I'm hoping there is a better way.
[[UIDevice currentDevice] systemVersion]
In objective C you can check to see if a class exists. Say something like
if([UIApplicationShortcutItem class]){
//Handle shortcut launch
}
I think in case of Swift the best way for checking the API availability is Automatic operating system API availability checking that's new feature released with iOS9 and Swift2
if #available(iOS 9, *) {
// use UIApplicationLaunchOptionsShortcutItemKey
} else {
// no available
}
#available is going to check whether are we using iOS 9 or later, or any other unknown platforms like watchOS so the * is also here.
If your code is inside a function then you can use #available with guard like this.
guard #available(iOS 9, *) else {
return
}
Mark your methods and class as well like
#available(iOS 9, *)
func useMyStackView() {
// use UIStackView
}
#available works similarly to #available so If your deployment target is iOS7 or less than 9, you can't call that useMyStackView()
For Objc, it also can use #available (iOS 9, *) to check the OS version
if (#available(iOS 9, *)) {
//use UIApplicationLaunchOptionsShortcutItemKey
}

Swift - Use both UIAlertController & UIAlertView for iOS 7 / iOS 8 and above

My app's deployment target is 7.0 . I want to use both UIAlertController and UIAlertView. I read somewhere that checking for iOS versions is not good, so i used this code :
if (NSClassFromString("UIAlertController") != nil) {
// UIAlertController
} else {
// UIAlertView
But even if do that, i still get that "correctable" error "UIAlertController is only available on iOS 8.0 or newer" and i have to choose between 3 'Fix-it' options :
Add 'if #available' version check ( if #available(iOS 8.0, *) { ... } else { ... })
Add #available attribute to enclosing instance method
Add #available attribute to enclosing class
What should i do ? Currently using Xcode 7 GM
As stated, the best way to do this is using the #available function. I've attached a code example for you.
if #available(iOS 8.0, *) {
} else {
}
#available is the best way to do these checks.

Resources