Swift
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Optionals
-
Section 4: Object-Oriented and Value Types
-
Section 5: Memory Management
-
Section 6: Generics and Error Handling
-
Section 7: Concurrency
-
Section 8: Working with Collections
-
Section 9: Codable and Data Handling
-
Section 10: Protocol-Oriented Programming
-
Section 11: Testing and Tooling
-
Section 12: Practical Projects
-
Section 13: Interview Practice
-
Section 14: More Practice Exercises
-
Section 15: More Standard Library
-
Section 16: Advanced Concurrency
-
Section 17: Foundation Framework Deep Dive
-
Section 18: URLSession and Networking Deep Dive
-
Section 19: Combine Framework
-
Section 20: SwiftUI Fundamentals for Swift Developers
-
Section 21: Server-Side Swift with Vapor
-
Section 22: Swift Package Manager Deep Dive
-
Section 23: Swift Concurrency Deep Dive
-
Section 24: More Language Features
-
Section 25: Error Handling Deep Dive
-
Section 26: Testing Deep Dive
-
Section 27: Data Structures and Algorithms in Swift
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Swift Macros (Swift 5.9+)
-
Section 31: Property Wrappers Ecosystem
-
Section 32: Swift Interop Deep Dive
-
Section 33: iOS App Architecture Patterns
-
Section 34: Performance and Debugging
-
Section 35: App Distribution and CI/CD
-
Section 36: More Practical Projects
-
Section 37: SwiftData and Persistence
-
Section 38: More Design Patterns
-
Section 39: More Review and Practice
-
Section 40: More Foundation Deep Dive
-
Section 41: Advanced Collections in Swift
-
Section 42: Advanced Generics Practice
-
Section 43: UIKit for Legacy and Hybrid Apps
-
Section 44: watchOS and visionOS Development Basics
-
Section 45: More Networking Patterns
-
Section 46: More Testing Practice
-
Section 47: Accessibility in Swift Apps
-
Section 48: Localization
-
Section 49: More Practical Projects Round 2
-
Section 50: Swift Charts Framework
-
Section 51: More Interview and Algorithm Practice
-
Section 52: Final Practice and Mastery
-
Section 53: Swift Compiler and Build System
-
Section 54: More Concurrency Practice
-
Section 55: App Store Guidelines and Review
-
Section 56: More Design and Architecture
26: Generic Functions and Types
Why can't I just use "Any" if I want a function to handle different types?
This is the first thing most people ask when they hit generics. I get it—Any seems like the shortcut. But here is the problem: Any throws away all your type information. If you put an Int into a list of Any, Swift forgets it was an Int. When you pull it back out, you have to manually cast it using as? Int, which is tedious and error-prone.
Generics are different. They aren't about "accepting anything"; they're about "placeholder types." When you use a generic, you're telling Swift: "I don't know what the type is yet, but whatever it is, it'll be the same type throughout this whole operation."
// The "Any" way (Clunky and unsafe)
func printFirst(items: [Any]) {
if let first = items.first as? String {
print("It's a string: \(first)")
}
}
// The Generic way (Clean and type-safe)
func printFirst<T>(items: [T]) {
if let first = items.first {
print("The first item is \(first)")
// Swift knows 'first' is of type T, no casting needed!
}
}
How do I actually build a generic type, like a custom container?
You've seen generic functions, but the real power comes when you apply this to structs or classes. Let's say you're building a Stack. Whether you're stacking integers, strings, or complex User objects, the logic for push and pop is identical. Why write three different versions?
By adding <T> (or any letter, though T for "Type" is the convention) after the struct name, you make the entire structure generic.
struct Stack<Element> {
var items: [Element] = []
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element? {
return items.popLast()
}
}
// Now I can make a stack for any type
var intStack = Stack<Int>()
intStack.push(10)
var nameStack = Stack<String>()
nameStack.push("Swift")
I personally prefer naming the placeholder Element or Value rather than T when I'm writing structs; it makes the code much more readable for whoever has to maintain it six months from now.
What if I need the generic type to actually be able to do something?
Here is where you'll hit a wall: if you use a generic T, Swift assumes it knows nothing about that type. You can't use +, >, or any specific methods because Swift can't guarantee that every possible type in the universe supports them. If you try to compare two T values, the compiler will yell at you.
To fix this, we use Type Constraints. You tell Swift, "T can be anything, as long as it conforms to this specific protocol." For example, if you want to find the largest item in an array, T must be Comparable.
func findMax<T: Comparable>(items: [T]) -> T? {
var maxItem: T?
for item in items {
if maxItem == nil || item > maxItem! {
maxItem = item
}
}
return maxItem
}
let numbers = [1, 5, 3, 2]
print(findMax(items: numbers)!) // Works! Int is Comparable.
let strings = ["Apple", "Zebra", "Banana"]
print(findMax(items: strings)!) // Works! String is Comparable.
If you tried to pass an array of a custom User struct into this function, it would fail to compile—unless you made your User struct conform to Comparable. This is the "secret sauce" of Swift's type system: flexibility without sacrificing safety.
📋 Practical Task
Build a Generic DataCache
Your task is to create a generic caching system that can store a single value of any type, associated with a string key. This will simulate a simple settings or session cache.
- Create a struct named
DataCachethat is generic over a typeValue. - Inside the struct, create a dictionary called
storagewhere the keys areStringand the values are of typeValue. - Implement a method
save(key: String, value: Value)that adds the item to the dictionary. - Implement a method
retrieve(key: String) -> Value?that returns the cached value.
To test your implementation, instantiate one DataCache for Int to store a "userAge" and another DataCache for String to store a "username". Ensure that you cannot accidentally save a string into the integer cache.
There are no comments for now.