Top Swift Interview Questions and Answers for 2025
Basic Swift Interview Questions
Swift is a powerful, open-source, and type-safe programming language developed by Apple. It is optimized for performance and safety while remaining easy to read and write.
- Type-safe and memory-safe
- Supports functional, object-oriented, and protocol-oriented programming
- Optionals to handle nil values safely
- Generics and higher-order functions
- Automatic Reference Counting (ARC) for memory management
- Closures and Tuples for better data handling
Feature | let (Constant) | var (Variable) |
---|---|---|
Mutability | Immutable | Mutable |
Reassignment | Not Allowed | Allowed |
Performance | More optimized | Less optimized |
let language = "Swift"
// Cannot be changed
var version = 5.9 // Can be updated
version = 6.0
Output
version: 6.0
Example of declaring an optional:
var name: String? = "John" // Can be nil
- Forced Unwrapping:
name!
(Unsafe, may crash if nil) - Optional Binding:
if let unwrappedName = name {
print(unwrappedName)
} - Nil Coalescing Operator (??):
print(name ?? "Default Name")
Feature | Struct | Class |
---|---|---|
Memory Allocation | Stack | Heap |
Reference Type | Value Type | Reference Type |
Inheritance | Not Supported | Supported |
Performance | Faster | Slower (due to heap allocation) |
struct Person {
var name: String
}
class Animal {
var species: String = ""
}
Example:
let person = ("John", 30)
print(person.0) // Output: John
let person = (name: "John", age: 30)
print(person.name) // Output: John
Feature | if Statement | guard Statement |
---|---|---|
Usage | Used for conditional execution | Used for early exit |
Scope | Variables are limited to the if block | Variables remain in the outer scope |
func checkAge(age: Int?) {
guard let validAge = age,
validAge >= 18 else {
print("Not
eligible")
return
}
print("Eligible")
}
Common higher-order functions:
- map: Transforms elements
- filter: Filters elements based on a condition
- reduce: Combines elements into a single value
let numbers = [1, 2, 3, 4]
let squared = numbers.map { $0 * $0 }
print(squared) // Output: [1, 4, 9, 16]
Example:
let name = "Swift" // Swift
infers it as String
let age = 25 // Inferred as
Int
Example:
let greet = { (name: String) -> String in
return "Hello, \(name)"
}
print(greet("John")) // Output:
Hello, John
Advanced Swift Interview Questions
Example:
func fetchData(completion: @escaping () -> Void) {
DispatchQueue.global().async {
// Simulating network
request
sleep(2)
completion() // Executed
later
}
}
Here, @escaping
allows completion
to be stored
and executed after fetchData
returns.
Reference Type | Ownership | Retains Object? | Use Case |
---|---|---|---|
strong | Default | Yes | Keeps object alive |
weak | No ownership | No | Avoids retain cycles (Optional) |
unowned | No ownership | No | Avoids retain cycles (Non-Optional) |
class A {
var name: String = "A"
}
class B {
weak var a: A? // Avoids retain cycle
}
Here, weak var a: A?
ensures A
is deallocated when
no other strong references exist.
willSet
and didSet
.
Example:
class User {
var age: Int = 0 {
willSet {
print("Age will be set to \(newValue)")
}
didSet {
print("Age changed from \(oldValue) to \(age)")
}
}
}
var user = User()
user.age = 25
Age changed from 0 to 25
Feature | protocol | extension | protocol extension |
---|---|---|---|
Defines behavior | Yes | No | No |
Adds behavior | No | Yes | Yes (for all conforming types) |
protocol Greetable {
func greet()
}
extension String {
func reversedString() -> String {
return String(self.reversed())
}
}
extension Greetable {
func greet() {
print("Hello
from protocol extension!")
}
}
A protocol extension allows default method implementations for all conforming types.
Example:
class Person: NSObject {
@objc dynamic var name: String = "John"
}
let person = Person()
let observation = person.observe(\.name, options: [.new, .old]) { obj, change
in
print("Name changed from
\(change.oldValue ?? "") to \(change.newValue ?? "")")
}
person.name = "Mike"
KVO is primarily used in Objective-C-based APIs like UIKit.
Method | Execution | Thread Blocking | Use Case |
---|---|---|---|
async | Asynchronous | No | UI updates, background tasks |
sync | Synchronous | Yes (Deadlock Risk) | Immediate execution, avoid in main thread |
DispatchQueue.global().async {
print("Background Task")
DispatchQueue.main.async {
print("UI
Update")
}
}
Using sync
on DispatchQueue.main
inside a main
thread causes deadlock.
Codable
protocol combines Encodable
and Decodable
to
serialize and deserialize JSON.
Example:
struct User: Codable {
var name: String
var age: Int
}
let jsonData = """
{
"name": "Alice",
"age": 25
}
""".data(using: .utf8)!
let user = try? JSONDecoder().decode(User.self,
from: jsonData)
print(user?.name ?? "") //
Output: Alice
Codable
simplifies working with JSON and other data formats.
Result
type handles success and failure cases in a structured way.
Example:
enum NetworkError: Error {
case noInternet
}
func fetchData(completion: (Result<String, NetworkError>) -> Void) {
let success = true
if success {
completion(.success("Data
received"))
} else {
completion(.failure(.noInternet))
}
}
fetchData { result in
switch result {
case .success(let data):
print(data)
case .failure(let error):
print(error)
}
}
This avoids using Optional
for error handling.
lazy var
initializes only when accessed, improving performance.
Example:
class Example {
lazy var expensiveComputation:
String = {
print("Computing value...")
return "Computed"
}()
}
let obj = Example()
print("Object created")
print(obj.expensiveComputation) // Computation happens
here
Computing value...
Computed
Without lazy
, the property would initialize immediately.
@autoclosure
allows passing an expression as a closure without wrapping it manually.
Example:
func logMessage(_ message: @autoclosure () -> String) {
print(message())
}
logMessage("Hello, Swift!") // No need to use `{}`
This is useful for delayed evaluation in logging or assertions.