Daniil Korotin
Daniil Korotin

Reputation: 730

Database column names in Vapor

I'm getting familiar with Vapor 2.0 server-side Swift framework, and what confuses me is the extensive use of string literals. For example, implementing the Model protocol you have to parse and serialize rows like this (taken from auto-generated example project):

// Initializes the Post from the database row
init(row: Row) throws {
    content = try row.get("content")
}

// Serializes the Post to the database
func makeRow() throws -> Row {
    var row = Row()
    try row.set("content", content)
    return row
}

As you can see, for every property you use its database name twice as a string literal just for this particular protocol. And there's more in other cases — say, Database protocol, your own methods, etc.

Using literal strings as parameters here has obvious disadvantage of static analyser not checking them (just like key-value parameters in Objective-C), making this approach highly error-prone. Is there any best practice I'm missing?

Upvotes: 1

Views: 335

Answers (2)

hhanesand
hhanesand

Reputation: 1000

Nope! You're doing it right. There is little we can do to mitigate this due to the lack of advanced runtime libraries in Swift.

However, in Swift 4, you can use key paths for the same purpose.

\Post.content will create a statically checked keypath for the content variable.

The WWDC session on key paths is definitely worth a watch.

Upvotes: 0

tanner0101
tanner0101

Reputation: 4065

You can easily minimize the number of times you repeat the string by storing it as a static property on the model and referencing that instead.

final class Post: Model {
    // Keys
    static let contentKey = "content"

    // Properties
    var content: String

    // Initializes the Post from the database row
    init(row: Row) throws {
        content = try row.get(Post.contentKey)
    }

    // Serializes the Post to the database
    func makeRow() throws -> Row {
        var row = Row()
        try row.set(Post.contentKey, content)
        return row
    }

    ...
}

extension Post: Preparation {
    static func prepare(_ database: Database) throws {
        try database.create(self) { builder in
            builder.id()
            builder.string(Post.contentKey)
        }
    }

    ...
}

Unfortunately that's probably all you can meaningfully do to make things more type-safe. There's currently no way to give the compiler information about the actual structure of the database (or any other string-y thing, like JSON). It would be really amazing if we could do that, though! Maybe some day.

(I'll look into updating the API and Web templates to include this, track the issue here: https://github.com/vapor/api-template/issues/35)

Upvotes: 1

Related Questions