Extended property if not available - ios

With Swift 2, Apple introduced the API availability checking which allows one to execute certain code only on a specified version or later, like this:
if #available(iOS 9, *) {
// use UIStackView
} else {
// use fallback
}
For instance, iOS 9.0 introduces the localizedUppercaseString property:
/// An uppercase version of the string that is produced using the current
/// locale.
public var localizedUppercaseString: String { get }
What I want is to create an exact replica of this property that is only available for versions lower than 9.0 so I do not have to check if #available(iOS 9, *) whenever I need to use this (or any other) property/method.
The best result I could get was the following:
extension String {
#available(iOS 8.0, *)
var localizedUppercaseString: String {
return uppercaseStringWithLocale(NSLocale.currentLocale())
}
}
With this, I can call localizedUppercaseString, no matter if the iOS version is 8.0 or 9.0. The problem is that this extension overrides the "original" property when executed with iOS 9.0.

extension String {
var myLocalizedUppercaseString: String {
if #available(iOS 9, *) {
return localizedUppercaseString
} else {
return uppercaseStringWithLocale(NSLocale.currentLocale())
}
}
}
Now you just have to use myLocalizedUppercaseString property.

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.

How do I determine if current device does have a P3 capable display?

Is there a way to conditionally select a Standard RGB color or a P3 color if the device supports it?
I thought about something like the following for iOS versions:
if #available(iOS 12.0, *) {
...
} else {
...
}
UITraitCollection has a displayGamut property, which is an enum UIDisplayGamut
#available(iOS 10.0, *)
public enum UIDisplayGamut : Int {
case unspecified // UIKit will not set this anymore, instead a sensible default is chosen based on the device capabilities and settings always
case SRGB
case P3
}
You can query the “main screen”
let hasP3Display = UIScreen.main.traitCollection.displayGamut == .P3
or the display of a specific view (which can be different if an external monitor is used)
let hasP3Display = view.traitCollection.displayGamut == .P3

iOS - #available does not guard availability here; use if (#available) instead

I am using below code but getting the warning,
bool versionSupports = (#available(iOS 10, *));
#available does not guard availability here; use if (#available)
instead
There is a solution where I can use
if (#available(iOS 10, *)){
//compile
}else{
//fallback
}
I was curious, why an output is placeable inside if() condition but not placeable into a boolean variable?
#available(iOS 10, *) is not a BOOL expression. It does not return an indication of whether the code is being run on that version of iOS or not.
It needs to be in the form of:
if (#available(iOS 10, *)) {
// Code that requires iOS 10 or later
} else {
// Code for iOS 9 or earlier
}
If you want a variable that indicates whether the current device is running iOS 10 or later then you can do something like:
BOOL versionSupports = [UIDevice currentDevice].systemVersion.floatValue >= 10;
You may also find Objective-C #available guard AND'ed with more conditions helpful depending on your needs.
That says,
#available may only be used as condition of an 'if', 'guard' or 'while' statement
so, you can directly use that in if statements to check the availability instead of taking it into a variable.
if (#available(iOS 10, *)) {
// PUT YOUR CODE HERE
} else {
}
In Swift 4.1
if #available(iOS 10.0, *) {
} else {
}
A workaround can be:
BOOL isGoodIos = false;
if(#available(iOS 11, *)) { isGoodIos = true; }
....
BOOL shouldDoMagic = true;
if(shouldDoMagic && isGoodIos) {
// TODO: magic
}

How can I create instance variable based on OS version

How can I define instance variable based on the iOS verison, for example
CNContactStore is available since iOS9.0 and ABAddressBook is deprecated in 9.0 but I want to create two variable depends upon iOS version. My Approach is
if #available(iOS 9.0, *) {
var addressBookRef: ABAddressBook? = nil
} else {
var contactStore : CNContactStore? = nil
}
If I do this inside a method body, works fine but I want it to be define globally and can be access throughout the class but giving me error if I do it like this
#available(iOS 9.0, *)
var contactStore : CNContactStore? = nil
Need some suggestions. How do I achieve this. I've no idea if I release #available(iOS 9.0, *), it will crash my app on iOS 8.x or below?
In that case I would use a wrapper class mimic even ABAddressBook and CNContactStore behaviour so all the call to their methods should be done through this class.
A quick approach for what you are looking could be like this:
class ContactWrapper {
var contactBook: NSObject?
init() {
if #available(iOS 9, *) {
contactBook = CNContactStore.init()
} else {
contactBook = ABAddressBookCreate().takeRetainedValue() as? NSObject
}
}
}

Check for class existence in Swift

I want to use NSURLQueryItem in my Swift iOS app. However, that class is only available since iOS 8, but my app should also run on iOS 7. How would I check for class existence in Swift?
In Objective-C you would do something like:
if ([NSURLQueryItem class]) {
// Use NSURLQueryItem class
} else {
// NSURLQueryItem is not available
}
Related to this question is: How do you check for method or property existence of an existing class?
There is a nice section in https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/AdvancedAppTricks/AdvancedAppTricks.html#//apple_ref/doc/uid/TP40007072-CH7-SW4 called Supporting Multiple Versions of iOS, which explains different techniques for Objective-C. How can these be translated to Swift?
Swift 2.0 provides us with a simple and natural way to do this.It is called API Availability Checking.Because NSURLQueryItem class is only available since iOS8.0,you can do in this style to check it at runtime.
if #available(iOS 8.0, *) {
// NSURLQueryItem is available
} else {
// Fallback on earlier versions
}
Simplest way I know of
if NSClassFromString("NSURLQueryItem") != nil {
println("NSURLQueryItem exists")
}else{
println("NSURLQueryItem does not exists")
}
Try this:
if objc_getClass("NSURLQueryItem") != nil {
// iOS 8
} else {
// iOS 7
}
I've also done it like this too:
if let theClass: AnyClass = NSClassFromString("NSURLQueryItem") {
// iOS 8
} else {
// iOS 7
}
Or, you can also check system version like so, but this isn't the best practice for iOS dev - really you should check if a feature exists. But I've used this for a few iOS 7 hacks... pragmatism over purity.
switch UIDevice.currentDevice().systemVersion.compare("8.0.0", options: NSStringCompareOptions.NumericSearch) {
case .OrderedSame, .OrderedDescending:
iOS7 = false
case .OrderedAscending:
iOS7 = true
}

Resources