cocoa design patterns in swift
DESCRIPTION
We have this new language, Swift, which takes some familiar Apple patterns, and introduces some new ones. With tools like closures and method chaining, there are definitely some new ways to solve the age-old Obj-c architecture challenges. This talk will walk through some of the most common Obj-c design patterns rewritten in Swift, and showcase the strengths and weaknesses of this new language.TRANSCRIPT
Cocoa Design Patternsin Swift@MicheleTitolo
• New language features
• Functional patterns
• Patterns in Swift
• Anti-patterns
What we’ll cover
New language features
Tuples
Tuples group multiple values into a single compound value.
let http404Error = (404, "Not Found")println(http404Error.0)
var http200Response: (statusCode: Int, statusText: String, hasBody: Bool)
http200Response.statusCode = 200http200Response.statusText = "OK"http200Response.hasBody = true
You can put any kind of object in a tuple
Generics
<T>
More than just an id
Abstraction
Create functions without declaring type
struct Stack<T> { var items = [T]() mutating func push(item: T) { items.append(item) } mutating func pop() -> T { return items.removeLast() }}
<T: Equitable>
AnyObject
...used a lot more like id
Closures
“Closures are behaviors with attached state”
- Peter Norvig
Like blocks, but better!
var closure = { (params) -> returnType in statements}
let
Immutable variable
var myString: String? = someFucntion()
if let greeting = myString { println(greeting)}else { println("Not a string") }
Structs + Enums
Structs:Pass-by-value
struct Rect { var origin: Point var size: Size}
Structs can have methods, and conform to protocols
Enums:also pass-by-value
Also can conform to protocols and have methods
enum Rank: Int { case Ace = 1 case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten case Jack, Queen, King func description() -> String { switch self { case .Ace: return "ace" case .Jack: return "jack" case .Queen: return "queen" case .King: return "king" default: return String(self.toRaw()) } }}
Functional Patterns
Function Passing
func printExcitedly(string: String){ println(string + "!!!!!!!")}
var excitedFunc = printExcitedly
Pass functions into functions
func printVeryExcitedly(excitedFunction: (String) -> Void, message: String){
excitedFunction(message.uppercaseString)}
printVeryExcitedly(printExcitedly, "Hello");
Return functions from functions
func beExcited(excitementLevel: Int) -> (String) -> Void { ...}
Patterns
Composition
...because we can pass functions!
The OO way
class Car { let numWheels: Int let numCylinders: Int init (numWheels: Int, numCylinders: Int) { self.numWheels = numWheels self.numCylinders = numCylinders }}
var newCar = Car(numWheels: 4,numCylinders: 4)var otherCar = Car(numWheels: 4, numCylinders: 6)
var motorcycle = ??
class Motorcycle { ...}
This is not ideal
protocol Vehicle { var numWheels: Int {get set} var numCylinders: Int {get set} func drive()}
Better
How do we handle this at scale?
struct WheelSet { var wheelSize: Int var wheelFrictionCoefficient: Float}
struct BodyType { var numWheels: Int var wheels: WheelSet var wheelBase: Float}
enum EngineType: Int { case Automatic, Manual}
protocol Vehicle { var body: BodyType {get set} var transmission: EngineType {get set} func drive(force: Float) func turn(speed: Float)}
Better
...but we can still improve
Factories
Abstract away objectcreation and composition
protocol VehicleCreator { func createVehicle(bodyType: BodyType, engineType: EngineType) -> Vehicle}
class VehicleFactory: VehicleCreator { func createVehicle(bodyType: BodyType, engineType: EngineType) -> Vehicle {}}
protocol VehicleCreator { func createVehicle(bodyType: BodyType, engineType: EngineType) -> Vehicle}
class VehicleFactory: VehicleCreator { func createVehicle(bodyType: BodyType, engineType: EngineType) -> Vehicle {}}
class MotorcycleFactory: VehicleCreator { func createVehicle(bodyType: BodyType, engineType: EngineType) -> Vehicle {}}
This still looks very OO
class VehicleFactory { func wheelSetGenerator(wheelSize: Int, friction: Float) -> WheelSet { return WheelSet(wheelSize: wheelSize, wheelFrictionCoefficient: friction) } func generateBodyType(wheelCount: Int, wheelType: WheelSet, wheelBase: Float) -> (Void) -> BodyType { func bodyGen() -> BodyType { return BodyType(numWheels:wheelCount, wheels:wheelType, wheelBase:wheelBase) } return bodyGen } func createVehicle( bodyGenerator:(wheelCount: Int, wheelType: WheelSet, wheelBase: Float) -> BodyType, transmission: EngineType) -> Vehicle {}}
let factory: VehicleFactory = VehicleFactory()let motorcycleWheelSet: WheelSet = factory.wheelSetGenerator(28, friction: 0.5)let motorcycleBodyGen = factory.generateBodyType(2, wheelType:motorcycleWheelSet, wheelBase: 45)let motorcycle = factory.createVehicle(motorcycleBodyGen, transmission: .Manual)let electricMotorcycle = factory.createVehicle(motorcycleBodyGen, transmission: .Auomatic)
Factories in Swift can be incredibly flexible
Command
A delegate chainthat returns a function
protocol Commander { func commandSomething(String) -> (Int) -> Dictionary<String, String>}
class ViewController: { let delegate: Commander}
Why is this better than closures?
It’s more explicit
It’s more flexible
Enumeration
Not the type enum!
We have new ways of transforming collections
[1,2,3,4].map { (var number) -> Int in return number * number}
var array = [3, 2, 5, 1, 4]array.sort { $0 < $1 }
let array = [1, 2, 3, 4, 5]let reversedArray = array.reverse()// reversedArray = [5, 4, 3, 2, 1]
And of course, this is useful for more than just math
var vehicles = [suv, motorcycle, electricMotorcycle, car, truck]var manualVehicles = vehicles.filter { (var vehicle: Vehicle) -> Vehicle in return (vehicle.transmission == .Manual) }// manualVehicles = [motorcycle, truck]
Encapsulate better
func meanMedianMode(a: Array<Int>) -> (mean:Int, median:Int, mode:Int) { var mean = findMean(a) var median = findMedian(a) var mode = findMode(a) return (mean, median, mode)}
Tuples with names are incredibly useful
Anti-Patterns
No changes to handling protocols
This is the slide I was hoping to put something magical on
class ViewController: UIViewController, UITableViewDataSource { var tableView: UITableView func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! { var cell : UITableViewCell = tableView.dequeueReusableCellWithIdentifier("Cell") as UITableViewCell if (cell == nil) { cell = UITableViewCell(style: UITableViewCellStyle.Default, reuseIdentifier: "Cell") } cell.textLabel.text = "Hello World" return cell } func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int { return 1 }}
Protocols are first class citizens in Swift, just like in Obj-c
Tuples all the way down
Yes, you can put a tuple in a tuple
These are temporary data structures
We have better OO tools available
Operator overloading
Don’t overload default operators
func decode(json: JSON) -> User? { return _JSONObject(json) >>> { d in User.create <^> d["id"] >>> _JSONInt <*> d["name"] >>> _JSONString <*> d["email"] >>> _JSONString }}
Operators make code harder to read
Type is still important
Getting classes from AnyObject are kind of a pain
Still be explicit when you can
var music: [String: String] = ["AC/DC": "Hells Bells", "Red Hot Chili Peppers": "Californication"]
In Summary
Swift gives us new tools
But Objective-C, and its patterns, aren’t going away
anytime soon
• Today 11:30amInteroperating Swift with Lower Level Code by Stephan Tramer in Terrace
• Tomorrow 1:45pm Functional Programming in Swift by Chris Eidhof in Vail
More Swift!
• Swift Programming Guide from Apple
• Design Patterns in Dynamic Programming by Peter Norvig
• Erica Sadun’s many blog posts on Swift
• Functional Programming in Swift, ebook by Objc.io founders (See Chris’ talk tomorrow!)
• Instance Methods and Curried Functions in Swift by Ole Begemann
Resources
Thanks!@MicheleTitolo