Swift
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Optionals
-
Section 4: Object-Oriented and Value Types
-
Section 5: Memory Management
-
Section 6: Generics and Error Handling
-
Section 7: Concurrency
-
Section 8: Working with Collections
-
Section 9: Codable and Data Handling
-
Section 10: Protocol-Oriented Programming
-
Section 11: Testing and Tooling
-
Section 12: Practical Projects
-
Section 13: Interview Practice
-
Section 14: More Practice Exercises
-
Section 15: More Standard Library
-
Section 16: Advanced Concurrency
-
Section 17: Foundation Framework Deep Dive
-
Section 18: URLSession and Networking Deep Dive
-
Section 19: Combine Framework
-
Section 20: SwiftUI Fundamentals for Swift Developers
-
Section 21: Server-Side Swift with Vapor
-
Section 22: Swift Package Manager Deep Dive
-
Section 23: Swift Concurrency Deep Dive
-
Section 24: More Language Features
-
Section 25: Error Handling Deep Dive
-
Section 26: Testing Deep Dive
-
Section 27: Data Structures and Algorithms in Swift
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Swift Macros (Swift 5.9+)
-
Section 31: Property Wrappers Ecosystem
-
Section 32: Swift Interop Deep Dive
-
Section 33: iOS App Architecture Patterns
-
Section 34: Performance and Debugging
-
Section 35: App Distribution and CI/CD
-
Section 36: More Practical Projects
-
Section 37: SwiftData and Persistence
-
Section 38: More Design Patterns
-
Section 39: More Review and Practice
-
Section 40: More Foundation Deep Dive
-
Section 41: Advanced Collections in Swift
-
Section 42: Advanced Generics Practice
-
Section 43: UIKit for Legacy and Hybrid Apps
-
Section 44: watchOS and visionOS Development Basics
-
Section 45: More Networking Patterns
-
Section 46: More Testing Practice
-
Section 47: Accessibility in Swift Apps
-
Section 48: Localization
-
Section 49: More Practical Projects Round 2
-
Section 50: Swift Charts Framework
-
Section 51: More Interview and Algorithm Practice
-
Section 52: Final Practice and Mastery
-
Section 53: Swift Compiler and Build System
-
Section 54: More Concurrency Practice
-
Section 55: App Store Guidelines and Review
-
Section 56: More Design and Architecture
131: Common Swift Interview Questions on Concurrency
If you're prepping for a senior Swift role, you can bet your life that concurrency is going to come up. Interviewers love it because it's where most developers' mental models break. They aren't just looking for you to know the syntax of async/await; they want to see if you understand why we moved away from Grand Central Dispatch (GCD) and how the compiler actually protects your data.
Let's start with a snippet I see all the time. It looks perfectly fine at a glance, but it'll trigger a runtime warning (or a crash) the moment you run it in a real app.
class UserProfileViewModel: ObservableObject {
@Published var username: String = "Guest"
let service = NetworkService()
func updateUsername() {
Task {
// Fetching data from a remote API
let newName = await service.fetchUsername()
// This is where the wheels fall off
self.username = newName
}
}
}
The Background Thread UI Violation
The bug here is a classic: updating a @Published property—which triggers a UI update—from a background thread. You might think, "But I'm using Task, isn't that handled?" Not necessarily. While Task inherits the actor context of the place it's created, if updateUsername() is called from a non-isolated context or a background queue, that assignment to self.username happens off the main thread. SwiftUI will scream at you, and your app's behavior becomes unpredictable.
Enforcing Main-Thread Execution with @MainActor
In the old days, we would have wrapped that assignment in DispatchQueue.main.async { ... }. That worked, but it was "opt-in" safety—you had to remember to do it every single time. The modern way is to make the safety "opt-out" using @MainActor.
@MainActor
class UserProfileViewModel: ObservableObject {
@Published var username: String = "Guest"
let service = NetworkService()
func updateUsername() {
Task {
let newName = await service.fetchUsername()
// Now the compiler guarantees this happens on the main thread
self.username = newName
}
}
}
By marking the entire class as @MainActor, you're telling Swift that every property and method in this class must be accessed on the main thread. If you try to call a @MainActor method from a background context, the compiler will force you to use await, making the thread jump explicit and safe.
The "GCD vs. Swift Concurrency" Interview Question
You'll likely be asked: "Why use async/await over GCD?" Don't just say "it's cleaner." Talk about Structured Concurrency. In GCD, when you dispatch a block of code, it's "fire and forget." You lose the relationship between the caller and the called. If the caller is cancelled, the GCD block keeps running in the background, wasting resources (a "leak" of work).
With Swift Concurrency, tasks have a hierarchy. If a parent task is cancelled, that cancellation propagates down to all child tasks automatically. This prevents the "zombie" network requests that used to plague iOS apps for years.
Explaining Actors and Data Races
Another favorite: "What is an Actor and when should you use one?" I usually explain it as a "synchronized class." An actor ensures that only one task can access its mutable state at a time. This eliminates data races—where two threads try to change the same variable simultaneously, leading to memory corruption.
- Class: Reference type, not thread-safe. You'd need a
NSLockor a private serial queue to make it safe. - Actor: Reference type, thread-safe by design. The compiler manages the locking mechanism for you.
If the interviewer asks for a use case, suggest a DatabaseManager or an ImageCache. These are shared resources that multiple parts of an app hit at once. Using an actor there ensures that your cache doesn't crash when five different views try to save an image at the exact same millisecond.
Task vs. Task.detached
Watch out for this nuance. A standard Task { ... } inherits the current actor's context and priority. If you're in a @MainActor class, the Task starts on the main actor. Task.detached { ... }, however, creates a completely new top-level task that inherits nothing. Use detached only when you have a heavy computational job (like processing a large image) that absolutely must not block the main thread, even for a fraction of a second.
📋 Practical Task
Implement a Thread-Safe Download Coordinator
You are building a download manager that tracks the status of multiple file downloads. If multiple threads update the status of the same file simultaneously, the app will crash due to a data race.
Your Goal: Create an actor called DownloadCoordinator that manages a dictionary of download statuses. Implement the following:
- A private dictionary
statuses: [URL: String]to track the status (e.g., "Downloading", "Completed", "Failed"). - A function
updateStatus(for url: URL, to status: String)that updates the dictionary. - A function
getStatus(for url: URL) -> Stringthat returns the current status or "Unknown" if the URL isn't in the dictionary.
Then, write a small piece of code using a Task to simulate updating a status and printing it back. Remember that because DownloadCoordinator is an actor, you must use await when calling its methods from outside.
There are no comments for now.