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.
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 toself
a weak reference. If you know that the reference will never benil
then you can add[unowned self]
instead.
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 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!
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
you can create your own operators
Pro tip: Don't if it's not
==
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")
}
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 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 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 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 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 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 {
}
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.
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
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 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 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!")
}
Quick
for testing harness
swift-hamcrest
for assertions
Homebrewd mocking framework
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")))
}
}
}
}
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")))