Server side Swift for Laravel developers part 3
Building a simple CRUD-app in Swift with Vapor 2
Server side Swift for Laravel developers 1
Server side Swift for Laravel developers 2
Create views
Unfortunately, Xcode does not render Leaf-templates correctly. You could manually set the type to HTML, but this will revert every time you build. Instead, I went back to good old Sublime for editing templates. First install Leaf — Packages — Package Control
Open Resources/Views/base.leaf
and add Bootstrap 4 in the header. We cannot hurt our eyes with unsettled content :-)
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css">
As you can see Leaf works very similar to Laravel and Blade.
Create Resources/Views/index.leaf
and add a simple index-page
#extend("base") #export("title") { Stations } #export("content") { <table class="table">
<tr>
<th>ID</th>
<th>Name</th>
<th>Country</th>
<th>Stream</th>
</tr> #loop(stations, "station") {
<tr>
<td><a href="/station/#(station.id)">#(station.id)</a></td>
<td>#(station.name)</td>
<td>#(station.country)</td>
<td>#(station.stream)</td>
</tr>
} </table> <a href="/create" class="btn btn-default">Add a station</a> }
Instead of section, Leaf is using export as markup.
Create Resources/Views/edit.leaf
for creating records
#extend("base") #export("title") { Stations }#export("content") { <form class="form" action="/station/#(station.id)" method="post">
<div class="form-group">
<label for="name">Name</label>
<input class="form-control" type="text" name="name" value="#(station.name)">
</div>
<div class="form-group">
<label for="description">Description</label>
<textarea class="form-control" name="description" id="description" cols="30" rows="10">#(station.description)</textarea>
</div>
<div class="form-group">
<label for="country">Country</label>
<input class="form-control" type="text" name="country" value="#(station.country)">
</div>
<div class="form-group">
<label for="stream">Stream URL</label>
<input class="form-control" type="text" name="stream" value="#(station.stream)">
</div>
<button type="submit" class="btn btn-primary">Save</button> <a href="/" class="btn btn-default">Back</a>
</form> }
Connect the dots
Like Laravel, you can create controllers and RESTful resources. But for this tutorial, we will just use the Routes.swift
and doing all operations directly.
First make an index view, should not be any surprises if you are used to Laravel.
builder.get { req in
// Get all stations
let stations = try Station.makeQuery().all()
return try self.view.make("index", [ "stations": stations ])
} builder.get("station", ":id") { req in
// Make sure the request contains an id
guard let stationId = req.parameters["id"]?.int else {
throw Abort.badRequest }
let station = try Station.makeQuery().find(stationId) return
try self.view.make("edit", [ "station": station ])
} builder.get("station", "create") { req in
return try self.view.make("edit")
} builder.post("station") { req in
guard let form = req.formURLEncoded else {
throw Abort.badRequest }
let station = try Station(node: form)
try station.save() return Response(redirect: "/")
}
Finally, we are adding a route for updating an existing record. As mentioned earlier, Swift is strict and just refresh the model would require a lot of checks. By going via Vapor’s Node package and create a new model and assigning it back to the original record was the easiest way I found. If you have better solutions, feel free letting me know.
builder.post("station", ":id") { req in // Make sure it's a form posted
guard let form = req.formURLEncoded else {
throw Abort.badRequest } // Make sure the request contains an id
guard let stationId = req.parameters["id"]?.int else {
throw Abort.badRequest } guard let station = try Station.makeQuery().find(stationId) else { throw Abort.notFound } // Use Vapor's node functions to create a new entity
let newStation = try Station(node: form) // Assign the new values back to the old
station.country = newStation.country
station.name = newStation.name station.description = newStation.description //
...and save
try station.save() return Response(redirect: "/")
}
Debugging in Xcode
A very nice feature using Xcode is that you get all the debugging features that you would expect from an IDE.
Try putting a breakpoint on the route for getting a station, and you can inspect the results.
Conclusions
Overall Vapor was a delightful surprise that it feels very Laravel-ish, I am sure the developers of Vapor have looked a lot a Laravel. For me using the same language for the backend and your apps will be a deal maker for many developers, especially if you are using Vapor as a RESTful backend. Vapor feels super snappy, and the performance is incredible, and the memory footprint is very low.
Swift in its nature of being strictly type hinted makes some operations like models to have a lot of boiler plate. This could be resolved by a code generator like GitHub — krzysztofzablocki/Sourcery: Meta-programming for Swift, stop writing boilerplate code. Unfortunately, there is no such tool creating Vapor models here yet.
Update 170810 — Swift 4 will have the new codable protocol which will make parsing JSON much easier with less boilerplate code.
All of your edits also demands that everything is re-compiled. Apple has made tremendous efforts to make this more speedy. Obviously, it’s not so easy as save your changes and hit reload in the browser.
Will I change? If I am about to build a JSON backend for an iOS-app, I will most likely look into Vapor. In some parts, I could even reuse the same code between the iOS-app and the backend. For me building a SAAS with a lot of party in the front end I would for sure stay with Laravel and maybe use Laravel Spark, because of the more mature tooling like components, seedings, Mix, Vue.js and so forth.
Originally published at eastwest.se.