Server side Swift for Laravel developers part 2
Building a simple CRUD-app in Vapor 2
Server side Swift for Laravel developers part 1
Server side Swift for Laravel developers part 3
If you have never programmed in Swift, there are something you need to know. Apple has designed Swift to be a static type-safe language with static dispatch. There cannot be any loose ends at compile time; everything must add up. Why is this? Apple’s motifs for this is performance, low memory usage, and stability; bugs should be caught at compilation and not in runtime. By defaulting to making structs and immutable variables is one way for example.
Swift 3 has all the bells and whistles you would expect from modern languages with influences from Rust, Haskell, Ruby, Python and C#. With Swift 2.0 Apple also introduced Protocol-Oriented Programming in replacement of Object Oriented programming. There are a lot of resources that can explain this better than I can.
The nature of the web is very stringy and combine this with Swift’s strict typing, and you have a bit of a problem. Vapor tries to resolve this with a package called Node to help and overcome this issue. Swift also misses multi line strings, which is planned to Swift 4. There are some quirks that you are not used to when doing PHP.
Some operation demands a lot more of boiler plate code than you are maybe used to. Swift features a very limited runtime and has no meta-programming features, which leads our projects to contain more boilerplate code. One solution to this could be the tool like Sourcery. Unfortunately there is no such tool yet. I think this is only beginning.
Create a model
Create a file in Model/Station.swift
and create a class that extends Model like this
import Vapor
import MySQLProvider final class Station: Model {
// The storage property is there to allow Fluent to store extra information on your model--things like the model's database id. let storage = Storage()
var id: Node?
var name: String
var description: String
var country: String
var stream: String // Just static variables holding the names
// to prevent typos static let nameKey = "name" static
static let descriptionKey = "description"
static let countryKey = "country"
static let streamKey = "stream" init(name: String, description: String, country: String, stream: String) {
self.id = nil
self.name = name
self.description = description
self.country = country
self.stream = stream
} // The Row struct represents a database row. Your models should be able to parse from and serialize to database rows. Here's the code for parsing the Stations from the database. init(row: Row) throws {
id = try row.get(Station.idKey)
name = try row.get(Station.nameKey)
description = try row.get(Station.descriptionKey)
country = try row.get(Station.countryKey)
stream = try row.get(Station.streamKey)
} // Init model from a Node-structure
init(node: Node) throws {
name = try node.get(Station.nameKey)
description = try node.get(Station.descriptionKey)
country = try node.get(Station.countryKey)
stream = try node.get(Station.streamKey)
} func makeRow() throws -> Row {
var row = Row()
try row.set(Station.nameKey, name)
try row.set(Station.descriptionKey, description)
try row.set(Station.countryKey, country)
try row.set(Station.streamKey, stream)
return row
}
}
We need to show Vapor how to save it back into the database. We add a method called makeNode()
with instructions. Just to make it more clear we create an extension that conforms to the NodePrespesentable protocol. Extensions can add new functionality to an existing class, structure, enumeration, or protocol type. Interfaces vs Inheritance in Swift
extension Station: NodeRepresentable {
func makeNode(in context: Context?) throws -> Node {
var node = Node(context)
try node.set(Station.idKey, id?.int)
try node.set(Station.nameKey, name)
try node.set(Station.descriptionKey, description)
try node.set(Station.countryKey, country)
try node.set(Station.streamKey, stream) return node
}
}
The cool thing is that protocols can be adopted by classes, structs, and enums. Base classes and inheritance are restricted to class types. You can decorate with default behaviors from multiple protocols. Unlike multiple inheritances of classes which some programming languages support, protocol extensions do not introduce any additional state.
Finally, we have to make a migration, called preparation in Vapor for the model.
extension Station: Preparation {
static func prepare(_ database: Database) throws {
try database.create(self) {
builder in builder.id()
builder.string(Station.nameKey)
builder.string(Station.descriptionKey)
builder.string(Station.countryKey)
builder.string(Station.streamKey) } } static func revert(_ database: Database) throws {
try database.delete(self)
}
}
Next, we are going to add MySQL and Leaf-providers to the droplet, as you would do in Laravel
Source/App/Config+Setup.swift
import LeafProvider
import MySQLProvider extension Config {
public func setup() throws {
// allow fuzzy conversions for these types
// (add your own types here)
Node.fuzzy = [JSON.self, Node.self]
try setupProviders()
} /// Configure providers private func setupProviders() throws {
try addProvider(LeafProvider.Provider.self)
try addProvider(MySQLProvider.Provider.self) // Run migrations aka preparations
preparations.append(Station.self)
}
}
Open Sources/App/Routes.swift
and add a route.
builder.get {
req in return try Station.makeQuery().all().makeJSON()
}
Manually add some records in the database. Vapor does not have any handy seeding functions.
Press the Run-button in Xcode
Visit http://localhost:8080 to see the JSON output
Originally published at eastwest.se.