How does request.parameters.next work in Vapor mechanism? - vapor

When using Vapor to build a API, always use a method to fetch a object,
request.parameters.next(type.self)
It seems a method to extract the url parameter, why it return the result from the database ? Could not found any clues from the framework's source code. How can I find the answer ? Thanks.

This how Parameter protocol looks like
public protocol Parameter {
associatedtype ResolvedParameter
static var routingSlug: String { get }
static func resolveParameter(_ parameter: String, on container: Container) throws -> ResolvedParameter
}
when you conform anything to Parameter you should implement this function
static func resolveParameter(_ parameter: String, on container: Container) throws -> ResolvedParameter
which should parse string parameter from URL and return some object as a result. And as Parameter is generic you can return any type you want.
Let's write an extension to conform Date to Parameter
extension Date: Parameter {
public static func resolveParameter(_ parameter: String, on container: Container) throws -> Date {
guard let timeIntervalSince1970 = TimeInterval(parameter) else {
throw Abort(.notAcceptable, reason: "Unable to parse \"\(parameter)\" parameter")
}
return Date(timeIntervalSince1970: timeIntervalSince1970)
}
}
Parameter's method parses String parameter from URL and returnsDate, but you also can return something else, e.g. TimeInterval since Parameter is generic protocol
extension Date: Parameter {
public static func resolveParameter(_ parameter: String, on container: Container) throws -> TimeInterval {
guard let timeIntervalSince1970 = TimeInterval(parameter) else {
throw Abort(.notAcceptable, reason: "Unable to parse \"\(parameter)\" parameter")
}
return timeIntervalSince1970
}
}
When you conform your Fluent model to Parameter it already have an implementation for Parameter protocol in its extensions, that's why you shouldn't declare resolveParameter function manually.
Let's take a look how it works if you declare resolveParameter method manually in e.g. User model
extension User {
public static func resolveParameter(_ parameter: String, on container: Container) throws -> Future<User> {
// e.g. User's primary key is UUID
guard let id = UUID(parameter) else {
throw Abort(.notAcceptable, reason: "Unable to parse \"\(parameter)\" into UUID")
}
// getting database connection from pool
return container.requestPooledConnection(to: .psql).flatMap { conn in
// querying user by provided primary key
return User.query(on: conn)
.filter(\.id == id)
.first()
.unwrap(or: Abort(.notFound, reason: "Unable to find user by provided primary key"))
.always {
// will release connection in any case
try? container.releasePooledConnection(conn, to: .psql)
}
}
}
}
So as you can see you can implement resolveParameter to return anything you want.

Related

Create a protocol with default implementation that gives name/title/self of Enum as string

I am trying to create a protocol with default implementation that returns enum itself as string. But not able to find the correct syntax.
This is the code I tried so far, but it is not taking default implementation
protocol TestSelf {
static var desc: String { get set }
}
extension TestSelf {
get {
static var desc: String {
String(describing: self)
}
set {
print(\(new value))
}
}
enum Test: TestSelf { }
Access
print(Test.desc)
Asking me to implement the desc in the enum saying 'static var' declaration requires an initializer expression or an explicitly stated getter. I don't want to initialize it again. It should work with Default Implementation.
Ask:
At all places I don't need to write
String(describing: Test.self)
instead I can directly use
Test.desc
I think you want something like this (this is playground code):
protocol SelfDescriber {
static var descriptionOfSelf: String {get}
}
extension SelfDescriber {
static var descriptionOfSelf: String {
String(describing: Self.self)
}
}
enum Test: SelfDescriber {}
print(Test.descriptionOfSelf) // "Test"

Converting Dictionary Key type in Swift 5 with Dictionary(uniqueKeysWithValues:)

I am writing an application with networking capabilities for iOS 13.4 (Swift 5, Xcode 11) using Alamofire 5. I have created my custom type typealias KeyedParameters = [ParameterKeys: Any] to be able to use my API Parameter Keys in a 'swifty' short way (i.e. .login instead of KeyedParameters.login.rawValue).
The problem is when I try do convert this type back to default Alamofire's Parameters, I receive following error: Cannot convert return expression of type 'Dictionary<ParameterKeys, Any>' to return type 'Parameters' (aka 'Dictionary<String, Any>').
Casting:
extension KeyedParameters {
var parameters: Parameters {
Dictionary(uniqueKeysWithValues: map { ($0.key.rawValue, $0.value) })
}
}
ParameterKeys:
enum ParameterKeys: String {
// MARK: - Auth and User
case id, login, password, email, name
case createdAt = "created_at"
...
}
How error looks:
I think this might be just a case of a bad error message.
Your extension KeyedParameters (a typealias for [ParameterKeys: Any]) is actually equivalent to:
extension Dictionary where Key == ParameterKeys, Value: Any { ...
Swift has some odd behaviour when calling an initializer for a generic type within the declaration of that type itself. If the generic types are different, it won't handle that properly.
Here's a simpler example without so many red herrings (type aliases, enum raw values, etc.) and dependencies:
extension Dictionary {
func returnADifferentDict() -> [Character: String] {
let words = [
"apple", "anchovies",
"bacon", "beer",
"celery"
]
return Dictionary(uniqueKeysWithValues:
words.map { ($0.first!, $0) }
)
// fixed:
// return Dictionary<Character, String>(uniqueKeysWithValues:
// words.map { ($0.first!, $0) }
// )
}
}
The solution is to explicitly specify the generic type parameters of the generic type you're initializing. In your case,
extension KeyedParameters {
var parameters: Parameters {
Dictionary<String, Any>(uniqueKeysWithValues: map { ($0.key.rawValue, $0.value) })
}
}
You'd better explicitly highlight type like this:
extension KeyedParameters {
var parameters: Parameters {
return Parameters(uniqueKeysWithValues:
self.map { (key, value) in (key.rawValue, value) }
)
}
}
Worked for me.

Swift Type Erasure with Generic Enum and Generic Protocol

I have had to use type erasure in Swift a few times however it always involved a generic protocol. In this case, it involves both a generic enum and and generic protocol and I'm stumped.
Here is my generic enum and generic protocol with the necessary extension:
enum UIState<T> {
case Loading
case Success([T])
case Failure(ErrorType)
}
protocol ModelsDelegate: class {
associatedtype Model
var state: UIState<[Model]> { get set }
}
extension ModelsDelegate {
func getNewState(state: UIState<[Model]>) -> UIState<[Model]> {
return state
}
func setNewState(models: UIState<[Model]>) {
state = models
}
}
And here is my type erased generic class:
class AnyModelsDelegate<T>: ModelsDelegate {
var state: UIState<[T]> {
get { return _getNewState(UIState<[T]>) } // Error #1
set { _setNewState(newValue) }
}
private let _getNewState: ((UIState<[T]>) -> UIState<[T]>)
private let _setNewState: (UIState<[T]> -> Void)
required init<U: ModelsDelegate where U.Model == T>(_ models: U) {
_getNewState = models.getNewState
_setNewState = models.setNewState
}
}
I'm getting the following errors (they are marked in the code sample):
Error #1:
Cannot convert value of type '(UIState<[T]>).Type' (aka 'UIState<Array<T>>.Type') to expected argument type 'UIState<[_]>' (aka 'UIState<Array<_>>')
I have been working on this for awhile and there have been quite a few variations on this code that "almost worked". The error always has to do with the getter.
The problem that causes this error, as #dan has pointed out, is that on this line you're trying to pass a type as an argument, instead of an instance of that type:
get { return _getNewState(UIState<[T]>) }
However, I would question your use of an argument to this function in the first place, surely a getting function should have no argument at all? In this case you'll simply want your _getNewState function to have the signature () -> UIState<[T]>, and call it like so:
get { return _getNewState() }
Also, if your getNewState and setNewState(_:) functions in your protocol extension only exist in order to forward the getting and setting of your state property to the type-erasure – you can simplify your code by getting rid of them entirely and use closure expressions in the type-erasure's init instead:
_getNewState = { models.state }
_setNewState = { models.state = $0 }
(These work by capturing a reference to the models argument, for more info see Closures: Capturing Values)
Finally, I suspect that you mean to refer to UIState<T> rather than UIState<[T]> throughout your code, as T in this case refers to an element in the array that your .Success case has as an associated value (unless you want a 2D array here).
All in all, with the above proposed changes, you'll want your code to look something like this:
enum UIState<T> {
case Loading
case Success([T])
case Failure(ErrorType)
}
protocol ModelsDelegate: class {
associatedtype Model
var state: UIState<Model> { get set }
}
class AnyModelsDelegate<T>: ModelsDelegate {
var state: UIState<T> {
get { return _getNewState() }
set { _setNewState(newValue) }
}
private let _getNewState: () -> UIState<T>
private let _setNewState: (UIState<T>) -> Void
required init<U: ModelsDelegate where U.Model == T>(_ models: U) {
_getNewState = { models.state }
_setNewState = { models.state = $0 }
}
}

Downcast Generic AnyObject to Protocol Associated Type Self.Model

I am developing a library Restofire in which i want to keep a configuration object. I want to have a ResponseSerializer in the configuration object but the thing is ResponseSerializer is a generic.
public struct Configuration<M> {
/// The Default `Configuration`.
static let defaultConfiguration = Configuration<AnyObject>()
/// The base URL. `nil` by default.
public var baseURL: String!
/// The `ResponseSerializer`
public var responseSerializer: ResponseSerializer<M, NSError> = AlamofireUtils.JSONResponseSerializer()
/// The logging, if enabled prints the debug textual representation of the
/// request when the response is recieved. `false` by default.
public var logging: Bool = false
}
I set up the defaultConfiguration with baseUrl Configuration.defaultConfiguration.baseUrl = "http://httpbin.org/"
I have a protocol with associatedType requirement which uses the defaultConfiguration as default implementation. But i require to change the generic AnyObject to the associatedType Model so the responseSerializer of configuration object returns the type Model.
public protocol Configurable {
associatedtype Model
/// The Restofire configuration.
var configuration: Configuration<Model> { get }
}
public extension Configurable {
/// `Restofire.defaultConfiguration`
// Cannot convert return expression of Configuration<AnyObject> to return type Configuration <Self.Model>
public var configuration: Configuration<Model> {
return Restofire.defaultConfiguration
}
}
I get the error Cannot convert return expression of Configuration<AnyObject> to return type Configuration <Self.Model>
How can i downcast to use Model instead of AnyObject ?
I also have a protocol Requestable that inherits from Configurable
public protocol Requestable: Configurable {
/// The type of object returned in response.
associatedtype Model
/// The base URL.
var baseURL: String { get }
/// The path relative to base URL.
var path: String { get }
/// The request parameters.
var parameters: AnyObject? { get }
/// The logging.
var logging: Bool { get }
/// The Response Serializer
var responseSerializer: ResponseSerializer<Model, NSError> { get }
}
// MARK: - Default Implementation
public extension Requestable {
/// `configuration.BaseURL`
public var baseURL: String {
return configuration.baseURL
}
/// `nil`
public var parameters: AnyObject? {
return nil
}
/// `configuration.logging`
public var logging: Bool {
return configuration.logging
}
/// `configuration.responseSerializer`
var responseSerializer: ResponseSerializer<Model, NSError> {
return configuration.responseSerializer
}
}
RealCode at https://github.com/Restofire/Restofire/compare/Add/DefaultResponseSerializer
I could do directly below but then the user will not be able to set it using the configuration object.
// MARK: - Default Implementation
public extension Requestable {
/// `configuration.responseSerializer`
var responseSerializer: ResponseSerializer<Model, NSError> {
return AlamofireUtils.JSONResponseSerializer()
}
}
Is there any other way ?
What you are trying to do is not possible. This is because of the strong type safety system in Swift.
It looks like you want to restrict the configuration to the associated type Model, but you also want to provide a default configuration. The issue here is that your default configuration has no guarantee that its generic type will be the same as the type of Model.
The Swift type system won't let you pass <AnyObject> as type <Model>, as it has no way to ensure that the object actually IS of the Model type.
When you make something conform to Configurable and declare the type of the Model, you are saying, "My configuration must use the type I've given." The defaultConfiguration can't guarantee that, so it cannot be used in this way.
From looking at your code, you are using this to determine the type of the responseSerializer to use. But, if you are using different Configurable objects, they will all need different responseSerializers, so they can't all use the default configuration.
I've spent a lot of time thinking about this problem, and I don't see any way around it. You are going to have to change your design in some way.
If you move the responseSerializer on to the Configurable protocol, then you could make the Configuration non-generic. In that case, you would be able to create the responseSerializer in a protocol extension on Configurable. If you need to use the responseSerializer with a configuration independently of a Configurable object, then you would have to create a responseSerializer<AnyObject, NSError> wherever it is that you are using it. I'm not familiar with the entire design and intent of your library, so I'm not sure if this will work for what you are trying to achieve.
The one thing I can tell you with certainty is that your design has to change in order to use the defaultConfiguration.

Calling async function in lazy var property

Is there a way to call an async function from a lazy or computed property?
struct Item {
lazy var name: String = {
API.requestThing({ (string: String) in // Xcode didn't like this
return string // this would not work here
})
}()
}
class API {
class func requestThing(completion: String -> Void) {
completion("string")
}
}
Your completion handler in API.requestThing returns a String, yet it is supposed to have no return value:
(completion: String -> Void)
I got this to work:
struct Item {
lazy var name: String = {
API.requestThing({ (string: String) in
return string
})
}()
}
class API {
class func requestThing(completion: String -> String) -> String {
return completion("string")
}
}
There is no good reason to use "lazy" in this case. lazy is for initialization. Just create a normal func and pass a completion handler.
First, requestThing returns () (ie void) and not String. So the type of the following expression is also () and not String:
API.requestThing { string in
return string
}
Second, the call to requestThing is asynchronous, so even if you defined name as a lazy var, the call to the var body function is still synchronousand will return immediately.
So if you can transform name into a function like this:
func name(completion: String -> ()) {
API.requestThing { string in
completion(string)
}
}
// Later you call it in this way
myItem.name { name in
// use the value of name
}
If in addition you want to cache the retrieved value you can modify Item to a class and use the following code
class Item {
private var nameValue: String?
func name(completion: String -> ()) {
if let value = nameValue {
// return cached value
return completion(value)
} else {
// request value
API.requestThing { string in
// cache retrieved value
self.nameValue = string
// return retrieved value
completion(string)
}
}
}
}
There's probably no compelling reason to do this, but the following approach seems to be reasonable:
Instead having a variable of type String - we sometimes require a "Future" of that thing, e.g. Future<String>. A future represents the eventual result of an asynchronous operation - that is exactly what's given in your question.
The future itself is a "normal" variable and can be lazy evaluated, too. It just doesn't yet have its eventual value. That means, the underlying task will only be started when explicitly requested (e.g. lazily). From a design or architectural point of view, this may make sense.
func fetchString() -> Future<String> { ... }
lazy var name: Future<String> = fetchString()
Later in your code, you obtain the variable as follows:
item.name.map { string in
print(string)
}
If this is the first access to the lazy property, it will start the underlying asynchronous operation which calculates the string. Then, when the variable is available, the mapping function as provided in the map function will be called with the variable as an argument - possibly some time later, too.
Otherwise (if this is not the first access), it will just provide the string in the parameter when it is available, possibly immediately.
Since operations may fail, a "Future" also provides means to handle this:
item.name.map { string in
print(string)
}.onFailure { error in
print("Error: \(error)")
}
See also: https://en.wikipedia.org/wiki/Futures_and_promises
There are implementations for Futures in Swift and Objective-C, also often called "Promise".

Resources