Monday, March 30, 2015

Apple Swift language - crash course & language reference

Apple recognised that Objective-C is not only not modern but very much not appealing to developers who are used to modern languages. As this can be a barrier to entry it was the right choice to introduce a new language called Swift.

While the language is similar to most modern languages, it has its own quirks. On the one hand it's somewhat debatable why did Apple invent a brand new language instead of using another existing one, like Google did with Java for Android, on the other hand Swift is actually a well thought and fun to use language. Unlike with Google's own Go, I did not miss anything from the language as I was building my app with it.

However, it's worth to note that unlike Google's Go, it's not just a language with a compiler, it's a fully supported platform: every OSX and iOS API can be called using Swift, XCode fully supports Swift (eg.: code completion), and most of online Apple Developer documentation is translated to show both Objective-C and Swift samples.

It's not a managed platform

Jumping on Swift might feel that it's a managed platform, just like Python, Java, or .NET. It is not, there is no garbage collection or anything like that that will magically save us from memory leaks. It is a fully native platform so any memory management is still on us. Obviously Apple helps us with ARC but we still have to understand what is a strong, weak, or unowned reference is, or what is the difference between a class and a struct.

However, this comes with benefits as well: there is a full interoperability between the Swift and Objective-C code or libraries we use, as they are pretty much compiled to the same binary platform. If we really want to, we can even mix and match the Swift and Objective-C files in the same project; for instance, build the new features in Swift, and maintain the old ones in Objective-C.

The language basics

As I was reading through the Swift documentation I quickly recognised that it's sometimes too verbose; for developers who haven't really seen other languages maybe. For a "Swift crash course" I collected the most notable language features as simple code samples with comments on what they do. I believe this should be enough to start using the language, but if I missed something please don't hesitate to leave a comment. The left side can be copy-pasted into a Swift playground to try it out.

The bad

With any new platforms, Swift comes with its own problems. The biggest problems I've seen are:

  • XCode instability - it frequently crashes with Swift source code. 
  • Compiler issues - I've seen a compiler problem where I had to modify to source code as the only workaround.
  • The language changes quite a bit - since it's release it had two major not-backward compatible updates already.



//
// Variables, constants
//


var str = "Adam"
var num = 11
let comb = str + String(num)
var optionalName : String?
var eventuallyAssigned : String!

optionalName = "Adam"

if let name = optionalName {
    println(name.endIndex)
}

let notOptionalName = optionalName!

eventuallyAssigned = "Adam"
println(optionalName?.endIndex)
println(optionalName!.endIndex)
println(eventuallyAssigned.endIndex)


//
// String formatting
//


println("Formatted \(str + String(num)) :)")
println("Formatted \(str)\(num) :)")


//
// Dictionary
//


var dict = [String : Float]()

dict["Adam"] = 11
println(dict["Adam"])


//
// Loops
//


let numbers = [12,3,12,23,43,53]

var sum = 0

for number in numbers {
    sum += number
}

println(sum)

for var i = 0; i < 4; i++ {
    println(i)
}

for i in 0..<4 {
    println(i)
}

for i in 0...4 {
    println(i)
}


//
// Methods, method pointers
//


func greet(name : String, day : String) -> 
  String {
    return "Good \(day) \(name)"
}

println(greet("Adam", "evening"))

func minmax(numbers : [Int]) -> 
  (min : Int, max : Int) {
    var min = numbers[0]
    var max = numbers[0]
    
    for number in numbers {
        if number < min {
            min = number
        }
        
        if number > max {
            max = number
        }
    }
    
    return (min, max)
}

func avg(numbers : Int...) -> Double {
    var avg = 0.0
    var cnt = 0.0
    
    for number in numbers {
        avg += Double(number)
        cnt++
    }
    
    return avg / cnt
}

var mm = minmax([4,3,1,2,6,8])

println(mm.max)
println(mm.0)

println(avg(4,65,7,2,1))

func innerFunc(n : Int) -> Int {
    func addFive(m : Int) -> Int {
        return m + 5
    }
    
    return addFive(n)
}

println(innerFunc(10))

func makeIncrementer(toAdd : Int) ->
  (Int -> Int) {
    func adder(num : Int) -> Int {
        return num + toAdd;
    }
    
    return adder
}

let fiveAdder = makeIncrementer(5)
fiveAdder(10)

func myfilter(numbers : [Int], predicate: 
  Int -> Bool) -> [Int] {
    var arr = [Int]()
    
    for number in numbers {
        if predicate(number) {
            arr.append(number)
        }
    }
    
    return arr
}


func pred(m : Int) -> Bool {
    return m < 5
}

myfilter([1,2,3,4,5,6], pred)


//
// Closures (lambdas)
//


myfilter([1,2,3,4,5,6], {(m : Int) -> Bool in
    return m < 5})

let tran = [1,2,3].map({(m :Int) ->
  String in String(2*m) + ":)"})
println(tran)

let tran2 = [1,2,3].map({ "a" + String($0 * 2) })

println(tran2)

let r1 = [1,2,3].reduce(0) { $0 + $1 }

println(r1)


let r2 = [1,2,3,4].reduce("= ", 
  combine: {(s :String, i: Int) -> 
  String in s + String(i)})
println(r2)


let f1 = [1,2,3].filter{ $0 < 3 }

println(f1)


//
// Classes
//


class Person {
    let  name : String
    let age : Int

    // can be nil
    weak var child : Person?
    
    // cannot be nil
    // unowned var parent : Person
    
    
    init(name : String, age : Int = 18) {
        self.name = name
        self.age = age
    }
    
    func print(){
        println("Hello")
    }
    
    deinit {
        println("removed person \(name)")
    }
}

class Employee : Person {
    let salary : Int
    var selfRank : Int = 0
    
    init(name: String, age: Int, salary : Int){
        self.salary = salary
        super.init(name: name, age: age)
    }
    
    var rank : Int {
        get {
            return selfRank;
        }
        set {
            selfRank = newValue
        }
    }
    
    func selfReferenceWatchOut() {
        // Closures are reference types, 
        // may hold strong reference to self
        var closure = {
            // Capture list - capturing self, 
            // no strong ref to self
            [unowned self](a: Int) -> Int in
            return self.rank
        }
    }
    
    deinit {
        println("removed employee \(name)")
    }
}

var p = Person(name: "Adam", age: 11)

var e = 
  Employee(name: "Adam", age: 11, salary: 12)
e.rank = 11


//
// Generics
//


func repeat<T>(item : T, times : Int) -> [T] {
    var arr = [T]()
    
    for i in 0..<times {
        arr.append(item)
    }
    
    return arr
}

repeat("Adam", 3)
repeat(true, 3)


//
// Protocols (interfaces), and extentions
//


protocol Printer {
    func toString() -> String
}

class Printable : Printer {
    func toString() -> String {
        return "Just implement protocol"
    }
}

extension Int : Printer {
    func toString() -> String {
        return "Or add an extension to \(self)"
    }
}

7.toString()
//
//
//


String variable, which cannot be nil.
Int variable, which cannot be nil.
String constant. Cannot be changed.
Nillable String variable.
Auto unwrapped, nillable variable.

Assigning a variable.

Unwrapping a potentially nil variable.
Only executed if variable wasn't nil.


Unwrap to a constant. If nil, breaks.

Assigning a variable.
Optionally reads endIndex, if var is not nil.
Reads endIndex. If var is nil, breaks.
Auto unwrapped. If nil, breaks.


//
//
//


Prints "Formatted Adam11 :)".
Prints "Formatted Adam11 :)".


//
//
//


Creates String->Float dictionary.

Stores 11 under key "Adam".
Prints 11. If it was nil, prints nil.


//
//
//


Constant array of Ints.



For-each loop.



Prints 146.

Classical for cycle (0,1,2,3)



Shorter for cycle (0,1,2,3)



For cycle inclusive upper bound  (0,1,2,3,4)




//
//
//


Method taking String, String, returning String.




Executing and printing method result.
"Good evening Adam".
Method returning two named results.














Returning named results.


Method accepting list of Int arguments.











Holds a tuple of 1 and 8.

Prints 8.
Prints 1.

Executing method with list of arguments.


Declaring inner method within method.






Prints 15.

A method that takes an Int and returns another method
that takes an Int and returns an Int.




Adder is a method.


FiveAdder is a method that takes an Int and returns an Int.
It's 15.

A method that takes a list of numbers and another method
as a parameter.



If the result of the predicate is true,
appends to the new, filtered list.



Returns the new filtered list.







[1,2,3,4]


//
//
//


Execute the filter with a locally defined closure.
[1,2,3,4]

Execute the map function on the array with a local closure
and named parameters.
["2:)", "4:)", "6:")]

Execute the map function on the array with a local closure
and unnamed parameters.
[a2, a4, a6]

Execute the reduce function on the array with local closure
and unnamed parameters.
6.



Execute the reduce function on the array with local closure
and named parameters.
"= 1234"


Executes the filter method on the array with local closure
and unnamed parameters.
[1,2]


//
//
//



Constant, must be initialized in constructor.
Constant, must be initialized in constructor.


Weak reference to itself, can be nil.


Unowned (weak) reference to itself, which couldn't be nil.


Age default parameter is 18.








Destructor.




Inheritance.

selfRank is initialized before constructor.



Initialize parent constructor.


Property with getter and setter methods.









Without the unowned capture list it would be a 
strong self reference, and would memory leak.
Any closure that uses "self" has to maintain a 
capture list to avoid memory leaks.







Destructor.



Create a Person object.


Create an Employee object.



//
//
//


Generic method that accepts a T typed item and an Int.
Create a dynamic array of type T.








["Adam", "Adam", "Adam"]
[true, true, true]


//
//
//


Protocol (interface) definition.



Implement a protocol.





Adding an extension to another closed class.





Executing the extension on the class.
"Or add an extension to 7"


1 comment: