---
title: "Swift & iOS: App Development Fundamentals"
description: "Test your knowledge of Swift and iOS development: optionals, structs versus classes, protocols, closures and capture lists, error handling, SwiftUI state, the app lifecycle, and async/await with actors."
author: "Mohammad Abu Mattar"
canonical: https://mkabumattar.com/quizzes/post/swift-ios-fundamentals-quiz
---

# Swift & iOS: App Development Fundamentals

Most Swift confusion traces back to a small number of ideas, and once those land the rest of the language stops producing surprises. This quiz is built around three of them.

_Copy semantics, optionals as types, and where SwiftUI keeps state_

Twenty-eight questions across optionals, value and reference semantics, protocols, closures and capture lists, error handling, SwiftUI state, the app lifecycle, and structured concurrency. Every option has an explanation, not just the correct one, because knowing why the other three are wrong is usually the part that sticks.

## Questions

### 1. Why does `s.count` fail to compile when `s` is declared as `String?`

- `String?` is a different type from `String` and simply has no `count` member
- It compiles but crashes at runtime if `s` is nil
- `count` is only available on `var`, not `let`
- Strings need to be bridged to `NSString` before `count` works

**Hint:** Ask yourself whether the optionality is a runtime condition or part of the type itself.

### 2. What is the practical difference between `if let` and `guard let`?

- `guard let` binds the value in the enclosing scope and its else branch must exit
- `guard let` is faster because it skips the optional check
- `if let` cannot unwrap more than one optional
- `guard let` only works inside loops

**Hint:** Think about where the unwrapped value is usable afterwards.

### 3. What does the nil-coalescing operator `??` return?

- The unwrapped left value if non-nil, otherwise the right value, as a non-optional
- An optional containing either the left or the right value
- The right value always, since the left is optional
- It throws if the left side is nil

**Hint:** Consider the type of the result, not just the value.

### 4. When is force unwrapping with `!` genuinely reasonable?

- When nil would mean a programmer error, and crashing is the correct response
- Whenever you have already checked the value is not nil on the previous line
- In production code, to avoid the performance cost of optional checks
- Never, under any circumstances

**Hint:** Think of it as a claim you are making, not a shortcut you are taking.

### 5. What is `String??` and how do you flatten it?

- A double optional, flattened with `flatMap { $0 }`
- A syntax error that will not compile
- Shorthand for a force-unwrapped optional
- The same thing as `String?`, with Swift collapsing it automatically

**Hint:** It is a real type, not a typo.

### 6. Given `struct P { var x = 1 }`, `var a = P()`, `var b = a`, and `b.x = 99`, what is `a.x`?

- 1
- 99
- nil, because `a` was invalidated by the copy
- It fails to compile because `P` has no explicit initialiser

**Hint:** Structs are value types.

### 7. Why does `let box = Box(); box.x = 99` compile when `Box` is a class?

- `let` fixes the reference, and the referenced object is still mutable
- Classes ignore `let` entirely
- Because `x` is declared with `var` inside the class
- The compiler makes an implicit copy first

**Hint:** What exactly is the constant here?

### 8. Which of these Swift standard library types is a reference type?

- `AnyObject`
- `Array`
- `Dictionary`
- `String`

**Hint:** Most of the collections are not what people expect.

### 9. What does copy-on-write mean for a Swift Array?

- The buffer is shared until a mutation happens, and only then is it duplicated
- Every assignment immediately duplicates the whole buffer
- Writes are queued and applied when the array is next read
- The array becomes immutable once copied

**Hint:** Think about when the actual allocation happens.

### 10. When would you choose a class over a struct?

- When instances need identity, shared mutable state, inheritance, or a deinitialiser
- Whenever the type has more than a few properties, for performance
- Whenever the type needs to conform to a protocol
- Whenever the type needs to be stored in an array

**Hint:** Think about identity.

### 11. What is a protocol extension with a default implementation used for?

- Sharing behaviour across all conforming types, including structs and enums
- Forcing every conforming type to implement the method
- Adding stored properties to a protocol
- Making the protocol usable as a generic constraint

**Hint:** It is what makes protocol-oriented programming practical.

### 12. What does an `associatedtype` in a protocol do?

- Declares a placeholder type that each conforming type specifies
- Creates a type alias visible to all conformers
- Restricts the protocol to class types only
- Allows the protocol to inherit from another protocol

**Hint:** It is a placeholder resolved by the conforming type.

### 13. What is the difference between `some Shape` and `any Shape` as a return type?

- `some` is one concrete type hidden from the caller; `any` is a box that could hold different types
- They are synonyms with different spellings
- `some` means the value may be nil
- `any` is only valid for class-constrained protocols

**Hint:** One hides a single concrete type, the other allows many.

### 14. In `items.map { $0 * 2 }`, what makes the braces valid without parentheses?

- Trailing closure syntax, since the closure is the last argument
- `map` is a language keyword with special syntax
- The braces define a computed property
- `$0` triggers an implicit conversion that changes the call syntax

**Hint:** The closure is the last argument.

### 15. Why is `[weak self]` needed in a closure a view controller stores as a property?

- To break a retain cycle between the controller and the closure it stores
- To let the closure run on a background thread
- To make the closure escaping
- Because closures cannot access `self` without a capture list

**Hint:** Trace who holds a strong reference to whom.

### 16. What does `@escaping` on a closure parameter mean?

- The closure may be stored and called after the function has returned
- The closure can throw errors
- The closure runs asynchronously
- The closure captures everything weakly by default

**Hint:** Think about the closure outliving the function call.

### 17. What is the difference between `try?` and `try!`?

- `try?` turns an error into nil; `try!` crashes if an error is thrown
- `try?` retries the call once; `try!` retries indefinitely
- `try?` requires a do/catch block; `try!` does not
- They are identical except `try!` logs the error

**Hint:** Both remove the do/catch, in very different ways.

### 18. What does `defer` guarantee inside a function?

- It runs on every exit from the scope, including a thrown error
- It runs only after a successful return
- It runs before the function body
- It runs asynchronously after the function returns

**Hint:** Think about every possible way to leave the scope.

### 19. What is `Result<Success, Failure>` used for now that Swift has async/await?

- Representing success or failure as a value you can store and inspect later
- Nothing, it is deprecated in favour of async/await
- Wrapping optionals to avoid force unwrapping
- Making synchronous code run asynchronously

**Hint:** Think about storing an outcome rather than propagating it.

### 20. Why can a SwiftUI `View` struct not simply store mutable state in a normal `var`?

- The struct is thrown away and rebuilt on every render, so plain properties do not survive
- Structs cannot have `var` properties
- SwiftUI views must be classes
- Mutable state must always live in a view model

**Hint:** Think about the lifetime of the struct itself.

### 21. When do you use `@StateObject` rather than `@ObservedObject`?

- `@StateObject` when this view creates and owns the object, so it survives rebuilds
- `@StateObject` for value types and `@ObservedObject` for reference types
- `@StateObject` only inside the app entry point
- They are interchangeable

**Hint:** The question is who creates the object.

### 22. What does the `$` prefix on a SwiftUI state property give you?

- A `Binding`, giving read-write access to the underlying storage
- A copy of the value, isolated from the original
- An optional version of the value
- A publisher that emits on every change

**Hint:** You need it for a TextField and not for a Text.

### 23. In a UIKit view controller, when is `viewDidLoad` called?

- Once, after the view hierarchy loads, and never again for that instance
- Every time the view appears on screen
- Before the view hierarchy exists
- Only when the controller is presented modally

**Hint:** How many times does it run over the controller's life?

### 24. What replaced `AppDelegate` methods as the entry point in a SwiftUI lifecycle app?

- The `App` protocol with a `@main` type returning a `Scene`
- `SceneDelegate`, which SwiftUI generates for you
- A `main.swift` file with a top-level `run()` call
- `UIHostingController`, which must be the root

**Hint:** It is a protocol with a `body`.

### 25. What guarantee does an `actor` give you?

- Its mutable state is accessed by one task at a time, so it cannot be raced
- All of its methods run on the main thread
- Its methods can never throw
- It automatically retries failed operations

**Hint:** Think about one thing at a time.

### 26. What does `@MainActor` on a method mean?

- The method is guaranteed to run on the main thread, checked by the compiler
- The method becomes the app's entry point
- The method runs with the highest thread priority
- The method cannot be called from another actor

**Hint:** Where does UI work have to happen?

### 27. What is the difference between `async let` and a `TaskGroup`?

- `async let` is for a fixed known set of child tasks; a TaskGroup handles a dynamic number
- `async let` is for value types and TaskGroup for reference types
- `async let` runs sequentially; only a TaskGroup is concurrent
- A TaskGroup cannot propagate errors

**Hint:** Think about whether the number of child tasks is known at compile time.

### 28. What happens to an unstructured `Task { }` when the view that created it disappears?

- It keeps running, unless you retain the handle and cancel it yourself
- It is cancelled automatically when the view disappears
- It is suspended and resumed if the view reappears
- It throws a cancellation error immediately

**Hint:** Unstructured is the important word.
