Vapor - How to access the FluentValue type? - vapor

This is a Swift, Vapor related question.
How do I access the FluentValue type when trying to create a ResponseRepresentable Class?
The Vapor Model Protocol demands an ID; I want to assign my ID variable as a FluentValue optional. However, I can't find it (even after Import Vapor & Import Fluent).
import Vapor
import VaporMySQL
let drop = Droplet()
drop.post(String.self, "boughtcoffee") { request, buyerName in
let coffeeledger = CoffeeLedger(name: buyerName, time: 0)
guard let atLeastOneDrinker = request.data["drinker"].string else {
throw Abort.custom(status: .badRequest, message: "Please enter some drinkers")
}
return coffeeledger
}
import Vapor
import Fluent
final class CoffeeLedger: Model {
var id: FluentValue?
var name: String
var time: Int
init (name: String, time: Int){
self.name = name
self.time = time
}
}
I am following the Vapor video on setting up a Model that is Response Representable. https://www.youtube.com/watch?v=E1ir7_s_VTw

FluentValue became Node in Vapor 0.15. (Current version of Vapor at this time is 0.16).
Additionally, the init and serialize methods became init(node: ...) and makeNode().
The code on the model page (https://vapor.github.io/documentation/fluent/model.html) should be up to date and working with Vapor 0.15 and later.
If there is a specific error you are getting, create another question here with that error message and I can try to help!

Great. That works beautifully. The following is based on your documentation. Hopefully it helps others who are using Vapor:
import Vapor
import Fluent
final class CoffeeLedger: Model {
var id: Node?
var buyername: String
var drinkername: String
var time: Int
init(buyername: String, drinkername: String, time: Int) {
self.buyername = buyername
self.drinkername = drinkername
self.time = time
}
init(node: Node, in context: Context) throws {
id = try node.extract("id")
buyername = try node.extract("buyername")
drinkername = try node.extract("drinkername")
time = try node.extract("time")
}
func makeNode() throws -> Node {
return try Node(node: [
"id": id,
"buyername": buyername,
"drinkername": drinkername,
"time": time
])
}
static func prepare(_ database: Database) throws {
try database.create("Ledger") { users in
users.id()
users.string("buyername")
users.string("drinkername")
users.int("time")
}
}
static func revert(_ database: Database) throws {
try database.delete("Ledger")
}
}

Related

Protocols and Enums in Swift with Apollo

I am using Apollo for Swift in an iOS app. I have multiple types that all represent the same object. These types are auto-generated from a schema file and look something like this.
struct CurrentUser {
var id: String
...
}
struct MyUser {
var id: String
...
}
Basically Apollo generates multiple Swift types (one for each query) for the same underlying data type.
I want to create a new struct that unifies these types.
I would like to do something like this:
protocol UserProtocol {
var id: String { get }
}
struct User {
var id: String
...
init(_ data: UserProtocol) {
self.id = data.id
...
}
}
This approach however gives me an error when I try to construct a user object, telling me that "Type MyUser does not conform to UserProtocol". If I try to coerce the type with data as! UserProtocol I get a crash.
The only solution I've found is the following:
enum UserType {
case .currentUser(CurrentUser)
case .myUser(MyUser)
}
struct User {
var id: String
...
init(_ data: UserType) {
switch data {
case .myUser(let user):
self.id = data.id
...
case .currentUser(let user):
self.id = data.id
...
}
}
}
This approach works, but it leads to a lot of duplicated code in the init function. Is there a better way to do this in Swift?
I suspect the problem is that you need to explicitly conform the Apollo generated types to your protocol:
extension CurrentUser: UserProtocol { }
extension MyUser: UserProtocol { }
Remember that Swift is not duck-typed like some other languages, so a type with member var id: String is not UserProtocol until you declare it as such.
If for some reason you need to do some transformation of the Apollo types to fit the app models in the future, those extensions are a good place to do that, too.

Use switch cases based on condition

I have made a file called Constants.swift. Within this, I have made a class like so...
public class WebServices {
static let getMyPlants : String = "plant/getPlants"
static let getMyOrganizations: String = "organization/getOrganizations"
}
Now whenever, I use an api anywhere in my project, I do Webservices.getMyPlants.
Now I also have a base-url for each of the API's. That is mentioned below public class WebServices.... like so..
struct envDev {
var BASEURL : String = "http://api-proj-dev.ii.the-co.com/api/"
}
Now, the base-url for Webservices.getMyOrganizations is different. I want to use a condition within struct envDev that if I have selected Webservices.getMyOrganizations, then I can give a different BASEURL. Something like...
//The below code isn't right. I just wrote it to represent the kind of solution I wish to have.
struct envDev {
var BASEURL : String = "http://api-proj-dev.ii.the-co.com/api/"
if Webservices.getMyOrganizations {
var BASEURL : String = "http://my second base-url.."
}
}
EDIT 1 Giving below the signature of APIHelper
class APIHelper: NSObject {
var API: NSString
var json: NSString
var receivedJSON: NSString?
var arrResult: NSMutableArray = []
let esmatBaseUrl = AppDelegate().currentUser //This is given in AppDelegate as `var currentUser = envDev()`
()
EDIT 2 Inclusion of baseUrl computed property in APIHelper & the error.
class APIHelper: NSObject {
var API: NSString
var json: NSString
var receivedJSON: NSString?
var arrResult: NSMutableArray = []
let esmatBaseUrl = AppDelegate().currentUser //This is given in AppDelegate as `var currentUser = envDev()`
()
var baseUrl: String {
esmatBaseUrl.baseUrl(forApi: API as String) // Here I'm getting the error as `Value of type 'envDev' has no member 'baseUrl'`
}
envDev has no way of knowing what happens in APIHelper, so you need a way to pass in the API from APIHelper to envDev. This means that BASEURL should not be a property, but a method:
func baseUrl(forApi api: String) -> String {
switch api {
case WebServices.getMyPlants: return "some url"
case WebServices.getMyOrganizations: return "some other url"
default: fatalError()
}
}
Then in APIHelper, you can add a baseUrl computed property that calls the above method:
var baseUrl: String {
esmatBaseUrl.baseUrl(forApi: API as String)
}
This would mean that you need to change all occurrences of esmatBaseUrl.BASEURL in your existing code to just baseUrl.
Also, I would suggest not using NSString, NSArray, etc in Swift. You should their Swift counterparts: String and [T].
I understood your query. You want to create an ENUM for your server-environment's, instead of hard-coding baseUrl's you probably want to use ENUMS to select different environments, right.
So accordingly, I've created an ENUM for you to add different server-environments so it will be feasible for you to use it frequently every-where.
private enum ServerEnv: String {
case stage, prod, test, my_plants, my_organization
var domainValue: String {
switch self {
case .test, .my_plants: return "http://api-testing-proj-dev.ii.the-co.com/api/"
case .stage: return "http://api-staging-proj-dev.ii.the-co.com/api/"
case .prod: return "http://api-production-proj-dev.ii.the-co.com/api/"
case .my_organization: return "http://api-my_organization-proj-dev.ii.the-co.com/api/"
}
}
}
Example :
let baseUrl = ServerEnv.my_organization.domainValue
Output => baseURL = "http://api-my_organization-proj-dev.ii.the-co.com/api/"
let baseUrl = ServerEnv.my_plants.domainValue
Output => baseURL = "http://api-testing-proj-dev.ii.the-co.com/api/"
I hope, I've solved your query here.
Happy Coding :-)

Using custom model class with Backendless in Swift

I'm trying to retrieve data from an online data storage using the func that I found online on the official Backendless docs! but when I try to use persona like a Lista(my own class) Object, I get the error: Could not cast value of type '__NSDictionaryM' (0x10c1ccfc0) to 'InLIsta_.Lista' (0x108439790).
I search over this site but the answer aren't specific for the Backendless case, so I hope that anyone can help me
this is my code (obviously I've declared all the var and let necessary to the code to run):
class Lista : NSObject {
var nome: String?
var pr: String?
var pagamento = 0
var entrato: Bool = false
var commenti: String?
var objectId: String?
var created: NSDate?
var updated: NSDate?
}
func findQ() {
Types.tryblock({ () -> Void in
let startTime = NSDate()
let found = self.backendless.persistenceService.of(Lista.ofClass()).find(self.query)
let currentPage = found.getCurrentPage()
print("Loaded \(currentPage.count) name objects")
print("Total name in the Backendless storage - \(found.totalObjects)")
for person in currentPage {
let persona = person as! Lista // here i get error
print("Restaurant <\(Lista.ofClass())> name = \(persona.nome)")
self.nomi.append(persona.nome!)
}
print("Total time (ms) - \(1000*NSDate().timeIntervalSinceDate(startTime))")
},
catchblock: { (exception) -> Void in
print("Server reported an error: \(exception as! Fault)")
}
)
}
The backendless persistence service has a method -(void)mapTableToClass:(NSString *)tableName type:(Class)type; that you need to call for each of your custom classes so they'll be used during the deserialisation.
self.backendless.persistenceService.mapTableToClass("Lista", type: Lista.self)
This needs to be done before any calls are made to use the persistence service.
Note that the classes, if not defined in obj-c, must be exported to obj-c. Note that this also means you can't have any optionals.
Ideally you should use the platform code generation to create your model class definitions to ensure all of the attributes are created with the appropriate types. A failure to map to your custom class could be caused by type mismatches or name mismatches. Optionals will always fail in the current SDK implementation.

Open and close db with Swift + FMDB

What is the recommended way to open and close a sqlite db connection with Swift + FMDB?
I'm following this tutorial which suggests that you should open and close a database like:
let db = FMDatabase(path: databasePath as String)
if db.open() {
//do stuff
db.close()
Closing the db outside of a finally block would be a red flag in other languages I've used. I know swift/iOS exception handling is different than most languages and dev environments. But I'm still concerned that this is still a really unsafe way to close the database connection.
Is this method of closing a db actually safe & recommended?
Should I be using something like SwiftTryCatchFinally instead?
let db = FMDatabase(path: databasePath as String)
SwiftTryCatch.try({
connection = db.open()
if connection {
//do stuff
}
}, catch: {
}, finally: {
if connection {
db.close()
}
})
iOS exception handling is so foreign to me :P
This is the approach I take which might give you an insight. I have the database calls in the programs but the table methods (create, add, update) are in a class that is called multiple times as and when required.
So to create a database I call, in this case when a button is pressed:
#IBAction func buttonCreateTable(sender: AnyObject) {
var locationRecord: LocationRecord = LocationRecord()
var isCreated = DatabaseFunctions.instance.createLocationTable()
if isCreated {
NSLog("Locations Database Created")
} else {
NSLog("Locations Database Not Created")
}
}
The class, DatabaseFunctions.swift, would read ...
import UIKit
let sharedInstance = DatabaseFunctions()
class DatabaseFunctions: NSObject {
var database: FMDatabase? = nil
class var instance: DatabaseFunctions {
sharedInstance.database = FMDatabase(path: Utilities.getPath("yourDatabase.sqlite"))
var path = Utilities.getPath("yourDatabase.sqlite")
return sharedInstance
}
// yourTableFunctions *************************
func createLocationTable() -> Bool {
sharedInstance.database!.open()
let sqlStatement = "CREATE TABLE IF NOT EXISTS MYTABLE (ID INTEGER PRIMARY KEY AUTOINCREMENT, TIMESTAMP DATETIME DEFAULT CURRENT_TIMESTAMP, TIMESTAMPLASTEDIT DATETIME DEFAULT CURRENT_TIMESTAMP, NAME TEXT, nil)"
let isCreated = sharedInstance.database!.executeUpdate(sqlStatement, withArgumentsInArray:nil)
sharedInstance.database!.close()
return isCreated
}
}
The LocationsRecord is held separate in another swift, LocationInfo.swift, file like:
import UIKit
// Locations Class
class LocationRecord: NSObject {
var locationSelected : Int32 = Int32()
var locationRecordNo : Int32 = Int32()
var locationName: String = String()
}

Cannot assign to 'parameters' in 'self' error - from CS193p Stanford Online Course

I am currently working on the Smashtag application which can be downloaded here: http://web.stanford.edu/class/cs193p/cgi-bin/drupal/
I have been following along with the iTunes U video (YouTube link: https://www.youtube.com/watch?v=ZIwaIEfPAh8&spfreload=10 ) and I can't get past the errors I have been getting.
Even after downloading the final copy of the app from the Stanford website I am seeing these Swift Compiler Errors still popping up.
It continues to say Cannot assign to 'parameters' in 'self'
Could this be from Xcode being updated to 6.3 after this Smashtag app learning course was released in February/March 2015?
I appreciate your help, not sure how to solve this at the moment.
private var twitterAccount: ACAccount?
public class TwitterRequest
{
public let requestType: String
public let parameters = Dictionary<String, String>()
// designated initializer
public init(_ requestType: String, _ parameters: Dictionary<String, String> = [:]) {
self.requestType = requestType
**self.parameters = parameters**
}
// convenience initializer for creating a TwitterRequest that is a search for Tweets
public convenience init(search: String, count: Int = 0, _ resultType: SearchResultType = .Mixed, _ region: CLCircularRegion? = nil) {
var parameters = [TwitterKey.Query : search]
if count > 0 {
parameters[TwitterKey.Count] = "\(count)"
}
switch resultType {
case .Recent: parameters[TwitterKey.ResultType] = TwitterKey.ResultTypeRecent
case .Popular: parameters[TwitterKey.ResultType] = TwitterKey.ResultTypePopular
default: break
}
if let geocode = region {
parameters[TwitterKey.Geocode] = "\(geocode.center.latitude),\(geocode.center.longitude),\(geocode.radius/1000.0)km"
}
self.init(TwitterKey.SearchForTweets, parameters)
}
public enum SearchResultType {
case Mixed
case Recent
case Popular
}
As of Swift 1.2, giving your constant variables an initial value (not in init) means they cannot be reassigned a value in init. Therefore, because you're setting an initial value of parameters to be an empty dictionary, you cannot give parameters a new value in init. To solve this change your code to:
public class TwitterRequest {
public let requestType: String
// Note - now we just define the type here.
public let parameters: Dictionary<String, String>
public init(_ requestType: String, _ parameters: Dictionary<String, String> = [:]) {
self.requestType = requestType
self.parameters = parameters
}
// Other stuff
}
Alternatively you could change parameters to be a var, as pointed out by #Thomas Kilian in his comment. However, if you're not going to change the values stored in parameters it makes more sense to declare it as let and use the code above.

Resources