
This video is only available to subscribers. Start a subscription today to get access to this and 472 other videos.
Refactoring to Protocols
This episode is part of a series: Server-side Swift with Vapor.
1. Getting Started with Vapor 12 min |
2. Vapor Routing 14 min |
3. Leaf Templates 13 min |
4. Nesting Templates and Partials 8 min |
5. Vapor Demo: Tokenizr 21 min |
6. Setting up a Database with Fluent 19 min |
7. Creating, Updating, and Deleting Records with Fluent 19 min |
8. Vapor Futures 20 min |
9. Setting up Vapor with Postgresql 17 min |
10. UUID Primary Keys 13 min |
11. Timestamp Fields 4 min |
12. Refactoring to Protocols 12 min |
13. Parent Child Relationships and Foreign Keys 14 min |
14. Pivot Tables for Many to Many Relationships 14 min |
15. Vapor Controllers 15 min |
16. Decoding Request Parameters 9 min |
Creating an Issue Model
Let's create a new model for storing Issues for our project:
final class Issue : Model {
// A bunch of boilerplate copied from Project
static var name: String = "issues"
enum Status : String, PostgreSQLEnum {
case open
case closed
case wontFix
}
var id: UUID?
var createdAt: Date?
var updatedAt: Date?
var subject: String
var body: String
var status: Status = .open
}
Having to copy & paste a bunch of code to make our models consistent isn't ideal. Let's extract some of this logic so we can reuse it.
Extracting a Protocol For UUID Primary Keys
protocol UUIDModel : Model, PostgreSQLTable where Self.ID == UUID, Self.Database == PostgreSQLDatabase {
var id: UUID? { get set }
}
extension UUIDModel {
static var idKey: IDKey { return \.id }
}
Here you can see we're defining our primary key property and also declaring that this uses a Postgres database.
Extracting the UUID migration logic into a helper
Most of the customized logic is actually when we run our migrations. If we extract this into a protocol on any type that implements UUIDModel
, we can easily reuse this in other models.
extension SchemaCreator where Model : UUIDModel {
func uuidPrimaryKey() {
let pk = PostgreSQLColumnConstraint.primaryKey(default: nil, identifier: nil)
let defaultUUID = PostgreSQLColumnConstraint.default(.function("uuid_generate_v4"), identifier: nil)
field(for: \.id, type: .uuid, pk, defaultUUID)
}
}
Extracting a Protocol For Timestamps
Next let's look at extracting the timestamp logic.
protocol TimestampModel : Model {
var createdAt: Date? { get set }
var updatedAt: Date? { get set }
}
extension TimestampModel {
static var createdAtKey: TimestampKey? { return \.createdAt }
static var updatedAtKey: TimestampKey? { return \.updatedAt }
}
And again, we can do the same trick above to make the migrations easier:
extension SchemaCreator where Model : TimestampModel {
func timestampFields() {
field(for: \.createdAt)
field(for: \.updatedAt)
}
}
Cleaning up our models
If we look at Project.swift
now, it looks a lot cleaner:
final class Project : UUIDModel, TimestampModel {
static var name: String = "projects"
var id: UUID?
var title: String
var description: String
var createdAt: Date?
var updatedAt: Date?
init(title: String, description: String) {
self.title = title
self.description = description
}
}
extension Project : Migration {
static func prepare(on conn: PostgreSQLConnection) -> Future<Void> {
return PostgreSQLDatabase.create(self, on: conn) { builder in
builder.uuidPrimaryKey()
builder.field(for: \.title, type: .varchar(500))
builder.field(for: \.description)
builder.timestampFields()
}
}
}
We are also set up to use this on any of our models going forward, keeping things consistent.