Extensions and Generics
## Learning Objectives
- Extend existing types with extensions
- Use protocol delegation
- Master generics basics
- Write generic functions and types
## Extensions
### Adding Functionality
```swift
extension Int {
func squared() -> Int {
return self * self
}
}
let number = 5
print(number.squared()) // 25
```
### Adding Computed Properties
```swift
extension Double {
var kmToMiles: Double {
return self * 0.621371
}
var milesToKm: Double {
return self * 1.60934
}
}
let distance = 10.0
print("\(distance) km = \(distance.kmToMiles) miles")
```
### Adding Initializers
```swift
struct Point {
var x: Int
var y: Int
}
extension Point {
init(xy: Int) {
self.init(x: xy, y: xy)
}
}
let origin = Point(xy: 0) // Point(x: 0, y: 0)
```
### Adding Methods
```swift
extension Array {
func average() -> Double? {
guard !isEmpty else { return nil }
return Double(reduce(0, +)) / Double(count)
}
}
let scores = [90, 85, 92, 88]
print(scores.average()) // Optional(88.75)
```
## Protocol Conformance
### Extending to Conform
```swift
protocol Printable {
func description() -> String
}
extension Int: Printable {
func description() -> String {
return "Number: \(self)"
}
}
print(42.description()) // "Number: 42"
```
### Adding Protocol Requirements
```swift
extension Array where Element: Equatable {
func contains(_ element: Element) -> Bool {
for item in self {
if item == element {
return true
}
}
return false
}
}
let nums = [1, 2, 3, 4, 5]
print(nums.contains(3)) // true
print(nums.contains(10)) // false
```
## Protocol Delegation
### Defining Protocols
```swift
protocol DataHandlerDelegate: AnyObject {
func didReceiveData(_ data: Data)
func didFailWithError(_ error: Error)
}
class DataHandler {
weak var delegate: DataHandlerDelegate?
func fetchData() {
// ... fetch data
let data = Data()
delegate?.didReceiveData(data)
}
}
```
### Implementing Delegate
```swift
class ViewController: DataHandlerDelegate {
let handler = DataHandler()
init() {
handler.delegate = self
}
func didReceiveData(_ data: Data) {
print("Received \(data.count) bytes")
}
func didFailWithError(_ error: Error) {
print("Error: \(error)")
}
}
```
### Delegate Pattern Benefits
- Loose coupling
- Flexibility
- Multiple delegates possible
- Clear responsibility separation
## Generics
### Why Generics?
```swift
// Without generics - repeated code
func swapInts(_ a: inout Int, _ b: inout Int) {
let temp = a
a = b
b = temp
}
func swapStrings(_ a: inout String, _ b: inout String) {
let temp = a
a = b
b = temp
}
// With generics - one function
func swap(_ a: inout T, _ b: inout T) {
let temp = a
a = b
b = temp
}
var x = 1, y = 2
swap(&x, &y)
var s1 = "Hello", s2 = "World"
swap(&s1, &s2)
```
## Generic Functions
### Syntax
```swift
func identity(_ value: T) -> T {
return value
}
let num = identity(42) // Inferred as Int
let str = identity("hello") // Inferred as String
```
### Multiple Type Parameters
```swift
func combine(_ a: A, _ b: B) -> (A, B) {
return (a, b)
}
combine(1, "one") // (1, "one")
```
### Type Constraints
```swift
// Comparable constraint
func maximum(_ a: T, _ b: T) -> T {
return a > b ? a : b
}
maximum(5, 10) // 10
maximum("apple", "banana") // "banana"
```
## Generic Types
### Generic Struct
```swift
struct Stack {
var items: [Element] = []
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element? {
return items.popLast()
}
func peek() -> Element? {
return items.last
}
}
var intStack = Stack()
intStack.push(1)
intStack.push(2)
print(intStack.pop()) // Optional(2)
var stringStack = Stack()
stringStack.push("Hello")
stringStack.push("World")
```
### Generic Class
```swift
class Queue {
private var items: [Element] = []
func enqueue(_ item: Element) {
items.append(item)
}
func dequeue() -> Element? {
return items.removeFirst()
}
func peek() -> Element? {
return items.first
}
}
```
### Generic Enum
```swift
enum Result {
case success(Value)
case failure(Error)
}
let success: Result = .success(42)
let failure: Result = .failure("Error occurred")
```
## Extension Type Constraints
### Type Constraints Syntax
```swift
func findIndex(of value: T, in array: [T]) -> Int? {
for (index, item) in array.enumerated() {
if item == value {
return index
}
}
return nil
}
```
### Multiple Constraints
```swift
func findString(in collection: T) -> T.Element?
where T.Element == String {
return collection.first(where: { !$0.isEmpty })
}
```
### Protocol Constraints
```swift
func process(values: [T]) -> T? {
return values.filter { $0 > 0 }.max()
}
process(values: [1, -2, 3, 0]) // Optional(3)
```
## Extensions with Generics
### Extending Generic Types
```swift
extension Stack where Element: Comparable {
func sorted() -> [Element] {
return items.sorted()
}
}
var stack = Stack()
stack.push(3)
stack.push(1)
stack.push(2)
print(stack.sorted()) // [1, 2, 3]
```
### Extension with Self
```swift
extension Numeric {
func squared() -> Self {
return self * self
}
}
let num: Int = 5
print(num.squared()) // 25
let dbl: Double = 3.0
print(dbl.squared()) // 9.0
```
## Associated Types
### Protocol with Associated Type
```swift
protocol Container {
associatedtype Item
mutating func append(_ item: Item)
var count: Int { get }
subscript(i: Int) -> Item { get }
}
```
### Generic Struct Conforming
```swift
struct IntContainer: Container {
private var items: [Int] = []
mutating func append(_ item: Int) {
items.append(item)
}
var count: Int { items.count }
subscript(i: Int) -> Int {
return items[i]
}
}
```
## Opaque Types
### some Keyword
```swift
protocol Shape {
func draw()
}
struct Circle: Shape {
func draw() { print("Circle") }
}
struct Square: Shape {
func draw() { print("Square") }
}
func makeShape() -> some Shape {
return Circle()
}
let shape = makeShape()
shape.draw() // Circle
```
### Use Cases
```swift
// SwiftUI uses opaque return types
func makeContentView() -> some View {
Text("Hello")
}
```
## Summary
- Extensions add functionality to existing types
- Computed properties, methods, initializers can be added
- Use `where` clause for conditional conformance
- Delegation separates concerns via protocols
- Generics enable type-safe reusable code
- Type constraints: ``
- Generic functions: `func identity(_ x: T) -> T`
- Generic types: `struct Stack`
- Associated types for generic protocols
- `some` for opaque return types
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →