De 25 belangrijkste Swift-sollicitatievragen voor iOS-ontwikkelaars

Voorbereiding op iOS-sollicitatiegesprekken met de 25 meest gestelde Swift-vragen: optionals, closures, ARC, protocols, async/await en geavanceerde patronen.

Swift-sollicitatievragen voor iOS-ontwikkelaars

iOS-sollicitatiegesprekken toetsen Swift-basiskennis en geavanceerde concepten grondig. Deze 25 vragen behandelen de onderwerpen die het vaakst worden gesteld door hiring managers, van taalbasics tot moderne concurrency-patronen.

Hoe deze gids te gebruiken

Elke vraag bevat een gedetailleerd antwoord met codevoorbeelden. De vragen zijn geordend op oplopende moeilijkheidsgraad, van basisconcepten tot geavanceerde onderwerpen.

Swift-basisconcepten

1. Wat is het verschil tussen let en var?

let declareert een constante waarvan de waarde na initialisatie niet meer kan worden gewijzigd, terwijl var een veranderlijke variabele declareert. Bij referentietypen (klassen) voorkomt let het opnieuw toewijzen van de referentie, maar niet het wijzigen van het object zelf.

Constants.swiftswift
// let = constant, immutable value
let maximumAttempts = 3
// maximumAttempts = 5  // ❌ Compile error

// var = variable, mutable value
var currentAttempt = 0
currentAttempt += 1  // ✅ OK

// Careful with reference types
class User {
    var name: String
    init(name: String) { self.name = name }
}

let user = User(name: "Alice")
user.name = "Bob"  // ✅ OK - modifying object, not reference
// user = User(name: "Charlie")  // ❌ Error - reassignment forbidden

Best practice: standaard let gebruiken en alleen naar var overschakelen wanneer mutatie noodzakelijk is. Dit maakt code voorspelbaarder en gemakkelijker te begrijpen.

2. Wat zijn Optionals in Swift?

Optionals vertegenwoordigen de mogelijke afwezigheid van een waarde. Een optional kan een waarde van het opgegeven type bevatten of nil. Swift gebruikt optionals om veiligheid te garanderen tijdens het compileren, door expliciete afhandeling af te dwingen van gevallen waarin een waarde kan ontbreken.

Optionals.swiftswift
// Declaring an optional with ?
var username: String? = nil  // Can hold a String or nil

// Optional binding with if let (safe unwrapping)
if let name = username {
    print("Hello, \(name)")  // Only executes if username != nil
} else {
    print("Anonymous user")
}

// Guard let for early return
func greet(user: String?) {
    guard let name = user else {
        print("No user provided")
        return  // Early exit if nil
    }
    print("Hello, \(name)")  // name is guaranteed non-nil here
}

// Nil-coalescing operator (??) for default value
let displayName = username ?? "Anonymous"

// Force unwrapping (!) - DANGEROUS, avoid this
// let forced = username!  // Crashes if nil

3. Wat is het verschil tussen struct en class?

struct zijn waardetypen (gekopieerd bij toewijzing) terwijl class referentietypen zijn (delen dezelfde instantie). Dit fundamentele verschil heeft invloed op prestaties, geheugenbeheer en codegedrag.

ValueVsReference.swiftswift
// Struct = value type (copy)
struct Point {
    var x: Int
    var y: Int
}

var p1 = Point(x: 10, y: 20)
var p2 = p1      // Independent copy
p2.x = 100       // Only modifies p2
print(p1.x)      // 10 - p1 unchanged

// Class = reference type (shared)
class Rectangle {
    var width: Int
    var height: Int
    init(width: Int, height: Int) {
        self.width = width
        self.height = height
    }
}

let r1 = Rectangle(width: 10, height: 20)
let r2 = r1      // Same shared instance
r2.width = 100   // Modifies the shared instance
print(r1.width)  // 100 - r1 also modified!

Wanneer welke gebruiken:

  • Struct: eenvoudige gegevens, onveranderlijke waarden, geen overerving nodig
  • Class: identiteit is belangrijk, overerving vereist, gedeeld gedrag

4. Hoe werkt pattern matching met switch?

De switch-instructie van Swift is krachtig en uitputtend: alle mogelijke gevallen moeten worden afgedekt. Het ondersteunt pattern matching op typen, bereiken, tuples en aanvullende voorwaarden met where.

PatternMatching.swiftswift
enum NetworkError: Error {
    case timeout
    case serverError(code: Int)
    case noConnection
}

func handleError(_ error: NetworkError) {
    switch error {
    case .timeout:
        print("Request timed out")

    case .serverError(let code) where code >= 500:
        print("Critical server error: \(code)")

    case .serverError(let code):
        print("Server error: \(code)")

    case .noConnection:
        print("No connection")
    }
    // No default needed: all cases covered
}

// Pattern matching on ranges and tuples
let point = (x: 5, y: 10)
switch point {
case (0, 0):
    print("Origin")
case (let x, 0):
    print("On X axis at \(x)")
case (0...10, 0...10):
    print("In the 10x10 square")
default:
    print("Elsewhere")
}

5. Closures en hun syntaxis

Closures zijn zelfstandige codeblokken die referenties naar variabelen uit hun omringende context vastleggen en opslaan. Ze komen overeen met lambda's of anonieme functies in andere talen.

Closures.swiftswift
// Full syntax
let add: (Int, Int) -> Int = { (a: Int, b: Int) -> Int in
    return a + b
}

// Shorthand syntax (inferred types, implicit return)
let multiply: (Int, Int) -> Int = { $0 * $1 }

// Trailing closure syntax
let numbers = [3, 1, 4, 1, 5]
let sorted = numbers.sorted { $0 > $1 }  // [5, 4, 3, 1, 1]

// Closure capturing a variable
func makeCounter() -> () -> Int {
    var count = 0  // Captured variable
    return {
        count += 1  // Closure "closes over" count
        return count
    }
}

let counter = makeCounter()
print(counter())  // 1
print(counter())  // 2 - count remembered between calls
Capture-semantiek

Closures leggen variabelen standaard vast via referentie. Voor het vastleggen via waarde wordt een capture list gebruikt: { [count] in ... }.

Geheugenbeheer en ARC

6. Hoe werkt ARC (Automatic Reference Counting)?

ARC beheert geheugen automatisch door sterke referenties naar elke klasse-instantie te tellen. Wanneer de telling naar nul daalt, wordt de instantie vrijgegeven. In tegenstelling tot garbage collection is ARC deterministisch en voorspelbaar.

ARC.swiftswift
class Person {
    let name: String

    init(name: String) {
        self.name = name
        print("\(name) is initialized")
    }

    deinit {
        print("\(name) is deallocated")
    }
}

// Demonstrating lifecycle
var person1: Person? = Person(name: "Alice")  // refCount = 1
var person2 = person1                          // refCount = 2
person1 = nil                                  // refCount = 1 (not deallocated)
person2 = nil                                  // refCount = 0 → deinit called
// Output: "Alice is deallocated"

7. Wat is een retain cycle en hoe kan dit worden voorkomen?

Een retain cycle ontstaat wanneer twee objecten sterke referenties naar elkaar houden, waardoor hun deallocatie wordt verhinderd. De trefwoorden weak en unowned doorbreken deze cycli.

RetainCycle.swiftswift
class Department {
    let name: String
    var manager: Employee?  // Strong reference

    init(name: String) { self.name = name }
    deinit { print("Department \(name) deallocated") }
}

class Employee {
    let name: String
    // weak prevents retain cycle - can become nil
    weak var department: Department?

    init(name: String) { self.name = name }
    deinit { print("Employee \(name) deallocated") }
}

// Without weak: retain cycle → memory leak
// With weak: proper deallocation
var dept: Department? = Department(name: "Engineering")
var emp: Employee? = Employee(name: "Bob")
dept?.manager = emp
emp?.department = dept

dept = nil  // ✅ Deallocated thanks to weak
emp = nil   // ✅ Deallocated

8. Wat is het verschil tussen weak en unowned?

Beide doorbreken retain cycles, maar met verschillende garanties. weak is optioneel en wordt nil als het gerefereerde object wordt vrijgegeven. unowned gaat ervan uit dat het object altijd bestaat en crasht bij toegang na deallocatie.

WeakVsUnowned.swiftswift
class Customer {
    let name: String
    var card: CreditCard?

    init(name: String) { self.name = name }
}

class CreditCard {
    let number: String
    // unowned because a card always exists with its customer
    unowned let customer: Customer

    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
    }
}

// Card cannot exist without customer
let customer = Customer(name: "Alice")
customer.card = CreditCard(number: "1234", customer: customer)
// If customer is deallocated, accessing card.customer would crash

Vuistregel: standaard weak gebruiken. Alleen unowned inzetten wanneer de levensduur van het gerefereerde object gegarandeerd gelijk of langer is.

Klaar om je iOS gesprekken te halen?

Oefen met onze interactieve simulatoren, flashcards en technische tests.

Protocols en Generics

9. Protocols in Swift

Protocols definiëren een contract (vereiste eigenschappen en methoden) dat conforme typen moeten implementeren. Ze maken polymorfisme mogelijk en vormen de basis van Protocol-Oriented Programming (POP) in Swift.

Protocols.swiftswift
// Protocol definition
protocol Drawable {
    var color: String { get set }  // Required property (read/write)
    func draw()                     // Required method
}

// Protocol extension with default implementation
extension Drawable {
    func draw() {
        print("Default drawing in \(color)")
    }
}

// Protocol conformance
struct Circle: Drawable {
    var color: String
    var radius: Double

    // draw() inherits default implementation
}

struct Square: Drawable {
    var color: String
    var side: Double

    // Override default implementation
    func draw() {
        print("Square \(color) with side \(side)")
    }
}

// Polymorphic usage
let shapes: [Drawable] = [Circle(color: "red", radius: 5), Square(color: "blue", side: 10)]
shapes.forEach { $0.draw() }

10. Wat is een Associated Type?

Associated types stellen protocols in staat generieke typen te definiëren die door conforme typen worden gespecificeerd. Dit mechanisme maakt protocols zoals Collection zo flexibel.

AssociatedTypes.swiftswift
// Protocol with associated type
protocol Container {
    associatedtype Item  // Type defined by conformant
    var items: [Item] { get set }
    mutating func add(_ item: Item)
    func count() -> Int
}

// Implementation with Item = String
struct StringBox: Container {
    typealias Item = String  // Optional, Swift can infer
    var items: [String] = []

    mutating func add(_ item: String) {
        items.append(item)
    }

    func count() -> Int { items.count }
}

// Implementation with Item = Int
struct IntStack: Container {
    var items: [Int] = []

    mutating func add(_ item: Int) {
        items.append(item)
    }

    func count() -> Int { items.count }
}

11. Hoe werken Generics?

Generics maken het mogelijk flexibele, herbruikbare code te schrijven die met elk type werkt. Ze voorkomen codeduplicatie met behoud van typeveiligheid tijdens het compileren.

Generics.swiftswift
// Generic function
func swap<T>(_ a: inout T, _ b: inout T) {
    let temp = a
    a = b
    b = temp
}

// Type constraint with where
func findIndex<T: Equatable>(of item: T, in array: [T]) -> Int? {
    for (index, element) in array.enumerated() {
        if element == item { return index }  // Equatable required for ==
    }
    return nil
}

// Generic struct
struct Queue<Element> {
    private var elements: [Element] = []

    mutating func enqueue(_ element: Element) {
        elements.append(element)
    }

    mutating func dequeue() -> Element? {
        guard !elements.isEmpty else { return nil }
        return elements.removeFirst()
    }
}

var intQueue = Queue<Int>()
intQueue.enqueue(1)
intQueue.enqueue(2)
print(intQueue.dequeue())  // Optional(1)

12. Het Codable-protocol

Codable (een alias voor Encodable & Decodable) maakt automatische serialisatie van Swift-typen naar en van formaten zoals JSON mogelijk. De compiler genereert de implementatie als alle eigenschappen zelf Codable zijn.

Codable.swiftswift
struct User: Codable {
    let id: Int
    let name: String
    let email: String
    let createdAt: Date

    // CodingKeys to map different JSON names
    enum CodingKeys: String, CodingKey {
        case id
        case name
        case email
        case createdAt = "created_at"  // snake_case → camelCase
    }
}

// JSON decoding
let json = """
{
    "id": 1,
    "name": "Alice",
    "email": "alice@example.com",
    "created_at": "2026-01-15T10:30:00Z"
}
""".data(using: .utf8)!

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601

do {
    let user = try decoder.decode(User.self, from: json)
    print(user.name)  // "Alice"
} catch {
    print("Decoding error: \(error)")
}

// Encoding to JSON
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let data = try encoder.encode(user)

Concurrency en async/await

13. Hoe werkt async/await in Swift?

async/await vereenvoudigt asynchrone code door niet-blokkerende operaties sequentieel te laten schrijven. Een async-functie kan worden opgeschort zonder de thread te blokkeren, zodat andere taken kunnen worden uitgevoerd.

AsyncAwait.swiftswift
// Asynchronous function
func fetchUser(id: Int) async throws -> User {
    let url = URL(string: "https://api.example.com/users/\(id)")!

    // await suspends execution until response
    let (data, response) = try await URLSession.shared.data(from: url)

    guard let httpResponse = response as? HTTPURLResponse,
          httpResponse.statusCode == 200 else {
        throw NetworkError.invalidResponse
    }

    return try JSONDecoder().decode(User.self, from: data)
}

// Calling from async context
func loadUserProfile() async {
    do {
        let user = try await fetchUser(id: 42)
        print("User: \(user.name)")
    } catch {
        print("Error: \(error)")
    }
}

// Calling from synchronous context with Task
func buttonTapped() {
    Task {
        await loadUserProfile()
    }
}

14. Wat is een Actor?

Actors zijn referentietypen die hun interne staat beschermen tegen gelijktijdige toegang. Ze garanderen dat slechts één taak tegelijk toegang heeft tot hun veranderlijke eigenschappen, waardoor data races worden geëlimineerd.

Actors.swiftswift
// Actor protects its state automatically
actor BankAccount {
    private var balance: Double = 0

    func deposit(_ amount: Double) {
        balance += amount  // Automatic thread-safe access
    }

    func withdraw(_ amount: Double) -> Bool {
        guard balance >= amount else { return false }
        balance -= amount
        return true
    }

    func getBalance() -> Double {
        return balance
    }
}

// Usage - await required to access actor
let account = BankAccount()

Task {
    await account.deposit(100)
    let success = await account.withdraw(30)
    let balance = await account.getBalance()
    print("Balance: \(balance)")  // 70
}

15. Task en TaskGroup

Task creëert een eenheid van asynchroon werk. TaskGroup maakt het mogelijk meerdere taken parallel uit te voeren en hun resultaten te verzamelen.

TaskGroup.swiftswift
// Simple Task
let task = Task {
    return await fetchUser(id: 1)
}
let user = try await task.value

// TaskGroup for parallelization
func fetchMultipleUsers(ids: [Int]) async throws -> [User] {
    try await withThrowingTaskGroup(of: User.self) { group in
        // Launch all requests in parallel
        for id in ids {
            group.addTask {
                try await fetchUser(id: id)
            }
        }

        // Collect results as they complete
        var users: [User] = []
        for try await user in group {
            users.append(user)
        }
        return users
    }
}

// All 3 requests run in parallel
let users = try await fetchMultipleUsers(ids: [1, 2, 3])

16. Hoe werkt @MainActor?

@MainActor zorgt ervoor dat code op de hoofdthread wordt uitgevoerd. Dit is essentieel voor UI-updates die altijd op de hoofdthread moeten plaatsvinden.

MainActor.swiftswift
// UI class annotated with @MainActor
@MainActor
class UserViewModel: ObservableObject {
    @Published var user: User?
    @Published var isLoading = false
    @Published var error: String?

    func loadUser() async {
        isLoading = true  // ✅ On main thread automatically

        do {
            // Network operation on background thread
            user = try await fetchUser(id: 42)
        } catch {
            self.error = error.localizedDescription
        }

        isLoading = false  // ✅ Automatic return to main thread
    }
}

// Or for a specific function
func updateUI() async {
    await MainActor.run {
        // This block runs on main thread
        label.text = "Updated"
    }
}

Patronen en architectuur

17. Het Delegate-patroon

Het Delegate-patroon stelt een object in staat bepaalde verantwoordelijkheden te delegeren aan een ander object. Het is alomtegenwoordig in UIKit (UITableViewDelegate, UITextFieldDelegate enz.).

DelegatePattern.swiftswift
// 1. Define the delegate protocol
protocol DownloadManagerDelegate: AnyObject {
    func downloadDidStart()
    func downloadDidProgress(_ progress: Double)
    func downloadDidComplete(data: Data)
    func downloadDidFail(error: Error)
}

// 2. Class that uses the delegate
class DownloadManager {
    // weak to avoid retain cycles
    weak var delegate: DownloadManagerDelegate?

    func startDownload(url: URL) {
        delegate?.downloadDidStart()

        // Simulated download
        Task {
            for progress in stride(from: 0.0, to: 1.0, by: 0.1) {
                try await Task.sleep(nanoseconds: 100_000_000)
                delegate?.downloadDidProgress(progress)
            }
            delegate?.downloadDidComplete(data: Data())
        }
    }
}

// 3. Class that implements the delegate
class ViewController: UIViewController, DownloadManagerDelegate {
    let manager = DownloadManager()

    override func viewDidLoad() {
        super.viewDidLoad()
        manager.delegate = self  // Register as delegate
    }

    func downloadDidStart() { print("Started") }
    func downloadDidProgress(_ progress: Double) { print("\(progress * 100)%") }
    func downloadDidComplete(data: Data) { print("Complete") }
    func downloadDidFail(error: Error) { print("Error: \(error)") }
}

18. Wat is het MVVM-patroon?

MVVM (Model-View-ViewModel) scheidt presentatielogica van de view. Het ViewModel stelt observeerbare gegevens beschikbaar die de View weergeeft, zonder details van de View te kennen.

MVVM.swiftswift
// Model
struct Article: Identifiable {
    let id: UUID
    let title: String
    let content: String
    let publishedAt: Date
}

// ViewModel
@MainActor
class ArticleListViewModel: ObservableObject {
    @Published private(set) var articles: [Article] = []
    @Published private(set) var isLoading = false
    @Published var errorMessage: String?

    private let repository: ArticleRepository

    init(repository: ArticleRepository = .shared) {
        self.repository = repository
    }

    func loadArticles() async {
        isLoading = true
        errorMessage = nil

        do {
            articles = try await repository.fetchArticles()
        } catch {
            errorMessage = "Failed to load articles"
        }

        isLoading = false
    }
}

// View (SwiftUI)
struct ArticleListView: View {
    @StateObject private var viewModel = ArticleListViewModel()

    var body: some View {
        Group {
            if viewModel.isLoading {
                ProgressView()
            } else {
                List(viewModel.articles) { article in
                    Text(article.title)
                }
            }
        }
        .task { await viewModel.loadArticles() }
    }
}

Klaar om je iOS gesprekken te halen?

Oefen met onze interactieve simulatoren, flashcards en technische tests.

19. Dependency Injection

Dependency injection houdt in dat de afhankelijkheden van een object van buitenaf worden aangeleverd in plaats van intern te worden aangemaakt. Dit verbetert de testbaarheid en ontkoppeling.

DependencyInjection.swiftswift
// Protocol for abstraction
protocol UserServiceProtocol {
    func fetchUser(id: Int) async throws -> User
}

// Real implementation
class UserService: UserServiceProtocol {
    func fetchUser(id: Int) async throws -> User {
        // Real API call
        let url = URL(string: "https://api.example.com/users/\(id)")!
        let (data, _) = try await URLSession.shared.data(from: url)
        return try JSONDecoder().decode(User.self, from: data)
    }
}

// ViewModel with injection
class ProfileViewModel: ObservableObject {
    private let userService: UserServiceProtocol

    // Constructor injection
    init(userService: UserServiceProtocol = UserService()) {
        self.userService = userService
    }

    func loadProfile(id: Int) async {
        // Uses injected service
    }
}

// Mock for testing
class MockUserService: UserServiceProtocol {
    func fetchUser(id: Int) async throws -> User {
        return User(id: id, name: "Test User", email: "test@test.com")
    }
}

// In tests
let viewModel = ProfileViewModel(userService: MockUserService())

20. Hoe het Singleton-patroon te implementeren?

Het Singleton-patroon zorgt ervoor dat een klasse slechts één globaal toegankelijke instantie heeft. In Swift wordt hiervoor een statische eigenschap met een private initializer gebruikt.

Singleton.swiftswift
class NetworkManager {
    // Single globally accessible instance
    static let shared = NetworkManager()

    // Private initializer prevents creating other instances
    private init() {
        // Initial configuration
    }

    private let session = URLSession.shared

    func request<T: Decodable>(_ url: URL) async throws -> T {
        let (data, _) = try await session.data(from: url)
        return try JSONDecoder().decode(T.self, from: data)
    }
}

// Usage
let user: User = try await NetworkManager.shared.request(url)
Voorzichtig met Singletons

Singletons creëren globale staat die het testen en ontkoppelen bemoeilijkt. Waar mogelijk verdient dependency injection de voorkeur.

Geavanceerde concepten

21. @escaping bij closures

Een closure is @escaping wanneer deze kan worden aangeroepen nadat de functie die deze ontvangt is teruggekeerd. Dit komt vaak voor bij asynchrone callbacks en het opslaan van closures.

Escaping.swiftswift
class DataLoader {
    // Storage of completion handlers
    private var completionHandlers: [() -> Void] = []

    // @escaping because closure is stored and called later
    func loadData(completion: @escaping () -> Void) {
        completionHandlers.append(completion)

        DispatchQueue.global().async {
            // Async work...
            Thread.sleep(forTimeInterval: 1)

            DispatchQueue.main.async {
                // Closure called after loadData returns
                completion()
            }
        }
    }

    // Non-escaping by default: closure called before return
    func transform(data: Data, using transformer: (Data) -> String) -> String {
        return transformer(data)  // Called immediately
    }
}

// With @escaping, watch for retain cycles
class ViewController {
    var loader = DataLoader()
    var data: String?

    func load() {
        loader.loadData { [weak self] in  // [weak self] avoids retain cycle
            self?.data = "Loaded"
        }
    }
}

22. Wat is @propertyWrapper?

Property wrappers kapselen de opslag- en toegangslogica van een eigenschap in. Ze maken het mogelijk patronen zoals validatie, logging of persistentie te hergebruiken.

PropertyWrapper.swiftswift
// Property wrapper for positive values only
@propertyWrapper
struct Positive {
    private var value: Int = 0

    var wrappedValue: Int {
        get { value }
        set { value = max(0, newValue) }  // Force positive
    }

    // Projected value accessible via $
    var projectedValue: Bool {
        value > 0
    }

    init(wrappedValue: Int) {
        self.wrappedValue = wrappedValue
    }
}

// Usage
struct Player {
    @Positive var score: Int = 0
    @Positive var health: Int = 100
}

var player = Player()
player.score = -50   // Becomes 0 (clamped)
print(player.score)  // 0
print(player.$score) // false (projectedValue)

player.score = 100
print(player.$score) // true

23. Result Builders

Result builders maken het mogelijk complexe waarden op te bouwen met declaratieve syntaxis. Dit mechanisme vormt de basis van de DSL-syntaxis van SwiftUI.

ResultBuilder.swiftswift
// Result builder definition
@resultBuilder
struct StringBuilder {
    static func buildBlock(_ components: String...) -> String {
        components.joined(separator: " ")
    }

    static func buildOptional(_ component: String?) -> String {
        component ?? ""
    }

    static func buildEither(first component: String) -> String {
        component
    }

    static func buildEither(second component: String) -> String {
        component
    }
}

// Function using the builder
func buildGreeting(@StringBuilder _ content: () -> String) -> String {
    content()
}

// Usage with declarative syntax
let greeting = buildGreeting {
    "Hello"
    "and"
    "welcome"
    if Bool.random() {
        "!"
    } else {
        "."
    }
}
print(greeting)  // "Hello and welcome !" or "Hello and welcome ."

24. Hoe werken some en Opaque Types?

some declareert een opaque type: het exacte type is bekend bij de compiler maar verborgen voor de aanroeper. Dit is essentieel voor protocols met associated types en maakt optimalisaties mogelijk.

OpaqueTypes.swiftswift
// Without some: error because Collection has associated type
// func makeCollection() -> Collection { ... }  // ❌ Error

// With some: exact type is hidden but consistent
func makeArray() -> some Collection {
    return [1, 2, 3]  // Always returns the same concrete type
}

// Used in SwiftUI for body
struct ContentView: View {
    var body: some View {  // Exact type inferred but hidden
        VStack {
            Text("Hello")
            Text("World")
        }
    }
}

// Difference with any (existential)
func processAny(_ collection: any Collection) {
    // Can accept different types, runtime overhead
}

func processSome(_ collection: some Collection) {
    // Type fixed at compile time, no overhead
}

25. Swift-macro's

Macro's (Swift 5.9+) genereren code tijdens het compileren. Ze verminderen boilerplate-code met behoud van typeveiligheid en debugbaarheid.

Macros.swiftswift
// Freestanding macro: generates an expression
let (x, y) = #unwrap(optionalX, optionalY)
// Expands to: guard let x = optionalX, let y = optionalY else { ... }

// Attached macro: modifies a declaration
@Observable  // Macro that generates Observable boilerplate
class UserModel {
    var name: String = ""
    var email: String = ""
}
// Automatically generates @ObservationTracked, ObservationRegistrar, etc.

// Macro for Codable with customization
@Codable
struct Product {
    let id: Int
    @CodableKey("product_name") let name: String  // Renames JSON key
    @CodableIgnored var cache: Data?              // Excludes from coding
}

// Creating a custom macro
@attached(member, names: named(init))
public macro AutoInit() = #externalMacro(module: "MyMacros", type: "AutoInitMacro")

@AutoInit
struct Point {
    let x: Int
    let y: Int
    // init(x: Int, y: Int) generated automatically
}
Macro's debuggen

In Xcode kan via een rechtermuisklik op een macro en "Expand Macro" de gegenereerde code worden bekeken. Handig voor het begrijpen en debuggen.

Conclusie

Deze 25 vragen behandelen de fundamenten die elke Swift-ontwikkelaar moet beheersen om te slagen in iOS-sollicitatiegesprekken. Van geheugenbeheer met ARC tot moderne concurrency-patronen, elk concept past in het Swift-ecosysteem.

Checklist voor herhaling

  • Optionals en hun verschillende unwrapping-methoden beheersen
  • Het verschil tussen waarde- en referentietypen begrijpen
  • Retain cycles met weak/unowned kunnen herkennen en oplossen
  • async/await en actors voor concurrency inzetten
  • Delegate-, MVVM- en Dependency Injection-patronen implementeren
  • Protocols, generics en Codable kennen
  • Geavanceerde concepten begrijpen: property wrappers, result builders, macro's

Aanvullende bronnen

Voor diepgaander leren blijft de officiële Swift-documentatie de belangrijkste referentie. Regelmatig oefenen met eigen projecten en programmeeruitdagingen helpt deze concepten te verankeren.

Begin met oefenen!

Test je kennis met onze gespreksimulatoren en technische tests.

Tags

#swift
#ios
#interview
#career
#apple

Delen

Gerelateerde artikelen