How to use a slug in Vapor 3 - swift

How would I go about generating a slug for a SQLiteModel object, and using it in Leaf for Vapor 3? My goal is to provide a search engine friendly link to the detail page of my model (for example, /tag/swift/ vs. /tag/0/), rather than link using the id.
I found the Parameter protocol, which means my model has a uniqueSlug property. Unfortunately using it in Leaf #(model.uniqueSlug) doesn't work.
Here's my leaf code:
#for(tag in tags) {
<a class="tag" href="tag/#(tag.id)">#(tag.name)</a>
}
Here's my model:
final class Tag: SQLiteModel, Codable {
var id: Int?
var name: String
var description: String
init(id: Int? = nil, name: String, description: String) {
self.id = id
self.name = name
self.description = description
}
}
extension Tag: Migration { }
extension Tag: Content { }
extension Tag: Parameter { }
I also tried adding a computed property to the model:
var slug: String {
return "test"
}
That also doesn't work.

Related

How can I fetch a json file using Vapor for my leaf template to show the data?

I have a JSON hosted somewhere and I want to fetch the content, put it in a context for my leaf template to read.
However, I cannot make it work. I get the code to compile, but I get an error in the localhost
{"error":true,"reason":"Unsupported Media Type"}
Can somebody help me please! Happy holidays for all.
struct WebsiteController: RouteCollection {
func boot(routes: RoutesBuilder) throws {
routes.get(use: indexHandler)
}
func indexHandler(_ req: Request) -> EventLoopFuture<View> {
return req.client.get("https://streeteasydaily.s3.us-west-2.amazonaws.com/streeteasy1.json").flatMap { res in
do {
let json = try res.content.decode([Listing].self)
print(json[0].photos[0])
let context = IndexContext(title: "Homepage", listings: json)
return try req.view.render("index", context)
} catch {
// Handle error
print("cayo en error")
return req.eventLoop.makeFailedFuture(error)
}
}
}
}
struct IndexContext: Encodable {
let title: String
let listings: [Listing]
}
Model
final class Listing: Model {
static let schema = "listings" //basically the table name
#ID
var id: UUID?
#Field(key: "address")
var address: String
#Field(key: "description")
var description: String
#Field(key: "photos")
var photos: [String]
init() {}
//to initialize the db
init(id: UUID? = nil, address: String, description: String, photos: [String]) {
self.id = id
self.address = address
self.description = description
self.photos = photos
}
}
//to make acronym conform to CONTENT, and use it in Vapor
extension Listing: Content {}
This error is because the decode is failing to identify all the fields in your JSON to match against those defined in Listing and/or the array of such objects. The filenames must match those in the JSON exactly - i.e. case-sensitive and every field in the structure/model must exist in the JSON. Additional fields in the JSON that are not needed/included in the structure/model are fine.

What is the difference between { } and = when assigning an enum to a variable in Swift?

What is the difference between { } and = when assigning an enum to a variable in Swift?
Why would you say var type: ItemType { .recipe } over var type: ItemType = .video.
Does the {} indicate that it is a computed property?
Also, is the { get } needed after the type in the protocol?
enum ItemType: String, Decodable {
case video
case recipe
}
protocol Item: Decodable {
var type: ItemType { get }
var title: String { get }
var imageURL: URL { get }
}
struct Video: Item {
var type: ItemType = .video
var title: String
var imageURL: URL
var url: URL
var duration: String
var resolution: String
}
struct Recipe: Item {
var type: ItemType { .recipe }
var title: String
var imageURL: URL
var text: String
var ingredients: [String]
}
Does the {} indicate that it is a computed property?
Yes.
Also, is the { get } needed after the type in the protocol?
To specify that the property, whether it be computed or stored, needs to support at least a getter. The setter is optional.

How to wrap relationship children into array in Vapor?

I have a parent-child relationship, and the children need to wrap into array how can I do it?
event.testPrices = release.testPrices
final class Event: Content {
var id: String
var inProgress: Bool?
var name: String
var purpose: String?
var testPrices: [TestPrice]
init(id: String, name: String) {
self.id = id
self.name = name
}
}
extension Release {
var testPrices: Children<Release, TestPrice> {
return children(\.releaseId)
}
}
The assignment gives the error:
Cannot assign value of type 'Children' to type '[TestPrice]'
You can use a query to form the Future array and then map it. Assuming you are in some controller/route where event contains the appropriate Event and release contains the appropriate Release, try this:
{
release, event in
_ = release.testPrices.query(on:request).all().map { testP in
// testP is now [TestPrice]
event.testPrices = testP
}
}

Variable declaration with underscore

I have seen this in some videos on Youtube.
class Student {
private var _name: String!
private var _studentID: Int!
var name: String {
return _name
}
var studentID:Int {
return _studentID
}
init(name: String, studentID: Int) {
self._name = name
self._studentID = studentID
}
}
Any reason why they are doing this (adding _name and _studentID) instead of:
class Student {
private var name: String!
private var studentID: Int!
init(name: String, studentID: Int) {
self.name = name
self.studentID = studentID
}
}
Thank you very much.
The first examples are essentially creating properties that are publicly readable but privately writable.
The second set of code does not do the same thing as the first set.
The proper way to write this code is:
private (set) var name: String // no need for the !
private (set) var studentID: Int // no need for the !
init(name: String, studentID: Int) {
self.name = name
self.studentID = studentID
}
This makes the properties readable by outside users but only settable by the class. This is what the 1st set of code implements but in a much more verbose and needless manner.
The use of underscore is just a naming convention carried over from Objective-C when creating private instance variables.
Personally, I'd avoid videos and tutorials that use the 1st set of code.

Confusion why Realm LinkingObjects() isn't working :(

I have defined two very basic Object as follows:
class Language: Object {
dynamic var id: String = "" //"english", "chinese"
let versions = List<Version>()
convenience init(id: String, versions: [Version]) {
self.init()
self.id = id
self.versions.append(objectsIn: versions)
}
override static func primaryKey() -> String? {
return "id"
}
}
class Version: Object {
dynamic var id: String = "" //"kjv", "cus"
dynamic var name: String = "" //"Union Simplified"
let language = LinkingObjects(fromType: Language.self, property: "versions")
convenience init(id: String, name: String) {
self.init()
self.id = id
self.name = name
}
override static func primaryKey() -> String? {
return "id"
}
}
Every Language can have multiple Versions, and every Version has a reference (LinkingObject) to what Language holds it.
Next, I have the following code:
let kjvVersion = Version(id: "kjv", name: "King James Version")
let englishLanguage = Language(id: "english", versions: [kjvVersion])
print(kjvVersion)
Inspecting kjvVersion.language gives me back an empty LinkingObject array. Why?! What am I doing wrong here?
LinkingObjects objects contain the objects in the Realm which link to the containing object. Your objects are not managed by a Realm, so there will never be any objects in a Realm which link to them.