Skip to main content

Swift Interview Questions

Introductionā€‹

Swift is Apple's powerful and intuitive programming language used for iOS, macOS, watchOS, and tvOS app development. Created by Apple in 2014, Swift was designed to be safer, more performant, and easier to learn than Objective-C. As Swift continues to grow in popularity, proficiency in this language has become essential for developers seeking positions in the Apple ecosystem.

This guide covers common Swift interview questions you might encounter during technical interviews. Whether you're a beginner looking to land your first iOS development role or an experienced programmer transitioning to Swift, these questions and explanations will help you prepare.

Basic Swift Conceptsā€‹

What is Swift and what are its key features?ā€‹

Answer: Swift is a modern programming language developed by Apple for iOS, macOS, watchOS, and tvOS app development.

Key features include:

  • Type Safety: Swift encourages you to be clear about the types of values your code works with.
  • Performance: Swift was built to be fast, using the high-performance LLVM compiler.
  • Modern Syntax: Clean, expressive, and easier to read and write than Objective-C.
  • Optionals: Explicitly handles the absence of a value.
  • Memory Management: Automatic Reference Counting (ARC) manages memory automatically.
  • Interoperability: Works seamlessly with Objective-C.
  • Open Source: Available for the community to contribute to its development.
  • Playgrounds: Interactive coding environment to see results immediately.

What are the primitive data types in Swift?ā€‹

Answer: Swift has several primitive data types:

  • Int: Integer values (e.g., 42, -23)
  • Float: 32-bit floating-point number (e.g., 3.14159)
  • Double: 64-bit floating-point number (higher precision than Float, e.g., 3.14159265359)
  • Bool: Boolean values (true or false)
  • Character: Single character (e.g., "A", "šŸ˜Š")
  • String: Sequence of characters (e.g., "Hello, Swift!")

Example:

swift
let integerValue: Int = 42
let floatValue: Float = 3.14
let doubleValue: Double = 3.14159265359
let boolValue: Bool = true
let characterValue: Character = "A"
let stringValue: String = "Hello, Swift!"

print(type(of: integerValue)) // Output: Int
print(type(of: floatValue)) // Output: Float

What are Optionals in Swift and why are they useful?ā€‹

Answer: Optionals are a powerful feature in Swift that handle the absence of a value. An optional represents two possibilities: either there is a value of a specific type, or there is no value at all (nil).

Optionals are declared by adding a question mark (?) after the type:

swift
var name: String?  // This declares an optional String that is initially nil

Optionals are useful because:

  1. They make code safer by forcing you to handle nil cases explicitly
  2. They clearly indicate in your code which values might be absent
  3. They prevent unexpected nil values that could cause crashes

Example of working with optionals:

swift
var possibleNumber = "123"
var convertedNumber = Int(possibleNumber) // convertedNumber is an Int?

// Using if-let to safely unwrap an optional
if let actualNumber = convertedNumber {
print("The string \(possibleNumber) has an integer value of \(actualNumber)")
} else {
print("The string \(possibleNumber) couldn't be converted to an integer")
}
// Output: The string 123 has an integer value of 123

// What happens with invalid input:
possibleNumber = "hello"
convertedNumber = Int(possibleNumber) // This returns nil
if let actualNumber = convertedNumber {
print("The string \(possibleNumber) has an integer value of \(actualNumber)")
} else {
print("The string \(possibleNumber) couldn't be converted to an integer")
}
// Output: The string hello couldn't be converted to an integer

Intermediate Swift Conceptsā€‹

Explain the difference between structures and classes in Swiftā€‹

Answer: Structures and classes are both used to define custom data types in Swift, but they have several key differences:

FeatureStructuresClasses
TypeValue typeReference type
InheritanceNo inheritanceCan inherit from other classes
Default initializerHas memberwise initializerNo memberwise initializer
MutabilityNeed to mark methods as mutating to modifyCan modify properties without special keywords
Memory managementAutomatically managedUses ARC (Automatic Reference Counting)
DeinitializationNo deinitializerHas deinitializer (deinit)

Example:

swift
// Structure example
struct Point {
var x: Double
var y: Double

// Mutating method requires 'mutating' keyword
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
}

// Class example
class Person {
var name: String
var age: Int

init(name: String, age: Int) {
self.name = name
self.age = age
}

// No 'mutating' keyword needed
func celebrateBirthday() {
age += 1
}

deinit {
print("\(name) is being deinitialized")
}
}

// Value type behavior: independent copies
var point1 = Point(x: 0, y: 0)
var point2 = point1 // Creates a separate copy
point2.moveBy(x: 5, y: 5)
print("Point1: (\(point1.x), \(point1.y))") // Output: Point1: (0.0, 0.0)
print("Point2: (\(point2.x), \(point2.y))") // Output: Point2: (5.0, 5.0)

// Reference type behavior: shared instances
var person1 = Person(name: "Alice", age: 30)
var person2 = person1 // Both variables point to the same instance
person2.age = 31
print("\(person1.name)'s age: \(person1.age)") // Output: Alice's age: 31
print("\(person2.name)'s age: \(person2.age)") // Output: Alice's age: 31

When to use structures vs. classes:

  • Use structures for simple data types where value semantics are appropriate
  • Use classes when you need reference semantics, inheritance, or deinitializers

What are closures in Swift and how are they used?ā€‹

Answer: Closures are self-contained blocks of functionality that can be passed around and used in your code. They're similar to blocks in Objective-C and lambdas in other programming languages. Closures can capture and store references to variables and constants from the context in which they're defined.

Syntax of a closure:

swift
{ (parameters) -> ReturnType in
// Code
}

Types of closures in Swift:

  1. Global functions (with a name and not capturing any values)
  2. Nested functions (with a name and can capture values from their enclosing function)
  3. Closure expressions (unnamed, written in a lightweight syntax that can capture values)

Example of using closures with the sorted(by:) method:

swift
let names = ["Tim", "Bob", "Anna", "Zack", "Eve"]

// Full closure expression syntax
let sortedNames1 = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 < s2
})

// Inferred type syntax
let sortedNames2 = names.sorted(by: { s1, s2 in return s1 < s2 })

// Implicit return syntax
let sortedNames3 = names.sorted(by: { s1, s2 in s1 < s2 })

// Shorthand argument names
let sortedNames4 = names.sorted(by: { $0 < $1 })

// Operator methods
let sortedNames5 = names.sorted(by: <)

print(sortedNames1) // Output: ["Anna", "Bob", "Eve", "Tim", "Zack"]

Practical use of closures in asynchronous operations:

swift
func fetchData(completion: @escaping (Result<String, Error>) -> Void) {
// Simulate an asynchronous network request
DispatchQueue.global().async {
// Simulate successful data retrieval
let data = "Fetched data from network"

// Call the completion handler on the main queue
DispatchQueue.main.async {
completion(.success(data))
}
}
}

// Using the closure
fetchData { result in
switch result {
case .success(let data):
print("Success: \(data)")
case .failure(let error):
print("Error: \(error.localizedDescription)")
}
}
// Output (after a short delay): Success: Fetched data from network

What is Protocol-Oriented Programming in Swift?ā€‹

Answer: Protocol-Oriented Programming (POP) is a programming paradigm that Swift strongly supports. It's centered around designing your code primarily using protocols rather than focusing on classes and inheritance. This approach was introduced by Apple at WWDC 2015, where they encouraged "thinking in terms of protocols first."

Key concepts of Protocol-Oriented Programming:

  1. Protocols Define Behavior: Protocols define what methods and properties an entity should have without specifying implementation.

  2. Protocol Extensions: Swift allows you to extend protocols to provide default implementations.

  3. Value Types and Protocols: Structs and enums can adopt protocols, enabling a powerful alternative to class inheritance.

  4. Composition over Inheritance: POP encourages composing functionality through protocol conformance rather than class inheritance hierarchies.

Example of Protocol-Oriented Programming:

swift
// Define a protocol
protocol Animal {
var name: String { get }
var sound: String { get }
func makeSound()
}

// Provide a default implementation through protocol extension
extension Animal {
func makeSound() {
print("\(name) says \(sound)!")
}
}

// Create concrete types that adopt the protocol
struct Dog: Animal {
let name: String
let sound: String = "Woof"
}

struct Cat: Animal {
let name: String
let sound: String = "Meow"

// Custom implementation overriding the default
func makeSound() {
print("\(name) says \(sound) \(sound)!")
}
}

// Using the protocol types
let animals: [Animal] = [
Dog(name: "Rex"),
Cat(name: "Whiskers")
]

for animal in animals {
animal.makeSound()
}
// Output:
// Rex says Woof!
// Whiskers says Meow Meow!

Protocol composition example:

swift
protocol Flying {
var airspeedVelocity: Double { get }
func fly()
}

extension Flying {
func fly() {
print("Flying at \(airspeedVelocity) km/h")
}
}

protocol Swimming {
var swimmingSpeed: Double { get }
func swim()
}

extension Swimming {
func swim() {
print("Swimming at \(swimmingSpeed) km/h")
}
}

// A type conforming to multiple protocols
struct Duck: Animal, Flying, Swimming {
let name: String
let sound: String = "Quack"
let airspeedVelocity: Double = 72
let swimmingSpeed: Double = 8
}

let duck = Duck(name: "Donald")
duck.makeSound() // Output: Donald says Quack!
duck.fly() // Output: Flying at 72.0 km/h
duck.swim() // Output: Swimming at 8.0 km/h

Benefits of Protocol-Oriented Programming:

  • Better code organization
  • Increased code reuse
  • More flexible than class inheritance
  • Works well with value types (structs and enums)
  • Makes testing easier through protocol abstractions

Advanced Swift Conceptsā€‹

Explain the concept of memory management in Swiftā€‹

Answer: Swift uses Automatic Reference Counting (ARC) to manage memory. ARC automatically keeps track of class instances and frees up the memory used by instances when they're no longer needed.

Key concepts in Swift memory management:

  1. Reference Counting: Each time you create a reference to a class instance, ARC adds 1 to the reference count. When a reference is removed, ARC subtracts 1. When the count reaches 0, the instance is deallocated.

  2. Strong References: By default, all references in Swift are strong, meaning they increase the reference count of an instance.

  3. Reference Cycles: A common memory leak occurs when two class instances hold strong references to each other, creating a cycle where neither can be deallocated.

  4. Weak References: Marked with weak, these references don't increase the reference count and automatically become nil when the instance they refer to is deallocated.

  5. Unowned References: Similar to weak references but must always refer to an instance that outlives them. They don't increase the reference count but don't become nil when the instance is deallocated.

Example of a reference cycle and its solution:

swift
class Person {
let name: String
var apartment: Apartment?

init(name: String) {
self.name = name
print("\(name) is being initialized")
}

deinit {
print("\(name) is being deinitialized")
}
}

class Apartment {
let unit: String
// Using weak breaks the reference cycle
weak var tenant: Person?

init(unit: String) {
self.unit = unit
print("Apartment \(unit) is being initialized")
}

deinit {
print("Apartment \(unit) is being deinitialized")
}
}

// Reference cycle example
func createReferenceCycle() {
let john = Person(name: "John")
let unit4A = Apartment(unit: "4A")

// Create a cycle
john.apartment = unit4A
unit4A.tenant = john

// Without weak, these would never be deinitialized
}

createReferenceCycle()
// Output:
// John is being initialized
// Apartment 4A is being initialized
// John is being deinitialized
// Apartment 4A is being deinitialized

When to use weak vs. unowned:

  • Use weak when the referenced instance might become nil during its lifetime
  • Use unowned when you're certain the referenced instance will outlive the reference

What are generics in Swift and how do they enhance code reusability?ā€‹

Answer: Generics allow you to write flexible, reusable functions and types that can work with any type, subject to the requirements you define. They enable you to avoid code duplication while maintaining type safety.

Key benefits of generics:

  1. Type safety at compile time
  2. Code reusability
  3. Performance optimization (no need for type casting)
  4. Expressive API design

Basic syntax for generic functions:

swift
func swapValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}

// Using the generic function with different types
var firstInt = 10
var secondInt = 20
swapValues(&firstInt, &secondInt)
print("firstInt: \(firstInt), secondInt: \(secondInt)")
// Output: firstInt: 20, secondInt: 10

var firstString = "hello"
var secondString = "world"
swapValues(&firstString, &secondString)
print("firstString: \(firstString), secondString: \(secondString)")
// Output: firstString: world, secondString: hello

Generic types:

swift
// A generic Stack data structure
struct Stack<Element> {
private var items: [Element] = []

mutating func push(_ item: Element) {
items.append(item)
}

mutating func pop() -> Element? {
return items.isEmpty ? nil : items.removeLast()
}

var top: Element? {
return items.last
}

var isEmpty: Bool {
return items.isEmpty
}
}

// Using the generic Stack with Integers
var intStack = Stack<Int>()
intStack.push(1)
intStack.push(2)
intStack.push(3)
print(intStack.top ?? "Empty stack") // Output: 3
print(intStack.pop() ?? "Empty stack") // Output: 3
print(intStack.top ?? "Empty stack") // Output: 2

// Using the generic Stack with Strings
var stringStack = Stack<String>()
stringStack.push("Swift")
stringStack.push("is")
stringStack.push("awesome")
print(stringStack.top ?? "Empty stack") // Output: awesome

Type constraints with generics:

swift
// Only types that conform to Comparable can use this function
func findSmallestValue<T: Comparable>(_ values: [T]) -> T? {
guard !values.isEmpty else { return nil }

var smallest = values[0]
for value in values[1...] {
if value < smallest {
smallest = value
}
}

return smallest
}

let numbers = [5, 3, 9, 1, 7]
if let smallestNumber = findSmallestValue(numbers) {
print("The smallest number is \(smallestNumber)") // Output: The smallest number is 1
}

let words = ["apple", "banana", "cherry", "date"]
if let firstAlphabetically = findSmallestValue(words) {
print("The first word alphabetically is \(firstAlphabetically)") // Output: The first word alphabetically is apple
}

Multiple type constraints example:

swift
protocol Container {
associatedtype Item
var count: Int { get }
subscript(i: Int) -> Item { get }
}

// Extension that requires types to be both a Container and have elements that are Equatable
extension Container where Item: Equatable {
func indexOf(item: Item) -> Int? {
for i in 0..<count {
if self[i] == item {
return i
}
}
return nil
}
}

// Make Array conform to Container protocol
extension Array: Container {}

let fruits = ["apple", "banana", "cherry", "date"]
if let index = fruits.indexOf(item: "cherry") {
print("Found cherry at index \(index)") // Output: Found cherry at index 2
}

How do you handle concurrency in Swift?ā€‹

Answer: Swift offers several ways to handle concurrent operations, with significant updates in Swift 5.5 introducing new async/await syntax. Here are the main approaches:

1. Grand Central Dispatch (GCD)ā€‹

GCD is a low-level API for managing concurrent operations:

swift
// Performing work on a background queue
DispatchQueue.global(qos: .background).async {
// Perform time-consuming task
let result = performExpensiveComputation()

// Update UI on the main queue
DispatchQueue.main.async {
print("Computation result: \(result)")
}
}

func performExpensiveComputation() -> Int {
// Simulate a time-consuming task
sleep(2)
return 42
}

2. Operation and OperationQueueā€‹

These provide an object-oriented approach to managing concurrent operations:

swift
// Create an operation
let operation = BlockOperation {
// Perform time-consuming task
sleep(2)
print("Operation completed")
}

// Set up completion handling
operation.completionBlock = {
print("Operation finished executing")
}

// Add to an operation queue
let queue = OperationQueue()
queue.addOperation(operation)

3. Modern Concurrency with async/await (Swift 5.5+)ā€‹

The newer, more intuitive way to handle asynchronous code:

swift
// Define an asynchronous function
func fetchUserData() async throws -> String {
// Simulate network delay
try await Task.sleep(nanoseconds: 2_000_000_000)
return "User data"
}

// Using the async function
func updateUserProfile() async {
do {
let userData = try await fetchUserData()
print("Received data: \(userData)")
} catch {
print("Error fetching user data: \(error)")
}
}

// Call the async function from synchronous code
Task {
await updateUserProfile()
}

4. Tasks and Task Groupsā€‹

For managing multiple concurrent operations:

swift
func fetchAllData() async throws -> [String] {
try await withThrowingTaskGroup(of: String.self) { group in
// Add multiple concurrent tasks
group.addTask { try await fetchUserData() }
group.addTask { try await fetchWeatherData() }
group.addTask { try await fetchNewsData() }

// Collect and return results
var results: [String] = []
for try await result in group {
results.append(result)
}
return results
}
}

func fetchWeatherData() async throws -> String {
try await Task.sleep(nanoseconds: 1_500_000_000)
return "Weather data"
}

func fetchNewsData() async throws -> String {
try await Task.sleep(nanoseconds: 1_000_000_000)
return "News data"
}

// Usage
Task {
do {
let allData = try await fetchAllData()
print("All data received: \(allData)")
} catch {
print("Error: \(error)")
}
}

5. Actors (Swift 5.5+)ā€‹

Actors provide data isolation for safe concurrent access:

swift
// Define an actor for thread-safe counter
actor Counter {
private var value = 0

func increment() -> Int {
value += 1
return value
}

func decrement() -> Int {
value -= 1
return value
}

func getValue() -> Int {
return value
}
}

// Using the actor
Task {
let counter = Counter()

// These calls are automatically synchronized
async let increment1 = counter.increment()
async let increment2 = counter.increment()
async let increment3 = counter.increment()

let results = await [increment1, increment2, increment3]
let finalValue = await counter.getValue()

print("Increment results: \(results)")
print("Final counter value: \(finalValue)")
// Output: Increment results: [1, 2, 3]
// Output: Final counter value: 3
}

Choosing the right concurrency approach:

  • For modern Swift apps (iOS 15+), prefer async/await and actors
  • For compatibility with older iOS versions, use GCD or Operation
  • For simple background tasks, GCD is often sufficient
  • For complex dependencies between tasks, OperationQueue offers more control

iOS-Specific Swift Questionsā€‹

What are property wrappers in Swift and how can they be used in SwiftUI?ā€‹

Answer: Property wrappers are a feature introduced in Swift 5.1 that allows you to add a layer of behavior to properties. They help separate the code that manages how a property is stored from the code that defines the property.

Basic property wrapper syntax:

swift
@propertyWrapper
struct Trimmed {
private var value: String = ""

var wrappedValue: String {
get { return value }
set { value = newValue.trimmings(.whitespacesAndNewlines) }
}

init(wrappedValue initialValue: String) {
self.wrappedValue = initialValue
}
}

// Using the property wrapper
struct User {
@Trimmed var name: String
@Trimmed var email: String
}

var user = User(name: " John Doe ", email: " [email protected] ")
print("Name: '\(user.name)'") // Output: Name: 'John Doe'
print("Email: '\(user.email)'") // Output: Email: '[email protected]'

SwiftUI uses property wrappers extensively:

  1. @State: For simple properties that belong to a view and need to trigger view updates when changed.
swift
struct CounterView: View {
@State private var count = 0

var body: some View {
VStack {
Text("Count: \(count)")
Button("Increment") {
count += 1
}
}
}
}
  1. @Binding: For properties that are connected to a source of truth defined elsewhere.
swift
struct ToggleView: View {
@Binding var isOn: Bool

var body: some View {
Toggle("Feature enabled", isOn: $isOn)
}
}

struct ParentView: View {
@State private var featureEnabled = false

var body: some View {
VStack {
Text("Feature is \(featureEnabled ? "enabled" : "disabled")")
ToggleView(isOn: $featureEnabled)
}
}
}
  1. @ObservedObject: For external objects that conform to ObservableObject and can trigger view updates.
swift
class UserSettings: ObservableObject {
@Published var username = ""
@Published var isLoggedIn = false
}

struct ProfileView: View {
@ObservedObject var settings: UserSettings

var body: some View {
VStack {
TextField("Username", text: $settings.username)
Button(settings.isLoggedIn ? "Log Out" : "Log In") {
settings.isLoggedIn.toggle()
}
}
}
}
  1. @EnvironmentObject: For shared data that's accessible throughout the view hierarchy.
swift
class AppSettings: ObservableObject {
@Published var colorScheme: String = "light"
@Published var fontSize: Int = 14
}

struct SettingsView: View {
@EnvironmentObject var settings: AppSettings

var body: some View {
Form {
Picker("Theme", selection: $settings.colorScheme) {
Text("Light").tag("light")
Text("Dark").tag("dark")
}

Stepper("Font Size: \(settings.fontSize)", value: $settings.fontSize, in: 10...24)
}
}
}

// In SceneDelegate or App struct:
// contentView.environmentObject(AppSettings())
  1. @StateObject: Similar to @ObservedObject but ensures the object is created only once for the view's lifecycle.
swift
struct ContentView: View {
@StateObject private var viewModel = ViewModel()

var body: some View {
List(viewModel.items) { item in
Text(item.name)
}
.onAppear {
viewModel.fetchItems()
}
}
}

class ViewModel: ObservableObject {
@Published var items: [Item] = []

func fetchItems() {
// Fetch data from network or database
}
}

These property wrappers are fundamental to SwiftUI's declarative programming model, allowing views to automatically update when their data changes.

What is Swift Package Manager and how do you use it?ā€‹

Answer: Swift Package Manager (SPM) is a tool for managing the distribution and dependencies of Swift code. It's integrated directly into the Swift build system and Xcode, making it easy to share code and incorporate external dependencies.

Key features of Swift Package Manager:

  1. Dependency Resolution: Automatically handles downloading and updating dependencies.
  2. Build Configuration: Manages how packages are built across different platforms.
  3. Versioning: Uses semantic versioning to ensure compatibility between package versions.
  4. Integration with Xcode: Built into Xcode since version 11, allowing easy package management.

Creating a Swift Packageā€‹

swift
// In Terminal:
// swift package init --type library

// Creates a package with this structure:
// PackageName/
// ā”œā”€ā”€ Package.swift
// ā”œā”€ā”€ README.md
// ā”œā”€ā”€ Sources/
// ā”‚ ā””ā”€ā”€ PackageName/
// ā”‚ ā””ā”€ā”€ PackageName.swift
// ā””ā”€ā”€ Tests/
// ā””ā”€ā”€ PackageNameTests/
// ā””ā”€ā”€ PackageNameTests.swift

Example Package.swift file:

swift
// swift-tools-version:5.5
import PackageDescription

let package = Package(
name: "MyUtilities",
platforms: [
.iOS(.v13),
.macOS(.v10_15)
],
products: [
.library(
name: "MyUtilities",
targets: ["MyUtilities"]),
],
dependencies: [
.package(url: "https://github.com/example/examplePackage.git", from: "1.0.0"),
],
targets: [
.target(
name: "MyUtilities",
dependencies: ["examplePackage"]),
.testTarget(
name: "MyUtilitiesTests",
dependencies: ["MyUtilities"]),
]
)

Adding a Package Dependency in Xcodeā€‹

  1. In Xcode, go to File > Add Packages...
  2. Enter the package repository URL (e.g., https://github.com/example/examplePackage.git)
  3. Choose the version requirements (exact version, version range, branch, or commit)
  4. Select the target where you want to use the package
  5. Click "Add Package"

Using Swift Package in Codeā€‹

swift
import MyUtilities

func exampleFunction() {
let result = MyUtilities.calculate(value: 42)
print("The result is \(result)")
}

Managing Dependencies via Command Lineā€‹

bash
# Initialize a new package
swift package init --type executable

# Build the package
swift build

# Run tests
swift test

# Generate Xcode project
swift package generate-xcodeproj

# Update dependencies to latest versions
swift package update

# Show current dependencies
swift package show-dependencies

Benefits of Swift Package Manager:

  • Open-source and integrated with Swift
  • No need for third-party dependency managers
  • Direct Xcode integration
  • Cross-platform support (iOS, macOS, tvOS, watchOS, Linux)
  • Version control system integration

Swift Best Practicesā€‹

What are some best practices for writing clean Swift code?ā€‹

Answer: Writing clean Swift code is essential for maintainability, readability, and collaboration. Here are some best practices:

1. Follow Swift Style Guidelinesā€‹

swift
// Use camelCase for variables and functions
let userName = "John"
func calculateTotal() { }

// Use PascalCase for types
struct UserProfile { }
class NetworkManager { }
enum ConnectionState { }

// Use descriptive names
// Bad:
let x = 10
// Good:
let userAge = 10

2. Leverage Swift's Type Systemā€‹

swift
// Use type inference when appropriate
let name = "Swift" // Swift infers this as String
let numbers = [1, 2, 3] // Swift infers this as [Int]

// Be explicit when needed for clarity
let scores: [Double] = [85.5, 92.0, 78.5]
let completion: (Result<User, Error>) -> Void = { result in
// Handle result
}

3. Use Meaningful Enums for Constantsā€‹

swift
// Instead of string literals or magic numbers
enum HTTPMethod {
static let get = "GET"
static let post = "POST"
static let put = "PUT"
static let delete = "DELETE"
}

// Usage
let request = URLRequest(url: url, method: HTTPMethod.get)

4. Favor Value Types (Structs) When Appropriateā€‹

swift
// Use structs for data models without complex inheritance
struct GeoPoint {
let latitude: Double
let longitude: Double

func distance(to other: GeoPoint) -> Double {
// Calculate distance
return 0.0 // Simplified
}
}

5. Use Extensions to Organize Codeā€‹

swift
// Break up large types with extensions
struct User {
let id: String
let name: String
let email: String
}

// Authentication-related methods


If you spot any mistakes on this website, please let me know at [email protected]. Iā€™d greatly appreciate your feedback! :)