Swift

Overview

Variables

Note: Use let instead of var to create a constant. It is best practice to use let as much as possible. In Swift, let is immutable, var is mutable.

var faveNumber = 666      //type inferred as Int
var firstName = "Lucifer" // type iniferred as String
var isAwesome = true      // type inferred as Bool

// Basic data types
var myInt : Int           // type annotation (: == is of Type Int)
var myFloat : Float       // 32-bit floating point
var myDouble : Double     // 64-bit floating point

var myString : String
var myChar : Character
var myBool : Bool         // true or false

Range Operators

... - closed range operator

// Examples
0...100   35...66   0...someVar

..< - half-open range operator. This does not include the last number on the right

0..<100   35..<66   0..<someArray.count

String Interpolation

Use \() for string interpolation

let location = "Hell"
var temperature = 666

println("It is \(temperature) degrees today in \(location)")

let quantity = 22
let unitPrice = 45.64

println("The amount is \(Double(quantity) * price)")

Conditionals

if myVar < 666 {
  // do something
} else {
  // do something else
}

Conditions must be true or false:

var x = 0 // typed as Int
if x {    //will not compile, as it is not a boolean.
  // doSomething()
}

Multiple conditions

// logical AND: &&
if a > 50 && b < 80 { ... }
// logical OR: ||
if isActive || isAdmin { ... }
// use parentheses with complex conditions
if (quantity < 10 && isAvailable) || quantity >= 10 { ... }

Switch Statements

let quantity = 5

switch quantity {
  case 1:
    println("Do this for case 1")
  case 2:
    println("Do this for case 2")
  case 3...10:    //3 dots is the range operator
    println("DO this for 3 to 10")
  default:
      break  //to explicitly end this otherwise empty case
}

Loops

The preferred way is using for-in loop

var total = 0
for index in 1...10 {     //for each-item in some-collection
  total = total + index
}
println("The total is \(total")

var name = "karlo"
for eachChar in name {
  println(eachChar)
}

// while loop
while condition {}
  //...
}

//Do loop
do {
    // ...
} while condition

Functions

By default, function parameters are treated as constants, not variables


// simple function
func myFunction() {
  println("This is a simple function")
}
 // with 1 param, ':' memans is type of
func myFunction(name : String) {

}

func myFunction(age : Int) {
  age = age + 1 // not allowed - age is constant
}

// function with mutable parameter
func myFunction(var age : Int) { // to set fxn param as a variable
  age = age + 1                  // age va can now be updated
}

// function that return values
func myFunction() -> String {
  return "Hello"
}

// using a default value param
func myFunction(name : String = "John Doe") {
  println("Hello, \(name)")
}
// to call
myFunction("Jane")      // Errror - will not work
myFunction()            // OK - uses default param value
myFunction(name:"Lucy") // OK - provide named argument

Arrays

let daysInMonth = [34,2,34,345,45,5]
var colors = ["red","black","white"]

var bands : [String]  // array of strings
bands = ["Motorhead", "Slayer", "Cannibal Corpse"]

//adding to end of array
flavors.append("Nepolitan")   //add to end of array
flavors += ["Wintergreen"]

// Insert at specific position
flavors.insert("Coconut", atIndex: 3)

Dictionaries

var countries = ["US" : "United Sates", "PH" : "Philippines", "GB" : "United Kingdom"]

// declare dictionary of Int as String values
var products : [Int:String]

// Accessing dictionary values
println(countries["PH"])
// updating or inserting
countries["CA"] = "Canada"  // will change OR insert
countries.updateValue("Singapore", forKey:"SG") // longhand method of above syntax
countries.updateValue("France", forKey:"FR")

//to delete a key/value pair
countries["FR"] = nil
countries.removeValueForKey("FR")

// loop a dictionary
for (key, value) in countries {          // a tuple
  println("\(key) is short for \(value)")
}

Tuples

var str = "Lucy"
let num = 666

var myTuple = (str, num)
var myOtherTuple = (str, num, 9876, "Evil One")

// return a tuple from a function
func getNameAndNumber() -> (name: String, number: Int) {
   return("Lucy Fer", 666)
}

// call function
let result = getNameAndNumber()
// decomposing - option 1
println("The name is \(result.0) and the number is \(result.1)")
// option 2 (using a label)
println("The name is \(result.name) and the number is \(result.number)")
// option 3, assign to a constant
let (name, number) = getNameAndNumber()
println("The name is \(name) and the number is \(number)")

Optionals

var temperature : Int?    // optional Int, the `?` declares that the variable to be a valid `Int` or `nil`

temperature = 65
if temperature != nil {
  // forced unwrapping, use `!`
  println("The temperature is \(temperature!) degrees")
}

var countries = ["US" : "United Sates", "PH" : "Philippines", "GB" : "United Kingdom"]

// Optional binding
if let result = countries["XX"] {
    println("The country name is \(result)")
} else {
    println("No matching key found")
}

var result = countries["XX"]  // since XX does not exist

Enumerations

enum SeatPreference {
  case Window
  case Middle
  case Aisle
  case NoPreference
  // or: case Window, Middle, Aisle, NoPreference
}

var myPref : SeatPreference
myPref = SeatPreference.Aisle   // .Aisle for shorthand
var otherPref = .Window

Closures


// a simple closure
{
  println("This is the simplest closure in the world.")
}

func performSixTimes( myClosureParameter : ()->() ) {
  for i in 1...6 {
    myClosureParameter()
  }
}

performSixTimes { ()->() in
    println("This is the simplest closure in the world.")
}

// A function that explicitly takes no parameters and returns nothing
func myFunction ()->()  // alternative way of declaring a function that returns nothing

// A closure that explicitly takes no parameters and returns no values
{ ()->() in
    println("This is a simple closure.")
}

// Using sorted function
let unsortedArray = [9182,223,1,244,227,34,11,234,44]

let sortedArray = sorted(unsortedArray, { (first : Int, second : Int) -> Bool in
  return first < second
})

sortedArray

Classes

class User {
    // STORED properties
    var firstName : String
    var lastName : String
    var score : Int

    // COMPUTED properties, get & set blocks are optional and can be omitted
    var fullName : String {
            // return the computed propery
            return firstName + " " + lastName
    }

    // methods
    func description() -> String {
        return("User \(firstName) has the score of \(score)")
    }

    // default initializer, init is a reserved keyword
    init() {
        firstName = "John"
        lastName = "Doe"
        score = 0
    }

    // custom initializer with parameters
    init(first : String, last : String) {
        self.firstName = first    // use self to refer to current instance of this class' name
        self.lastName = last
        self.score = 20
    }

    deinit {
    // any necessary cleanup code; automatically called when object reach end of its lifetime
    // takes no parameter
    // not needed for majority of classes, but useful if you have a class that iopens a connection to a database or file
    //
    }

}

// instatntiate a new Player object
var firstUser = User()

// use dot syntax
firstUser.firstName = "Player 1"
firstUser.score = 66
println(firstUser.description())

var secondUser = User(first: "Ozzy", last: "Osbourne")
println(secondUser.fullName)

class EliteUser : User {
    //additional properties
    var userLevel : String

    // override required when you subclass a superclass because an init() already exists in the superclass
    override init() {
        userLevel = "Elite"
        super.init()  //this is required to also initialize the superclass variables
    }

    override func description() -> String {
        let originalMessage = super.description()
        return("\(originalMessage) and is a \(userLevel) user")
    }

    // additional methods
    func calculateBonus() {
        self.score += 1000
        println("New score is \(self.score)")
    }
}

var newPlayer = EliteUser()
newPlayer.description()
newPlayer.calculateBonus()

Lazy properties

import UIKit

class Gamer {
    // properties
    var name : String = "John Doe"
    var score = 0
    lazy var bonus = getDailyRandomNumber()

    // methods
    func description() -> String {
        return("Player \(name) has a score of \(score)")
    }

}

var anotherGamer = Gamer()
println(anotherGamer.bonus)

Property Observers

class Gamer {
    // properties
    var name : String = "John Doe" {
        // these two methods are automatically called when the poroperty name changes
        willSet {
            // called just before the property changes
            //'newValue is an implict parameter
            println("About to chnage name to \(newValue)")
        }
        didSet {
            // called right after the property changes
            // oldValue is an implicit parameter
            println("Have changed name from \(oldValue)")
        }
    }
    var score = 0
    lazy var bonus = getDailyRandomNumber()

    // methods
    func description() -> String {
        return("Player \(name) has a score of \(score)")
    }

}

var anotherGamer = Gamer()
anotherGamer.name = "Kerry King"

Access Levels

Structures

Structures Vs Classes

//Pass by value
//function that changes an Int
func changeValue(var number : Int) -> Int {
    number += 1000
    return number
}
var myNumber = 666

//define an Int variable (value type)
changeValue(myNumber)

//original var is unchanged = 666
myNumber
// Pass by reference
class SimpleClass {     //change class to struct will keep original value of 'value'
  var value : Int = 99
}

// Simple function that chnages an object
func changeObject(var object : SimpleClass) -> Int {
  object.value += 1000
  return object.value
}
// create a new instance (reference type)
var myObject = SimpleClass()

// pass the object into the function - Pass By Reference
changeObject(myObject)

// the original object has been changed.
myObject.value

Operators

Equality and Identity

// Note: this will not work on structures, only on objects
===    // identical to
!==     // not identical to
var dateA = NSDateComponents()
dateA.year = 2015
dateA.month = 01
dateA.day = 01

var dateB = NSDateComponents()
dateB.year = 2015
dateB.month = 01
dateB.day = 01

// check equality: ==
if dateA == dateB {
    println("Yes dateA and dateB are equal to each other")
}

// check Identity
if dateA === dateB {
    println("Yes dateA and dateB are equal to each other")
} else {
    println("They might be equal, but not identical")
}

Advanced Operators

var website = personalSite ?? defaultSite


* Remainder operator - `%`
  - can also work with negative numbers and floats, unlike in other languages where only positive integers are allowed

## Advanced Language Features

### Type Casting & Casting

* There are times when the data types you use needs to be treated as a different data type.
* Use `?` and `!` to check and unwrap properties of a variable typecasted to a different object type

```swift
let myButton    = UIButton()
let mySlider    = UISlider()
let myTextField = UITextField()
let myDatePicker = UIDatePicker()

let controls = [myButton, mySlider, myTextField, myDatePicker]

for item in controls {
    //option 1: check type with "is"
//    if item is UIDatePicker {
//        println("We have a UIDatePicker")
//        // downcast with "as"
//        let picker = item as UIDatePicker
//        picker.datePickerMode = UIDatePickerMode.CountDownTimer
//    }
    // option 2: try to downcast with as?
//    let picker = item as? UIDatePicker
//    //then check if works, and unwrap the original
//    if picker != nil {
//        // use ! to force unwrap it to get to its properties
//        picker!.datePickerMode = UIDatePickerMode.CountDownTimer
//    }
    // option 3 - most succint way, combination of option 1 & 2
    if let picker = item as? UIDatePicker {
        picker.datePickerMode = UIDatePickerMode.CountDownTimer
    }
}

AnyObject and Any

var someObject : AnyObject  // needs to be an object
var something : Any         // means any object or any data type

var arrayOfObjects : [AnyObject]    // array of any type of objects
var arrayOfAnything : [Any]         // array of any type of objects or non-object types including tuples, closures
import UIKit

someObject = "This is a simple message"
someObject = 666

if someObject is String {
    let wordsArray = someObject.componentsSeparatedByString(" ")
}

Protocols

protocol ExampleProtocol {
    //method signatures
    func simpleMethod() -> Bool
    var simpleProperty : Int { get }
}

class MyClass : ExampleProtocol {
    // provide anything else you need this class to do
    func simpleMethod() -> Bool {
        // do some work
        return true
    }

    var simpleProperty : Int {
        return 666
    }
}

Adding functionality with Extensions

extension String {
    func reverseWords() -> String {
        let wordsArray = self.componentsSeparatedByString(" ")
        let reversedArray = wordsArray.reverse()
        var newString = ""
        for eachWord in reversedArray {
            newString += eachWord + " "
        }
    return newString
    }
}

var message = "I want to reverse all the words in this string"

message.reverseWords()

Generics

import UIKit

class SimpleClass {
    var singleProperty : String = "A string"
}

let myNumbers = [666, 113, 34, 45, 12, 456, 234]
let myStrings = ["death", "hell", "grave"]
let myObjects = [SimpleClass(), SimpleClass(), SimpleClass(), SimpleClass()]

func displayArray<T>(theArray : [T]) -> T{
    println("Printing the array:")
    for eachItem in theArray {
        print(eachItem)
        print(" : ")
    }
    println()
    let finalElement : T = theArray[theArray.count-1]
    return finalElement
}

var finalInt = displayArray(myNumbers)
++finalInt
var finalString = displayArray(myStrings)
finalString.uppercaseString
displayArray(myStrings)
displayArray(myNumbers)