Cocoa Design Patterns in Swift

Post on 17-Dec-2014

2.584 views 0 download

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 of Cocoa Design Patterns in Swift

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