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
95: Vapor Routing Basics
Routing is really the heart of any web API. In Vapor, it's how we tell the server: "When a request comes in at this specific URL using this specific HTTP method, run this specific piece of Swift code." It's essentially a giant switch statement for the internet.
To get a feel for this, let's build a tiny API for a personal book collection. We aren't worrying about a database yet; we'll just use a hardcoded array of strings to keep things moving.
Creating the main catalog list
I always start with the simplest "GET" request. I want a route where, if I visit /books, I get a list of everything I own. In Vapor, we use the app instance to define these routes.
app.get("books") { req -> String in
let myBooks = ["The Hobbit", "Neuromancer", "Foundation"]
return myBooks.joined(separator: ", ")
}
Notice that I'm returning a String. Vapor is smart enough to know that if you return a string, it should wrap that in an HTTP 200 OK response with a text/plain content type. It's a very clean way to handle simple responses.
Fetching a specific book by ID
Now, returning a whole list is fine, but usually, we want a specific item. This is where dynamic routing comes in. We use a colon (:) to tell Vapor that a segment of the URL is actually a variable parameter, not a literal string.
app.get("books", ":bookID") { req -> String in
let bookID = req.parameters.get("bookID") ?? "unknown"
return "You are looking for book number \(bookID)"
}
By adding :bookID, any request to /books/1 or /books/42 will hit this handler. I'm using req.parameters.get to pull that value out of the URL.
Fixing a parameter mismatch
Here is where I usually trip up when I'm moving quickly. I once spent ten minutes wondering why my route was returning "unknown" every single time, even though the URL looked perfect. I'd written this:
// The Mistake
app.get("books", ":bookID") { req in
let id = req.parameters.get("id") // I called it "id" here...
return "Book \(id ?? "unknown")"
}
The problem? The string I used in req.parameters.get("id") didn't match the name I gave the parameter in the route definition (:bookID). Vapor doesn't know they are the same thing; it's looking for a key named "id" that doesn't exist in the request parameters. I had to go back and make sure both were exactly "bookID". Always double-check those strings.
Handling a "New Arrival" with POST
GET is for reading, but if we want to add a book to our collection, we need a POST route. This is structurally very similar to our GET routes, but it signals to the client that we are creating something new on the server.
app.post("books") { req -> String in
// In a real app, we'd decode a JSON body here.
// For now, we'll just simulate a success.
return "Book added to the collection!"
}
Now we have a basic set of routes: one to list books, one to find a specific one, and one to add a new one. That's the foundation of almost every REST API you'll ever encounter.
📋 Practical Task
Building a Book Category Filter
Your task is to expand the book API. Create a new GET route that allows a user to filter books by category. The URL should follow the pattern /books/category/:categoryName (for example, /books/category/sci-fi).
The route should:
- Capture the
categoryNameparameter from the URL. - Return a string that says: "Listing all books in the [categoryName] category."
- Ensure the parameter name used in
req.parameters.get()matches the one defined in the route path exactly.
There are no comments for now.