swiftvaporleaf

Swift Vapor - 'Cannot invoke 'add' with an argument list of type...' errors in configure.swift


I am doing an e-commerce tutorial from Paul Hudson on YouTube. I am getting following errors in configure.swift:

import FluentSQLite
import Vapor
import Leaf // added
/// Called before your application initializes.
public func configure(_ config: inout Config, _ env: inout Environment, _ services: inout Services) throws {
    // Register providers first
    try services.register(FluentSQLiteProvider())

    // Register routes to the router
    let router = EngineRouter.default()
    try routes(router)
    services.register(router, as: Router.self)

    // Register middleware
    var middlewares = MiddlewareConfig() // Create _empty_ middleware config
    // middlewares.use(FileMiddleware.self) // Serves files from `Public/` directory
    middlewares.use(ErrorMiddleware.self) // Catches errors and converts to HTTP response
    services.register(middlewares)

    // Configure a SQLite database
    let sqlite = try SQLiteDatabase(storage: .memory)
    // Register the configured SQLite database to the database config.
    var databases = DatabasesConfig()
    databases.add(database: sqlite, as: .sqlite)
    services.register(databases)

    // Configure migrations
    var migrations = MigrationConfig()
    migrations.add(model: Todo.self, database: .sqlite)
    services.register(migrations)
    let leafProvider = LeafProvider()    // added
    try services.register(leafProvider)  // added
    config.prefer(LeafRenderer.self, for: ViewRenderer.self)// added
      // http://localhost:8080/ already in use so adding new server http://localhost:8080/ below -
    let serverConfigure = NIOServerConfig.default(hostname: "0.0.0.0", port: 9090)
    services.register(serverConfigure)

    let directoryConfig = DirectoryConfig.detect()
    services.register(directoryConfig)
    try services.register(FluentSQLiteProvider())
    var databaseConfig = DatabasesConfig()
    let db =    try SQLiteDatabase(storage: .file(path:   "\(directoryConfig.workDir)pizza.db"))
     databaseConfig.add(model: db, database: as .sqlite)
    /**
    The line just above shows errors - 
    errror 1 -> Cannot invoke 'add' with an argument list of type '(model:      
    SQLiteDatabase)'
    error 2 -> Expected expression in list of expressions 
    */       
    services.register(databaseConfig)
    var migrationConfig =  MigrationConfig()
    migrationConfig.add(model: pizza.self,  database: sqlite)
    /**
    The line just above shows error -> Cannot invoke 'add' with an argument list of type     '(model: pizza.Type, database: SQLiteDatabase)'
    */
    services.register(migrationConfig)
} 
// configure.swift ends here

I am also pasting my Package.swift code for reference -

 // swift-tools-version:4.0

import PackageDescription

let package = Package(
name: "vv",
products: [
    .library(name: "vv", targets: ["App"]),
],
dependencies: [
    // 💧 A server-side Swift web framework.
    .package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"),

    // 🔵 Swift ORM (queries, models, relations, etc) built on SQLite 3.
    .package(url: "https://github.com/vapor/fluent-sqlite.git", from: "3.0.0"),
.package(url: "https://github.com/vapor/leaf.git", from: "3.0.0"),
],
targets: [
    .target(name: "App", dependencies: ["Leaf", "Vapor","FluentSQLite"]),
 .target(name: "Run", dependencies: ["App"]),
    .testTarget(name: "AppTests", dependencies: ["App"])
]
)

pizza.swift file -

 import Foundation
 import Vapor
 import FluentSQLite

 struct pizza: Encodable, Content, Decodable, SQLiteModel {
     var id:  Int?
     var name: String
     var description: String
     var  price: Int
 }

I am unable to figure out what the above errors mean! Thanks.

routes.swift file -

import Routing
import Vapor
import FluentSQLite

public func routes(_ router: Router) throws {
router.get { req -> Future <View> in
let Newyorker = Pizza(id: 5, name: "statinisland", description: "impracticaljokers", price: 55)
    let Traditional = Pizza(id: 5, name: "rome", description: "pope", price: 55)
    return try req.view().render("welcome",["pizza":[Newyorker,Traditional]])
}

 router.post(Pizza.self, at: "add") { req, pizza ->
     Future<Response> in
    return Pizza.save(on:req).map(to:Response.self) {Pizza
        in
        return req.redirect(to: "/")
    }
   }
/*Error - Type 'Pizza' has no member 'save'; did you mean 'name'?
 Replace 'save' with 'name' */  
 }

Solution

  • This is a re-write of my original answer to address all the issues:

    configure.swift

    1. Don't define multiple instances of MigrationConfig() and DatabaseConfig().
    2. Grouping related tasks (e.g. adding models) together will make your code easier to debug and maintain.
    3. You have added two instances of .sqlite databases.
    4. You omitted the . in .sqlite when you were adding the pizza model.

    So, the relevant part of your configure.swift becomes:

    let directoryConfig = DirectoryConfig.detect()
    services.register(directoryConfig)
    let sqlite = try SQLiteDatabase(storage: .file(path:   "\(directoryConfig.workDir)pizza.db"))
    // Register the configured SQLite database to the database config.
    var databases = DatabasesConfig()
    databases.add(database: sqlite, as: .sqlite)
    services.register(databases)
    
    var migrations = MigrationConfig()
    migrations.add(model: Todo.self, database: .sqlite)
    migrations.add(model: Pizza.self,  database: .sqlite)
    services.register(migrations)
    

    pizza.swift:

    1. I prefer using final class rather than a struct as it gives you more flexibility (e.g. in your initialisers).
    2. You need to make it conform to the Migration.

    So, a minimal Pizza model becomes:

    final class Pizza:SQLiteModel {
        var id:  Int?
        var name: String
        var description: String
        var  price: Int
    
        init(id: Int? = nil, name: String, desc:String, price:Int)
        {
            self.id = id
            self.description = desc
            self.name = name
            self.price = price
        }
     }
    
    extension Pizza: Migration { }
    

    For completeness, your configure.swift becomes:

    import FluentSQLite
    import Vapor
    import Leaf // added
    public func configure(_ config: inout Config, _ env: inout Environment, _ services: inout Services) throws {
        // Register providers first
        try services.register(FluentSQLiteProvider())
    
        // Register routes to the router
        let router = EngineRouter.default()
        try routes(router)
        services.register(router, as: Router.self)
    
        // Register middleware
        var middlewares = MiddlewareConfig() // Create _empty_ middleware config
        // middlewares.use(FileMiddleware.self) // Serves files from `Public/` directory
        middlewares.use(ErrorMiddleware.self) // Catches errors and converts to HTTP response
        services.register(middlewares)
    
        let directoryConfig = DirectoryConfig.detect()
        services.register(directoryConfig)
        let sqlite = try SQLiteDatabase(storage: .file(path:"\(directoryConfig.workDir)pizza.db"))
        var databases = DatabasesConfig()
        databases.add(database: sqlite, as: .sqlite)
        services.register(databases)
    
        var migrations = MigrationConfig()
        migrations.add(model: Todo.self, database: .sqlite)
        migrations.add(model: Pizza.self, database: .sqlite)
        services.register(migrations)
    
        let leafProvider = LeafProvider()    // added
        try services.register(leafProvider)  // added
        config.prefer(LeafRenderer.self, for: ViewRenderer.self)// added
        let serverConfigure = NIOServerConfig.default(hostname: "0.0.0.0", port: 9090)
        services.register(serverConfigure)
    }