Server Side Swift with Vapor - Part 2: Hello World | Ray Wenderlich

Hi Tim,

Is there a way that we can install the stable Vapor (eg Vapor 2) and install beta Vapor 3 at the same time and easily do a switch if needed?

Thanks!

@rubiriffic when you say install Vapor do you mean that framework that powers your application or the toolbox that you run with the vapor command from the command line?

Iā€™m guessing the framework given there isnā€™t a beta of the toolbox at the moment and the answer is yes! In a different application. You can have two different applications running two different versions of the framework. To start a Vapor 2 project just run vapor new MyVapor2Project - just be warned that Vapor 2 is very different to Vapor 3. Hope this helps!

@0xtim is it possible to deploy Vapor 3 apps in production when it is still in beta? Can you include in your lesson, deploy to Digital Ocean too? Thanks.

@rubiriffic yes definitely should be, though there is a nasty compiler bug that is causing issues building in release mode in some circumstances that may cause issues. Unfortunately there wasnā€™t time to cover deployment in the videos but there are several chapters dedicated to the different options in the book!

1 Like

@0xtim what if you have two parameters that are dynamic? How do you write it in code?

router.get(String.parameter, ???) { req -> String in
    let parameter1 = try req.parameter(String.self)
    let parameter2 = try req.parameter(???)
    return "\(parameter1) \(parameter2)"
}

req.parameter will get the next parameter that matches that type. So If you have two strings, you can do:

router.get(String.parameter, String.parameter) { req -> String in
    let parameter1 = try req.parameter(String.self)
    let parameter2 = try req.parameter(String.self)
    return "\(parameter1) \(parameter2)"
}

Note that this could cause routing issues though it you have generic everything and then register a route at /something/else - you may not get the result you expect

I tried that initially @0xtim but I am not able to pinpoint the strings specifically. Example:
If I decided to use the second parameter only, so I will comment out the first parameter:

router.get(String.parameter, String.parameter) { req -> String in
    //let parameter1 = try req.parameter(String.self)
    let parameter2 = try req.parameter(String.self)
    return "Hello \(parameter2)"
}

Result will be the first parameter instead of the intended second parameter.

Isnā€™t there a better way to name and access these parameters?

Ah I see. Well I would argue that if you donā€™t care about the first parameter then it shouldnā€™t really be dynamic - thatā€™s more of a design issue.

Have a look at the routing docs for Vapor 2 - I believe the :name format has been carried over to Vapor 3

https://docs.vapor.codes/2.0/routing/parameters/#type-safe

1 Like

@0xtim Thanks for this documentation link.

I tried following this: (just changed the drop to router)

router.get(":users", ":id") { request in
    guard let username = request.parameters["users"]?.string else {
        throw Abort.badRequest
    }

    guard let userId = request.parameters["id"]?.int else {
        throw Abort.badRequest
    }

    return "You requested User: \(username) #\(userId)"
}

and I received the following errors on the variables username and userId

Cannot call value of non-function type ā€˜ParameterContainer.Parametersā€™ (aka ā€˜Arrayā€™)

So I tried using parameter instead of parameters, just like your initial example, and result is:

Error: Argument passed to call that takes no arguments

I think the only solution is to conform the parameter to Parameterizable first, then set it as a parameter. Is this the best way? Thanks.

Note: Iā€™m just trying to experiment if I can create two dynamic parameters (with same Type) and access the parameter.

Thanks for posting this! I wasnā€™t looking to use two dynamic parameters but it helped me solve my issues at 7:43 in the video:

get("hello", ":name") {req in
  let name = req.parameters["name"]?.string
  return "Hello \(name!)!"
}

I was curious about your experiment so I then updated the code to this:

  get(":greeting", ":name") {req in
    let greeting = req.parameters["greeting"]?.string
    let name = req.parameters["name"]?.string
    return "\(greeting!) \(name!)!"
  }

Which worked for me. Iā€™m not sure if Iā€™m on a different Vapor than you, Iā€™m on 3.1.4

1 Like

I am getting the following error

Unexpected error: :warning: [TCP.TCPError.bind: Address already in use] [/Users/mergner/Desktop/Vapor/HelloVapor/.build/checkouts/sockets.git-972942978533733666/Sources/TCP/Socket/TCPServer.swift:122:33] .
Program ended with exit code: 0

Scanning my machine with a network utility I can see that there is no other usage to port 8080, my /etc/host has an entry for localhost

@0xtim Can you please help with this when you get a chance? Thank you - much appreciated! :]

@ckbrown when you ran vapor new did you do --branch=beta? Looks like your on Vapor 2. Note that there is a difference between the Vapor toolbox and the Vapor framework. You can tell the version of the framework by looking at the Package.swift

Let me have an experiment and get back to you

@hjm something will be listening on :thinking: otherwise add this to you configure.swift and choose a different port number

I am working on the example of this screencast, using Safari 11.0.3, Chrome 64.0.3282.167 , Firefox 58.0.2 as well was Rested 2.7 and Paw 3.1.5 as clients.
When stopping the server with command-. from within Xcode and restarting the server after a small modification then I 'll get this message pretty often. Waiting for some time (finishing CLOSE_WAIT ? ) enables me to start again, so my assumption is that there might be an issue with cleanup of the sockets when stopping the server with command-. from within Xcode.
Using another port is of course a painful workaround as I have a couple of test scripts for PAW and Rested

Others are raising this on Slack - seems like the port isnā€™t being closed quick enough before the new binary tries to connect. Itā€™s being investigated I imagine, but try stopping before running the app if you see the issue

@ckbrown thanks for trying it out.

Iā€™m on 3.1.4

But iā€™m still having error with your code:

Cannot subscript a value of type ā€˜ParameterContainer.Parametersā€™ (aka ā€˜Arrayā€™) with an index of type ā€˜Stringā€™

on both the greeting and name variable.

@rubiriffic what does your Package.swift say - thatā€™s what points to the version of the framework you are using.

The toolbox is 3.1.4, there is lotā€™s of confusion around this! Iā€™ll try and add a clarification in one of the videos when I re-record them. The snippet that @ckbrown posted looks like Vapor 2