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

208 Views Asked by At

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' */  
 }
1

There are 1 best solutions below

9
On BEST ANSWER

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)
}