Four Xcode projects designed for senior iOS interview preparation, covering concurrency patterns, SwiftUI development, unit testing, and advanced Swift concurrency.
Requirements: Xcode 15+, iOS 17+
git clone https://github.com/christopherkmoore/iOS-Practice.git
cd iOS-PracticeEach project includes a pre-generated .xcodeproj. Open any project directly:
open ConcurrencyBugHunt/ConcurrencyBugHunt.xcodeproj
open SwiftUIBuilder/SwiftUIBuilder.xcodeproj
open TestabilityWorkshop/TestabilityWorkshop.xcodeproj
open ConcurrencyWorkshop/ConcurrencyWorkshop.xcodeprojUsing xcodegen? Each project also includes a project.yml for regeneration:
cd ConcurrencyBugHunt && xcodegen generatePurpose: Code review practice — identify and fix concurrency bugs
How to use:
- Run the app and navigate to each exercise
- Each view contains intentionally buggy code
- Read the code, identify the bug(s), then verify by running
- Try to fix each bug before looking at solutions
| Section | Exercises | Key Bugs |
|---|---|---|
| GCD Issues | Race Condition | Unsynchronized shared mutable state |
| Main Thread Violation | UI updates from background thread | |
| Deadlock | Sync dispatch to current queue, lock ordering | |
| Async/Await | Task Cancellation | Missing cancellation checks, stale results |
| Actor Reentrancy | State changes during await suspension | |
| Unstructured Task Leak | Tasks outliving views, missing cleanup | |
| Combine | Publisher Retain Cycle | Strong self in sink closures |
| Missing Cancellable | Subscriptions not stored |
Purpose: Build UI iteratively with mock API data
How to use:
- Each exercise is a complete, working implementation
- Study the patterns, then try recreating from scratch
- Modify and extend to practice variations
| Section | Exercises | Patterns Covered |
|---|---|---|
| Lists | Basic API List | async/await data loading, error states |
| Pull to Refresh | .refreshable, last-updated timestamp |
|
| Search & Filter | .searchable, debounced search, combined filters |
|
| Grids | Photo Grid | LazyVGrid, category filtering, sheet presentation |
| Adaptive Grid | GridItem(.adaptive), list/grid toggle with animation |
|
| Navigation | Master-Detail | NavigationLink(value:), navigationDestination |
| Modal Presentation | .sheet, .fullScreenCover, .confirmationDialog, .alert |
|
| State | Form Validation | Real-time validation, password strength, submit gating |
| Shared State | @StateObject, @EnvironmentObject, cart pattern |
Shared Resources:
MockAPIService.swift— Actor-based mock API with simulated delays and random failures- Models:
User,Post,Photo,Product
Purpose: Refactor untestable code and write comprehensive tests
How to use:
- Read the "Before" (untestable) code in each exercise
- Study the "After" (testable) refactored version
- Examine the corresponding test file
- Practice writing tests yourself before looking at solutions
- Run tests with
Cmd+U
| Section | Exercise | Refactoring Pattern |
|---|---|---|
| Dependency Injection | Singleton Dependencies | Extract protocols, inject via initializer |
| Network Coupling | HTTPClient protocol, mock responses |
|
| Date/Time | DateProviding protocol, controllable time |
|
| Side Effects | UserDefaults | KeyValueStore protocol, in-memory store |
| File System | FileSystemProtocol, in-memory fake |
|
| Architecture | ViewModel Testing | @MainActor, state transitions, @Published |
| Protocol Mocking | Spy/Stub/Fake patterns, call verification | |
| Async | Async/Await Testing | Async test methods, Task cancellation |
Test Target Structure:
TestabilityWorkshopTests/
├── Mocks/TestMocks.swift # Reusable mock implementations
├── SingletonExerciseTests.swift
├── NetworkCouplingExerciseTests.swift
├── DateTimeExerciseTests.swift
├── UserDefaultsExerciseTests.swift
├── FileSystemExerciseTests.swift
├── ViewModelExerciseTests.swift
├── ProtocolMockingExerciseTests.swift
└── AsyncTestingExerciseTests.swift
Purpose: Deep-dive into Swift Concurrency and Combine with interactive learning
How to use:
- Each module has three tabs: Try It (interactive demo), Learn (Q&A), Code (full source)
- Run the demos to see concurrency patterns in action
- Study the Q&A section for conceptual understanding
- Review the source code and try exercises
| Section | Modules | Patterns Covered |
|---|---|---|
| AsyncSequences | Consuming AsyncSequence | for await, cancellation, breaking early |
| AsyncStream | yield, finish, onTermination, error handling |
|
| Bridging Delegates | Wrapping delegate APIs with continuations | |
| Continuations | Checked & Unsafe | withCheckedContinuation, timeout patterns, one-resume rule |
| Combine Operators | Combining Publishers | combineLatest, merge, zip |
| Transforming Streams | flatMap, switchToLatest, schedulers |
|
| Sendable | Thread Safety | Sendable protocol, @unchecked Sendable, crossing isolation |
| MainActor | UI Patterns | @MainActor, nonisolated, Swift 6 strict concurrency |
Module Structure:
- Each view includes working code examples you can run
- Q&A sections explain the "why" behind each pattern
- Exercises challenge you to extend or modify the code
- GCD:
DispatchQueue, serial vs concurrent,syncvsasync - Race conditions and data races
- Main thread safety for UI updates
- Deadlocks (queue reentry, lock ordering)
- Swift Concurrency:
async/await,Task,TaskGroup - Task cancellation and cooperative cancellation
- Actor isolation and reentrancy
- Structured vs unstructured concurrency
- Combine: Publishers, Subscribers,
AnyCancellable, retain cycles - AsyncSequence and
for awaitloops - AsyncStream for bridging imperative code
- Continuations:
withCheckedContinuation,withUnsafeContinuation -
Sendableprotocol and thread-safe types -
@MainActorand UI isolation patterns - Swift 6 strict concurrency preparation
- State management:
@State,@Binding,@StateObject,@ObservedObject,@EnvironmentObject - Lists and
ForEach -
LazyVGrid/LazyHGridwithGridItem - Navigation:
NavigationStack,NavigationLink,navigationDestination - Sheets, full-screen covers, alerts, confirmation dialogs
-
.searchableand.refreshablemodifiers -
.taskfor async data loading - Form validation patterns
- Animations and transitions
- Test structure: Arrange-Act-Assert
-
setUp()andtearDown() - Dependency injection for testability
- Protocol-based mocking (Stub, Spy, Fake)
- Testing
@Publishedproperties -
@MainActortest classes for ViewModels - Async test methods (
func test_x() async) - Testing error cases with
XCTAssertThrowsError - Verifying mock interactions (call counts, arguments)
- Protocols and protocol extensions
- Generics
- Value types vs reference types
- Access control
- Error handling
- Closures and capture lists (
[weak self]) -
Codable -
Resulttype
- Read systematically — Don't jump to conclusions
- Look for shared mutable state — Who reads? Who writes? When?
- Trace the thread — Which queue/actor is this running on?
- Check completion handlers — Are they always called? Only once?
- Verify cancellation — Is
Task.isCancelledchecked after awaits?
- Start with the data model — What state do you need?
- Choose the right property wrapper — Who owns this state?
- Handle loading/error/empty states — Always show something
- Test on device — Simulators hide performance issues
- Test behavior, not implementation — What should happen, not how
- One assertion focus per test — Tests should fail for one reason
- Use descriptive test names —
test_methodName_condition_expectedResult - Inject everything — If you can't inject it, you can't test it
- Reset mocks in tearDown — Tests must be independent
# Open in Xcode
open ConcurrencyBugHunt/ConcurrencyBugHunt.xcodeproj
open SwiftUIBuilder/SwiftUIBuilder.xcodeproj
open TestabilityWorkshop/TestabilityWorkshop.xcodeproj
open ConcurrencyWorkshop/ConcurrencyWorkshop.xcodeproj
# Run tests (TestabilityWorkshop)
# Cmd+U in Xcode, or:
xcodebuild test -project TestabilityWorkshop/TestabilityWorkshop.xcodeproj \
-scheme TestabilityWorkshop \
-destination 'platform=iOS Simulator,name=iPhone 15'iOS-Practice/
├── README.md # This file
├── ConcurrencyBugHunt/
│ ├── ConcurrencyBugHuntApp.swift
│ ├── ContentView.swift
│ └── Exercises/
│ ├── GCD/
│ ├── AsyncAwait/
│ └── Combine/
├── SwiftUIBuilder/
│ ├── SwiftUIBuilderApp.swift
│ ├── ContentView.swift
│ ├── Shared/MockAPIService.swift
│ └── Exercises/
│ ├── List/
│ ├── Grid/
│ ├── Navigation/
│ └── State/
├── TestabilityWorkshop/
│ ├── TestabilityWorkshopApp.swift
│ ├── ContentView.swift
│ ├── Exercises/
│ │ ├── DependencyInjection/
│ │ ├── SideEffects/
│ │ ├── Architecture/
│ │ └── Async/
│ └── TestabilityWorkshopTests/
│ ├── Mocks/TestMocks.swift
│ └── *Tests.swift
└── ConcurrencyWorkshop/
├── ConcurrencyWorkshopApp.swift
├── ContentView.swift
├── Shared/ # Reusable tab views, code viewer
├── ModuleContent/ # Q&A and exercise content
└── Modules/
├── AsyncSequences/
├── Continuations/
├── CombineOperators/
├── Sendable/
└── MainActor/