Irbisa · Cheatsheet

Swift Basics

Variables, optionals, types, control flow, closures, value vs reference

Junior Developer · 25 questions

  1. 01

    What is the difference between let and var in Swift?

    A let constant can be assigned exactly once, while a var variable can be reassigned any number of times; use let by default and switch to var only when the value really has to change.

    let name = "Ada"// name = "Grace"          // error: cannot assign to a let constant var attempts = 0attempts += 1              // fine…
  2. 02

    What is an optional in Swift, how is it implemented, and how do you unwrap it safely?

    An optional either holds a value or holds nil, so "this may be missing" is checked by the compiler instead of crashing at runtime; T? is shorthand for the generic enum Optional<T> with the cases .some(T) and .none.

    var nickname: String? = "Ada" if let nickname {  print(nickname.uppercased())     // runs only when there is a value}…
  3. 03

    What is the difference between if let and guard let, and when do you reach for guard?

    Both unwrap an optional, but if let keeps the value inside its own braces, while guard let makes it available for the rest of the scope and forces the else branch to leave that scope.

    struct User { let name: String; let isActive: Bool } func greet(_ user: User?) {  guard let user, user.isActive else {    print("nothing to greet")    return                      // else has to leave the scope…
  4. 04

    What does this code print, and why does the first line show Optional("Ada")?

    let name: String? = "Ada"let bonus: Int? = 5 print("Hello, \(name)")print("Hello, \(name ?? "guest")")print(bonus ?? 0 + 1)

    It prints Hello, Optional("Ada"), then Hello, Ada, then 5: interpolating an optional prints the description of the Optional wrapper, and ?? binds more loosely than +.

  5. 05

    What is the difference between String? and String!, and why does an @IBOutlet declared as UILabel! crash with "unexpectedly found nil"?

    String! is an implicitly unwrapped optional: the same Optional<String> underneath, but the compiler inserts a force unwrap wherever you use it as a non-optional, so reading it while it is nil crashes exactly like ! does.

  6. 06

    What is the difference between value types and reference types in Swift?

    A value type is copied when you assign it or pass it to a function, so every variable owns its own data; a reference type is shared, so several variables point at one instance and see each other's changes.

    struct Point { var x: Int }let a = Point(x: 1)var b = a          // an independent copyb.x = 99print(a.x)         // 1…
  7. 07

    What is the difference between == and === in Swift?

    == asks whether two values are equal by content and comes from the Equatable protocol; === asks whether two class references point at the very same instance.

    struct Point: Equatable { var x: Int }print(Point(x: 1) == Point(x: 1))   // true: same content final class Box {}let a = Box()let b = Box()…
  8. 08

    What is the difference between is, as, as? and as!?

    is checks a value's runtime type, as? attempts a downcast and returns an optional, as! forces the downcast and crashes if it fails, and plain as is an upcast or bridging conversion the compiler can prove safe.

    class Animal {}final class Dog: Animal { func bark() { print("woof") } } let pets: [Animal] = [Dog(), Animal()]for pet in pets {  print(pet is Dog)                       // true, then false…
  9. 09

    What is the difference between Any and AnyObject?

    Any can hold a value of any type, including structs, enums, optionals and functions; AnyObject holds only class instances, so it is also the constraint for class-only protocols.

    protocol Delegate: AnyObject {}      // class-only protocol final class Screen {  weak var delegate: Delegate?     // weak needs a class-bound type}…
  10. 10

    What is the difference between Array, Set and Dictionary, and how do you choose between them?

    Choose by what you need: an Array keeps order and allows duplicates, a Set stores unique elements with fast lookup, and a Dictionary maps unique keys to values.

    var tags = ["swift", "ios", "swift"]tags.insert("ui", at: 0)              // O(n): shifts every element let unique = Set(tags)                // 3 elements, order unspecifiedprint(unique.contains("ios"))         // O(1) on average…
  11. 11

    What is the difference between map, compactMap and flatMap in Swift?

    map transforms every element one-to-one, compactMap transforms and drops the nil results, and flatMap turns each element into a sequence and joins all of them into one array.

    let raw: [String?] = ["swift", nil, "ios"]let lengths = raw.compactMap { $0?.count }        // [5, 3]let withNils = raw.map { $0?.count }              // [Int?]: 5, nil, 3 let numbers = ["1", "two", "3"].compactMap(Int.init)   // [1, 3]let flat = [[1, 2], [3]].flatMap { $0 }               // [1, 2, 3]…
  12. 12

    What can Swift's switch do that a C-style switch cannot?

    Swift's switch matches patterns rather than just integers, must be exhaustive, and never falls through into the next case unless you write fallthrough.

    enum Download {  case idle  case running(progress: Double)  case failed(retryIn: Int)}…
  13. 13

    Why can't you add an Int to a Double in Swift, and how do Float, Double and CGFloat differ?

    Swift never converts numeric types implicitly, so Int plus Double is a compile error until you write Double(count); Float is 32-bit, Double is 64-bit and the default, and CGFloat is the type Core Graphics and UIKit use for sizes.

    let count = 7let total = 10// let ratio = count / total * 100.0   // error: Int and Double do not mixlet ratio = Double(count) / Double(total) * 100   // 70.0 let half = 1 / 2           // 0: both literals default to Int…
  14. 14

    What is a closure in Swift, and is it a value type or a reference type?

    A closure is a block of code you can store in a variable and pass around, and it remembers the variables of the scope where it was created; closures, like functions, are reference types.

    func makeCounter() -> () -> Int {  var count = 0  return {                 // captures count and keeps it alive    count += 1    return count  }…
  15. 15

    What is the difference between weak and unowned, and when do you need [weak self] in a closure?

    Neither keeps the object alive: weak is an optional that becomes nil when the object is deallocated, while unowned is non-optional and crashes if you touch it after the object is gone.

    final class ProfileViewModel {  var onUpdate: (() -> Void)?  var title = ""   func start() {    // self owns onUpdate: capturing self strongly would be a retain cycle…
  16. 16

    How does error handling work in Swift: throws, try, try?, try!, do/catch and Result?

    A function that can fail is marked throws, every call to it must be marked with try, and the error is either handled in do/catch or passed up to the caller, so failure is visible in the function's signature.

    enum AuthError: Error {  case invalidCredentials  case throttled(retryAfter: Int)} struct User { let email: String }…
  17. 17

    What does this code print, and why does changing an element of the copy also change the original array?

    final class Item {  var title: String  init(title: String) { self.title = title }} var original = [Item(title: "A"), Item(title: "B")]var copy = originalcopy[0].title = "Changed"copy.append(Item(title: "C")) print(original[0].title)print(original.count)

    It prints Changed and then 2: the array itself was copied, so appending to copy does not touch original, but both arrays hold references to the same Item objects.

  18. 18

    Find the bug: tapping "Add" on a product that is already in the cart never increases its quantity.

    struct CartItem {  let name: String  var quantity: Int} final class Cart {  private(set) var items: [CartItem] = []   func add(_ name: String) {    if var existing = items.first(where: { $0.name == name }) {      existing.quantity += 1    } else {      items.append(CartItem(name: name, quantity: 1))    }  }}

    if var existing = items.first(...) binds a copy of the struct, so quantity += 1 changes that local copy and the array keeps the old value: every product stays at quantity 1.

  19. 19

    What does this code print, and what does the capture list [value] change?

    func demo() {  var value = 1  let captured = { print("captured:", value) }  let copied = { [value] in print("capture list:", value) }   value = 2  captured()  copied()} demo()

    It prints captured: 2 and then capture list: 1: a normal closure captures the variable itself and reads it at call time, while a capture list copies the value at the moment the closure is created.

  20. 20

    What does this code print, and when exactly does a defer block run?

    func demo() {  var counter = 0  func next() -> Int {    defer { counter += 1 }    return counter  }  print(next(), counter)} func order() {  defer { print("A") }  defer { print("B") }  print("C")} demo()order()

    It prints 0 1, then C, B, A: a defer block runs when its scope exits, after the return value has already been computed, and several defer blocks run in reverse order.

  21. 21

    What does inout do, and how is it different from passing a class instance?

    An inout parameter is copy-in, copy-out: the function works on a mutable copy of your variable and writes it back when it returns, so the caller sees the change; you pass the argument with &.

    func normalize(_ values: inout [Double]) {  guard let maxValue = values.max(), maxValue > 0 else { return }  for i in values.indices { values[i] /= maxValue }} var samples = [2.0, 4.0, 8.0]…
  22. 22

    Why can't you write name[3] on a Swift String, and what is a Substring?

    A Swift Character is a grapheme cluster that can take several bytes, so there is no constant-time way to jump to the fourth one; String deliberately has no integer subscript, and you move through it with String.Index instead.

    let s = "Cafe\u{301}"               // "Café": 5 scalars, 4 charactersprint(s.count)                       // 4print(s.unicodeScalars.count)        // 5print(s == "Café")                   // true // let c = s[3]                      // error: no Int subscript…
  23. 23

    let rest = items.dropFirst() and then rest[0] crashes with "Index out of bounds", although rest is not empty. Why?

    dropFirst returns an ArraySlice that keeps the original array's indices, so its first element sits at index 1, not 0, and rest[0] really is out of range.

  24. 24

    Three items at 1.10 add up to 3.3000000000000003 on a receipt. What went wrong, and what type should store money?

    Double is binary floating point, and values like 1.1 or 0.1 have no exact binary representation, so each operation adds a tiny error that shows up once the result is printed; store money as integer minor units or as Decimal.

    print(1.1 * 3)                           // 3.3000000000000003print(0.1 + 0.2 == 0.3)                  // falseprint(Int(4.35 * 100))                   // 434, not 435 let priceCents = 110let totalCents = priceCents * 3          // 330, exact…
  25. 25

    What is the difference between assert, precondition and fatalError, and which of them still work in a Release build?

    assert is checked only in Debug builds, precondition is checked in both Debug and Release, and fatalError always stops the app; pick by whether a broken assumption is allowed to continue in production.

    assert(index >= 0, "negative index")       // Debug only, not evaluated in Releaseprecondition(!items.isEmpty, "empty cart")  // Debug and Release func make() -> Int {  fatalError("unreachable")                 // returns Never, always traps}