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
93: NavigationStack Basics
Wait, do I just wrap my whole view in a NavigationStack?
Pretty much. Think of the NavigationStack as the manager of your app's visual history. It keeps track of where the user is and where they've been, allowing them to "push" a new view onto the stack and "pop" back to the previous one using that standard back button. If you don't wrap your root view in one, your NavigationLinks simply won't do anything—they'll just sit there like dead buttons.
I usually put the NavigationStack at the very top level of my main view. You only need one for a particular navigation flow. Don't nest them inside each other, or you'll end up with a messy UI where you have multiple navigation bars fighting for space.
struct LibraryView: View {
var body: some View {
NavigationStack {
List {
Text("The Great Gatsby")
Text("1984")
Text("The Hobbit")
}
.navigationTitle("My Library")
}
}
}
How do I actually move from the list to a detail screen?
The most common way is using a NavigationLink. In the old days of SwiftUI, we used to put the destination view directly inside the link, but that's inefficient because SwiftUI would initialize all those destination views immediately, even if the user never clicked them. Now, we use value-based navigation.
You tell the link: "When this is clicked, send this specific piece of data into the stack." Then, you use a modifier to tell the stack how to handle that data. It's a much cleaner separation of concerns.
struct Book: Hashable {
let title: String
let author: String
}
struct LibraryView: View {
let books = [
Book(title: "The Great Gatsby", author: "F. Scott Fitzgerald"),
Book(title: "1984", author: "George Orwell")
]
var body: some View {
NavigationStack {
List(books, id: \.title) { book in
// We pass the 'book' object as the value
NavigationLink(book.title, value: book)
}
.navigationDestination(for: Book.self) { book in
// This is where the actual transition happens
BookDetailView(book: book)
}
.navigationTitle("My Library")
}
}
}
Why do I have to make my data model Hashable?
You probably noticed I added Hashable to the Book struct. This is a requirement for the modern NavigationStack. Because the stack tracks the path of views as a collection of data, it needs a way to uniquely identify each piece of data to know exactly which view to push or pop.
If you're using a simple String or Int, you're already covered because those are Hashable by default. But for your own custom structs, just add the protocol. It's a tiny bit of boilerplate that saves you from a lot of runtime headaches. I've seen plenty of beginners get stuck here, wondering why their navigationDestination isn't firing—9 times out of 10, it's because the data type isn't Hashable.
struct BookDetailView: View {
let book: Book
var body: some View {
VStack(spacing: 20) {
Text(book.title)
.font(.largeTitle)
Text("Written by \(book.author)")
}
.navigationTitle("Details")
}
}📋 Practical Task
Build a City Travel Guide Navigator
Create a simple app that allows a user to browse a list of cities and view a description for each. Follow these requirements:
- Create a
Citystruct that conforms toHashable, containing anameand adescription. - Set up a
NavigationStackcontaining aListof at least three cities. - Use
NavigationLink(value:)to pass theCityobject when a row is tapped. - Implement a
.navigationDestination(for: City.self)modifier that navigates to aCityDetailView. - The
CityDetailViewshould display the city's name as a title and its description in a text block.
There are no comments for now.