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
96: Fluent ORM for Vapor
Alright, so we've got our Vapor routes working, but everything is currently living in memory. The second we restart the server, our data vanishes. We need a real database. I've always preferred Fluent because it lets us think in Swift objects rather than writing raw SQL strings, which is just asking for a syntax error at 3 AM.
I want to build a simple "Reading List" app. I'll start by trying to create a Book model. My first instinct is just to make a standard Swift class, but let's see if that actually works with Fluent.
The "Just a Class" Fail
class Book {
var title: String
var author: String
init(title: String, author: String) {
self.title = title
self.author = author
}
}
If I try to call book.save(on: database) now, the compiler is going to scream at me. Why? Because Book is just a plain object. Fluent has no idea how this maps to a database table. It doesn't know which property is the primary key or what the column names should be. To fix this, I need to conform to Model.
Mapping Objects to Tables
I'll adjust the model. I need to use the @ID and @Field property wrappers. These are the "magic" parts of Fluent that handle the translation between Swift and SQL.
import Fluent
import Vapor
final class Book: Model {
static let schema = "books" // This is the actual table name in SQL
@ID(key: .id)
var id: UUID?
@Field(key: "title")
var title: String
@Field(key: "author")
var author: String
init() { } // Fluent needs an empty init for decoding from the DB
init(id: UUID? = nil, title: String, author: String) {
self.id = id
self.title = title
self.author = author
}
}
Now the compiler is happy, but if I run the app and try to save a book, I'll get a runtime error saying the table books doesn't exist. I forgot that databases are rigid. I can't just define a class in Swift and expect the database to magically create the table. I need a Migration.
Teaching the Database the Schema
I'll create a migration file. Think of this as a version-control script for your database schema. I'm telling Fluent: "Hey, create a table called 'books' with these specific columns."
struct CreateBook: Migration {
func prepare(on database: Database) -> EventLoopFuture<Void> {
database.schema("books")
.id()
.field("title", .string, .required)
.field("author", .string, .required)
.create()
}
func revert(on database: Database) -> EventLoopFuture<Void> {
database.schema("books").delete()
}
}
I've got the migration, but it's just sitting there. I need to actually tell Vapor to run it. I'll head over to configure.swift and add it to the app's migrations list. I'll also make sure I've configured a database driver—I'll use SQLite for now because it's just a file on disk and doesn't require a separate server setup.
app.databases.use(.sqlite(.file("db.sqlite")), as: .sqlite)
app.migrations.add(CreateBook())
Testing the Persistence
Now for the moment of truth. Let's create a route that actually saves a book and then fetches it back. I'll use a simple POST request to save and a GET to verify.
app.post("books") { req -> EventLoopFuture<Book> in
let book = try req.content.decode(Book.self)
return book.save(on: req.db).map { book }
}
app.get("books") { req -> EventLoopFuture<[Book]> in
return Book.query(on: req.db).all()
}
I'll fire off a request with {"title": "The Swift Programming Language", "author": "Apple"}. It works! The data is now sitting in db.sqlite. If I stop the server and start it again, the books are still there. That's the power of the ORM—I'm interacting with Swift types, and Fluent is handling the heavy lifting of translating those into SQL queries behind the scenes.
📋 Practical Task
Exercise: Implementing a Reading List Category System
Currently, our Book model only has a title and an author. To make this a real application, we need to categorize our books (e.g., "Technical", "Fiction", "Biography").
Your Task:
- Modify the
Bookmodel to include a new@Fieldcalledcategory(a String). - Create a new
MigrationcalledAddCategoryToBookthat updates the existingbookstable to add thecategorycolumn. Note: Do not delete the first migration; create a new one to evolve the schema. - Register the new migration in
configure.swift. - Update your
POSTroute to accept the category in the request body and save it to the database. - Verify the change by saving a book with a category and retrieving it via the
GETroute.
There are no comments for now.