Basics

See

swift --version
swift-driver version: 1.62.15 Apple Swift version 5.7.2 (swiftlang-5.7.2.135.5 clang-1400.0.29.51)
Target: x86_64-apple-macosx13.0

To start the REPL commandline:

swift repl
./code/1-hello.swift
// swiftc ./1-hello.swift

print("Hello, world!")
// Note:
// 1. It is not println but it still prints a new line
// 2. There is no semicolon
// 3. There is no main(), like Python
swiftc ./1-hello.swift

It will generate an executable ./1-hello.

otool -L ./1-hello

./1-hello:
  /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0)
  /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0)
  /usr/lib/swift/libswiftCore.dylib (compatibility version 1.0.0, current version 5.7.1)

ls -lh 1-hello

-rwxr-xr-x  1 fangjun  staff    33K Jan 21 11:55 1-hello
./code/2-variables.swift
var myVariable = 42
myVariable = 50
let myConstant = 43
print("myVariable is \(myVariable)")
print("myConstant is \(myConstant)")

let implicitInteger = 1
let implicitDouble = 1.0
let explicitDouble: Double = 1

let label = "The width is "
let width = 100
var widthLabel = label + String(width)
widthLabel = "\(label)\(width)"
print(widthLabel)


// """
let s = """
  abc
  def
  """
// Note: there are leading spaces before the ending """
print(s)
./code/3-array-dict.swift
var fruits = ["strawberries", "limes", "apples"]
fruits[1] = "grapes"

print(fruits) // ["strawberries", "grapes", "apples"]

fruits.append("blueberries")

var occupations = [
  "Tom": "Captain",
  "Jerry": "Mechanic",
]
print(occupations) // ["Tom": "Captain", "Jerry": "Mechanic"]
occupations["Tom"] = "Teacher"

let emptyArray: [String] = []
let emptyDict: [String: Float] = [:]
./code/4-if.swift
let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
  if score > 50 {
    teamScore += 3
  } else {
    teamScore += 1
  }
}
// 11
print(teamScore)

var optionalString: String? = "Hello"
// false
print(optionalString == nil)

var optionalName: String? = "Tom"
var greeting = "Hello!"

// if optionalName is nil, then the condition is false
// if optionalName is not nil, then the condition is true and optionalName is unwrapped and assigned to name
if let name = optionalName {
  greeting = "Hello,\(name)"
}
// Hello,Tom
print(greeting)

let nickname: String? = nil
let fullName: String = "Tom Green"
// Hi, Tom Green
let informalGreeting = "Hi, \(nickname ?? fullName)"
print(informalGreeting)

if let nickname {
  print("Hey, \(nickname)")
}
./code/5-switch.swift
let vegetable = "red pepper"
switch vegetable {
  case "celery":
    print("celery")
  case "cucumber", "watercress":
    print("cucumber or watercress")
  case let x where x.hasSuffix("pepper"):
    print("x")
  default:
    print("Everything tastes good in soup.")
}
// 1. no need to use break
// 2. default is mandatory so that it is exhaustive
./code/6-for.swift
let interestingNumbers = [
  "Prime": [2, 3, 5, 7, 11, 13],
  "Fibonacci": [1, 1, 2, 3, 5, 8],
  "Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (_, numbers) in interestingNumbers {
  for number in numbers {
    if number > largest {
      largest = number
    }
  }
}
print(largest) // 25

var total = 0
for i in 0..<4 {
  total += i
}
print(total) // 6

total = 0
for i in 0...4 {
  total += i
}
print(total) // 10
./code/7-while.swift
var n = 2
while n < 100 {
  n *= 2;
}
print(n) // 128

var m = 2
repeat {
  m *= 2;
} while (m < 100)

print(m) // 128
./code/8-func.swift
func greet(person: String, day: String) -> String {
  return "Hello \(person), today is \(day)"
}

print(greet(person: "Bob", day: "Tuesday"))
// print(greet("Bob", day: "Tuesday")) // error: missing argument label 'person:' in call

func greet2(_ person: String, on day: String) -> String {
  return "Hello \(person), today is \(day)"
}
// print(greet2(person: "Bob", day: "Tuesday")) // error: incorrect argument labels in call (have 'person:day:', expected '_:on:')
print(greet2("Bob", on: "Tuesday"))

func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
  var min = scores[0]
  var max = scores[1]
  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])
print(statistics.sum) // 120
print(statistics.2) // 120

func returnFifteen() -> Int {
  var y = 10
  func add() {
    y += 5
  }
  add()
  return y
}
print(returnFifteen()) // 15

func makeIncrementer() -> ((Int) -> Int) {
  func addOne(number: Int) -> Int {
    return number + 1
  }

  return addOne
}

var increment = makeIncrementer()
print(increment(7)) // 8

func hasAnyElement(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]
print(hasAnyElement(list: numbers, condition: lessThanTen)) // true

// the closure has to be put in {}
print(hasAnyElement(list: numbers, condition: {(number: Int) -> Bool in number < 1})) // false

// we can omit the input argument type and return type
print(hasAnyElement(list: numbers, condition: {number in number < 1})) // false

print(numbers.map({ (number: Int) -> Int in
  let result = 3 * number
  return result
})) // [60, 57, 21, 36]

let mappedNumber = numbers.map({number in 3 * number})
print(mappedNumber) // [60, 57, 21, 36]

var sortedNumbers = numbers.sorted {$0 > $1}
print(sortedNumbers) // [20, 19, 12, 7]

sortedNumbers = numbers.sorted {$0 < $1} // also ok
print(sortedNumbers) // [7, 12, 19, 20]
./code/9-class.swift
class Shape {
  var numberOfSides = 0
  func simpleDescription() -> String {
    return "A shape with \(numberOfSides) sides."
  }
}

var shape = Shape()
print(shape.numberOfSides) // 0
shape.numberOfSides = 4
print(shape.numberOfSides) // 4
var simpleDescription = shape.simpleDescription()
print(simpleDescription) // A shape with 4 sides

// with constructor
class NamedShape {
  var numberOfSides = 0
  var name: String

  init(name: String, numberOfSides: Int) {
    self.name = name
    self.numberOfSides = numberOfSides
  }

  func simpleDescription() -> String {
    return "\(name): A shape with \(numberOfSides) sides"
  }
}

var namedShape = NamedShape(name: "Hello", numberOfSides: 10)
print(namedShape.simpleDescription())

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 side length \(sideLength)"
  }
}
var square = Square(sideLength: 10, name: "MySquare")
print(square.simpleDescription()) // A square with side length 10.0
print(square.area()) // 100.0

// property getter/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 {
      // note: the new value has the implicit name newValue
      sideLength = newValue / 3.0
    }
  }

  override func simpleDescription() -> String {
    return "An equilateral triangle with side of length \(sideLength)."
  }
}
var triangle = EquilateralTriangle(sideLength: 10, name: "MyTriangle")
print(triangle.perimeter) // 30.0
triangle.perimeter = 15
print(triangle.perimeter) // 15.0
print(triangle.sideLength) // 5.0
./code/10-enum.swift
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"
      default:
        return String(self.rawValue)
    }
  }
}
let ace = Rank.ace
let aceRawValue = ace.rawValue
print(ace) // ace
print(aceRawValue) // 1

let two = Rank(rawValue: 2)
print(two ?? "zwei") // two
./code/11-memory-layout.swift
assert(MemoryLayout<Bool>.size == 1)
assert(MemoryLayout<Bool>.stride == 1)
assert(MemoryLayout<Bool>.alignment == 1)

assert(MemoryLayout<Int>.size == 8)
assert(MemoryLayout<Int>.stride == 8)
assert(MemoryLayout<Int>.alignment == 8)

assert(MemoryLayout<Int32>.size == 4)
assert(MemoryLayout<Int32>.stride == 4)
assert(MemoryLayout<Int32>.alignment == 4)

// Like C/C++
struct Example {
  let foo: Int // 8
  let bar: Bool // 1
}
assert(MemoryLayout<Example>.size == 9)
assert(MemoryLayout<Example>.stride == 16)
assert(MemoryLayout<Example>.alignment == 8)

let ex = Example(foo: 10, bar: true)
assert(MemoryLayout.size(ofValue: ex) == 9)
assert(MemoryLayout.stride(ofValue: ex) == 16)
assert(MemoryLayout.alignment(ofValue: ex) == 8)


struct Example2 {
  let bar: Bool // 1
  let foo: Int // 8
}
assert(MemoryLayout<Example2>.size == 16)
assert(MemoryLayout<Example2>.stride == 16)
assert(MemoryLayout<Example2>.alignment == 8)
./code/12-weak-reference.swift
class Author {
  let name: String
  // weak reference is required
  weak var post: Post?

  init(name: String) {self.name = name}
  deinit {print("Author deinit")}
}

class Post {
  let title: String

  var author: Author?
  init(title: String) {self.title = title}
  deinit {print("Post deinit")}
}

var author: Author? = Author(name: "John Snow")
var post: Post? = Post(title: "foo bar")
post?.author = author
author?.post = post

print(author?.post) // Optional(main.Post)
post = nil // Post deinit
print(author?.post) // nil
author = nil // Author deinit
./code/13-pointers.swift
// Unsafe[Mutable][Raw][Buffer]Pointer[Type]
//
// Mutable: means you can change the value
// Raw: means it points to a blob of bytes
// Buffer: means it works like a collection
// Type: means generic typed pointers
//
// UnsafePointer<T>
// UnsafeMutablePointer<T>
//
// UnsafeRawPointer
// UnsafeMutableRawPointer
//
// UnsafeRawBufferPointer
// UnsafeMutableRawBufferPointer
//
// UnsafeBufferPointer<T>
// UnsafeMutableBufferPointer<T>

let count = 2
let stride = MemoryLayout<Int>.stride
let alignment = MemoryLayout<Int>.alignment
let byteCount = count * stride

let pointer = UnsafeMutableRawPointer.allocate(byteCount: byteCount, alignment: alignment)

defer {
  pointer.deallocate()
}

pointer.storeBytes(of: 30, as: Int.self)
pointer.advanced(by: stride).storeBytes(of: 3, as: Int.self)
assert(pointer.load(as: Int.self) == 30)
assert(pointer.load(fromByteOffset: stride, as: Int.self) == 3)
assert(pointer.advanced(by: stride).load(as: Int.self) == 3)

let bufferPointer = UnsafeRawBufferPointer(start: pointer, count: byteCount)
for (index, byte) in bufferPointer.enumerated() {
  print("byte \(index) -> \(byte)")
}
/*
byte 0 -> 30
byte 1 -> 0
byte 2 -> 0
byte 3 -> 0
byte 4 -> 0
byte 5 -> 0
byte 6 -> 0
byte 7 -> 0
byte 8 -> 3
byte 9 -> 0
byte 10 -> 0
byte 11 -> 0
byte 12 -> 0
byte 13 -> 0
byte 14 -> 0
byte 15 -> 0
*/
./code/14-typed-pointers.swift
let count = 2
let stride = MemoryLayout<Int>.stride
let pointer = UnsafeMutablePointer<Int>.allocate(capacity: count)
pointer.initialize(repeating: 0, count: count)

defer {
  pointer.deinitialize(count: count)
  pointer.deallocate()
}

pointer.pointee = 42
pointer.advanced(by: 1).pointee = 6

let bufferPointer = UnsafeBufferPointer(start: pointer, count: count)

for (index, value) in bufferPointer.enumerated() {
  print("value \(index) -> \(value)")
}
/*
value 0 -> 42
value 1 -> 6
*/