Ilya Loshkarev loshkarev.i@gmail.com
SFEDU 2017
func greet(person name: String, from hometown: String) -> String {
return "Hello, \(name)! Glad you could visit from \(hometown)!"
}
print(greet(person: "Ivan", from: "Rostov"))
You can ommit a label by using _
func greet(_ name: String, _ hometown: String) -> String {
return "Hello, \(name)! Glad you could visit from \(hometown)!"
}
print(greet("Ivan", "Rostov"))
Labels allow function to be called in an expressive manner
Think hard before you ommit an argument label
func swapTwo(_ a: inout Int, _ b: inout Int){
let c = a; a = b; b = c
}
var a = 1, b = 2
swapTwo(&a, &b)
All parameters are constant by default
Swift doesn't encourage functions with side effect
func add(_ a: Int, _ b: Int) -> Int {
return a + b;
}
func mult(_ a: Int, _ b: Int) -> Int {
return a * b;
}
func calc (_ op: (Int, Int) -> Int, _ a: Int, _ b: Int)) {
print(op(a, b))
}
let a = 5, b = 6
calc(add, a, b)
calc(mult, a, b)
Function's type consists of
types of parameters and return value
func +(_ left: Bool, _ right: Bool) {
return left || right
}
let a = true, b = false
let c = a + b
func counter(forward: Bool) -> (Int) -> Int {
func stepForward(input: Int) -> Int {
return input + 1
}
func stepBackward(input: Int) -> Int {
return input - 1
}
return forward ? stepForward : stepBackward
}
func counter(increment: Int, forward: Bool) -> (Int) -> Int {
func backward(input: Int) -> Int {
return input - increment
}
return !forward ? backward :
{ (input: Int) -> Int in return input + increment }
}
Closures are blocks of code that can capture
local constants and variables
// global function
func counter(increment: Int, forward: Bool) -> (Int) -> Int {
// local function – increment is captured
func backward(input: Int) -> Int {
return input - increment
}
return !forward ? backward :
// closure expression – increment is captured
{ (input: Int) -> Int in return input + increment }
}
Local functions and closure expressions
can capture any value in context
Global functions don't capture any values
let names = ["Anna", "Ivan", "Maria", "John"]
var reversedNames = names.sorted(by: {
(s1: String, s2: String) -> Bool in
return s1 > s2
})
Closure expression – unnamed block of executable code
that can capture values from local context
var reversedNames = names.sorted(by:
{ s1, s2 in return s1 > s2 })
Parameter types can be inferred from context
var reversedNames = names.sorted(by: { return $0 > $1 })
Parameters can be referenced by shorthand names
var reversedNames = names.sorted(by: { $0 > $1 })
Single-expression closure implicitly returns its result
var reversedNames = names.sorted { $0 > $1 }
Closure can be passed with trailing syntax
var reversedNames = names.sorted(by: >)
Any operator is a closure
func counter(withIncrement: Int) -> () -> Int {
var totalCount = 0
return { totalCount += withIncrement }
}
let newCounter = counter(withIncrement: 10)
print(newCounter()) // 10
let anotherCounter = newCounter
print(anotherCounter()) // 20
Closures are reference type
var names = ["Anna", "Ivan", "Maria", "John"]
let nextInLine = { names.remove(at:0) }
print("Next one is \(nextInLine()!") // Anna!
print(names.count) // 3
func whosNext(provider nextInLine: @autoclosure () -> String) {
print("Next one is \(nextInLine())")
}
whosNext(provider: names.remove(at:0))
@autoclosure
automaticaly wraps parameter
in closure expression
Global functions and interfaces from NSFoundation
are availiable in Swift
let flag = "🇷🇺"
print(flag.characters.count) // Charcter View
// Prints "1"
print(flag.unicodeScalars.count) // Unicode View
// Prints "2"
print(flag.utf16.count) // UTF-16 View
// Prints "4"
print(flag.utf8.count) // UTF-8 View
// Prints "8"
Each view has its own indices
let start = str.startIndex
let end = str.endIndex
let range = start.successor()..<end.advancedBy(-2)
str.replaceRange(range, with: " swift ")
var doubles: [Double] = []
var emptyFloats: Array = Array()
doubles.append(contentsOf: [3.0, 2.0, 1.0])
doubles += [0.0, -1.0]
var index = doubles.index(of: 2.0)
for number in doubles {
print("\(number)")
}
struct Point {
var x = 0.0, y = 0.0
}
class Polygon {
let points: [Point]
init (_ points: [Point]) {
self.points = points
}
var center: Point {
/* Calculate center */
return Point(x: xVal, y:yVal)
}
}
Classes are reference type
Structures are value type and can not be inherited
class Polygon {
let points: [Point] // has to be set in initializer
let name = "polygon" // has a default value
var area: Double { // read-only property
/* Calculate area */
}
var origin: Point {
get {/* do something */}
set(newOrigin) {/* do something */}
}
}
struct Point {
mutating func offset(_ xStep: Double, _ yStep: Double) {
/* Change x, y */
}
func distance(_ to: Point) -> Double {
/* Calculate distance */
}
}
class Polygon {
func contains(_ p: Point) -> Bool {
/* do something */
}
}
Structure methods that change properties has to be marked mutating
class Shady { // implicitly internal
private var things: [String] = []
private func stuff() {}
func allClear() {} // implicitly internal
public var totallyClear: String
}
private class Classified { // explicitly private
var nothing: String // implicitly private
}
All classes and class members are implicitly internal
class ARCout {
init(){
print ("initialized")
}
deinit() {
print ("deinitialized")
}
}
var reference1: ARCout? = ARCout() // initialized
var reference2 = reference1
reference1 = nil
reference2 = nil // deinitialized
Deallocates memory if no references to object exists
class ARCout {
var reference: ARCout?
}
var obj1 = ARCout() // initialized
var obj2 = ARCout() // initialized
obj1.reference = obj2
obj2.reference = obj1
obj1 = nil
obj2 = nil
// But nothing happened!
class ARCout {
weak var reference: ARCout?
}
weak
references don't increase retain count
unowned
references don't have to be optional values
class Triangle: Polygon {
init(_ a: Point, _ b: Point, _ c: Point) {
let verts = [a,b,c]
super.init(verts)
name = "triangle"
}
override var area: Double {
/* Calculate area */
}
func inradius: Double {
/* Calculate incircle radius */
}
}
super
provides access to superclass members
Any overwritten member should be marked with override
let shapes = [Polygon]()
/* appended something to shapes */
for shape in shapes {
if shape is Triangle {
print("it's a triangle")
}
}
for shape in shapes {
if let tri = shape as? Triangle {
print(tri.inradius)
}
}
as?
wraps result into optional,
returns nil
if variable cannot be downcasted
as!
triggers a runtime error instead
enum MyErrors: ErrorType {
case OutOfOptions
}
func chooseOption() throws -> Int {
throw MyErrors.OutOfOptions
}
ErrorType
– empty protocol for declaring errors
Only throwing functions can throw and propagate errors
do {
var option = try chooseOption()
}
catch MyErrors.OutOfOptions {
print("I'm all out of options here!")
}
try
can only be used in do-catch
block
do-catch
block stops error propagation
if it catches an error
var option = try? chooseOption() // Int?
func makeAnOption() -> Int {
var option = try! chooseOption() // just do it
return option
}
try?
wraps value into an optinal, retruns nil
if error occurs
try!
fails here and now, stops error popagation
guard let option = try? chooseOption() else {
print("Sorry, we're out of options!")
return
}
print("Congratulations, your option is \(option)")
guard
is assert-like operator that allow quick escape
if condition is not met
guard-bound optional is accesible outside of it's else clause