Swift Summary


  • Swift eliminates entire classes of unsafe code. Variables are always initialized before use, arrays and integers are checked for overflow, memory is automatically managed, and enforcement of exclusive access to memory guards against many programming mistakes
  • Another safety feature is that by default Swift objects can never be nil
  • Swift has an innovative feature known as optionals. An optional may contain nil, but Swift syntax forces you to safely deal with it using the ? syntax to indicate to the compiler you understand the behavior and will handle it safely.
  • Swift is a successor to both the C and Objective-C languages. It includes low-level primitives such as types, flow control, and operators. It also provides object-oriented features such as classes, protocols, and generics, giving Cocoa and Cocoa Touch developers the performance and power they demand.

  • Overview
    • Use let to make a constant and var to make a variable
    • String interlopation

        let name = "John" 
        let greeting = "Hello \(name)"
    • Create arrays and dictionaries using brackets ([]), and access their elements by writing the index or key in brackets. A comma is allowed after the last element.

        var myList = ["item1", "item2", "item3"]
        var myDictionary = [
            "Key1": "Value1",
            "Key2": "Value2
        myDictionary["Key1"] = "Value x"
        let emptyArray = [String]()
        let emptyDictionary = [String: Float]()
    • type information can be inferred

        myList = []
        myDictionary = [:]
    • Control Flow
      • Use if and switch to make conditionals, and use for-in, while, and repeat-while to make loops. Parentheses around the condition or loop variable are optional. Braces around the body are required.

          let vegetable = "red pepper"
          switch vegetable {
          case "celery":
              print("Add some raisins and make ants on a log.")
          case "cucumber", "watercress":
              print("That would make a good tea sandwich.")
          case let x where x.hasSuffix("pepper"):
              print("Is it a spicy \(x)?")
              print("Everything tastes good in soup.")
          // Prints "Is it a spicy red pepper?"
      • You can keep an index in a loop by using ..< to make a range of indexes.

          var total = 0
          for i in 0..<4 {
              total += i
          // Prints "6"
      • Use ..< to make a range that omits its upper value, and use ... to make a range that includes both values.

    • Functions and Closures
      • Use func to declare a function. Call a function by following its name with a list of arguments in parentheses. Use -> to separate the parameter names and types from the function’s return type.

          func greet(person: String, day: String) -> String {
              return "Hello \(person), today is \(day)."
          greet(person: "Bob", day: "Tuesday")
      • By default, functions use their parameter names as labels for their arguments. Write a custom argument label before the parameter name, or write _ to use no argument label.

          func greet(_ person: String, on day: String) -> String {
              return "Hello \(person), today is \(day)."
          greet("John", on: "Wednesday")
      • Use a tuple to make a compound value—for example, to return multiple values from a function. The elements of a tuple can be referred to either by name or by number.

          func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
              var min = scores[0]
              var max = scores[0]
              var sum = 0
              for score in scores {
                  if score > max {
                      max = score
                  } else if score < min {
                      min = score
                  sum += score
              return (min, max, sum)
          let statistics = calculateStatistics(scores: [5, 3, 100, 3, 9])
          // Prints "120"
          // Prints "120"
      • Functions can be nested. Nested functions have access to variables that were declared in the outer function. You can use nested functions to organize the code in a function that is long or complex.

          func returnFifteen() -> Int {
              var y = 10
              func add() {
                  y += 5
              return y
      • Functions are a first-class type. This means that a function can return another function as its value.

          func makeIncrementer() -> ((Int) -> Int) {
              func addOne(number: Int) -> Int {
                  return 1 + number
              return addOne
          var increment = makeIncrementer()
      • A function can take another function as one of its arguments.

          func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
              for item in list {
                  if condition(item) {
                      return true
              return false
          func lessThanTen(number: Int) -> Bool {
              return number < 10
          var numbers = [20, 19, 7, 12]
          hasAnyMatches(list: numbers, condition: lessThanTen)
      • You can write a closure without a name by surrounding code with braces ({}). Use in to separate the arguments and return type from the body.

          numbers.map({ (number: Int) -> Int in
              let result = 3 * number
              return result
      • You have several options for writing closures more concisely. When a closure’s type is already known, such as the callback for a delegate, you can omit the type of its parameters, its return type, or both. Single statement closures implicitly return the value of their only statement.

          let mappedNumbers = numbers.map({ number in 3 * number })
          // Prints "[60, 57, 21, 36]"
      • You can refer to parameters by number instead of by name—this approach is especially useful in very short closures. A closure passed as the last argument to a function can appear immediately after the parentheses. When a closure is the only argument to a function, you can omit the parentheses entirely.

          let sortedNumbers = numbers.sorted { $0 > $1 }
          // Prints "[20, 19, 12, 7]"
    • Objects and Classes

        class Shape {
            var numberOfSides = 0
            func simpleDescription() -> String {
                return "A shape with \(numberOfSides) sides."
      • Create an instance of a class by putting parentheses after the class name. Use dot syntax to access the properties and methods of the instance.

          var shape = Shape()
          shape.numberOfSides = 7
          var shapeDescription = shape.simpleDescription()
      • Set up the class when an instance is created. Use init to create one.

         class NamedShape {
             var numberOfSides: Int = 0
             var name: String
             init(name: String) {
                 self.name = name
             func simpleDescription() -> String {
                 return "A shape with \(numberOfSides) sides."
      • Use deinit to create a deinitializer if you need to perform some cleanup before the object is deallocated.
      • Subclasses include their superclass name after their class name, separated by a colon.
      • Methods on a subclass that override the superclass’s implementation are marked with override

          class Square: NamedShape {
              var sideLength: Double
              init(sideLength: Double, name: String) {
                  self.sideLength = sideLength
                  super.init(name: name)
                  numberOfSides = 4
              func area() -> Double {
                  return sideLength * sideLength
              override func simpleDescription() -> String {
                  return "A square with sides of length \(sideLength)."
          let test = Square(sideLength: 5.2, name: "my test square")
      • In addition to simple properties that are stored, properties can have a getter and a setter.

          class EquilateralTriangle: NamedShape {
              var sideLength: Double = 0.0
              init(sideLength: Double, name: String) {
                  self.sideLength = sideLength
                  super.init(name: name)
                  numberOfSides = 3
              var perimeter: Double {
                  get {
                      return 3.0 * sideLength
                  set {
                      sideLength = newValue / 3.0
              override func simpleDescription() -> String {
                  return "An equilateral triangle with sides of length \(sideLength)."
          var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
          // Prints "9.3"
          triangle.perimeter = 9.9
          // Prints "3.3000000000000003"
      • In the setter for perimeter, the new value has the implicit name newValue.
      • If you don’t need to compute the property but still need to provide code that is run before and after setting a new value, use willSet and didSet
      • When working with optional values, you can write ? before operations like methods, properties, and subscripting. If the value before the ? is nil, everything after the ? is ignored and the value of the whole expression is nil. Otherwise, the optional value is unwrapped, and everything after the ? acts on the unwrapped value. In both cases, the value of the whole expression is an optional value.

          let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
          let sideLength = optionalSquare?.sideLength
    • Enumerations and Structures
      • Use enum to create an enumeration. Like classes and all other named types, enumerations can have methods associated with them.

          enum Suit {
              case spades, hearts, diamonds, clubs
              func simpleDescription() -> String {
                  switch self {
                  case .spades:
                      return "spades"
                  case .hearts:
                      return "hearts"
                  case .diamonds:
                      return "diamonds"
                  case .clubs:
                      return "clubs"
          let hearts = Suit.hearts
          let heartsDescription = hearts.simpleDescription()

          enum Rank: Int {
              case ace = 1
              case two, three, four, five, six, seven, eight, nine, ten
              case jack, queen, king
              func simpleDescription() -> String {
                  switch self {
                  case .ace:
                      return "ace"
                  case .jack:
                      return "jack"
                  case .queen:
                      return "queen"
                  case .king:
                      return "king"
                      return String(self.rawValue)
          let ace = Rank.ace
          let aceRawValue = ace.rawValue
      • Use struct to create a structure. Structures support many of the same behaviors as classes, including methods and initializers. One of the most important differences between structures and classes is that structures are always copied when they are passed around in your code, but classes are passed by reference.

          struct Card {
              var rank: Rank
              var suit: Suit
              func simpleDescription() -> String {
                  return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
          let threeOfSpades = Card(rank: .three, suit: .spades)
          let threeOfSpadesDescription = threeOfSpades.simpleDescription()
    • Protocols and Extensions
      • Use protocol to declare a protocol.

          protocol ExampleProtocol {
              var simpleDescription: String { get }
              mutating func adjust()
      • Classes, enumerations, and structs can all adopt protocols.

          class SimpleClass: ExampleProtocol {
              var simpleDescription: String = "A very simple class."
              var anotherProperty: Int = 69105
              func adjust() {
                  simpleDescription += "  Now 100% adjusted."
          var a = SimpleClass()
          let aDescription = a.simpleDescription
          struct SimpleStructure: ExampleProtocol {
              var simpleDescription: String = "A simple structure"
              mutating func adjust() {
                  simpleDescription += " (adjusted)"
          var b = SimpleStructure()
          let bDescription = b.simpleDescription
      • Notice the use of the mutatingkeyword in the declaration of SimpleStructure to mark a method that modifies the structure. The declaration of SimpleClass doesn’t need any of its methods marked as mutating because methods on a class can always modify the class.
      • Use extension to add functionality to an existing type, such as new methods and computed properties. You can use an extension to add protocol conformance to a type that is declared elsewhere, or even to a type that you imported from a library or framework

          extension Int: ExampleProtocol {
              var simpleDescription: String {
                  return "The number \(self)"
              mutating func adjust() {
                  self += 42
          // Prints "The number 7"
    • Error Handling
      • You represent errors using any type that adopts the Error protocol.

          enum PrinterError: Error {
              case outOfPaper
              case noToner
              case onFire
      • Use throw to throw an error and throws to mark a function that can throw an error. If you throw an error in a function, the function returns immediately and the code that called the function handles the error.

          func send(job: Int, toPrinter printerName: String) throws -> String {
              if printerName == "Never Has Toner" {
                  throw PrinterError.noToner
              return "Job sent"
      • There are several ways to handle errors. One way is to use do-catch. Inside the do block, you mark code that can throw an error by writing try in front of it. Inside the catch block, the error is automatically given the name error unless you give it a different name.

      • You can provide multiple catch blocks that handle specific errors. You write a pattern after catch just as you do after case in a switch.

          do {
              let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
          } catch PrinterError.onFire {
              print("I'll just put this over here, with the rest of the fire.")
          } catch let printerError as PrinterError {
              print("Printer error: \(printerError).")
          } catch {
          // Prints "Job sent"
      • Another way to handle errors is to use try? to convert the result to an optional. If the function throws an error, the specific error is discarded and the result is nil. Otherwise, the result is an optional containing the value that the function returned.

          let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
          let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")
      • Use defer to write a block of code that is executed after all other code in the function, just before the function returns. The code is executed regardless of whether the function throws an error. You can use defer to write setup and cleanup code next to each other, even though they need to be executed at different times.

          var fridgeIsOpen = false
          let fridgeContent = ["milk", "eggs", "leftovers"]
          func fridgeContains(_ food: String) -> Bool {
              fridgeIsOpen = true
              defer {
                  fridgeIsOpen = false
              let result = fridgeContent.contains(food)
              return result
          // Prints "false"
    • Generics
      • Write a name inside angle brackets to make a generic function or type.

          func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
              var result = [Item]()
              for _ in 0..<numberOfTimes {
              return result
          makeArray(repeating: "knock", numberOfTimes: 4)
      • You can make generic forms of functions and methods, as well as classes, enumerations, and structures.

          // Reimplement the Swift standard library's optional type
          enum OptionalValue<Wrapped> {
              case none
              case some(Wrapped)
          var possibleInteger: OptionalValue<Int> = .none
          possibleInteger = .some(100)
      • Use where right before the body to specify a list of requirements—for example, to require the type to implement a protocol, to require two types to be the same, or to require a class to have a particular superclass.

          func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
              where T.Element: Equatable, T.Element == U.Element
              for lhsItem in lhs {
                  for rhsItem in rhs {
                      if lhsItem == rhsItem {
                          return true
              return false
          anyCommonElements([1, 2, 3], [3])
      • Writing <T: Equatable> is the same as writing <T> ... where T: Equatable.

    • Types
      • String
      • Int, UInt, Int32, UInt32, Int64, UInt64, Int8, UInt8
      • Doulbe, Float
      • Integer prefix: Decimal, binary 0b, octal 0o, hexadecimal 0x
      • Floating-point prefex: Decimal , hexadecimal 0x
      • Bool
      • Tuple (404, "Not Found") is a tuple of type (Int, String)


  • SwiftUI uses a declarative syntax so you can simply state what your user interface should do.