Swift
What I do
I am a general-purpose programming language developed by Apple, first released in 2014. I was designed as a modern replacement for Objective-C, featuring safety, performance, and expressiveness. I am used for iOS, macOS, watchOS, and tvOS app development, with growing use for server-side development with Vapor and other frameworks. I feature optionals for safe null handling, protocol-oriented programming, value types (structs) by default, generics, and powerful error handling.
When to use me
Use Swift when building iOS, macOS, watchOS, or tvOS applications, server-side applications with Vapor, when you need memory safety and modern language features, or when building applications that benefit from protocol-oriented design.
Core Concepts
- Optionals: Types that may contain a value or nil, using ? for declaration and ! for unwrapping.
- Value Semantics: Structs and enums have value semantics, copied on assignment; classes have reference semantics.
- Protocol-Oriented Programming: Protocols define interfaces that types conform to, enabling composition.
- Generics: Generic functions and types that work with any type while maintaining type safety.
- Type Inference: Compiler infers types in most cases, reducing verbosity while maintaining safety.
- Closures: First-class functions with capture semantics, concise syntax for passing operations.
- Error Handling: Throwing, catching, and propagating errors using try, catch, and throws.
- Access Control: Public, internal, fileprivate, and private access levels for encapsulation.
- Property Observers: willSet and didSet for observing and responding to property changes.
- Automatic Reference Counting (ARC): Memory management for class instances with strong/weak/unowned references.
Code Examples
Optionals and Safe Unwrapping:
let name: String? = nil
let length = name?.count ?? 0
print("Length: \(length)")
let items: [String?] = ["hello", nil, "world"]
items.forEach { item in
if let unwrapped = item {
print("Item: \(unwrapped.uppercased())")
} else {
print("Nil item found")
}
}
let numbers: [Int?] = [1, 2, nil, 4, 5]
let nonNilNumbers = numbers.compactMap { $0 }
print("Non-nil: \(nonNilNumbers)")
func printLength(_ str: String?) {
guard let unwrapped = str else {
print("String is nil")
return
}
print("Length: \(unwrapped.count)")
}
printLength("Swift")
printLength(nil)
let value: Any = "Hello"
if let stringValue = value as? String {
print("String: \(stringValue)")
}
switch value {
case let str as String:
print("String: \(str)")
case let num as Int:
print("Int: \(num)")
default:
print("Unknown type")
}
Structs, Classes, and Protocols:
struct Point {
var x: Int
var y: Int
var description: String {
"Point(x: \(x), y: \(y))"
}
mutating func moveBy(x deltaX: Int, y deltaY: Int) {
x += deltaX
y += deltaY
}
}
class Circle {
var center: Point
var radius: Double
init(center: Point, radius: Double) {
self.center = center
self.radius = radius
}
var area: Double {
Double.pi * radius * radius
}
func contains(_ point: Point) -> Bool {
let dx = Double(point.x - center.x)
let dy = Double(point.y - center.y)
return sqrt(dx * dx + dy * dy) <= radius
}
}
protocol Drawable {
func draw()
var color: String { get set }
}
struct Rectangle: Drawable {
var width: Double
var height: Double
var color: String
var area: Double {
width * height
}
func draw() {
print("Drawing rectangle \(color) with area \(area)")
}
}
struct Triangle: Drawable {
var side1: Double
var side2: Double
var side3: Double
var color: String
var color: String
func draw() {
print("Drawing triangle")
}
}
let shapes: [Drawable] = [
Rectangle(width: 10, height: 20, color: "red"),
Triangle(side1: 3, side2: 4, side3: 5, color: "blue")
]
shapes.forEach { $0.draw() }
Generics and Type Constraints:
struct Stack<Element> {
private var items: [Element] = []
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element? {
items.popLast()
}
var isEmpty: Bool {
items.isEmpty
}
var top: Element? {
items.last
}
}
func swapValues<T>(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
func findFirst<T: Equatable>(_ array: [T], _ value: T) -> Int? {
for (index, element) in array.enumerated() {
if element == value {
return index
}
}
return nil
}
protocol Comparable {
static func < (lhs: Self, rhs: Self) -> Bool
}
func findMin<T: Comparable>(_ array: [T]) -> T? {
guard var min = array.first else { return nil }
for element in array {
if element < min {
min = element
}
}
return min
}
var stack = Stack<Int>()
stack.push(1)
stack.push(2)
stack.push(3)
while let top = stack.pop() {
print("Popped: \(top)")
}
var a = 5
var b = 10
swapValues(&a, &b)
print("a: \(a), b: \(b)")
let numbers = [1, 2, 3, 4, 5]
if let index = findFirst(numbers, 3) {
print("Found at index: \(index)")
}
Error Handling:
enum NetworkError: Error {
case invalidURL
case noData
case decodingError
case serverError(Int)
}
struct User: Decodable {
let id: Int
let name: String
let email: String
}
func fetchUser(id: Int) async throws -> User {
guard let url = URL(string: "https://api.example.com/users/\(id)") else {
throw NetworkError.invalidURL
}
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse else {
throw NetworkError.noData
}
guard (200...299).contains(httpResponse.statusCode) else {
throw NetworkError.serverError(httpResponse.statusCode)
}
let decoder = JSONDecoder()
guard let user = try? decoder.decode(User.self, from: data) else {
throw NetworkError.decodingError
}
return user
}
func fetchMultipleUsers(ids: [Int]) async -> [User] {
var users: [User] = []
for id in ids {
do {
let user = try await fetchUser(id: id)
users.append(user)
} catch {
print("Failed to fetch user \(id): \(error)")
}
}
return users
}
Task {
do {
let user = try await fetchUser(id: 1)
print("User: \(user.name)")
} catch {
print("Error: \(error)")
}
}
Best Practices
- Prefer Structs Over Classes: Use structs with value semantics by default, classes only when inheritance is needed.
- Use Optionals Properly: Never force unwrap (!) except in tests; use optional chaining and guard let.
- Use Protocol Extensions: Extend protocols to provide default implementations for shared functionality.
- Use Access Control: Mark internal implementation details as private or fileprivate.
- Use Type Inference: Let compiler infer types when obvious; add explicit types for clarity.
- Use Property Observers: Use willSet/didSet for side effects on property changes.
- Use @State/@ObservedObject for SwiftUI: Use appropriate property wrappers for SwiftUI state management.
- Use Async/Await for Concurrency: Prefer async/await over completion handlers for asynchronous code.
- Follow Swift Naming Guidelines: Use camelCase, descriptive names, and follow API Design Guidelines.
- Use SwiftLint: Run SwiftLint to enforce code style and catch common mistakes.