Skip to content

Instantly share code, notes, and snippets.

@potmo
Last active February 14, 2016 20:01
Show Gist options
  • Save potmo/2a53eac853ca21e541c4 to your computer and use it in GitHub Desktop.
Save potmo/2a53eac853ca21e541c4 to your computer and use it in GitHub Desktop.
swift tutorial

Functions

func returnMyCoolStuff(name: String) -> String {
    return "freshness"
}

function arguments can also have default paremeter values

func writeLessCodeAndMakeItMoreCompexInstead(name: String = "some dude", age: Int = 6) -> String {
    return "\(name) is \(age) yo."
}

writeLessCodeAndMakeItMoreCompexInstead() // "some dude is 6 yo."
writeLessCodeAndMakeItMoreCompexInstead("carl") // "carl is 6 yo."
writeLessCodeAndMakeItMoreCompexInstead("carl", age: 12) // "carl is 12 yo."

Pro Tip: Put the default value arguments at the end. I don't think you have to but do it anyway

Pro Tip: Don't use default values

Function arguments have an internal and an external name. The first argument have _ as external name and whatever you write as internal

func singIt(one: String, two: String, three: String) -> String {
    return "\(one), \(two), \(three)! Doing the omoralisk schlager festival!"
}

// in this case you call the function as this
singIt("esseh one", two: "esseh two", three: "esseh one two three four")

you can also redefine whatever you want to show externally

func singItAgain(first one: String, second two: String, third three: String) -> String {
    return "\(one), \(two), \(three)! Doing the omoralisk schlager festival!"
}
singItAgain(first: "first", second: "the sec", third: "the thirdio")

You can also choose to hide the external names. You do that with the "I don't give a shit keyword _" remember that the default signature is (_ internal1, externalandinternal2 externalandinternal2, externalandinternal3 externalandinternal3) so the first one can be as is without the leading _

func singItPlease(one: String, _ two: String, _ three: String) -> String{
    return "\(one), \(two), \(three)! Doing the omoralisk schlager festival!"
}
singItPlease("first", "the sec", "the thirdio")

Pro tip: Confusionally this does not apply to the init constructor function that one has by default the signature (externalAndInternal1 externalAndInternal1, externalAndInternal2 externalAndInternal2) etc.

Closures

let reversed0 = ["Joe", "Ben", "Bill"].sort({ (s1: String, s2: String) -> Bool in
    return s1 > s2
})

the types can be inferred from the context so this works as well

let reversed1 = ["Joe", "Ben", "Bill"].sort( { s1, s2 in return s1 > s2 } )

Since this is a single expression the return in implicit so this works:

let reversed2 = ["Joe", "Ben", "Bill"].sort( { s1, s2 in s1 > s2 } )

you can also use the shorthand names. This works as well

let reversed3 = ["Joe", "Ben", "Bill"].sort( { $0 > $1 } )

if the last argument to a function is a closure you can use the trailing closure syntax put the closure after the parenteses

let reversed4 = ["Joe", "Ben", "Bill"].sort() { $0 > $1 }

and when you are at it just drop the parenteses totally is fine

let reversed5 = ["Joe", "Ben", "Bill"].sort{ $0 > $1 }

Bonus: since the operator in this case is also a function you can actually drop the arguments

let reversed6 = ["Joe", "Ben", "Bill"].sort(>)

Pro tip: When you hoist (i.e. use a variable from the scope outside the closure) you might leak memory. There is an annotation that looks somethign like [weak self] that you can add to make the reference to self a weak reference. If you know that the reference will never be nil then you can add [unowned self] instead.

Type Casting

Casting is done with the as operator.

let nsString:NSString = NSString(string: "woot")
let string:String = nsString as String

depending on if the compiler can promise that this works you might have to handle fail cases

let number: Float = 100.123

if let anotherString = nsString as? String {
    // yes it worked
}

or with a guard

guard let yetAnotherString = nsString as? String else {
    // no it did not work
    // (we would return here but it's a playground and other rules apply)
    // return
}
print(yetAnotherString)

as usual you can be brave and do:

let forcedString = nsString as! String

and have it blow up in your face if it failed

Pro tip: Don't

You can also check if it is a type with the is keyword

if number is Double {
    // do fancy double stuff
} else if number is Int {
    // do not so fancy integer stuff
}

Pro tip: this can be done in a switch too

switch number {
case let integer as Int:
    // integer is now an int
    print("it was an int")
case let double as Double:
    print("it was a double")
}

Extensions

Extensions are not the same as extensions in for example java. I would rather call it "Monkey patching". dr. Dave calls it "Mirage". Not sure... Anyway... what you do is that you add functionality to an already existing type.

Extensions are added to a new file with the name TheClass+TheExtension.swift

For example you can make a class implement a protocol

protocol Serializable {
    func serialized() -> String
}

struct MyCoolThing {
    let name: String
}

extension MyCoolThing: Serializable {
    func serialized() -> String {
        return "<name>\(self.name)</name>"
    }
}

You dont have to extend with a protocol. You can just add stuff also

extension MyCoolThing {
    func withSugar() -> String {
        return "sugar \(self.name)"
    }
}

Pro Tip: You can do crazy things. Don't

extension Int {
    func repetitions(task: () -> Void) {
        for _ in 0..<self {
            task()
        }
    }
}

3.repetitions({
    print("Hello!")
})

// Hello!
// Hello!
// Hello!

Equatable

Stuff in swift is not automatically equatable so you'll have to implement it youreself You do that by implementing the Equatable protocol The Equatable protocol requires you to implement the == operator.

struct MyFancyThing: Equatable {
    let name: String
}

Add an protocol extension

func ==(leftHandSide: MyFancyThing, rightHandSide: MyFancyThing) -> Bool {
    return leftHandSide.name == rightHandSide.name
}

Pro Tip: remember that == is equal and === is the same instance in swift

Operator overloads

you can create your own operators

Pro tip: Don't if it's not ==

Switching

let approximateCount = 12
let naturalCount: String
switch approximateCount {
case 0:
    naturalCount = "no"
case 1..<5:
    naturalCount = "a few"
case 5..<12:
    naturalCount = "several"
case 12..<100:
    naturalCount = "dozens of"
case 100..<1000:
    naturalCount = "hundreds of"
default:
    naturalCount = "many"
}

Switching tuples

let somePoint = (1, 1)
switch somePoint {
case (0, 0):
    print("(0, 0) is at the origin")
case (_, 0):
    print("(\(somePoint.0), 0) is on the x-axis")
case (0, _):
    print("(0, \(somePoint.1)) is on the y-axis")
case (-2...2, -2...2):
    print("(\(somePoint.0), \(somePoint.1)) is inside the box")
default:
    print("(\(somePoint.0), \(somePoint.1)) is outside of the box")
}

Using the where clauses

let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
    print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
    print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
    print("(\(x), \(y)) is just some arbitrary point")
}

Fallthrough is not implicit

let something = true
switch something {
case true:
    print("it was true and ... ")
    fallthrough
case false:
    print("it was false")
}

Inout

func changeThings(inout name: String) {
    name = "caitlyn"
}

When passing in a variable to a inout function you have to prefix it with &

var myName = "bruce"
changeThings(&myName)
myName

// myName is now "caitlyn"

Generics

Generics works a bit like Java generics but there is no type erasure. (Yay!) Both functions and classes can be generic

func someFunction<T>(someT: T) -> T {
}

You can constrain the generic type

func someFunction<T: SomeClass>(someT: T) {
}

and even add constrains referring to other constrains

func allItemsMatch<
    C1: Container, C2: Container
    where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>(a:C1, b:C2) -> Bool {
}

Protocols

Protocols are jus like Java protocols. Kindof

You can have init as a requirement.

protocol SomeProtocol {
    init(someParameter: Int)
}

Then you'll have to mark the init with required.

class SomeClass: SomeProtocol {
    required init(someParameter: Int) {
    }
}

You can make a protocol only be adaptable by classes

protocol SomeClassOnlyProtocol: class {
    // class-only protocol definition goes here
}

You can require something to conform to multiple protocols

func justMakeItWork(dude: protocol<Developer, Operations>) {
}

You can make some functions optional in your protocol. But it has to be @objc

@objc protocol CounterDataSource {
    optional func canDoThis(count: Int) -> Int
    optional var mightHaveThis: Int { get }
}

Structs

Structs are like C-Structs. They are value types and are passed around. Some stuff that is different from classes are

  • No inheritence
  • No type casting
  • No deinitializers
  • It is a value type so each variable keeps their own copy. Just like an Int
struct ValueableThing {
    private let name: String
    init(name: String) {
        self.name = name
    }
}

You get a default init if you dont' have one from the government if all your fields are public

struct ValueableThingWithAutoConstructor {
    let name: String
}

let myThing = ValueableThing(name: "life") // oh that was cheesy

Anyway. If you want to mutate a struct with a function then you have to mark that with mutating

struct ValueableThingWithMutable {
    var name: String
    
    mutating func addName(name: String) {
        self.name += name
    }
}

The compiler will see this and stop you from mutating a struct if it is in an immutable let field. i.e.

let thing = ValueableThing(name: "hello")
//thing.addName(" world") // No soup for you

This is ok tho

var thingWithMutable = ValueableThingWithMutable(name: "hello")
thingWithMutable.addName(" world") // sure thing
thingWithMutable.name // is now `hello world`

Tuples

Tuples are just like a temporary stuct. Kindof.

let coordinate: (Int, Int) = (123, 123)

you can explode the tuple like this

let (x0, y0) = coordinate
// now you have x and y as variables

You can also access tuples with the default names (hint: its the index)

let x1 = coordinate.0
let y1 = coordinate.1

It is also possible to name the fields in a tuple

let coordinateWithNames = (x: 123, y: 123)
let x2 = coordinateWithNames.x
let y2 = coordinateWithNames.y

// this still works
let x3 = coordinate.0
let y3 = coordinate.1

Pro Tip: Tuples are value types so

var anotherCoordinate = (123, 234)
var otherCoordinate = anotherCoordinate
otherCoordinate.0 = 666

otherCoordinate.0 // is now 666
coordinate.0 // is still 123

Ranges

Ranges are pretty useful. You create one by

let range = Range(start: 0, end: 10)

Or you can use the syntactic sugar

let range = 1..<5

You can do this:

let numbers = [1,2,3,4,5,6,7,8,9]
numbers[1..<4] // [2,3,4]

x..<y is excluding y x...y is including `y

You can do a simple for loop like this

for i in 0..<5 {

}

Panic

When nothing works. Use panic It will log the line and function it was called from and then call fatalError that will break the program.

Error Handling

Swift errors are not specifying what are thrown. So it doesn't really work like java checked exceptions.

Erros are enums so to create one:

enum MyOwnError: ErrorType {
    case SomethingBroke
    case ThisSouldNeverHappenError
    case ItWentSouth
}

You add the throws keyword to say that it throws

func somethingThatThrows() throws -> String {
 throw MyOwnError.ItWentSouth
}

You catch errors just inside a do clause.

do {
    try somethingThatThrows()
} catch MyOwnError.ThisSouldNeverHappenError {
  // just this error
} catch {
 // everything else
}

Pro tip: You can use where here as well.

if you dont' want to bother (and crash hard if it throws)

try! somethingThatThrows()

Pro tip: Don't

///////////////////// ENUMS

Emums are a bit strange in swift

enum CoolEnum {
case One
case Two
case Three
}

When accessing any static vairable and there are no ambiguity you can ommit the type.

let someEnum: CoolEnum = .One

is the same as this since the type can be inferred.

let theSameEnum: CoolEnum = CoolEnum.One

Pro tip: This works for everything static

You must exhaust enums when switching but no need to explicitly break

switch someEnum {
case .One:
    print("something")
case .Two:
    print("something else")
case .Three:
    print("something more")
}

Enumerations can hold associated values. an associated value is like a constant atttached to the instance of the enum

enum CoolThings {
    case Yeah(gun: String)
    case Laser(watts: String)
}
switch coolThing {
case .Yeah(let gun):
	print("it was yeah with \(gun)"
case .Laser(let watts): 
	print("it was laser with \(formatter.formatEnergy(watt)")
}

Pro Tip: There are indidrect enumeration cases that can be recursive in swift 2.1

Mutability

variables are marked as mutable or immutable mutable is marked with var and immutable with let

var obama = "change"
let thatcher = "nah"

Pro Tip: When possible, opt for let

Constructors

Constructors is a strange thing in swift With class inheritence the current class must initialize all fields before calling super You are not allowed to access self before calling super. (except if you are setting the fields...)

class NotSoCoolClass {
    
}

class CoolClass: NotSoCoolClass {
    let boom: String
    var fresh: String
    var yeah: String?
    override init(){
        self.boom = "chackalack"
        self.fresh = "prince"
        self.yeah = nil
        
        super.init()
        
        self.iCanNowDoStuffs() // before `super.init()` call this is not ok
    }
    
    func iCanNowDoStuffs(){
        print("gg")
    }
}

Pro Tip: There is a deinit too if you feel brave and want to handle your memory yourself

Optionals

Optionals are evil and has a special place in swift compiler hell. It is actually just syntactic suger around the enum type Optional

Pro Tip: Don't use optionals if you don't have to

let maybe: String?

Is the same as

let maybeVerbose: Optional<String>

Unwrapping

You can force unwrap with the exclemation mark ! If the optional contains nil then the program will explode hard Try to not use it

let really: String = maybe! // explodes hard if `maybe` is `nil`

it is better to handle the nil case:

if let really = maybe {
    // really is now typed as `String` in here
}

It's nice to do early exit checks then use guard

guard let really = maybe else {
    // the guard will force you to leave the current scope with `return` or `break`
    return
}
//really will be typed as `String` down here

You don't have to come up with a new name really either this is perfectly fine and is used often in the wild same goes for the if let

guard let maybe = maybe else {
    //return // we should return but can't in playgrounds
}

If something is nil and you want to use a fallback value you can do a regular ternery expression

let orangutan:String? = "🐵"
let gorilla:String = "🐒"
let primate = orangutan != nil ? orangutan : gorilla

// and it can be expressed in a more compact way like this:
let primate2 = orangutan ?? gorilla
```swift

You can do optional chaining if you want to reach deep into an optional tree structure

```swift
let maybe: String? = something?.maybeHere?.couldBeThis?.shouldBeSomething?.emptyMaybe?.giveItToMe()

Of course you can swapp that ? with a ! it you think youre smarter than the compiler.

As said before. ? is just syntactic sugar around Optional<> and you can unwrap it manually if you want:

let thing: String? = ""

switch thing {
case .Some(let value)
	return value
case .None:
	panic("I don't know what to do!")
}

Testing

Quick for testing harness swift-hamcrest for assertions Homebrewd mocking framework

A simple test

import Foundation
import Quick
import Hamcrest
import XCTest

class MyCoolThingTest: QuickSpec {
    
    override func spec() {
        // Since this will be reassigned it needs to be a `var`.
        // Since the compiler can't promise that it will always be
        // assigned we tell the compiler to not bother with force unwrapped optional `MyCoolThing!`
        // that way you won't have to unwrap it all the time in your tests.
        var myCoolThing: MyCoolThing!
        beforeEach() {
            myCoolThing = MyCoolThing()
        }
        
        describe("doing cool things") {
            it("should return the cool thing") {
                let result = myCoolThing.getCoolString("crazy")
                assertThat(result, equalTo("my cool crazy thing"))
            }
        }
        
        describe("doing cool optional things") {
            it("should return the cool thing and not nil") {
                let result = myCoolThing.getCoolOptionalString()
                // use `presentAnd` to assert an optional and then assert the value
                assertThat(result, presentAnd(equalTo("my cool thing")))
            }
        }
    }
}

Mocking

Put the mocks in /Mocks and reuse them for old tests

class MockedMyCoolThing: MyCoolThingProtocol {
    // generic magic that creates a new HamcrestSpy1<String,String>
    var getCoolString_spy = spy(getCoolString)
    
    // creates a new HamcrestSpy0<String>
    var getCoolOptionalString_spy = spy(getCoolOptionalString)
    
    // creates a HamcrestSpy0Void
    var doCoolThing_spy = spy(doCoolThing)
    
    func getCoolString(what: String) -> String {
        return getCoolString_spy.call(what)
    }
    
    func getCoolOptionalString() -> String? {
        return getCoolOptionalString_spy.call()
    }
    
    func doCoolThing() -> Void {
        // you dont have to worry that this returns `Void`. No object is also an object
        // Actually, `Void` is an typealias for an empty tuple like `()`
        return doCoolThing_spy.call()
    }
}

and then to use it

var myCoolThing: MockedMyCoolThing!

myCoolThing = MockedMyCoolThing()

setup an expectation if you don't do that and call the mock then the test will panic with something hopefully usefull in the log

Pro Tip: Don't forget to expect the inquisition

return simple value for all arguments

myCoolThing.getCoolString_spy.whenCalledWith(anything(), thenReturn: "bananas")

return simple value

myCoolThing.getCoolString_spy.whenCalledWith(equalTo("wow"), thenReturn: "bananas")

returning different stuff depending on call num

myCoolThing.getCoolString_spy.whenCalledWith(anything(), thenReturnInOrder: ["bananas", "yes", "fresh"])

calling a mock implementation

myCoolThing.getCoolString_spy.whenCalledWith(anything(), thenReturnCall: {
    argument1 in
    return "sweet and \(argument1)"
})

for spies that does not take any params there are a flavour that does

myCoolThing.getCoolStringThatDoesNotTakeAnyParams_spy.whenCalled(thenReturn: "bananas")

and now to assertions on the spies

Just inspect a call to see what it was called with

myCoolThing.getCoolString_spy.inspectCall(0) {
    argument1 in
    assertThat(argument1, equalTo("cowboys"))
}

checking how many times it was called

assertThat(myCoolThing.getCoolString_spy, hasBeenCalled(times: 1))

checking that it was actually called with some argument

assertThat(myCoolThing.getCoolString_spy, hasBeenCalledWithArg(equalTo("laserz")))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment