In Xcode versions up until 14 (Swift versions up to 5.7), it was possible to use a lazy var to create a stored property whose data type is unavailable in your deployment target. For example, RelativeDateTimeFormatter is new in iOS 13 so if you wanted to use it in an app that targets iOS 12, you could have defined the following:
@available(iOS 13.0, *)
private lazy var relativeDateTimeFormatter: RelativeDateTimeFormatter = {
let formatter = RelativeDateTimeFormatter()
formatter.dateTimeStyle = .numeric
formatter.unitsStyle = .short
formatter.formattingContext = .standalone
return formatter
}()
And you'd wrap that call in if #available to use a different date formatter on iOS versions less than 13. Since it's expensive to create date formatters, it's important this be created only once and reused as you populate cells for example. The stored property worked well for this, or so we thought. Now you get a compile-time error:
Stored properties cannot be marked potentially unavailable with ‘@available’
The Xcode 14 release notes state:
Stored properties in Swift can’t have type information that is potentially unavailable at runtime. However, prior to Swift 5.7 the compiler incorrectly accepted @available attributes on stored properties when the property had either the lazy modifier or an attached property wrapper. This could lead to crashes for apps running on older operating systems. The Swift compiler now consistently rejects @available on all stored properties.
This brings us to the question: what solutions are there to implement this now?
A similar scenario to consider: say you want to add Live Text support to a view controller in your app. To do this, you create an ImageAnalyzer and store it as a property on the view controller. The problem is this API is new in iOS 16, so how do you add this so you can use it on iOS 16 while still maintaining support for older OS versions? This will not work:
@available(iOS 16.0, *)
private(set) lazy var analyzer = ImageAnalyzer()