Overview
Swift is Apple's powerful and intuitive programming language for iOS, macOS, watchOS, and tvOS development. Released in 2014, Swift combines the best of modern language thinking with decades of Apple engineering expertise. It's designed to be safe, fast, and expressive, making it the primary language for Apple platform development and increasingly popular for server-side applications.
Why Swift Matters
- Safety First: Eliminates entire classes of bugs through optionals and type safety
- Performance: As fast as C++ for most tasks
- Modern Syntax: Clean, expressive, and easy to read
- ARC: Automatic memory management without garbage collection
- Playgrounds: Interactive development and learning
- Open Source: Cross-platform with Linux and Windows support
- SwiftUI: Declarative UI framework integration
Basics
Variables and Constants
var mutableVariable = 10 // Can change
let immutableConstant = 20 // Cannot change
Data Types
// Basic types
let integer: Int = 42
let double: Double = 3.14
let float: Float = 3.14
let boolean: Bool = true
let string: String = "Hello"
let character: Character = "A"
// Type inference
let inferredInt = 42 // Int
let inferredString = "Hi" // String
String Interpolation
let name = "John"
let age = 30
let message = "\(name) is \(age) years old"
Optionals
Optional Declaration
var optionalString: String? = "Hello"
var nilValue: Int? = nil
Unwrapping Optionals
// Force unwrapping (dangerous!)
let value = optionalString!
// Optional binding (safe)
if let unwrapped = optionalString {
print(unwrapped)
}
// Guard statement
guard let unwrapped = optionalString else { return }
// Nil coalescing
let result = optionalString ?? "Default"
// Optional chaining
let length = optionalString?.count
Control Flow
If-Else
if condition {
// code
} else if anotherCondition {
// code
} else {
// code
}
Switch
switch value {
case 1:
print("One")
case 2...5:
print("Two to Five")
case let x where x > 5:
print("Greater than 5")
default:
print("Other")
}
Loops
// For-in loop
for i in 1...5 { } // 1,2,3,4,5
for i in 1..<5 { } // 1,2,3,4
for item in array { }
for (index, value) in array.enumerated() { }
// While loop
while condition { }
// Repeat-while
repeat { } while condition
Functions & Closures
Functions
// Basic function
func greet(name: String) -> String {
return "Hello, \(name)"
}
// Multiple parameters
func add(_ a: Int, to b: Int) -> Int {
return a + b
}
let sum = add(5, to: 3)
// Default parameters
func greet(name: String = "World") { }
// Variadic parameters
func sum(_ numbers: Int...) -> Int {
return numbers.reduce(0, +)
}
// inout parameters
func swap(_ a: inout Int, _ b: inout Int) {
(a, b) = (b, a)
}
Closures
// Full syntax
let closure = { (name: String) -> String in
return "Hello, \(name)"
}
// Trailing closure
array.map { $0 * 2 }
// Shorthand
let sorted = names.sorted(by: <)
// Capturing values
func makeIncrementer(amount: Int) -> () -> Int {
var total = 0
return {
total += amount
return total
}
}
Classes & Structs
Struct (Value Type)
struct Point {
var x: Double
var y: Double
// Computed property
var magnitude: Double {
return sqrt(x*x + y*y)
}
// Mutating method
mutating func moveBy(x: Double, y: Double) {
self.x += x
self.y += y
}
}
Class (Reference Type)
class Person {
var name: String
var age: Int
// Designated initializer
init(name: String, age: Int) {
self.name = name
self.age = age
}
// Convenience initializer
convenience init(name: String) {
self.init(name: name, age: 0)
}
// Deinitializer
deinit {
print("Person deinitialized")
}
}
// Inheritance
class Student: Person {
var grade: String
override init(name: String, age: Int) {
self.grade = "A"
super.init(name: name, age: age)
}
}
Properties
class Temperature {
// Stored property
var celsius: Double = 0
// Computed property
var fahrenheit: Double {
get { return celsius * 9/5 + 32 }
set { celsius = (newValue - 32) * 5/9 }
}
// Property observers
var kelvin: Double = 0 {
willSet { print("Will set to \(newValue)") }
didSet { print("Did change from \(oldValue)") }
}
// Lazy property
lazy var data = loadData()
// Static property
static let boilingPoint = 100.0
}
Protocols & Extensions
Protocols
protocol Vehicle {
var wheels: Int { get }
func drive()
}
// Protocol with default implementation
protocol Greetable {
var name: String { get }
}
extension Greetable {
func greet() {
print("Hello, \(name)")
}
}
// Conforming to protocol
struct Car: Vehicle {
let wheels = 4
func drive() { print("Driving") }
}
Extensions
// Extend existing types
extension Int {
var squared: Int { return self * self }
func times(closure: () -> Void) {
for _ in 0..<self { closure() }
}
}
// Usage
let squared = 5.squared // 25
3.times { print("Hi") }
Memory Management
ARC (Automatic Reference Counting)
// Strong reference (default)
var strongRef = Person()
// Weak reference (optional, no ownership)
weak var weakRef: Person?
// Unowned reference (non-optional, no ownership)
unowned let unownedRef: Person
// Closure capture lists
someFunction { [weak self] in
self?.doSomething()
}
// Avoiding retain cycles
class Node {
var value: Int
weak var parent: Node? // Weak to avoid cycle
var children: [Node] = []
}
Error Handling
Throwing Functions
enum ValidationError: Error {
case tooShort, tooLong
}
func validate(_ password: String) throws -> Bool {
guard password.count >= 6 else {
throw ValidationError.tooShort
}
guard password.count <= 20 else {
throw ValidationError.tooLong
}
return true
}
Handling Errors
// Do-catch
do {
try validate(password)
} catch ValidationError.tooShort {
print("Password too short")
} catch {
print("Unknown error: \(error)")
}
// Try? (returns optional)
let result = try? validate(password)
// Try! (force, crashes on error)
let result = try! validate(password)
Generics
Generic Functions
func swap<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
Generic Types
struct Stack<Element> {
private var items: [Element] = []
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element? {
return items.popLast()
}
}
Generic Constraints
func findIndex<T: Equatable>(of value: T, in array: [T]) -> Int? {
for (index, item) in array.enumerated() {
if item == value { return index }
}
return nil
}
Concurrency
Grand Central Dispatch (GCD)
// Main queue (UI updates)
DispatchQueue.main.async {
// Update UI
}
// Background queue
DispatchQueue.global(qos: .background).async {
// Heavy work
}
// Custom queue
let queue = DispatchQueue(label: "com.app.queue")
queue.async { }
Modern Concurrency (async/await)
// Async function
func fetchData() async throws -> Data {
let url = URL(string: "https://api.example.com")!
let (data, _) = try await URLSession.shared.data(from: url)
return data
}
// Using async function
Task {
do {
let data = try await fetchData()
} catch {
print("Error: \(error)")
}
}
// Async let (concurrent)
async let photo1 = downloadPhoto(id: 1)
async let photo2 = downloadPhoto(id: 2)
let photos = await [photo1, photo2]
Collections
Array
var array = [1, 2, 3]
array.append(4)
array.insert(0, at: 0)
array.remove(at: 1)
array.contains(3) // true
// Common operations
let mapped = array.map { $0 * 2 }
let filtered = array.filter { $0 > 2 }
let sum = array.reduce(0, +)
let sorted = array.sorted()
Dictionary
var dict = ["key": "value"]
dict["newKey"] = "newValue"
dict.removeValue(forKey: "key")
// Safe access
if let value = dict["key"] { }
// Iterating
for (key, value) in dict { }
Set
var set: Set = [1, 2, 3]
set.insert(4)
set.remove(2)
set.contains(3) // true
// Set operations
let union = set1.union(set2)
let intersection = set1.intersection(set2)
let difference = set1.subtracting(set2)
Common Patterns
Singleton
class NetworkManager {
static let shared = NetworkManager()
private init() { }
}
Delegate Pattern
protocol TableViewDelegate: AnyObject {
func didSelectRow(at index: Int)
}
class TableView {
weak var delegate: TableViewDelegate?
func selectRow(at index: Int) {
delegate?.didSelectRow(at: index)
}
}
Observer Pattern
class Observable<T> {
var value: T {
didSet { listener?(value) }
}
private var listener: ((T) -> Void)?
init(_ value: T) {
self.value = value
}
func bind(_ listener: @escaping (T) -> Void) {
self.listener = listener
listener(value)
}
}
Factory Pattern
protocol Animal {
func makeSound()
}
class AnimalFactory {
static func create(type: String) -> Animal? {
switch type {
case "dog": return Dog()
case "cat": return Cat()
default: return nil
}
}
}
Interview Tips
Common Questions
- Value vs Reference Types: Structs are value types (copied), Classes are reference types (shared)
- ARC: Swift uses Automatic Reference Counting for memory management
- Optionals: Use to handle absence of value safely
- Protocol-Oriented Programming: Prefer protocols over inheritance
- Access Control: private, fileprivate, internal (default), public, open
Best Practices
- Use
letovervarwhen possible - Prefer structs over classes unless you need reference semantics
- Use guard for early exits
- Avoid force unwrapping
- Use meaningful variable names
- Follow Swift naming conventions (camelCase)
- Use extensions to organize code
- Implement proper error handling
Performance Tips
- Use
lazyfor expensive computations - Prefer
isEmptyovercount == 0 - Use
ContiguousArrayfor performance-critical code - Avoid unnecessary type conversions
- Use value types to reduce heap allocations
SwiftUI Basics (if asked)
struct ContentView: View {
@State private var count = 0
var body: some View {
VStack {
Text("Count: \(count)")
Button("Increment") {
count += 1
}
}
}
}
Found this useful? Pass it on.