Junior Mobile Developer (iOS) Interview Questions: Complete Guide

Milad Bonakdar
Author
Master iOS development fundamentals with essential interview questions covering Swift, UIKit, SwiftUI, data persistence, and iOS app architecture for junior developers.
Introduction
iOS development has become one of the most sought-after skills in mobile app development. With Swift as the primary language and powerful frameworks like UIKit and SwiftUI, iOS developers create experiences for millions of users worldwide.
This guide covers essential interview questions for Junior iOS Developers. We explore Swift fundamentals, UI development, data persistence, networking, and iOS best practices to help you prepare for your first iOS developer role.
Swift Fundamentals (6 Questions)
1. What is the difference between var and let in Swift?
Answer:
var: Declares a mutable variable. Its value can be changed after initialization.let: Declares an immutable constant. Once set, its value cannot be changed.- Best Practice: Use
letby default for safety and clarity. Only usevarwhen you know the value will change.
let name = "John" // Cannot be changed
var age = 25 // Can be changed
age = 26 // Valid
// name = "Jane" // Error: Cannot assign to valueRarity: Very Common Difficulty: Easy
2. Explain Optionals in Swift. What is Optional Binding?
Answer:
Optionals represent a value that might be nil (absence of a value).
- Declaration: Use
?after the type:var name: String? - Unwrapping Methods:
- Force Unwrapping:
name!(dangerous, crashes if nil) - Optional Binding: Safely unwrap using
if letorguard let - Nil Coalescing:
name ?? "Default" - Optional Chaining:
user?.address?.city
- Force Unwrapping:
var email: String? = "user@example.com"
// Optional Binding
if let unwrappedEmail = email {
print("Email: \(unwrappedEmail)")
} else {
print("No email")
}
// Guard statement
guard let email = email else { return }
print(email)Rarity: Very Common Difficulty: Easy
3. What is the difference between a class and a struct in Swift?
Answer:
- Class: Reference type (stored on heap). Supports inheritance. Passed by reference.
- Struct: Value type (stored on stack). No inheritance. Passed by copy.
- When to use:
- Struct: For simple data models, when you want value semantics (independent copies)
- Class: When you need inheritance, reference semantics, or deinitializers
struct Point {
var x: Int
var y: Int
}
class Person {
var name: String
init(name: String) { self.name = name }
}
var p1 = Point(x: 0, y: 0)
var p2 = p1 // Copy created
p2.x = 10 // p1.x is still 0
var person1 = Person(name: "John")
var person2 = person1 // Same reference
person2.name = "Jane" // person1.name is also "Jane"Rarity: Very Common Difficulty: Medium
4. What are Closures in Swift?
Answer: Closures are self-contained blocks of functionality that can be passed around and used in your code. They're similar to lambdas or anonymous functions in other languages.
- Syntax:
{ (parameters) -> ReturnType in statements } - Capturing Values: Closures can capture and store references to variables from their surrounding context.
- Common Use: Completion handlers, callbacks, array operations.
// Simple closure
let greet = { (name: String) -> String in
return "Hello, \(name)!"
}
print(greet("Alice"))
// Trailing closure syntax
let numbers = [1, 2, 3, 4, 5]
let doubled = numbers.map { $0 * 2 }
print(doubled) // [2, 4, 6, 8, 10]Rarity: Very Common Difficulty: Medium
5. Explain the difference between weak and unowned references.
Answer: Both are used to prevent retain cycles (memory leaks) in reference types.
weak: Optional reference that automatically becomesnilwhen the referenced object is deallocated. Use when the reference might outlive the object.unowned: Non-optional reference that assumes the object will always exist. Crashes if accessed after deallocation. Use when you're certain the reference won't outlive the object.
class Person {
var name: String
weak var apartment: Apartment? // weak to prevent retain cycle
init(name: String) { self.name = name }
}
class Apartment {
var unit: String
unowned let tenant: Person // unowned - apartment always has tenant
init(unit: String, tenant: Person) {
self.unit = unit
self.tenant = tenant
}
}Rarity: Common Difficulty: Medium
6. What are Protocols in Swift?
Answer: Protocols define a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. Classes, structs, and enums can adopt protocols.
- Similar to: Interfaces in other languages
- Protocol Extensions: Can provide default implementations
- Protocol-Oriented Programming: Swift's preferred paradigm
protocol Drawable {
func draw()
}
struct Circle: Drawable {
var radius: Double
func draw() {
print("Drawing circle with radius \(radius)")
}
}
struct Rectangle: Drawable {
var width: Double
var height: Double
func draw() {
print("Drawing rectangle \(width)x\(height)")
}
}Rarity: Very Common Difficulty: Easy
UIKit Basics (5 Questions)
7. What is the difference between UIView and UIViewController?
Answer:
UIView: Represents a rectangular area on the screen. Handles drawing and event handling. Examples:UILabel,UIButton,UIImageView.UIViewController: Manages a view hierarchy. Handles view lifecycle, user interactions, and navigation. Contains one root view and manages its subviews.- Relationship: A view controller manages views. One view controller can have many views.
Rarity: Very Common Difficulty: Easy
8. Explain the View Controller lifecycle methods.
Answer: View controllers have specific lifecycle methods called in order:
viewDidLoad(): Called once when the view is loaded into memory. Setup that needs to happen once.viewWillAppear(_:): Called before the view appears on screen. Can be called multiple times.viewDidAppear(_:): Called after the view appears on screen. Start animations here.viewWillDisappear(_:): Called before the view disappears.viewDidDisappear(_:): Called after the view disappears. Clean up resources.
override func viewDidLoad() {
super.viewDidLoad()
// One-time setup: configure UI, load data
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Refresh data, start listening to notifications
}Rarity: Very Common Difficulty: Easy
9. What is Auto Layout and how do you use constraints?
Answer: Auto Layout is a constraint-based layout system that allows you to create adaptive UIs that work across different screen sizes and orientations.
- Constraints: Define relationships between views (width, height, spacing, alignment)
- Methods:
- Interface Builder: Visual editor in Xcode
- Programmatic: NSLayoutConstraint or NSLayoutAnchor API
- Visual Format Language: String-based (less common now)
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
button.widthAnchor.constraint(equalToConstant: 200),
button.heightAnchor.constraint(equalToConstant: 50)
])Rarity: Very Common Difficulty: Medium
10. What is the difference between frame and bounds?
Answer:
frame: The view's location and size in its superview's coordinate system. Used for positioning the view.bounds: The view's location and size in its own coordinate system. Origin is usually (0, 0). Used for drawing content inside the view.- Key Difference:
frameis relative to parent,boundsis relative to itself.
let view = UIView(frame: CGRect(x: 50, y: 100, width: 200, height: 150))
print(view.frame) // (50, 100, 200, 150) - position in superview
print(view.bounds) // (0, 0, 200, 150) - own coordinate systemRarity: Common Difficulty: Medium
11. How do you handle user input from a UITextField?
Answer: Multiple approaches exist:
- Target-Action: Add target for
.editingChangedevent - Delegate: Conform to
UITextFieldDelegateprotocol - Combine: Use publishers (modern approach)
class ViewController: UIViewController, UITextFieldDelegate {
let textField = UITextField()
override func viewDidLoad() {
super.viewDidLoad()
// Delegate approach
textField.delegate = self
// Target-Action approach
textField.addTarget(self, action: #selector(textChanged), for: .editingChanged)
}
@objc func textChanged(_ textField: UITextField) {
print("Text: \(textField.text ?? "")")
}
// Delegate method
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
}Rarity: Very Common Difficulty: Easy
SwiftUI Basics (4 Questions)
12. What is SwiftUI and how is it different from UIKit?
Answer: SwiftUI is Apple's declarative UI framework introduced in iOS 13.
- UIKit: Imperative (you tell the system how to build the UI step by step). Mature, widely used.
- SwiftUI: Declarative (you describe what the UI should look like). Modern, less code, automatic updates.
- Key Features:
- Live preview in Xcode
- Cross-platform (iOS, macOS, watchOS, tvOS)
- State-driven UI updates
- Built-in animations
// SwiftUI
struct ContentView: View {
@State private var count = 0
var body: some View {
VStack {
Text("Count: \(count)")
Button("Increment") {
count += 1
}
}
}
}Rarity: Very Common Difficulty: Easy
13. Explain @State, @Binding, and @ObservedObject in SwiftUI.
Answer: These are property wrappers for managing state in SwiftUI:
@State: Private state owned by the view. When it changes, the view re-renders.@Binding: Creates a two-way connection to a@Stateproperty owned by a parent view.@ObservedObject: References an external object (class conforming toObservableObject). View updates when the object's@Publishedproperties change.
class UserData: ObservableObject {
@Published var name = ""
}
struct ParentView: View {
@State private var isOn = false
@ObservedObject var userData = UserData()
var body: some View {
VStack {
ChildView(isOn: $isOn) // Pass binding
Text(userData.name)
}
}
}
struct ChildView: View {
@Binding var isOn: Bool // Receives binding
var body: some View {
Toggle("Switch", isOn: $isOn)
}
}Rarity: Common Difficulty: Medium
14. How do you create a list in SwiftUI?
Answer:
Use the List view to display scrollable collections of data.
struct ContentView: View {
let fruits = ["Apple", "Banana", "Orange", "Grape"]
var body: some View {
List(fruits, id: \.self) { fruit in
Text(fruit)
}
}
}
// With custom model
struct Task: Identifiable {
let id = UUID()
let title: String
}
struct TaskListView: View {
let tasks = [
Task(title: "Buy groceries"),
Task(title: "Walk the dog")
]
var body: some View {
List(tasks) { task in
Text(task.title)
}
}
}Rarity: Common Difficulty: Easy
15. What are View Modifiers in SwiftUI?
Answer: View modifiers are methods that create a new view with modified properties. They're chainable.
- Common Modifiers:
.padding(),.background(),.foregroundColor(),.font(),.frame() - Order Matters: Modifiers are applied in sequence
Text("Hello, World!")
.font(.title)
.foregroundColor(.blue)
.padding()
.background(Color.yellow)
.cornerRadius(10)
.shadow(radius: 5)
// Custom modifier
struct PrimaryButtonStyle: ViewModifier {
func body(content: Content) -> some View {
content
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
}
extension View {
func primaryButton() -> some View {
modifier(PrimaryButtonStyle())
}
}Rarity: Common Difficulty: Easy
Data & Networking (5 Questions)
16. How do you make a network request in iOS?
Answer:
Use URLSession for networking tasks.
func fetchData() {
guard let url = URL(string: "https://api.example.com/data") else { return }
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
print("Error: \(error)")
return
}
guard let data = data else { return }
// Parse JSON
do {
let decoder = JSONDecoder()
let result = try decoder.decode(MyModel.self, from: data)
print(result)
} catch {
print("Decoding error: \(error)")
}
}
task.resume()
}Rarity: Very Common Difficulty: Medium
17. What is Codable in Swift?
Answer:
Codable is a type alias for Encodable & Decodable protocols. It allows easy conversion between Swift types and external representations like JSON.
- Automatic Synthesis: Swift automatically generates encoding/decoding code if all properties are Codable.
- Custom Keys: Use
CodingKeysenum to map different property names.
struct User: Codable {
let id: Int
let name: String
let email: String
}
// Decoding JSON
let json = """
{
"id": 1,
"name": "John Doe",
"email": "john@example.com"
}
""".data(using: .utf8)!
let decoder = JSONDecoder()
let user = try decoder.decode(User.self, from: json)
// Encoding to JSON
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let jsonData = try encoder.encode(user)Rarity: Very Common Difficulty: Easy
18. How do you persist data locally in iOS?
Answer: Multiple options for local data persistence:
- UserDefaults: Simple key-value storage for small amounts of data (settings, preferences)
- File System: Save files to Documents or Cache directory
- Core Data: Apple's framework for object graph management and persistence
- SQLite: Relational database
- Keychain: Secure storage for sensitive data (passwords, tokens)
// UserDefaults
UserDefaults.standard.set("John", forKey: "username")
let username = UserDefaults.standard.string(forKey: "username")
// File System
let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let filePath = documentsPath.appendingPathComponent("data.json")
// Write
try data.write(to: filePath)
// Read
let loadedData = try Data(contentsOf: filePath)Rarity: Very Common Difficulty: Easy
19. What is the difference between synchronous and asynchronous operations?
Answer:
- Synchronous: Blocks the current thread until the operation completes. Can freeze the UI if run on the main thread.
- Asynchronous: Doesn't block the thread. Operation runs in the background, and a completion handler is called when done.
- Main Thread: UI updates must happen on the main thread. Use
DispatchQueue.main.asyncto switch to the main thread.
// Synchronous (BAD for network calls)
let data = try Data(contentsOf: url) // Blocks until complete
// Asynchronous (GOOD)
URLSession.shared.dataTask(with: url) { data, response, error in
// This runs on background thread
DispatchQueue.main.async {
// Update UI on main thread
self.label.text = "Data loaded"
}
}.resume()Rarity: Common Difficulty: Easy
20. What is Grand Central Dispatch (GCD)?
Answer: GCD is Apple's low-level API for managing concurrent operations.
- Dispatch Queues: FIFO queues that execute tasks serially or concurrently
- Main Queue: Serial queue for UI updates
- Global Queues: Concurrent queues with different priorities (background, utility, userInitiated)
// Run on background thread
DispatchQueue.global(qos: .background).async {
// Heavy computation
let result = performExpensiveOperation()
// Update UI on main thread
DispatchQueue.main.async {
self.label.text = result
}
}
// Delay execution
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
print("Executed after 2 seconds")
}Rarity: Common Difficulty: Medium





