Use New Awesome iOS Features While Maintaining Backward Compatibility
Every year at WWDC Apple blesses us with plenty of new awesome tools that we can’t wait to implement in our apps.
However, most of the time, our apps must support previous iOS versions, too.
This might seem a big show-stopper and, in fact, there are many developers that are holding on in adding the latest new features because of this.
Well, I’m here to tell you that, thanks to Swift, it is incredibly clean and easy to add new iOS features, while maintaining backward compatibility!
Example
Let’s use a real example: one of my new favorite toys from this year WWDC is UIPreviewInteraction
.
Basically, this (iOS 10.0+) class listens to the very same touch interactions of Peek and Pop for your UIView
, but gives you complete control on what to do with it.
To implement it, we must follow just two steps:
- Add a
UIPreviewInteraction
property to ourUIView
- Create a
UIPreviewInteractionDelegate
1. Adding a UIPreviewInteraction Property to our UIView
Obviously, if we add the property as it is, the compiler will complain:
class MyView: UIView {
fileprivate var myPreviewInteraction: UIPreviewInteraction
}
How do we overcome this? Easy: we don’t tell the compiler what the property is.
Furthermore, since we use the property only in iOS 10 and above, we will make it an optional, so the property will be nil for previous iOS versions:
class MyView: UIView {
fileprivate var myPreviewInteraction: Any? = nil
}
Ok, now we can compile! 🎉 However, at some point, we must declare that the property is a UIPreviewInteraction.
How?
Simple, we use one of the most precious Swift 2 gifts: #available
, a.k.a. Swift’s Automatic Operating System Availability Checking API:
class MyView: UIView {
fileprivate var myPreviewInteraction: Any? = nil
init() {
super.init(frame: CGRect.zero)
if #available(iOS 10.0, *) {
myPreviewInteraction = UIPreviewInteraction(view: self)
}
}
}
Great! Now our property will be:
nil
in any iOS prior to 10;- an instance of
UIPreviewInteraction
for iOS 10.0+!
2. Create a UIPreviewInteractionDelegate
For simplicity’s sake, I will use the same UIView
class as my delegate as well.
One more time, if we simply try to make our UIView
class conform to the UIPreviewInteractionDelegate
, the compiler will complain:
extension MyView: UIPreviewInteractionDelegate {
public func previewInteraction(_ previewInteraction: UIPreviewInteraction, didUpdatePreviewTransition transitionProgress: CGFloat, ended: Bool) { }
public func previewInteractionDidCancel(_ previewInteraction: UIPreviewInteraction) { }
}
And, one more time, Swift 2’s Automatic Operating System Availability Checking API comes to the rescue!
@available(iOS 10.0, *)
extension MyView: UIPreviewInteractionDelegate {
public func previewInteraction(_ previewInteraction: UIPreviewInteraction, didUpdatePreviewTransition transitionProgress: CGFloat, ended: Bool) { }
public func previewInteractionDidCancel(_ previewInteraction: UIPreviewInteraction) { }
}
Now, let’s not forget to add the view as its own delegate in our initializer…and we’re done!
class MyView: UIView {
fileprivate var myPreviewInteraction: Any? = nil
init() {
super.init(frame: CGRect.zero)
if #available(iOS 10.0, *) {
myPreviewInteraction = UIPreviewInteraction(view: self
(myPreviewInteraction as! UIPreviewInteraction).delegate = self
}
}
}
Conclusions
Even though our apps has to support older iOS versions, this must not stop us to implement the latest and greatest iOS features!
It’s true, this solution won’t add the same functionality to previous iOS releases, but it’s still way better than waiting years to drop support of older iOS versions to finally implement what’s available to us today.
Code Snippet
Here's the final, complete, code snippet:
import UIKit
class MyView: UIView {
fileprivate var myPreviewInteraction: Any? = nil
init() {
super.init(frame: CGRect.zero)
if #available(iOS 10.0, *) {
myPreviewInteraction = UIPreviewInteraction(view: self)
(myPreviewInteraction as! UIPreviewInteraction).delegate = self
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
@available(iOS 10.0, *)
extension MyView: UIPreviewInteractionDelegate {
public func previewInteraction(_ previewInteraction: UIPreviewInteraction, didUpdatePreviewTransition transitionProgress: CGFloat, ended: Bool) { }
public func previewInteractionDidCancel(_ previewInteraction: UIPreviewInteraction) { }
}