Video summary
iOS Dev Job Interview - Must Know Topics
Main summary
Key takeaways
Main ideas, concepts, and lessons
1) iOS networking basics with REST APIs + JSON (async/await + Swift)
Most iOS apps need to:
- Fetch data from a server
- Decode JSON responses into Swift models
- Update the UI to display results
JSON basics
- JSON objects use curly braces
{}for key/value pairs. - JSON can also return arrays
[]for lists of objects.
GitHub API example
- Use the GitHub REST API documentation to find the correct endpoint URL.
- Use
curlto test endpoints and inspect real JSON responses.
Step-by-step methodology for building a network call
Step 1: Build UI with dummy data
- Create the SwiftUI UI using placeholder/dummy values.
- Determine which fields you need on screen (e.g., avatar URL, login/username, bio).
- With third-party APIs, you’re limited to whatever data the API returns.
Step 2: Create Swift models from the JSON
- Create a
structmodel (e.g.,GitHubUser). - Make it conform to
Codable. - Ensure JSON property names match Swift property names (or handle differences).
- Common “gotchas”:
- JSON might use
snake_case(e.g.,avatar_url) - Swift typically uses
camelCase(e.g.,avatarURL) - Bridge naming styles with:
JSONDecoder.keyDecodingStrategy = .convertFromSnakeCase
- JSON might use
Step 3: Write the networking code
- Define a function like
getUser(...)that is:async(for concurrency)throws(because network errors are common)
- Use:
URLSession.shared.data(from: url)(GET request)
- Request/response handling:
- Convert the endpoint string to a
URL(and throw if invalid). - Check HTTP status code:
- Expect
200for success - Otherwise throw a custom error like
invalidResponse
- Expect
- Decode JSON:
- Create a
JSONDecoder - Configure snake_case decoding strategy if needed
- Decode into the model:
decoder.decode(GitHubUser.self, from: data)
- Create a
- Use
do/try/catchto throw errors such as:- invalid URL
- invalid response
- invalid data (decode failure)
- Convert the endpoint string to a
- Introduce the idea of a custom error enum (e.g.,
GHError).
Step 4: Connect networking to the UI
- In SwiftUI, trigger the async call using
.task { ... }. - Store results in
@State(e.g.,@State private var user: GitHubUser?). - On success:
- Assign the returned model to state so the UI updates automatically.
- On failure:
- Catch specific errors (invalid URL/response/invalid data) and handle appropriately.
- UI binding examples:
- Use optional-coalescing placeholders for missing
userfields. - Use
AsyncImageto load avatar images from a URL with a placeholder while loading.
- Use optional-coalescing placeholders for missing
- Demonstrated that changing the endpoint username updates the displayed data.
2) Interview concept: Classes vs structs (value types vs reference types)
Core rule
- Classes = reference types
- Structs = value types
Value type behavior (struct)
- Assigning/copying a struct creates a copy.
- Mutating the copy doesn’t affect the original.
- Analogy: copying/emailing an Excel spreadsheet to someone else.
Reference type behavior (class)
- Multiple variables reference the same underlying object.
- Mutating via one reference affects all references.
- Analogy: a shared Google Sheets document.
When to use which
- Use classes when you need inheritance or reference semantics.
- Use structs for lightweight/value semantics (highlighted as especially common in SwiftUI).
3) Generics (and the “balancing act”)
- Generics let you write type-safe code that works across many types.
- Generic syntax pattern:
func ...<T: SomeProtocol>(...) -> ...(angle brackets + constraints)
- Example idea:
- Constrain
TtoComparableso you can compare values of the same type.
- Constrain
- Standard library example:
- Arrays are generic (e.g.,
Array<Element>).
- Arrays are generic (e.g.,
Real-life example shown
- A generic network fetch pattern that decodes any
Decodabletype:- Fetch
User→ decodeUser.self - Fetch
Repository→ decodeRepository.self - With a generic function → decode into
T.self
- Fetch
Balancing act lesson
- Don’t add generics everywhere just for “future-proofing.”
- Prefer concrete types unless generics clearly reduce repetition (described as a “slam dunk”).
4) Closures, shorthand, and trailing closure syntax
- Closures are blocks of functionality that can be passed around (like functions).
Structure
- Parameters in parentheses (with types)
- Return type after parentheses
- Body inside
{ ... }
Closure shorthand
$0,$1, etc. correspond to parameters when parameter names are omitted.
Trailing closure syntax
- Used when the closure is the last argument to a function.
- Common in SwiftUI and Swift standard library APIs.
5) Escaping closures + memory management concerns (retain cycles)
- Closures marked
@escapingcan outlive the function that accepted them. - In network calls:
- The completion closure must persist until the async work finishes.
Retain cycle risk
- Closures may capture
selfto update UI afterward. - Use
weak self(or similar) to avoid retain cycles and memory leaks.
6) Functional programming: filter / map / reduce / compactMap / flatMap
-
Big idea: these are “shorthand for loops” over collections.
-
filter
- Keeps elements that match a condition (predicate returns true/false).
- map
- Transforms each element (e.g., extract a property or compute a new value).
- reduce
- Combines all elements into one value.
- Typically uses an initial value + operator.
- compactMap
- Removes
nilvalues from an array of optionals (map + filter out nil).
- Removes
- flatMap
- Flattens an array of arrays into one array.
- Can include transformation inside the
flatMapclosure.
7) Sets vs Arrays (collection choice)
Arrays
- Preserve order
- Allow duplicates
- Lookups/mutations cost
O(n)
Sets
- No duplicates
- Unordered
- Require
Hashable - Lookups/mutations are effectively constant time (conceptually “much faster”)
Set operations demonstrated
intersectionsubtractingisDisjointunionsymmetricDifferenceisSubset(of:),isSuperset(of:)insert,remove,contains
8) Optionals: if let, guard let, nil-coalescing, force unwrap, optional chaining
- Optionals represent “value or nil.”
Unwrapping strategies
if let- Works within the conditional scope; can lead to nesting (“pyramid of doom”).
guard let- Early exit pattern; avoids deep nesting.
- Makes the unwrapped variable available for the rest of the function scope.
- Nil-coalescing
value ?? defaultfor a fallback.
- Force unwrap
value!crashes if nil (generally avoid except in safe/controlled cases).
- Optional chaining
- When the whole object is optional (e.g.,
optionalUser?.name). - If the base is nil, the result becomes nil (to be handled with defaults or
if let).
- When the whole object is optional (e.g.,
9) Unit testing (what/why/how)
- A unit test checks a small function/module (“factory”) to ensure correct output.
Benefits
- Prevent regressions
- Increase confidence during refactoring
- Enable faster team work safely
Key lesson
- Tests help only when you cover edge cases you can foresee.
Example shown (tip calculator)
- Happy path test:
- Arrange inputs, Act to compute, Assert expected output
- Edge case test:
- Negative input should return
nil
- Negative input should return
- Uses
XCTestassertions like:XCTAssertEqualXCTAssertNil
Practical note
- Testing ROI depends on product/team size and how frequently code changes.
- Not all-or-nothing: test critical/unchanging logic more selectively.
10) Singleton pattern (pros/cons + when to use)
- Singleton = only one instance, globally accessible (e.g.,
UserDefaults.standard).
Use when
- You truly need exactly one shared instance; multiple instances would break correctness (e.g., persisted settings).
Pros
- Uniqueness
- Convenience/global access
Cons
- Global accessibility can create tangled dependencies (“spiderweb”).
- Testing becomes harder because code depends on shared global state.
Implementation pattern
static let shared = ...private init()to prevent external creation.
11) Dependency injection (DI)
- Dependency injection provides dependencies from outside rather than creating them inside the object.
Benefits
- Clearer data flow (more linear)
- Better separation of concerns
- Improved testability (swap real networking with mocks)
- Easier replacement of components
Example shown
- Parent view creates
NetworkManagerandBag. - Child view model receives them via initializer injection.
12) Delegates + protocols (one-to-one communication)
- Delegate pattern: one object communicates specific events to another via a protocol.
- Protocol = “job description” (list of commands, e.g.,
didSelectProduct(name:imageName:)). - Delegate property in the sender (destination VC) is set to the receiver (main VC).
Flow described
- Main VC presents bottom sheet (product selection VC).
- Product selection VC calls delegate method when the user picks an option.
- Main VC updates UI accordingly.
Emphasis
- Protocol defines capability.
- Delegate is the reference to the object that handles the callback.
13) View controller lifecycle methods (high-level ordering + use cases)
- Lifecycle methods are called automatically by the system.
Key ones
viewDidLoad- When the content view is created/loaded in memory (configure outlets guaranteed here).
viewWillAppear- Just before the view is added to the view hierarchy on screen.
viewDidAppear- After the view is in hierarchy; good for animations.
viewWillDisappear- Just before the view is removed; good for saving work.
viewDidDisappear- After removal from hierarchy.
Layout-related
viewWillLayoutSubviewsandviewDidLayoutSubviews- Called when bounds change (e.g., rotation), before/after subviews are laid out.
14) Concurrency + threading basics (main vs background, queues, GCD)
- Concurrency = multiple tasks at the same time (enabled by multi-core CPUs).
Threads
- Main thread handles UI and must stay responsive.
- Background threads handle heavy tasks.
Queues
- Serial queue
- Tasks execute one-by-one in order; predictable but slower.
- Avoids race conditions but can limit throughput.
- Concurrent queue
- Tasks can start in order but complete unpredictably.
- Faster, but you must avoid dependencies on completion order.
GCD usage example
DispatchQueue.main.async { ... }for UI updates after background work.- Mentioned
DispatchQueue.global(...)for background work (manual).
Race condition concept
- Concurrent execution order can break logic when tasks depend on each other.
15) Automatic Reference Counting (ARC) + retain cycles + weak references
- ARC tracks strong references to objects.
- Objects deallocate when the strong reference count reaches zero.
Retain cycle
- Two objects strongly reference each other, preventing deallocation.
Fix
- Make one side a
weakreference (or other non-strong reference).
Demonstrated example
- With
PersonandMacBook:- Strong mutual references prevent
deinit - Making owner weak allows both to deallocate
- Strong mutual references prevent
Speakers or sources featured
- The video appears to be taught by Shawn (referenced as “Shawn” with links like
shawnis.teachable.comandshawnallen.teachable.com). - Source material explicitly referenced:
- GitHub REST API documentation (for endpoints and example JSON responses)
- Apple frameworks/documentation implicitly referenced:
- URLSession, JSONDecoder/Codable, SwiftUI, UIKit lifecycle, GCD, ARC