Swift 치트시트
Swift - Apple's Powerful and Intuitive Programming Language
Swift는 iOS, macOS, watchOS, tvOS를 위한 강력하고 직관적인 프로그래밍 언어입니다. Swift 코드 작성은 대화형이고 재미있으며, 문법은 간결하면서도 표현력이 풍부하고, Swift는 개발자들이 좋아하는 현대적인 기능들을 포함하고 있습니다.
[No text was provided for this section]Would you like me to proceed with translating the rest of the document once you provide the remaining texts?```bash
Install Xcode from Mac App Store
Xcode includes Swift compiler and runtime
Verify Swift installation
swift —version
Swift REPL (Read-Eval-Print Loop)
swift
Swift Package Manager
swift package init swift build swift run
### Swift on Linux
```bash
# Download Swift for Linux from swift.org
# Extract and add to PATH
# Ubuntu/Debian
sudo apt-get install clang libicu-dev
# Install Swift
tar xzf swift-5.8-RELEASE-ubuntu20.04.tar.gz
export PATH=/path/to/swift-5.8-RELEASE-ubuntu20.04/usr/bin:$PATH
# Verify installation
swift --version
Basic Syntax
Hello World
// Simple print statement
print("Hello, World!")
// Multi-line string
let multilineString = """
This is a
multi-line string
in Swift
"""
print(multilineString)
// String interpolation
let name = "Swift"
let version = 5.8
print("Hello, \(name) \(version)!")
// Comments
// This is a single-line comment
/*
This is a
multi-line comment
*/
/// This is a documentation comment
/// - Parameter name: The name to greet
/// - Returns: A greeting string
func greet(name: String) -> String {
return "Hello, \(name)!"
}
Semicolons and Line Breaks
// Semicolons are optional
let a = 1
let b = 2
// Multiple statements on one line require semicolons
let x = 1; let y = 2
// Line breaks are used to separate statements
let firstName = "John"
let lastName = "Doe"
let fullName = firstName + " " + lastName
Variables and Constants
Declaration
// Variables (mutable)
var variableName = "I can change"
var age = 25
var height: Double = 5.9
// Constants (immutable)
let constantName = "I cannot change"
let pi = 3.14159
let maxUsers: Int = 100
// Type annotation
var explicitString: String = "This is a string"
var explicitInt: Int = 42
var explicitDouble: Double = 3.14
// Multiple variable declaration
var x = 0.0, y = 0.0, z = 0.0
// Deferred initialization
let deferredConstant: String
if someCondition {
deferredConstant = "Value A"
} else {
deferredConstant = "Value B"
}
Naming Conventions
// Use camelCase for variables and functions
var userName = "john_doe"
var isLoggedIn = true
func calculateTotalPrice() -> Double { return 0.0 }
// Use PascalCase for types
class UserManager { }
struct DatabaseConnection { }
enum NetworkError { }
// Use SCREAMING_SNAKE_CASE for constants
let MAX_RETRY_COUNT = 3
let API_BASE_URL = "https://api.example.com"
// Use descriptive names
var currentUserAge = 25 // Good
var a = 25 // Bad
// Boolean variables should be questions
var isVisible = true
var hasPermission = false
var canEdit = true
Data Types
Basic Types
// Integer types
let smallInt: Int8 = 127
let mediumInt: Int16 = 32767
let normalInt: Int32 = 2147483647
let bigInt: Int64 = 9223372036854775807
let unsignedInt: UInt = 42
// Floating-point types
let floatNumber: Float = 3.14159
let doubleNumber: Double = 3.141592653589793
// Boolean
let isTrue: Bool = true
let isFalse: Bool = false
// Character and String
let singleCharacter: Character = "A"
let stringValue: String = "Hello, Swift!"
// Type inference
let inferredInt = 42 // Int
let inferredDouble = 3.14 // Double
let inferredString = "Text" // String
let inferredBool = true // Bool
String Manipulation
// String creation
let emptyString = ""
let anotherEmptyString = String()
// String interpolation
let name = "Alice"
let age = 30
let message = "My name is \(name) and I'm \(age) years old."
// Multi-line strings
let poem = """
Roses are red,
Violets are blue,
Swift is awesome,
And so are you!
"""
// String operations
let greeting = "Hello"
let world = "World"
let combined = greeting + ", " + world + "!"
// String properties and methods
let text = "Swift Programming"
print(text.count) // 17
print(text.isEmpty) // false
print(text.uppercased()) // SWIFT PROGRAMMING
print(text.lowercased()) // swift programming
print(text.hasPrefix("Swift")) // true
print(text.hasSuffix("ing")) // true
// String indexing
let str = "Hello"
let firstChar = str[str.startIndex] // H
let lastChar = str[str.index(before: str.endIndex)] // o
let secondChar = str[str.index(str.startIndex, offsetBy: 1)] // e
// Substring
let range = str.index(str.startIndex, offsetBy: 1)..<str.index(str.startIndex, offsetBy: 4)
let substring = str[range] // "ell"
Collections Overview
// Array
var numbers = [1, 2, 3, 4, 5]
var strings: [String] = ["apple", "banana", "cherry"]
var emptyArray: [Int] = []
// Set
var uniqueNumbers: Set<Int> = [1, 2, 3, 3, 4] // {1, 2, 3, 4}
var emptySet: Set<String> = []
// Dictionary
var ages = ["Alice": 30, "Bob": 25, "Charlie": 35]
var emptyDict: [String: Int] = [:]
// Tuple
let coordinates = (x: 10, y: 20)
let httpStatus = (404, "Not Found")
let person = (name: "John", age: 30, isEmployed: true)
Operators
Arithmetic Operators
let a = 10
let b = 3
// Basic arithmetic
let sum = a + b // 13
let difference = a - b // 7
let product = a * b // 30
let quotient = a / b // 3
let remainder = a % b // 1
// Unary operators
let positive = +a // 10
let negative = -a // -10
// Compound assignment
var x = 5
x += 3 // x is now 8
x -= 2 // x is now 6
x *= 2 // x is now 12
x /= 3 // x is now 4
x %= 3 // x is now 1
Comparison Operators
let a = 5
let b = 10
// Comparison
let isEqual = (a == b) // false
let isNotEqual = (a != b) // true
let isGreater = (a > b) // false
let isLess = (a < b) // true
let isGreaterOrEqual = (a >= b) // false
let isLessOrEqual = (a <= b) // true
// String comparison
let str1 = "apple"
let str2 = "banana"
let result = str1 < str2 // true (alphabetical order)
// Tuple comparison
let tuple1 = (1, "zebra")
let tuple2 = (2, "apple")
let tupleResult = tuple1 < tuple2 // true (compares first elements)
Logical Operators
let a = true
let b = false
// Logical NOT
let notA = !a // false
// Logical AND
let andResult = a && b // false
// Logical OR
let orResult = a || b // true
// Short-circuit evaluation
let result = a || expensiveFunction() // expensiveFunction() not called if a is true
Range Operators
// Closed range (includes both endpoints)
let closedRange = 1...5 // 1, 2, 3, 4, 5
// Half-open range (excludes upper bound)
let halfOpenRange = 1..<5 // 1, 2, 3, 4
// One-sided ranges
let oneSidedRange1 = 2... // 2 to end of collection
let oneSidedRange2 = ...2 // beginning to 2
let oneSidedRange3 = ..<2 // beginning to 2 (excluding 2)
// Using ranges
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let subset = numbers[2...5] // [3, 4, 5, 6]
for i in 1...5 {
print(i) // Prints 1, 2, 3, 4, 5
}
Nil-Coalescing Operator
let optionalName: String? = nil
let defaultName = "Anonymous"
// Nil-coalescing operator
let name = optionalName ?? defaultName // "Anonymous"
// Equivalent to:
let name2 = optionalName != nil ? optionalName! : defaultName
// Chaining nil-coalescing
let a: String? = nil
let b: String? = nil
let c: String? = "Hello"
let result = a ?? b ?? c ?? "Default" // "Hello"
Control Flow
Conditional Statements
// if statement
let temperature = 25
if temperature > 30 {
print("It's hot!")
} else if temperature > 20 {
print("It's warm.")
} else {
print("It's cool.")
}
// Ternary operator
let message = temperature > 25 ? "Warm" : "Cool"
// guard statement
func processUser(name: String?) {
guard let userName = name, !userName.isEmpty else {
print("Invalid name")
return
}
print("Processing user: \(userName)")
}
// if let (optional binding)
let optionalNumber: Int? = 42
if let number = optionalNumber {
print("Number is \(number)")
} else {
print("Number is nil")
}
// Multiple optional binding
let optionalName: String? = "Alice"
let optionalAge: Int? = 30
if let name = optionalName, let age = optionalAge, age >= 18 {
print("\(name) is an adult")
}
Switch Statements
let character = "a"
switch character {
case "a", "e", "i", "o", "u":
print("Vowel")
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
print("Consonant")
default:
print("Not a letter")
}
// Switch with ranges
let score = 85
switch score {
case 90...100:
print("A")
case 80..<90:
print("B")
case 70..<80:
print("C")
case 60..<70:
print("D")
default:
print("F")
}
// Switch with tuples
let point = (1, 1)
switch point {
case (0, 0):
print("Origin")
case (_, 0):
print("On x-axis")
case (0, _):
print("On y-axis")
case (-2...2, -2...2):
print("Inside the box")
default:
print("Outside the box")
}
// Switch with value binding
let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
print("On x-axis at x = \(x)")
case (0, let y):
print("On y-axis at y = \(y)")
case let (x, y):
print("Point at (\(x), \(y))")
}
// Switch with where clause
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
print("On the line x == y")
case let (x, y) where x == -y:
print("On the line x == -y")
case let (x, y):
print("Point at (\(x), \(y))")
}
Loops
// for-in loop
let numbers = [1, 2, 3, 4, 5]
for number in numbers {
print(number)
}
// for-in with range
for i in 1...5 {
print("Count: \(i)")
}
// for-in with stride
for i in stride(from: 0, to: 10, by: 2) {
print(i) // 0, 2, 4, 6, 8
}
// for-in with enumerated
let fruits = ["apple", "banana", "cherry"]
for (index, fruit) in fruits.enumerated() {
print("\(index): \(fruit)")
}
// while loop
var count = 0
while count < 5 {
print("Count: \(count)")
count += 1
}
// repeat-while loop (do-while equivalent)
var number = 0
repeat {
print("Number: \(number)")
number += 1
} while number < 3
// Loop control
for i in 1...10 {
if i == 3 {
continue // Skip this iteration
}
if i == 8 {
break // Exit the loop
}
print(i)
}
// Labeled statements
outerLoop: for i in 1...3 {
innerLoop: for j in 1...3 {
if i == 2 && j == 2 {
break outerLoop
}
print("i: \(i), j: \(j)")
}
}
Functions
Basic Functions
// Simple function
func greet() {
print("Hello!")
}
greet()
// Function with parameters
func greet(name: String) {
print("Hello, \(name)!")
}
greet(name: "Alice")
// Function with return value
func add(a: Int, b: Int) -> Int {
return a + b
}
let sum = add(a: 5, b: 3)
// Function with multiple return values
func minMax(array: [Int]) -> (min: Int, max: Int) {
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("Min: \(bounds.min), Max: \(bounds.max)")
Function Parameters
// External and internal parameter names
func greet(person name: String, from hometown: String) -> String {
return "Hello \(name)! Glad you could visit from \(hometown)."
}
print(greet(person: "Bill", from: "Cupertino"))
// Omitting external parameter names
func add(_ a: Int, _ b: Int) -> Int {
return a + b
}
let result = add(5, 3) // No parameter labels
// Default parameter values
func greet(name: String, greeting: String = "Hello") -> String {
return "\(greeting), \(name)!"
}
print(greet(name: "Alice")) // Uses default greeting
print(greet(name: "Bob", greeting: "Hi")) // Uses custom greeting
// Variadic parameters
func average(_ numbers: Double...) -> Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
print(average(1, 2, 3, 4, 5)) // 3.0
// In-out parameters
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt: \(someInt), anotherInt: \(anotherInt)")
Function Types
// Function as a type
func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b
}
func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
return a * b
}
// Variable of function type
var mathFunction: (Int, Int) -> Int = addTwoInts
print(mathFunction(2, 3)) // 5
mathFunction = multiplyTwoInts
print(mathFunction(2, 3)) // 6
// Function as parameter
func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
// Function as return type
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
func stepForward(input: Int) -> Int { return input + 1 }
func stepBackward(input: Int) -> Int { return input - 1 }
return backward ? stepBackward : stepForward
}
let moveNearerToZero = chooseStepFunction(backward: true)
print(moveNearerToZero(5)) // 4
Nested Functions
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
func stepForward(input: Int) -> Int {
return input + 1
}
func stepBackward(input: Int) -> Int {
return input - 1
}
return backward ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
while currentValue != 0 {
print("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
print("zero!")
Closures
Basic Closures
// Closure expression syntax
let numbers = [1, 2, 3, 4, 5]
// Full closure syntax
let doubled = numbers.map({ (number: Int) -> Int in
return number * 2
})
// Inferring type from context
let doubled2 = numbers.map({ number in
return number * 2
})
// Implicit returns from single-expression closures
let doubled3 = numbers.map({ number in number * 2 })
// Shorthand argument names
let doubled4 = numbers.map({ $0 * 2 })
// Trailing closure syntax
let doubled5 = numbers.map { $0 * 2 }
// Multiple trailing closures
func loadPicture(from server: String, completion: (String) -> Void, onFailure: (String) -> Void) {
// Implementation
}
loadPicture(from: "server.com") { picture in
print("Loaded: \(picture)")
} onFailure: { error in
print("Failed: \(error)")
}
Capturing Values
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
let incrementByTen = makeIncrementer(forIncrement: 10)
print(incrementByTen()) // 10
print(incrementByTen()) // 20
print(incrementByTen()) // 30
let incrementBySeven = makeIncrementer(forIncrement: 7)
print(incrementBySeven()) // 7
print(incrementByTen()) // 40 (still independent)
Escaping Closures
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure() // Called before function returns
}
class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure { self.x = 100 }
someFunctionWithNonescapingClosure { x = 200 }
}
}
let instance = SomeClass()
instance.doSomething()
print(instance.x) // 200
completionHandlers.first?()
print(instance.x) // 100
Autoclosures
// Autoclosure delays evaluation
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: "Alex") // String is automatically wrapped in closure
// Autoclosure with escaping
var customerProviders: [() -> String] = []
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
customerProviders.append(customerProvider)
}
collectCustomerProviders("Barry")
collectCustomerProviders("Daniella")
print("Collected \(customerProviders.count) closures.")
for customerProvider in customerProviders {
print("Now serving \(customerProvider())!")
}
Classes and Structures
Basic Syntax
// Structure
struct Resolution {
var width = 0
var height = 0
// Computed property
var area: Int {
return width * height
}
// Method
mutating func scale(by factor: Int) {
width *= factor
height *= factor
}
}
// Class
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
// Initializer
init(name: String) {
self.name = name
}
// Method
func describe() -> String {
return "VideoMode: \(name ?? "Unknown")"
}
}
// Usage
var someResolution = Resolution(width: 1920, height: 1080)
print(someResolution.area) // 2073600
let someVideoMode = VideoMode(name: "HD")
someVideoMode.resolution = someResolution
someVideoMode.frameRate = 30.0
Initializers
struct Celsius {
var temperatureInCelsius: Double
// Designated initializer
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
// Default initializer
init(_ celsius: Double) {
temperatureInCelsius = celsius
}
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
let bodyTemperature = Celsius(37.0)
// Class initializers
class Person {
let 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)
}
}
// Failable initializers
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty {
return nil
}
self.species = species
}
}
let someCreature = Animal(species: "Giraffe")
if let giraffe = someCreature {
print("An animal was initialized with a species of \(giraffe.species)")
}
Value vs Reference Types
// Structures are value types
struct Point {
var x = 0.0, y = 0.0
}
var point1 = Point(x: 1.0, y: 2.0)
var point2 = point1 // Copy is made
point2.x = 3.0
print(point1.x) // 1.0 (unchanged)
print(point2.x) // 3.0
// Classes are reference types
class Size {
var width = 0.0, height = 0.0
}
let size1 = Size()
size1.width = 10.0
let size2 = size1 // Same reference
size2.width = 20.0
print(size1.width) // 20.0 (changed)
print(size2.width) // 20.0
// Identity operators for reference types
if size1 === size2 {
print("size1 and size2 refer to the same instance")
}
if size1 !== size2 {
print("size1 and size2 refer to different instances")
}
Properties
Stored Properties
struct FixedLengthRange {
var firstValue: Int
let length: Int
// Lazy stored property
lazy var expensiveProperty: String = {
// Expensive computation
return "Computed value"
}()
}
var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3)
rangeOfThreeItems.firstValue = 6
// Property observers
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
print("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
print("Added \(totalSteps - oldValue) steps")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
stepCounter.totalSteps = 360
Computed Properties
struct Circle {
var radius: Double = 0.0
var area: Double {
get {
return Double.pi * radius * radius
}
set(newArea) {
radius = sqrt(newArea / Double.pi)
}
}
// Read-only computed property
var circumference: Double {
return 2.0 * Double.pi * radius
}
}
var circle = Circle(radius: 5.0)
print(circle.area) // 78.54
circle.area = 100.0
print(circle.radius) // 5.64
Property Wrappers
@propertyWrapper
struct TwelveOrLess {
private var number = 0
var wrappedValue: Int {
get { return number }
set { number = min(newValue, 12) }
}
}
struct SmallRectangle {
@TwelveOrLess var height: Int
@TwelveOrLess var width: Int
}
var rectangle = SmallRectangle()
print(rectangle.height) // 0
rectangle.height = 10
print(rectangle.height) // 10
rectangle.height = 24
print(rectangle.height) // 12
// Property wrapper with parameters
@propertyWrapper
struct SmallNumber {
private var maximum: Int
private var number: Int
var wrappedValue: Int {
get { return number }
set { number = min(newValue, maximum) }
}
init() {
maximum = 12
number = 0
}
init(wrappedValue: Int) {
maximum = 12
number = min(wrappedValue, maximum)
}
init(wrappedValue: Int, maximum: Int) {
self.maximum = maximum
number = min(wrappedValue, maximum)
}
}
struct UnitRectangle {
@SmallNumber var height: Int = 1
@SmallNumber var width: Int = 1
}
struct NarrowRectangle {
@SmallNumber(maximum: 5) var height: Int = 2
@SmallNumber(maximum: 4) var width: Int = 3
}
Type Properties
struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 1
}
}
enum SomeEnumeration {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 6
}
}
class SomeClass {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 27
}
class var overrideableComputedTypeProperty: Int {
return 107
}
}
print(SomeStructure.storedTypeProperty) // "Some value."
print(SomeEnumeration.computedTypeProperty) // 6
print(SomeClass.overrideableComputedTypeProperty) // 107
Methods
Instance Methods
class Counter {
var count = 0
func increment() {
count += 1
}
func increment(by amount: Int) {
count += amount
}
func reset() {
count = 0
}
}
let counter = Counter()
counter.increment()
counter.increment(by: 5)
print(counter.count) // 6
counter.reset()
// Mutating methods for structures
struct Point {
var x = 0.0, y = 0.0
mutating func moveBy(x deltaX: Double, y deltaY: Double) {
x += deltaX
y += deltaY
}
mutating func moveToOrigin() {
self = Point(x: 0.0, y: 0.0)
}
}
var somePoint = Point(x: 1.0, y: 1.0)
somePoint.moveBy(x: 2.0, y: 3.0)
print("Point is now at (\(somePoint.x), \(somePoint.y))")
Type Methods
class SomeClass {
class func someTypeMethod() {
print("Type method called")
}
static func anotherTypeMethod() {
print("Static type method called")
}
}
SomeClass.someTypeMethod()
SomeClass.anotherTypeMethod()
struct LevelTracker {
static var highestUnlockedLevel = 1
var currentLevel = 1
static func unlock(_ level: Int) {
if level > highestUnlockedLevel {
highestUnlockedLevel = level
}
}
static func isUnlocked(_ level: Int) -> Bool {
return level <= highestUnlockedLevel
}
@discardableResult
mutating func advance(to level: Int) -> Bool {
if LevelTracker.isUnlocked(level) {
currentLevel = level
return true
} else {
return false
}
}
}
Inheritance
Basic Inheritance
// Base class
class Vehicle {
var currentSpeed = 0.0
var description: String {
return "traveling at \(currentSpeed) miles per hour"
}
func makeNoise() {
// Do nothing - an arbitrary vehicle doesn't necessarily make a noise
}
}
// Subclass
class Bicycle: Vehicle {
var hasBasket = false
override var description: String {
return "Bicycle: \(super.description)"
}
}
class Tandem: Bicycle {
var currentNumberOfPassengers = 0
override var description: String {
return "Tandem: \(super.description) with \(currentNumberOfPassengers) passengers"
}
}
// Usage
let bicycle = Bicycle()
bicycle.hasBasket = true
bicycle.currentSpeed = 15.0
print(bicycle.description)
let tandem = Tandem()
tandem.currentSpeed = 22.0
tandem.currentNumberOfPassengers = 2
print(tandem.description)
Overriding
class Train: Vehicle {
override func makeNoise() {
print("Choo Choo")
}
override var currentSpeed: Double {
didSet {
print("Train speed changed to \(currentSpeed)")
}
}
}
class Car: Vehicle {
var gear = 1
override var description: String {
return super.description + " in gear \(gear)"
}
override func makeNoise() {
print("Vroom Vroom")
}
}
let train = Train()
train.makeNoise() // "Choo Choo"
let car = Car()
car.currentSpeed = 25.0
car.gear = 3
print(car.description) // "traveling at 25.0 miles per hour in gear 3"
Preventing Overrides
class FinalVehicle {
final var maxSpeed = 100.0
final func startEngine() {
print("Engine started")
}
}
// This would cause a compile error:
// class FastCar: FinalVehicle {
// override func startEngine() { } // Error!
// }
// Final class cannot be subclassed
final class ImmutableClass {
let value = 42
}
// This would cause a compile error:
// class SubClass: ImmutableClass { } // Error!
Protocols
Basic Protocols
protocol SomeProtocol {
var mustBeSettable: Int { get set }
var doesNotNeedToBeSettable: Int { get }
func someMethod()
static func someTypeMethod()
}
protocol FullyNamed {
var fullName: String { get }
}
struct Person: FullyNamed {
var fullName: String
}
class Starship: FullyNamed {
var prefix: String?
var name: String
init(name: String, prefix: String? = nil) {
self.name = name
self.prefix = prefix
}
var fullName: String {
return (prefix != nil ? prefix! + " " : "") + name
}
}
let john = Person(fullName: "John Appleseed")
let ncc1701 = Starship(name: "Enterprise", prefix: "USS")
Method Requirements
protocol Togglable {
mutating func toggle()
}
enum OnOffSwitch: Togglable {
case off, on
mutating func toggle() {
switch self {
case .off:
self = .on
case .on:
self = .off
}
}
}
var lightSwitch = OnOffSwitch.off
lightSwitch.toggle() // lightSwitch is now .on
Protocol Inheritance
protocol InheritingProtocol: SomeProtocol {
func anotherMethod()
}
protocol PrettyTextRepresentable: CustomStringConvertible {
var prettyTextualDescription: String { get }
}
extension Person: PrettyTextRepresentable {
var description: String {
return fullName
}
var prettyTextualDescription: String {
return "Person: \(fullName)"
}
}
Class-Only Protocols
protocol SomeClassOnlyProtocol: AnyObject {
func someMethod()
}
class SomeClass: SomeClassOnlyProtocol {
func someMethod() {
print("Method implemented")
}
}
// This would cause an error:
// struct SomeStruct: SomeClassOnlyProtocol { } // Error!
Protocol Composition
protocol Named {
var name: String { get }
}
protocol Aged {
var age: Int { get }
}
struct PersonStruct: Named, Aged {
var name: String
var age: Int
}
func wishHappyBirthday(to celebrator: Named & Aged) {
print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!")
}
let birthdayPerson = PersonStruct(name: "Malcolm", age: 21)
wishHappyBirthday(to: birthdayPerson)
Optional Protocol Requirements
@objc protocol CounterDataSource {
@objc optional func increment(forCount count: Int) -> Int
@objc optional var fixedIncrement: Int { get }
}
class Counter {
var count = 0
var dataSource: CounterDataSource?
func increment() {
if let amount = dataSource?.increment?(forCount: count) {
count += amount
} else if let amount = dataSource?.fixedIncrement {
count += amount
}
}
}
Protocol Extensions
extension Collection {
var isNotEmpty: Bool {
return !isEmpty
}
}
let numbers = [1, 2, 3]
print(numbers.isNotEmpty) // true
// Protocol extension with constraints
extension Collection where Element: Equatable {
func allEqual() -> Bool {
for element in self {
if element != self.first {
return false
}
}
return true
}
}
let equalNumbers = [1, 1, 1]
print(equalNumbers.allEqual()) // true
Extensions
Basic Extensions
extension Double {
var km: Double { return self * 1_000.0 }
var m: Double { return self }
var cm: Double { return self / 100.0 }
var mm: Double { return self / 1_000.0 }
var ft: Double { return self / 3.28084 }
}
let oneInch = 25.4.mm
print("One inch is \(oneInch) meters") // 0.0254 meters
let threeFeet = 3.ft
print("Three feet is \(threeFeet) meters") // 0.914399970739201 meters
Adding Methods
extension Int {
func repetitions(task: () -> Void) {
for _ in 0..<self {
task()
}
}
mutating func square() {
self = self * self
}
}
3.repetitions {
print("Hello!")
}
var someInt = 3
someInt.square() // someInt is now 9
Adding Initializers
struct Size {
var width = 0.0, height = 0.0
}
struct Point {
var x = 0.0, y = 0.0
}
struct Rect {
var origin = Point()
var size = Size()
}
extension Rect {
init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x: originX, y: originY), size: size)
}
}
let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0))
Adding Subscripts
extension Int {
subscript(digitIndex: Int) -> Int {
var decimalBase = 1
for _ in 0..<digitIndex {
decimalBase *= 10
}
return (self / decimalBase) % 10
}
}
746381295[0] // 5
746381295[1] // 9
746381295[2] // 2
746381295[8] // 7
Adding Nested Types
extension Int {
enum Kind {
case negative, zero, positive
}
var kind: Kind {
switch self {
case 0:
return .zero
case let x where x > 0:
return .positive
default:
return .negative
}
}
}
func printIntegerKinds(_ numbers: [Int]) {
for number in numbers {
switch number.kind {
case .negative:
print("- ", terminator: "")
case .zero:
print("0 ", terminator: "")
case .positive:
print("+ ", terminator: "")
}
}
print("")
}
printIntegerKinds([3, 19, -27, 0, -6, 0, 7]) // + + - 0 - 0 +
Generics
Generic Functions
func swapTwoValues<T>(_ a: inout T, _ b: inout T) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoValues(&someInt, &anotherInt)
print("someInt: \(someInt), anotherInt: \(anotherInt)")
var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
print("someString: \(someString), anotherString: \(anotherString)")
Generic Types
struct Stack<Element> {
var items: [Element] = []
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
var isEmpty: Bool {
return items.isEmpty
}
var count: Int {
return items.count
}
}
var stackOfStrings = Stack<String>()
stackOfStrings.push("uno")
stackOfStrings.push("dos")
stackOfStrings.push("tres")
stackOfStrings.push("cuatro")
let fromTheTop = stackOfStrings.pop() // "cuatro"
Type Constraints
func findIndex<T: Equatable>(of valueToFind: T, in array: [T]) -> Int? {
for (index, value) in array.enumerated() {
if value == valueToFind {
return index
}
}
return nil
}
let doubleIndex = findIndex(of: 9.3, in: [3.14159, 0.1, 0.25])
let stringIndex = findIndex(of: "Andrea", in: ["Mike", "Malcolm", "Andrea"])
// Generic type with multiple constraints
func allItemsMatch<C1: Container, C2: Container>(_ someContainer: C1, _ anotherContainer: C2) -> Bool
where C1.Item == C2.Item, C1.Item: Equatable {
if someContainer.count != anotherContainer.count {
return false
}
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
return true
}
Associated Types
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}
struct IntStack: Container {
var items: [Int] = []
mutating func push(_ item: Int) {
items.append(item)
}
mutating func pop() -> Int {
return items.removeLast()
}
// Container protocol conformance
typealias Item = Int
mutating func append(_ item: Int) {
self.push(item)
}
var count: Int {
return items.count
}
subscript(i: Int) -> Int {
return items[i]
}
}
extension Stack: Container {
// No need to declare typealias Item = Element
// Swift can infer that Element is the appropriate type for Item
mutating func append(_ item: Element) {
self.push(item)
}
subscript(i: Int) -> Element {
return items[i]
}
}
Generic Where Clauses
func allItemsMatch<C1: Container, C2: Container>(_ someContainer: C1, _ anotherContainer: C2) -> Bool
where C1.Item == C2.Item, C1.Item: Equatable {
if someContainer.count != anotherContainer.count {
return false
}
for i in 0..<someContainer.count {
if someContainer[i] != anotherContainer[i] {
return false
}
}
return true
}
// Extensions with generic where clauses
extension Stack where Element: Equatable {
func isTop(_ item: Element) -> Bool {
guard let topItem = items.last else {
return false
}
return topItem == item
}
}
extension Container where Item: Equatable {
func startsWith(_ item: Item) -> Bool {
return count >= 1 && self[0] == item
}
}
extension Container where Item == Double {
func average() -> Double {
var sum = 0.0
for index in 0..<count {
sum += self[index]
}
return sum / Double(count)
}
}
Error Handling
Defining Errors
enum VendingMachineError: Error {
case invalidSelection
case insufficientFunds(coinsNeeded: Int)
case outOfStock
}
// Custom error with localized description
struct ValidationError: Error, LocalizedError {
let message: String
var errorDescription: String? {
return message
}
}
Throwing Functions
func canThrowErrors() throws -> String {
// Function implementation
return "Success"
}
func cannotThrowErrors() -> String {
// Function implementation
return "Success"
}
struct Item {
var price: Int
var count: Int
}
class VendingMachine {
var inventory = [
"Candy Bar": Item(price: 12, count: 7),
"Chips": Item(price: 10, count: 4),
"Pretzels": Item(price: 7, count: 11)
]
var coinsDeposited = 0
func vend(itemNamed name: String) throws {
guard let item = inventory[name] else {
throw VendingMachineError.invalidSelection
}
guard item.count > 0 else {
throw VendingMachineError.outOfStock
}
guard item.price <= coinsDeposited else {
throw VendingMachineError.insufficientFunds(coinsNeeded: item.price - coinsDeposited)
}
coinsDeposited -= item.price
var newItem = item
newItem.count -= 1
inventory[name] = newItem
print("Dispensing \(name)")
}
}
Handling Errors
let vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
// do-catch
do {
try vendingMachine.vend(itemNamed: "Candy Bar")
} catch VendingMachineError.invalidSelection {
print("Invalid Selection.")
} catch VendingMachineError.outOfStock {
print("Out of Stock.")
} catch VendingMachineError.insufficientFunds(let coinsNeeded) {
print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
} catch {
print("Unexpected error: \(error).")
}
// Multiple catch patterns
do {
try vendingMachine.vend(itemNamed: "Candy Bar")
} catch VendingMachineError.invalidSelection, VendingMachineError.insufficientFunds {
print("Invalid selection or insufficient funds.")
} catch {
print("Other error: \(error)")
}
// Converting errors to optional values
func someThrowingFunction() throws -> Int {
return 42
}
let x = try? someThrowingFunction()
// x is of type Int? and equals 42
let y: Int?
do {
y = try someThrowingFunction()
} catch {
y = nil
}
// Disabling error propagation
let z = try! someThrowingFunction()
// If someThrowingFunction() throws an error, you'll get a runtime error
Defer Statements
func processFile(filename: String) throws {
let file = openFile(named: filename)
defer {
closeFile(file)
}
// Work with the file
if someCondition {
return // closeFile(file) is called here
}
if anotherCondition {
throw SomeError.errorCondition // closeFile(file) is called here too
}
// closeFile(file) is called here as well
}
// Multiple defer statements
func deferExample() {
defer { print("First defer") }
defer { print("Second defer") }
defer { print("Third defer") }
print("End of function")
}
// Output:
// End of function
// Third defer
// Second defer
// First defer
Result Type
enum NetworkError: Error {
case badURL
case requestFailed
case unknown
}
func fetchData(from urlString: String) -> Result<Data, NetworkError> {
guard let url = URL(string: urlString) else {
return .failure(.badURL)
}
// Simulate network request
let success = Bool.random()
if success {
return .success(Data())
} else {
return .failure(.requestFailed)
}
}
// Using Result
let result = fetchData(from: "https://example.com")
switch result {
case .success(let data):
print("Received data: \(data)")
case .failure(let error):
print("Error: \(error)")
}
// Result with map and flatMap
let transformedResult = result
.map { data in
return String(data: data, encoding: .utf8) ?? ""
}
.mapError { error in
return "Network error: \(error)"
}
Memory Management
Automatic Reference Counting (ARC)
class Person {
let name: String
init(name: String) {
self.name = name
print("\(name) is being initialized")
}
deinit {
print("\(name) is being deinitialized")
}
}
var reference1: Person?
var reference2: Person?
var reference3: Person?
reference1 = Person(name: "John Appleseed")
// Prints "John Appleseed is being initialized"
reference2 = reference1
reference3 = reference1
// Strong reference count is now 3
reference1 = nil
reference2 = nil
// Strong reference count is now 1
reference3 = nil
// Strong reference count is now 0
// Prints "John Appleseed is being deinitialized"
Strong Reference Cycles
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) is being deinitialized") }
}
class Apartment {
let unit: String
init(unit: String) { self.unit = unit }
var tenant: Person?
deinit { print("Apartment \(unit) is being deinitialized") }
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil
unit4A = nil
// Neither deinitializer is called - memory leak!
Weak References
class Person {
let name: String
init(name: String) { self.name = name }
var apartment: Apartment?
deinit { print("\(name) is being deinitialized") }
}
class Apartment {
let unit: String
init(unit: String) { self.unit = unit }
weak var tenant: Person? // Weak reference
deinit { print("Apartment \(unit) is being deinitialized") }
}
var john: Person?
var unit4A: Apartment?
john = Person(name: "John Appleseed")
unit4A = Apartment(unit: "4A")
john!.apartment = unit4A
unit4A!.tenant = john
john = nil
// Prints "John Appleseed is being deinitialized"
print(unit4A!.tenant) // nil
unit4A = nil
// Prints "Apartment 4A is being deinitialized"
Unowned References
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
deinit { print("\(name) is being deinitialized") }
}
class CreditCard {
let number: UInt64
unowned let customer: Customer // Unowned reference
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
deinit { print("Card #\(number) is being deinitialized") }
}
var john: Customer?
john = Customer(name: "John Appleseed")
john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
john = nil
// Prints "John Appleseed is being deinitialized"
// Prints "Card #1234567890123456 is being deinitialized"
Closures and Strong Reference Cycles
class HTMLElement {
let name: String
let text: String?
lazy var asHTML: () -> String = { [unowned self] in
if let text = self.text {
return "<\(self.name)>\(text)</\(self.name)>"
} else {
return "<\(self.name) />"
}
}
init(name: String, text: String? = nil) {
self.name = name
self.text = text
}
deinit {
print("\(name) is being deinitialized")
}
}
var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
print(paragraph!.asHTML())
paragraph = nil
// Prints "p is being deinitialized"
// Capture list with weak reference
class SomeClass {
var value = 0
func doSomething() {
let closure = { [weak self] in
guard let self = self else { return }
print("Value: \(self.value)")
}
closure()
}
}
Concurrency
Async/Await (iOS 15+)
// Async function
func fetchUserID(from server: String) async -> Int {
let userID = 501
return userID
}
func fetchUsername(from server: String) async -> String {
let username = "Taylor"
return username
}
// Calling async functions
func connectUser(to server: String) async {
async let userID = fetchUserID(from: server)
async let username = fetchUsername(from: server)
let greeting = await "Hello \(username), user ID \(userID)"
print(greeting)
}
// Async sequence
func fetchData() async throws -> [String] {
// Simulate network delay
try await Task.sleep(nanoseconds: 1_000_000_000)
return ["Item 1", "Item 2", "Item 3"]
}
// Using async in SwiftUI
struct ContentView: View {
@State private var data: [String] = []
var body: some View {
List(data, id: \.self) { item in
Text(item)
}
.task {
do {
data = try await fetchData()
} catch {
print("Failed to fetch data: \(error)")
}
}
}
}
Tasks
// Creating tasks
func performWork() async {
let task1 = Task {
return await fetchUserID(from: "server1")
}
let task2 = Task {
return await fetchUsername(from: "server2")
}
let userID = await task1.value
let username = await task2.value
print("User: \(username) (\(userID))")
}
// Task cancellation
func cancellableWork() async {
let task = Task {
for i in 1...10 {
if Task.isCancelled {
print("Task was cancelled")
return
}
print("Working on step \(i)")
try await Task.sleep(nanoseconds: 1_000_000_000)
}
}
// Cancel after 3 seconds
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
task.cancel()
}
await task.value
}
// Task groups
func fetchMultipleUsers() async -> [String] {
await withTaskGroup(of: String.self) { group in
let servers = ["server1", "server2", "server3"]
for server in servers {
group.addTask {
return await fetchUsername(from: server)
}
}
var usernames: [String] = []
for await username in group {
usernames.append(username)
}
return usernames
}
}
Actors
actor BankAccount {
private var balance: Double
init(initialBalance: Double) {
balance = initialBalance
}
func deposit(amount: Double) {
balance += amount
}
func withdraw(amount: Double) -> Bool {
if balance >= amount {
balance -= amount
return true
}
return false
}
func getBalance() -> Double {
return balance
}
}
// Using actors
func bankingExample() async {
let account = BankAccount(initialBalance: 1000)
await account.deposit(amount: 500)
let success = await account.withdraw(amount: 200)
let balance = await account.getBalance()
print("Withdrawal successful: \(success), Balance: \(balance)")
}
// MainActor for UI updates
@MainActor
class ViewModel: ObservableObject {
@Published var data: [String] = []
func loadData() async {
let newData = await fetchDataFromNetwork()
// This runs on the main actor automatically
self.data = newData
}
}
func fetchDataFromNetwork() async -> [String] {
// Network operation
return ["Data 1", "Data 2", "Data 3"]
}
AsyncSequence
// Custom AsyncSequence
struct Counter: AsyncSequence {
typealias Element = Int
let howHigh: Int
struct AsyncIterator: AsyncIteratorProtocol {
let howHigh: Int
var current = 1
mutating func next() async -> Int? {
guard current <= howHigh else {
return nil
}
let result = current
current += 1
return result
}
}
func makeAsyncIterator() -> AsyncIterator {
return AsyncIterator(howHigh: howHigh)
}
}
// Using AsyncSequence
func useAsyncSequence() async {
for await number in Counter(howHigh: 5) {
print(number)
}
}
// Built-in AsyncSequence operations
func processAsyncData() async {
let numbers = Counter(howHigh: 10)
let evenNumbers = numbers.filter { $0 % 2 == 0 }
let doubled = evenNumbers.map { $0 * 2 }
for await number in doubled {
print("Even doubled: \(number)")
}
}
Collections
Arrays
// Array creation
var someInts: [Int] = []
var threeDoubles = Array(repeating: 0.0, count: 3)
var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
var sixDoubles = threeDoubles + anotherThreeDoubles
// Array literals
var shoppingList: [String] = ["Eggs", "Milk"]
var shoppingList2 = ["Eggs", "Milk"] // Type inferred
// Array operations
shoppingList.append("Flour")
shoppingList += ["Baking Powder"]
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
let firstItem = shoppingList[0]
shoppingList[0] = "Six eggs"
shoppingList[4...6] = ["Bananas", "Apples"]
shoppingList.insert("Maple Syrup", at: 0)
let mapleSyrup = shoppingList.remove(at: 0)
let apples = shoppingList.removeLast()
// Array iteration
for item in shoppingList {
print(item)
}
for (index, value) in shoppingList.enumerated() {
print("Item \(index + 1): \(value)")
}
// Array methods
let numbers = [1, 2, 3, 4, 5]
let doubled = numbers.map { $0 * 2 }
let evens = numbers.filter { $0 % 2 == 0 }
let sum = numbers.reduce(0, +)
let first = numbers.first
let last = numbers.last
let contains = numbers.contains(3)
Sets
// Set creation
var letters = Set<Character>()
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
// Set operations
favoriteGenres.insert("Jazz")
if let removedGenre = favoriteGenres.remove("Rock") {
print("\(removedGenre)? I'm over it.")
} else {
print("I never much cared for that.")
}
if favoriteGenres.contains("Funk") {
print("I get up on the good foot.")
} else {
print("It's too funky in here.")
}
// Set iteration
for genre in favoriteGenres {
print("\(genre)")
}
for genre in favoriteGenres.sorted() {
print("\(genre)")
}
// Set operations
let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
let union = oddDigits.union(evenDigits).sorted()
let intersection = oddDigits.intersection(evenDigits).sorted()
let subtracting = oddDigits.subtracting(singleDigitPrimeNumbers).sorted()
let symmetricDifference = oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted()
// Set membership and equality
let houseAnimals: Set = ["🐶", "🐱"]
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐦", "🐭"]
houseAnimals.isSubset(of: farmAnimals) // true
farmAnimals.isSuperset(of: houseAnimals) // true
farmAnimals.isDisjoint(with: cityAnimals) // true
Dictionaries
// Dictionary creation
var namesOfIntegers: [Int: String] = [:]
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
var airports2 = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
// Dictionary operations
airports["LHR"] = "London"
airports["LHR"] = "London Heathrow"
if let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") {
print("The old value for DUB was \(oldValue).")
}
if let airportName = airports["DUB"] {
print("The name of the airport is \(airportName).")
} else {
print("That airport is not in the airports dictionary.")
}
airports["APL"] = "Apple International"
airports["APL"] = nil
if let removedValue = airports.removeValue(forKey: "DUB") {
print("The removed airport's name is \(removedValue).")
} else {
print("The airports dictionary does not contain a value for DUB.")
}
// Dictionary iteration
for (airportCode, airportName) in airports {
print("\(airportCode): \(airportName)")
}
for airportCode in airports.keys {
print("Airport code: \(airportCode)")
}
for airportName in airports.values {
print("Airport name: \(airportName)")
}
let airportCodes = [String](airports.keys)
let airportNames = [String](airports.values)
Optionals
Optional Basics
// Optional declaration
var optionalString: String? = "Hello"
var optionalInt: Int? = nil
// Optional binding
if let actualString = optionalString {
print("The string is \(actualString)")
} else {
print("The string is nil")
}
// Multiple optional binding
if let string = optionalString, let int = optionalInt {
print("Both values exist: \(string), \(int)")
}
// Guard statement
func processOptional(_ value: String?) {
guard let unwrappedValue = value else {
print("Value is nil")
return
}
print("Processing: \(unwrappedValue)")
}
// Nil coalescing operator
let defaultName = "Anonymous"
let username = optionalString ?? defaultName
// Optional chaining
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
let john = Person()
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
} else {
print("Unable to retrieve the number of rooms.")
}
john.residence = Residence()
if let roomCount = john.residence?.numberOfRooms {
print("John's residence has \(roomCount) room(s).")
}
Implicitly Unwrapped Optionals
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // Requires an exclamation point
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // No need for an exclamation point
// Still can be treated as optional
if assumedString != nil {
print(assumedString!)
}
if let definiteString = assumedString {
print(definiteString)
}
Optional Map and FlatMap
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// Optional map
let doubled = convertedNumber.map { $0 * 2 }
print(doubled) // Optional(246)
// Optional flatMap
func doubleIfEven(_ number: Int) -> Int? {
return number % 2 == 0 ? number * 2 : nil
}
let result = convertedNumber.flatMap(doubleIfEven)
print(result) // nil (because 123 is odd)
// Chaining optionals
let numbers = ["1", "2", "three", "4"]
let validNumbers = numbers.compactMap { Int($0) }
print(validNumbers) // [1, 2, 4]
Best Practices
Code Style
// Use meaningful names
var userAge = 25 // Good
var a = 25 // Bad
func calculateTotalPrice() -> Double // Good
func calc() -> Double // Bad
// Use type inference when possible
let name = "John" // Good
let name: String = "John" // Unnecessary
// Use guard for early returns
func processUser(_ user: User?) {
guard let user = user else { return }
guard user.isActive else { return }
// Process user
}
// Use trailing closures
numbers.map { $0 * 2 } // Good
numbers.map({ $0 * 2 }) // Less preferred
// Use computed properties for simple calculations
struct Circle {
var radius: Double
var area: Double { // Good
return .pi * radius * radius
}
func getArea() -> Double { // Less preferred
return .pi * radius * radius
}
}
Performance
// Use lazy properties for expensive computations
class DataProcessor {
lazy var expensiveData: [String] = {
// Expensive computation
return processLargeDataSet()
}()
private func processLargeDataSet() -> [String] {
// Implementation
return []
}
}
// Use value types when possible
struct Point { // Good - value type
var x: Double
var y: Double
}
class PointClass { // Use only when reference semantics needed
var x: Double
var y: Double
init(x: Double, y: Double) {
self.x = x
self.y = y
}
}
// Use copy-on-write for large value types
struct LargeDataStructure {
private var _data: NSMutableArray
private var data: NSMutableArray {
mutating get {
if !isKnownUniquelyReferenced(&_data) {
_data = _data.mutableCopy() as! NSMutableArray
}
return _data
}
}
init() {
_data = NSMutableArray()
}
}
Error Handling
// Use specific error types
enum ValidationError: Error {
case emptyInput
case invalidFormat
case tooShort(minimumLength: Int)
case tooLong(maximumLength: Int)
}
// Provide meaningful error messages
extension ValidationError: LocalizedError {
var errorDescription: String? {
switch self {
case .emptyInput:
return "Input cannot be empty"
case .invalidFormat:
return "Input format is invalid"
case .tooShort(let minimumLength):
return "Input must be at least \(minimumLength) characters"
case .tooLong(let maximumLength):
return "Input cannot exceed \(maximumLength) characters"
}
}
}
// Use Result type for better error handling
func validateInput(_ input: String) -> Result<String, ValidationError> {
guard !input.isEmpty else {
return .failure(.emptyInput)
}
guard input.count >= 3 else {
return .failure(.tooShort(minimumLength: 3))
}
return .success(input)
}
Memory Management
// Use weak references to avoid retain cycles
class Parent {
var children: [Child] = []
func addChild(_ child: Child) {
child.parent = self
children.append(child)
}
}
class Child {
weak var parent: Parent?
}
// Use capture lists in closures
class ViewController {
var completion: (() -> Void)?
func setupCompletion() {
completion = { [weak self] in
self?.dismiss()
}
}
func dismiss() {
// Implementation
}
}
// Use unowned when reference will never be nil
class Customer {
let name: String
var card: CreditCard?
init(name: String) {
self.name = name
}
}
class CreditCard {
let number: UInt64
unowned let customer: Customer
init(number: UInt64, customer: Customer) {
self.number = number
self.customer = customer
}
}
요약
Swift는 안전성, 성능, 표현력을 위해 설계된 강력하고 현대적인 프로그래밍 언어입니다. 주요 특징은 다음과 같습니다:
- 타입 안전성: 강력한 타입 시스템이 많은 일반적인 프로그래밍 오류를 방지합니다
- 메모리 안전성: 자동 참조 카운팅(ARC)이 메모리를 자동으로 관리합니다
- 성능: 속도 최적화를 위해 컴파일되는 언어
- 표현력: 배우고 유지보수하기 쉬운 깔끔하고 읽기 쉬운 구문
- 상호운용성: Objective-C 및 C 라이브러리와의 원활한 통합
- 현대적 기능: 옵셔널, 제네릭, 클로저, 프로토콜 지향 프로그래밍
- 동시성: 안전한 동시 프로그래밍을 위한 내장 async/await 및 액터 모델
- 오픈 소스: Apple 생태계를 넘어 여러 플랫폼에서 사용 가능
Swift는 컴파일 언어의 성능과 효율성을 인기 있는 스크립팅 언어의 단순성과 상호작용성과 결합하여, iOS, macOS, watchOS, tvOS, 서버 측 개발에 탁월한 선택이 됩니다.