Using @available with stored properties

13,244

Solution 1

@available could be used around a whole class or one or more functions, but not for properties.

Regarding your UNUserNotificationCenter usage, current returns a singleton that never changes, so why not just remove the center constant, and just use UNUserNotificationCenter.current() where center is used?

Solution 2

Here is one potential solution (thanks to blog post). The idea is to use a stored property with a type of Any and then create a computed property that will cast the stored property (and instantiate it if necessary).

private var _selectionFeedbackGenerator: Any? = nil
@available(iOS 10.0, *)
fileprivate var selectionFeedbackGenerator: UISelectionFeedbackGenerator {
    if _selectionFeedbackGenerator == nil {
        _selectionFeedbackGenerator = UISelectionFeedbackGenerator()
    }
    return _selectionFeedbackGenerator as! UISelectionFeedbackGenerator
}

Another option is to use lazy (however, this makes the variable read-write):

@available(iOS 10.0, *)
private(set) lazy var center = UNUserNotificationCenter.current()

Solution 3

I know this is an older question but I wanted to add an answer for people who come here via Google as I did.

As kgaidis and Cœur mentioned, you can use @available on computed properties. However, lazy variables are considered computed properties and so you can use @available on them too. This has the nice benefit of removing the boilerplate of the extra stored property and the forced casts - in fact, it leaves no evidence of the property in your pre-iOS 10 code.

You can simply declare it like this:

@available(iOS 10.0, *)
private(set) lazy var center = UNUserNotificationCenter.current()

Unfortunately there's no way to make it completely read-only but the private(set) at least makes it read-only outside of the class.

Solution 4

Similar idea than kgaidis, by using a separate stored property of a type accepted in any version. But Any may be too generic, as it cannot be declared weak for instance, so you may want to replace it with a conforming protocol in some situations:

private weak var _notificationCenterDelegate: NSObjectProtocol?
@available(iOS 10.0, *)
var notificationCenterDelegate: UNUserNotificationCenterDelegate? {
    return _notificationCenterDelegate as? UNUserNotificationCenterDelegate
}
Share:
13,244
chickenparm
Author by

chickenparm

iOS Developer

Updated on June 16, 2022

Comments

  • chickenparm
    chickenparm about 2 years

    I have an app that uses local notifications and supports iOS 10. I am trying to add iOS 9 support which requires me to use the old location notification API. I am trying to use @available and #available on my iOS 10 code and I can't figure out how to get my center variable to only be for devices running iOS 10.

    When I set my target from iOS 10 to 9 I get the error message for this variable:

    UNUserNotificationCenter is only available on iOS 10.0 or newer.
    

    It suggests I add @available(iOS 10.0, *) to my entire class which I don't want to do since there is code in this class that will be used for iOS 9. I appreciate any suggestions on how to limit my center property to just iOS 10.

    class ViewController: UIViewController, UITextFieldDelegate {
      
      let center = UNUserNotificationCenter.current()
      ...
    }
    
  • willrichman
    willrichman about 7 years
    This is not a good way to check for availability. Apple's preferred method is the if #available() syntax. However, neither method (this answer or if #available) can be used on stored properties.
  • Toma Radu-Petrescu
    Toma Radu-Petrescu about 6 years
    That's clever! :)
  • LinusGeffarth
    LinusGeffarth over 5 years
    Thanks for sharing!
  • lewis
    lewis about 4 years
    For me this works at build time, but not at run time: stackoverflow.com/questions/62301128/…
  • Jayson
    Jayson about 4 years
    @lewis I haven't been able to reproduce this on an iOS 12.4 device, but you may need to look into weakly linking the framework like the one answer suggests
  • Jan Ehrhardt
    Jan Ehrhardt about 4 years
    Is there a way to achieve the same thing in Objective-C?
  • Lance Samaria
    Lance Samaria over 3 years
    another way to do this is: @available(iOS 14.0, *); private(set) lazy var whateverClass: WhateverClass? = nil
  • nathanwhy
    nathanwhy about 2 years
    cannot work anymore in Xcode 14 beta
  • Baran Gungor
    Baran Gungor about 2 years
    lazy var is nice idea