always use latest SDK to build and set Minimum deployment target for minimum supported OS release
//not suggested way as the class might be private in previous versions.
//or typo to make this condition true but the action caused crashes.
if([NSDataAsset class]) {
}
// not suggested as easy to typo and different syntax for from classes
if ([view responsToSelector:@selector(abc)] ) {
[view abc];
}
// not suggested as easy to typo and different syntax
if (&functionAvailableInIOS9) {
}
New Swift way to check OS Version
//Assume our min. deploy target is iOS 7
let locationManager = CLLocationManager()
//ERROR as requestWhenInUseAuthorization() not available in iOS 7
locationManager.requestWhenInUseAuthorization()
//do new version check
//compiler knows that method runs when it is iOS8 or up
if #available(iOS 8.0, *) {
locationManager.requestWhenInUseAuthorization()
}
using #available
so that the compiler can help developer to check the system version safely without worrying
#available(iOS 9.0, OSX 10.11, *)
to support multiple platform
Bailing out early
guard #available(iOS 9.0, *) else { return }
let asset = NSDataAsset(name: "Dragon")
class MyClass {
@available(iOS 8.0, *)
func functionThatWithiOS8() {
}
func otherMethod() { ... }
}
let myClass = MyClass()
myClass.otherMethod()
//must use #available to check in order to invoke
if #available(iOS 8.0 , *) {
myClass.functionThatWithiOS8()
}
@available(iOS 8.0, *)
class MyClass {
func functionThatWithiOS8() {
}
func otherMethod() { ... }
}
//must use #available to check in order to invoke
//in iOS 7 the class is not available
if #available(iOS 8.0 , *) {
let myClass = MyClass()
myClass.otherMethod()
myClass.functionThatWithiOS8()
}
class CustomBlurView : UIView { ... }
func makeBlurView() -> UIView {
if #available(iOS 8.0, *) {
//user newer api to make blur view
return UIEffectView(...)
}
return CustomBlurView(...)
}
let blurView = makeBlurView()
We normally use this have a UIImage
let appleImage = UIImage(named: "Apple")!
This comes with three issues
String
as name may caused typo and compiler can not checkA specific enum that solves the issues with strictly typed and no force unwrapping with additional benefits:
enum UIImage {
enum AssetIndentifer: String {
//in Xcode 7 beta 3, the case name will convert into String if no other value assigned
case Apple
case Orange
case Banana
...
}
//add unwrapped init
convenience init!(assetIdentifer: AssetIndentifer) {
self.init(named: assetIdentifer.rawValue)
}
}
Then we can use the enum outside
let appleImage = UIImage(assetIdentifer: .Apple)
We also have segue identifiers in each view controllers
someViewController.swift
override func prepareForSegue(segue: UIStoryBoardSegue, sender: AnyObject?) {
switch segue.identifier {
case "segue1"
case "segue2"
//error as this switch have not default case
}
}
We can use the techique before to wrap the identifer into enum. When we add a new enum case, compiler find the switch did not handle the case
class someViewController : UIViewController {
enum SegueIdentifer: String {
case Segue1
case Segue2
case Segue3
...
}
override func prepareForSegue(segue: UIStoryBoardSegue, sender: AnyObject?) {
guard let identifier = segue.identifer,
segueIdentifer = SegueIdentifer(rawValue: identifer)
else {
//not the segue identifer we want..
}
switch segue.identifier {
case Segue1
case Segue2
// error as not all cases handled
}
}
// we add extra function that accept enum as segue
func performSegueWithIdentifer( segueIdentifer : SegueIdentifer, sender : AnyObject? ) {
preformSegueWithIdentifier(segueIdentifer.rawValue, sender)
}
}
even so, if we have multiple view controllers, the enum and segue pattern will be repeated. Instead, we can use protocol to restrict the view controllers to have same enum and functions and we can have all logic in one place.
protocol SegueHandlerType {
//leverage the enum and related swift functions
typealias SegueIdentifier: RawPresentable
}
and use extension to limit what type can use this protocol
extension SegueType where Self: UIViewController, SegueIdentifer.RawValue == String {
func preformSegueWithIdentifier( segueIdentifer: SegueIdentifer, sender: AnyObject?) {
performSEgueWithIdentifier(segueIdentifer.rawValue, sender: sender)
}
func segueIdentiferForSegue(segue: UIStoryboardSegue) -> SegueIdentifer {
guard let identifer - segue.identifer, segueIdentifer = SegueIdentifer(rawValue: identifer)
else {
// invalid segue identifer..
}
return segueIdentifer
}
}
Then we set the view controllers conform that protocol
class someViewController : UIViewController, SegueHandlerType {
enum SegueIdentifer: String {
...
}
func handleAction(sender: AnyObject?) {
performSegueWithIdentifier(.Segue1, sender: sender)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
switch segueIdentiferForSegue(segue) {
case .Segue1
case .Segue2
...
//no more error for needing default case as all enum case known and handled
}
}
}
if we add a new segue, the compiler will tell us missing handling in prepareForSegue
and this promotes compiler time safety. This also helps reusability of the protocol and helps to reduce the error as constrainting the environment.