Swift interesting features

or What Swift Language Guide do not tell you.

This post contains the list of interesting features of Swift that I find while using it. To make it more interactive I've put it in a playground that you can checkout here. The list is intended to be updated in future. If you have something you want to share, pull requests are welcome.

Contents:

  1. Var in for-in loop
  2. Property observers for local variables
  3. ~= operator
  4. Curried functions
  5. Subscript with more than one parameter (by AirspeedVelocity)
Var in for-in loop

If you have an array of reference type objects you can mutate them in a loop just by adding var before loop variable. This will work only for reference types as value types are copied on assignment and so the items in array will be not modified. If you have value types though you will be able to modify loop variable inside the loop. It will work pretty much like mutable function arguments.

// Value type
var valuesArray = [1, 2, 3]  
for (var valueItem) in valuesArray {  
    ++valueItem // 2, 3, 4
}
valuesArray // 1, 2, 3

// Reference type
class Box {  
    var value: Int
    init(_ value:Int) {
        self.value = value
    }
}

var objectsArray = [Box(1), Box(2), Box(3)]  
for (var item) in objectsArray {  
    ++item.value // 2, 3, 4
}
objectsArray // 2, 3, 4


Property observers for local variables

This does not work in plyagrounds but give it a try in a real project and you will see "Will set 1" and "Did set 1":

func method() {

    var some: Int = 0 {
        willSet {
            print("Will set \(newValue)")
        }
        didSet {
            print("Did set \(some)")
        }
    }
    some = 1
}

method()  


~= operator

This is an expression matching operator. This is what switch statement uses for pattern matching. Outside switch you can use it i.e to find out if Range contains value.

let some = 1  
if 0...5 ~= some {  
    print("\(some) is between 0 and 5")
}

You can even override this operator like any other to crate some crazy things. For more information check out docs.

typealias Age = UInt

enum Gender {  
    case Male
    case Female
}

class Person {

    var name: String
    var age: Age
    var gender: Gender

    init(_ name: String, age: Age, gender: Gender) {
        self.name = name
        self.age = age
        self.gender = gender
    }

}

func ==(lhs: Person, rhs: Person) -> Bool {  
    return lhs.name == rhs.name && lhs.age == rhs.age && lhs.gender == rhs.gender
}

let Ilya = Person("Ilya", age: 28, gender: .Male)  
let Marina = Person("Marina", age: 27, gender: .Female)  
let me = Ilya

func ~=(pattern: Person, value: Person) -> Bool {  
    return pattern == value
}

switch me {  
case Ilya:  
    print("Hi, man!")
case Marina:  
    print("Hi, gorgeous!")
default: break  
}

func ~=(pattern:Range<Age>, value: Person) -> Bool {  
    return pattern ~= value.age
}

switch me {  
case 0...18:  
    print("You are too young for that")
case 18..<UInt.max:  
    print("Do what ever you want")
default: break  
}

Unfortunatelly complier will not let you to match your class against tuple or enum, which would be cool. But you can still use ~= directly if you define it with tuple or enum as a pattern to match. To make it better swap right and left hand statements. This can be fun but I don't see very good usecases for that.

func ~=(pattern: Person, value: Gender) -> Bool {  
    return pattern.gender == value
}

switch me {  
case _ where me ~= .Male:  
    print("Hi, man!")
case _ where me ~= .Female:  
    print("Hi, gorgeous!")
default: break  
}


Curried functions

Instance methods in Swift are actually curried functions. You can store it in variable and apply to different instances. Checkout this post by Ole Begemann for one of use cases for that feature.

extension Person {

    func growOlder(years: Age) {
        self.age += years
    }

}

let growOlder = Person.growOlder  
growOlder(Ilya)(5)  
growOlder(Marina)(5)  


Subscript with multiple parameters (by AirspeedVelocity)

Did you know that you can provide more that one paramtere to subscript? Also ++ will not just change returned value but will also write it back to that subscript. It's possible because subscript parameters are inout.

extension Dictionary {  
    subscript(key: Key, or or: Value) -> Value {
        get {
            return self[key] ?? or
        }
        set {
            self[key] = newValue
        }
    }
}

var dict = ["a": 1]  
dict["a"]?++ // 1  
dict // ["a": 2]

dict["b"]?++ // nil  
dict // ["a": 2]

dict["c", or: 3]++ // 3  
dict // ["c": 4, "a": 2]  
comments powered by Disqus