Senior iOS Developer Swift Que-Ans (2025 Edition)
Senior iOS Developer Swift Que-Ans (2025 Edition)
(2025 Edition)
IT
● Stored in Heap: Objects live in memory until there are no references left.
● Pass-by-Reference: When assigned to a new variable, only the reference (pointer) is
copied, not the data.
● Supports Inheritance: Classes can inherit properties and methods from another class.
● Mutable Even If Declared with let: Unlike structs, properties of a class can be
modified even if the instance is assigned to a constant.
H
Example:
class Person {
var name: String
C
init(name: String) {
self.name = name
}
}
U
person2.name = "Bob"
● Stored in Stack: Structs are destroyed when they go out of scope, making them more
memory efficient.
● Pass-by-Value: Assigning a struct to a new variable copies the data.
● No Inheritance: Structs do not support inheritance.
Example:
struct Person {
var name: String
}
IT
person2.name = "Bob"
print(person1.name) // "Alice" (No change in the original instance)
Example:
C
actor BankAccount {
private var balance: Int = 0
U
@propertyWrapper
IT
struct Capitalized {
private var value: String = ""
}
H }
set { value = newValue.capitalized }
C
struct User {
@Capitalized var name: String
}
U
var user = User(name: "john doe")
print(user.name) // "John Doe"
Without @autoclosure
With @autoclosure
IT
func logIfTrue(_ condition: @autoclosure () -> Bool) {
if condition() {
print("Condition met")
}
} H
logIfTrue(5 > 3) // No need to wrap in `{ }`
C
🔹 Use Case: Avoid unnecessary computation (e.g., assertions, logging).
U
Example:
class Car {
var model: String
init(model: String) {
self.model = model
print("\(model) initialized")
}
IT
5. Retain Cycles & Prevention
A retain cycle happens when two objects strongly reference each other.
H
Retain Cycle Example
class Person {
C
var name: String
var pet: Pet? // Strong reference
init(name: String) { self.name = name }
}
U
class Pet {
var name: String
var owner: Person? // Strong reference
init(name: String) { self.name = name }
R
class Person {
var name: String
weak var pet: Pet? // Weak reference prevents retain cycle
}
var value = 10
IT
doubleValue(&value)
print(value) // 20
Basic Example
U
IT
8. Escaping vs Non-Escaping Closures
Non-Escaping (Default)
}
H task() // Executed immediately
C
Escaping (@escaping)
IT
let sentence = makeSentence {
"Swift"
"is"
"awesome!"
}
H
print(sentence) // "Swift is awesome!"
Class Inheritance
Example:
IT
class Animal {
var name: String
init(name: String) { self.name = name }
}
H func makeSound() {
}
print("Some generic animal sound")
C
class Dog: Animal {
override func makeSound() {
print("Woof!")
U
}
}
Protocol Inheritance
Example:
protocol Swimmable {
func swim()
}
IT
}
🔹 Key Takeaway:
● Use class inheritance for shared state.
protocol Container {
R
associatedtype Item
func add(_ item: Item)
func getAll() -> [Item]
}
🔹 Why?
● Increases flexibility.
● Allows defining generic-like behavior inside protocols.
IT
OOP Approach
H
Example
class Animal {
C
func makeSound() { print("Some animal sound") }
}
POP Approach
R
Example
protocol SoundMaking {
func makeSound()
}
🔹 POP avoids deep hierarchies, making code more modular and flexible.
IT
14. Purpose of Self in Protocols
● Self refers to the conforming type inside a protocol.
H
Example: Returning Self
protocol Cloneable {
C
func clone() -> Self
}
IT
}
protocol Greetable {
U
func greet()
}
extension Greetable {
R
func greet() {
print("Hello, World!")
}
}
Example
protocol Shape {
func area() -> Double
IT
}
🔹 Why?
U
● Prevents exposing implementation details.
● Makes return types more flexible.
R
class Animal {}
class Dog: Animal {}
IT
🔹 Why? Helps maintain type safety.
H
19. Handling Multiple Protocol Conformances with
Conflicting Methods
When two protocols define the same method, Swift requires explicit disambiguation.
C
Example
protocol A {
func greet()
U
protocol B {
func greet()
R
struct MyStruct: A, B {
func greet() { print("Hello from MyStruct") }
}
extension MyStruct: A {
extension MyStruct: B {
func greet() { print("Hello from B") }
}
IT
20. @objc and Why It’s Needed
● Used to expose Swift methods to Objective-C runtime.
● Required for Selector-based APIs (e.g., #selector()).
H
Example
import Foundation
🔹 Why?
R
21. Explain the difference between Grand Central Dispatch (GCD) and
Operation Queues.
Grand Central Dispatch (GCD) and Operation Queues (NSOperationQueue) are both
concurrency mechanisms in iOS, but they have key differences in flexibility and complexity:
IT
Cancellation handle manually) NSOperation
Priority Control Uses QoS (Quality of Service) More advanced priority control with
dependencies
H
Thread Safety Basic synchronization tools More robust with dependencies and
execution states
👉 Use GCD when you need simple, lightweight, and low-overhead concurrency.
C
👉 Use Operation Queues when you need dependency management, cancellation, and
observability.
U
IT
t TaskGroup)
H
👉 Use async/await for structured, safe concurrency with cooperative cancellation.
👉 Use GCD (DispatchQueue) for simple concurrency without dependencies.
👉 Use NSOperationQueue for complex dependencies and task management.
C
23. What is an actor in Swift? How does it help in thread safety?
U
Actors in Swift are a concurrency-safe type that serializes access to its mutable state,
preventing race conditions.
actor BankAccount {
R
1. Encapsulation of State – All properties inside an actor are protected from data races.
2.
IT
3. Automatic Serialization – Multiple tasks calling an actor will be serialized, preventing
simultaneous writes.
👉 Use actors when you need safe concurrent access to shared mutable state.
H
24. Explain Task, TaskGroup, and DetachedTask in Swift concurrency.
Example:
U
Task {
await fetchData()
}
R
○
2. TaskGroup (Parallel Execution)
○ Enables concurrent child tasks within a single parent scope.
Example:
await withTaskGroup(of: Int.self) { group in
for i in 1...5 {
group.addTask { return i * i }
}
○
3. DetachedTask (Unstructured Concurrency)
○ Runs independently without inheriting priority or task context.
Example:
let handle = Task.detached {
return await fetchData()
}
IT
let result = await handle.value
○
👉 Use Task for structured concurrency, TaskGroup for parallel execution, and
DetachedTask when detachment from the parent context is required.
H
25. How does MainActor work? When should you use it?
C
@MainActor ensures that code runs on the main thread.
Example
U
@MainActor
class ViewModel {
var title: String = "Hello"
R
func updateTitle() {
title = "Updated"
}
}
Or for a function:
@MainActor
IT
Structured Tasks are managed within a scope, ensuring Task { await
Concurrency they complete before the function exits. fetchData() }
H
👉 Use structured concurrency (Task, TaskGroup) for safety, and unstructured
concurrency (DetachedTask) when needed.
}
C
27. Explain the differences between Task.sleep() and Thread.sleep().
U
Feature Task.sleep() Thread.sleep()
IT
A deadlock happens when two threads wait indefinitely for each other to release resources.
Example of Deadlock
}
H
queue.sync {
queue.sync { print("Deadlock") } // This will cause a deadlock
C
How to Prevent Deadlocks
30. How would you handle a network request that requires multiple
R
Model-View-Controller (MVC)
IT
● Structure:
○ Model: Manages data and business logic.
○ View: Handles UI representation.
○ Controller: Acts as a mediator between Model and View.
● Pros: Simple and widely used.
● Cons: Leads to "Massive View Controller" due to bloated logic.
H● Use Case: Best for small apps or when quick development is needed.
Model-View-ViewModel (MVVM)
● Structure:
C
○ Model: Data and logic.
○ View: UI representation.
○ ViewModel: Transforms data for the View and handles presentation logic.
● Pros: Better separation of concerns, improved testability.
● Cons: Requires more setup than MVC.
U
● Use Case: Best for medium to large apps, especially when using SwiftUI (because of
bindings).
View-Interactor-Presenter-Entity-Router (VIPER)
R
● Structure:
○ View: UI Layer.
○ Interactor: Handles business logic.
○ Presenter: Prepares data for View.
○ Entity: Model layer.
○ Router: Handles navigation.
● Pros: High modularity, excellent testability.
● Cons: Complex and requires more code.
● Use Case: Large-scale applications with multiple layers of logic.
Combine is Apple’s reactive framework that allows declarative and functional programming.
import Combine
IT
struct APIClient {
func fetchData() -> AnyPublisher<String, Error> {
let url = URL(string: "https://api.example.com/data")!
return URLSession.shared.dataTaskPublisher(for: url)
H }
.map { String(decoding: $0.data, as: UTF8.self) }
.mapError { $0 as Error }
.eraseToAnyPublisher()
C
}
func loadData() {
APIClient().fetchData()
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { _ in }, receiveValue: {
self.data = $0 })
.store(in: &cancellables)
}
}
ObservableObject
IT
A class that conforms to ObservableObject can notify SwiftUI views about state changes.
@State
IT
34. Dependency Injection in Swift
1. Constructor Injection
}
H
class NetworkService {
func fetchData() { }
C
class ViewModel {
let service: NetworkService
init(service: NetworkService) {
self.service = service
U
}
}
2. Property Injection
R
class ViewModel {
var service: NetworkService?
}
3. Factory Pattern
class DependencyFactory {
static func createService() -> NetworkService {
IT
Choosing the Right Approach
H
35. Implementing Deep Linking in iOS
C
Deep linking allows opening specific app screens via URLs.
Apple-native ✅ ❌
Performance Good Faster
IT
When to Choose?
H
37. Diffable Data Sources
● Automatic animations.
● No need to manually call reloadData().
IT
● Use lightweight views (drawRect sparingly).
● Enable prefetching (UITableViewDataSourcePrefetching).
● Use background threads for data processing.
H
39. Integrating SwiftUI with UIKit
}
}
40. What are App Clips, and how do you implement them?
● QR codes
● NFC tags
● Safari smart banners
● Messages and Maps links
● App Clip Codes (Apple-designed visual codes)
IT
● Paying for parking
● Renting bikes or scooters
● Checking into a hotel
This creates a lightweight version of your app with a separate bundle identifier.
U
handleIncomingURL(incomingURL)
This allows App Clips to recognize when launched via a URL or NFC tag.
IT
1. In Signing & Capabilities, add Associated Domains capability.
2. Include your website domain with applinks: and appclip: prefixes:
applinks:yourdomain.com
H3.
appclip:yourdomain.com
"applinks": {
R
"apps": [],
"details": [{
"appID": "TEAM_ID.com.example.yourApp",
"paths": ["/order/*"]
}]
},
"apps": ["TEAM_ID.com.example.yourAppClip"]
IT
2. Navigate to App Clip Experiences.
3. Configure Invocation Methods (QR, NFC, URLs).
4. Associate it with a relevant App Clip Card (appears in Safari, Messages, etc.).
● Xcode Instruments: Helps analyze memory leaks, CPU usage, disk writes, network
performance, etc.
R
● Time Profiler: Detects bottlenecks in CPU usage and function execution time.
● Leaks Instrument: Identifies memory leaks caused by retained objects.
● Zombies Instrument: Helps debug memory access issues by detecting messages sent
to deallocated objects.
● Energy Log: Monitors energy impact, ensuring app efficiency.
● Network Profiler: Monitors API call response times and network usage.
● Malloc and VM Tracker: Analyzes memory allocation and deallocation patterns.
● Static Analyzer: Detects issues in the code before runtime.
Reducing memory footprint ensures better performance and stability. Some best practices
include:
IT
needed.
● Avoid overuse of singletons, as they persist in memory throughout the app lifecycle.
● Compress images and use WebP format for better efficiency.
● Release unused objects and caches during memory warnings.
H
43. How does Instruments help in detecting memory leaks?
preventing deallocation.
Using these tools, developers can identify and fix memory leaks, preventing performance
degradation.
Lazy loading is a technique where data or resources are loaded only when needed instead of
during app startup.
IT
didSet {
loadImage()
}
}
45. What are the best practices for handling large images in an iOS app?
IT
● Faulting & Lazy Loading: Avoid preloading all objects into memory.
● Compact the SQLite Database: Run
NSPersistentStoreCoordinator.execute(_:) periodically.
● Use Fetch Limits & Predicates: Avoid fetching large datasets unnecessarily.
H
47. What are some strategies for improving app launch time?
● Reduce App Bundle Size: Remove unnecessary assets and use App Thinning.
C
● Defer Non-Essential Work: Load only critical components at startup.
● Optimize Storyboards: Avoid using a single massive storyboard.
● Lazy Load Heavy Objects: Defer database loading and networking calls.
● Preload Data Efficiently: Use background fetching and caching mechanisms.
● Optimize Static Initializers: Avoid expensive computations in AppDelegate.
U
management.
Lifecycle Management:
49. What are some security best practices for storing sensitive data in an
iOS app?
IT
● Avoid Hardcoded Secrets: Never store API keys or credentials in the app bundle.
● Use Secure Enclaves: Store biometric authentication data safely.
● Enable App Transport Security (ATS): Force HTTPS connections.
● Use Secure Storage: Prefer UserDefaults only for non-sensitive data.
● Implement Two-Factor Authentication (2FA): For better security.
H
50. How would you implement end-to-end encryption in an iOS app?
End-to-end encryption (E2EE) ensures that data remains encrypted from sender to receiver.
C
Steps to Implement:
1. Generate Encryption Keys: Use RSA (asymmetric) or AES (symmetric) encryption.
U
Encrypt Data Before Sending:
let key = SymmetricKey(size: .bits256)
let sealedBox = try AES.GCM.seal(data, using: key)
R