Swift is a powerful and fun language. Its protocol-oriented nature allows you to do retroactive modeling and promotes composition over inheritance via mixins. This results in slim and elegant architectures that can bend legacy and modern code to your will. Many features are not unique to Swift, but combined with protocol extensions and vibrant pattern matching capabilities, it is indeed a new breed of programming.
So I’ve decided to start a series of posts where I share Swift snippets that I’ve used across projects that I feel are really helpful in rapid development. Thanks to the Swift community and Stack Overflow đ
Collection Extensions
To start off, I’ve included a couple of useful extensions for collections. How many times have you needed to get the first item in a collection that matches a particular predicate? Normally you would do this:
items.filter({ $0.name == "John" })?.first
A bit annoying that I have to filter first. Why not make “first” accept a predicate? Now you can by extending the “SequenceType” protocol:
public extension SequenceType { /** Returns the first that satisfies the predicate includeElement, or nil. Similar to `filter` but stops when one element is found. - parameter predicate: Predicate that the Element must satisfy. - returns: First element that satisfies the predicate, or nil. */ public func first(@noescape predicate: Generator.Element -> Bool) -> Generator.Element? { for x in self where predicate(x) { return x } return nil } }
Now you can do this:
items.first { $0.name == "John" }
You can follow the same logic to create one for “last” by making the for-loop go in reverse. Thanks to bigonotetaking for this one.
While on the subject of collections, grabbing an item from an array can be risky business. If your index is out of range:
item[8] // Out of range
…then ?! Your app crashes from a simple problem. Instead, let’s leverage the power of optionals. This next extension will add a function to all arrays that returns the item or nil if it doesn’t exist (instead of crashing):
public extension Array { /** Optional element by indice. - parameter index: Indice of the array element. - returns: An optional element. */ public func get(index: Int) -> Element? { return self.indices.contains(index) ? self[index] : nil } }
Now you can safely retrieve an item by index:
tabBarController.tabBar.items?.get(2)?.selectedImage
View Controllers
In the future, I’ll keep these posts short and sweet. Since this is an intro to the series, I’d like to give you a taste of what’s to come though. So in this next example, I’ll extend the “UIViewController“.
How many times have you needed to display an alert message, but dreaded the thought of the amount of verbose code Apple requires you to write:
let alert = UIAlertController(title: "My Title", message: "This is my message.", preferredStyle: .Alert) alert.addAction(UIAlertAction(title: "OK", style: .Default) { alert in print("OK tapped") } self.presentViewController(alert, animated: true, completion: nil)
This has irritated me long enough so I created an extension for it with a rich set of optional parameters:
public extension UIViewController { /** Display an alert action in a convenient way. - parameter message: Body of the alert. - parameter title: Title of the alert. - parameter buttonTitle: Text for the button. - parameter additionalActions: Array of alert actions. - parameter handler: Call back handler when main action tapped. */ public func alert(title: String, message: String? = nil, buttonTitle: String = "OK", additionalActions: [UIAlertAction]? = nil, preferredStyle: UIAlertControllerStyle = .Alert, includeCancelAction: Bool = false, handler: (() -> Void)? = nil) { let alertController = UIAlertController( title: title, message: message, preferredStyle: preferredStyle ) if includeCancelAction { alertController.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: nil)) } if let additionalActions = additionalActions { additionalActions.forEach { item in alertController.addAction(item) } } alertController.addAction(UIAlertAction(title: buttonTitle) { handler?() }) presentViewController(alertController, animated: true, completion: nil) } } public extension UIAlertAction { public convenience init(title: String, handler: (() -> Void)) { self.init(title: title, style: .Default) { _ in handler() } } }
Now you can simply do this:
self.alert("My Title", message: "This is my message.") { print("OK tapped") } // or even alert("This is my message")
Sweet, huh? Thanks JavaScript đ
You can even take this concept to the “WKInterfaceController” on watchOS:
public extension WKInterfaceController { public func alert(title: String, message: String? = nil, buttonTitle: String = "OK", alertControllerStyle: WKAlertControllerStyle = .Alert, additionalActions: [WKAlertAction]? = nil, includeCancelAction: Bool = false, cancelHandler: (() -> Void)? = nil, handler: (() -> Void)? = nil) { var actions = [WKAlertAction( title: buttonTitle, style: .Default) { handler?() }] if includeCancelAction { actions.append(WKAlertAction( title: "Cancel", style: .Cancel) { cancelHandler?() }) } // Add additional actions if applicable if let additionalActions = additionalActions where !additionalActions.isEmpty { if alertControllerStyle != .SideBySideButtonsAlert { actions += additionalActions } else if actions.count < 2 { // Only two actions are needed for side by side alert actions.append(additionalActions.first!) } } presentAlertControllerWithTitle(title, message: message, preferredStyle: alertControllerStyle, actions: actions) } }
It’s much more interesting on the watch because the “alertControllerStyle” produces vastly different alerts, all wrapped in the same extension with a simple enum parameter.
Conclusion
As you can see with the collection samples, extending protocols is far reaching and makes them magically show up in code everywhere. You can even apply pattern matching on the extensions to zero in on what kind of types they should be applied to instead of a blanket approach. This produces a clean and intendful API and what really sets Swift apart from other languages. I hope you enjoyed these goodies and you have an idea of what’s to come. Feel free to share your own too!
Happy Coding!!
Leave a Reply