Unclear use of Storage - vapor

How do I use Storage in Vapor 4?
I tried the following:
if let someValue = req.storage.get(theKey) as? String {
// do something
} else {
req.storage.set(theKey, to: "Some Value")
}
However I get the following Errors:
error: type of expression is ambiguous without more context
if let original: String = req.storage.get(theKey) {
~~~~~~~~~~~~^~~~~~~~~~~
error: type of expression is ambiguous without more context
req.storage.set(theKey, to: "Some Value")
~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I also did not find any documentation on this topic.

The input key is actually a Protocol use must implement. If you want to see examples of how Store is used, do like #imike says and look for usages of the Storage class.

Storage is not string based key object storage. You have to declare a struct conforming to StorageKey, implement the typealias Value representing the object type you want to store.
If you need to store a string let's take this example :
struct MyStringKey: StorageKey {
typealias Value = String
}
...
request.storage.set(MyStringKey.self, to: "my string")
var myString: String? = request.storage.get(MyStringKey.self)

It is NOT possible for the Key to be any arbitrary String. The key must be predefined.

Related

Can #dynamicMemberLookup be used to call methods?

In the documentation for #dynamicMemberLookup it says,
Apply this attribute to a class, structure, enumeration, or protocol to enable members to be looked up by name at runtime.
If I'm not mistaken, instance methods are considered members of a struct / class. However, when I try to call a function dynamically I get an error saying:
Dynamic key path member lookup cannot refer to instance method foo()
To reproduce the problem:
struct Person {
var name: String
var age: Int
func greet() {
print("hello, my name is \(name)")
}
}
#dynamicMemberLookup
struct Wrapper {
var value: Person
subscript<T>(dynamicMember keypath: KeyPath<Person, T>) -> T {
value[keyPath: keypath]
}
}
let person = Person(name: "John Doe", age: 21)
let wrapper = Wrapper(value: person)
wrapper.greet() // << Error: Dynamic key path member lookup cannot refer to instance method `greet()`
// Or
let function = wrapper.greet // << Error: Dynamic key path member lookup cannot refer to instance method `greet()`
function()
How can I dynamically call greet() using #dynamicMemberLookup? Is there any way to achieve what I'm trying to do?
Thanks in advance!
No, dynamicMemberLookup does not work for methods. As the signature of the subscript suggests, it only works for things that can be represented as a KeyPath. Method calls cannot be part of a key path. :(
Key-Path Expression
A key-path expression refers to a property or subscript of a type.
The path consists of property names, subscripts, optional-chaining
expressions, and forced unwrapping expressions. Each of these key-path
components can be repeated as many times as needed, in any order.
At compile time, a key-path expression is replaced by an instance of
the KeyPath class.
I suspect the reason why it is called "dynamic member lookup" is because it also works with subscripts. The alternative of dynamicPropertyOrSubscriptLookup is rather a mouthful isn't it?
One rather hacky fix would be to change greet into a computed property:
var greet: () -> Void { {
print("hello, my name is \(name)")
} }
If greet has had parameters, you could also change it into a subscript, but I think that is an even uglier solution.

Can't perform methods of objects stored in Array[Any]

I want to store objects of different types in an array.
The program below is only a minimum demo. In the anyArray:[Any] an instance of Object1 is stored. The print statement prints out the expected object type. In the following line the test of the stored object's type returns true. This means, during run time the correct object type is known and every thing seems to be fine.
class Object1 {
var name = "Object1"
}
var anyArray:[Any] = [Object1()]
print("\(type(of: anyArray[0]))")
let testResult = anyArray[0] is Object1
print("Test result:\(testResult)")
//print("Name:\((anyArray[0]).name)")
Console output:
Object1
Test result:true
However, if I try to print out the name property of the object, I get an error message from the editor:
Value of type 'Any' has no member 'name'
Well, at compile time the object's type is unknown. That's why the compiler complains. How can I tell the compiler that it is OK to access the properties of the stored object?
The difference comes from the difference from Type Checking in:
runtime, or
compile time
The is operator checks at runtime whether the expression can be cast to the specified type. type(of:) checks, at runtime, the exact type, without consideration for subclasses.
anyArray[0].name doesn't compile since the Type Any doesn't have a name property.
If you're sure anyArray[0] is an Object1, you could use the downcast operator as!:
print("\((anyArray[0] as! Object1).name)")
To check at runtime if an element from anyArray could be an Object1 use optional binding, using the conditional casting operator as?:
if let:
if let object = anyArray[0] as? Object1 {
print(object.name)
}
Or use the guard statement, if you want to use that object in the rest of the scope:
guard let object = anyArray[0] as? Object1 else {
fatalError("The first element is not an Object1")
}
print(object.name)
If all objects in your array have a name property, and you don't want to go through all the hoops of optional binding repeatedly, then use a protocol. Your code will look like this:
protocol Named {
var name: String {get set}
}
class Object1: Named {
var name = "Object1"
}
var anyArray:[Named] = [Object1()]
print("\(type(of: anyArray[0]))")
let testResult = anyArray[0] is Object1
print("Test result:\(testResult)")
print("Name:\(anyArray[0].name)")
Notice that anyArray is now an array of Named objects, and that Object1 conforms to the Named protocol.
To learn more about protocols, have a look here.
You object is still of type Any. You just checked if it can be of type Object1, but you did not cast it. If you want the object as Object1, you need to cast it.
Also if multiple classes can have name, you need to use Protocol like #vadian has mentioned in his comment and cast it to that protocol.
protocol NameProtocol {
var name: String {get set}
}
class Object1: NameProtocol {
var name = "Object1"
}
if let testResult = anyArray[0] as? NameProtocol {
print(testResult.name)
}
Edit: "I want to store objects of different types in an array". The solution that you have marked as correct will not work if all the objects that you have do not conform to the protocol.

What's the difference between : and = in swift

Sorry if the title is rather confusing, but I'm curious to know the difference between these two lines:
var title = String()
var title: String
Is one being initialized and one only be declared? Which is more correct?
For example, if I have a struct should I use one of the other?
So the reason I ask this is because I'm learning about how to grab some JSON from a url and then display it in my app. One of the new ways of doing so is using Decodable. So, I have a struct in a model class like so:
struct Videos: Decodable {
var title = String()
var number_of_views : Int
var thumbnail_image_name: String
var channel: Channel
var duration: Int
}
In another class I have this:
URLSession.shared.dataTask(with: url){(data,response,error) in
if(error != nil){
print(error!)
return
}
guard let data = data else { return }
do{
self.Videos2 = try JSONDecoder().decode([Videos].self, from: data)
//self.collectionView?.reloadData()
}catch let jsonErr{
print(jsonErr)
}
}.resume()
So, should I declare or initialize the variables in my struct? I'm assuming I should just declare them like so:
var title: String?
Would that be the correct syntax in my struct?
UPDATE:
I understand this question was more broad then I originally proposed it to be. I'm sorry about that, but thank you so much for all your great answers that clarified a lot up for me.
The difference is that : defines the type of your variable, whereas = assigns an actual value to the variable.
So:
var title = String()
This calls the initializer of the String type, creating a new String instance. It then assigns this value to title. The type of title is inferred to be String because you're assigning an object of type String to it; however, you could also write this line explicitly as:
var title: String = String()
This would mean you are declaring a title variable of type String, and assigning a new String to it.
var title: String
This simply says you're defining a variable of type String. However, you are not assigning a value to it. You will need to assign something to this variable before you use it, or you will get a compile error (and if this is a property rather than just a variable, you'll need to assign it before you get to the end of your type's init() method, unless it's optional with ? after it, in which case it gets implicitly initialized to nil).
EDIT: For your example, I'd probably declare all the variables using let and :, assuming that your JSON provides values for all of those properties. The initializer generated by Decodable should then set all the properties when you create the object. So, something like:
struct Videos: Decodable {
let title: String
let number_of_views : Int
let thumbnail_image_name: String
let channel: Int
let duration: Int
}
This initializes a value
var title = String()
This declares a value but does not initialize it
var title: String
If you attempt to use the latter, such as print(title), you will get a compiler error stating Variable 'title' used before being initialized
It does not matter whether the value is a class or a struct.
The = operator is the assignment operator, it assigns a value to the object on the left of the =
Typically, class or struct properties are declared but not initialized until the init() is called. A simple class might be
class MyClass {
let myProperty: String
init(aString: String) {
self.myProperty = aString
}
}
Whereas inside the scope of a function you may declare a local variable that only lives inside the scope of the function.
func doSomethingToAString(aString: String) -> String {
let extraString = "Something"
let amendedString = aString + extraString
return amendedString
}
In your specific example, the struct synthesizes an initializer that will allow you to initialize the struct with all the values needed to fill your properties. The initializer generated by Decodable should then set all the properties when you create a Videos struct, you will do it something like:
let aVideos = Videos(title: "My Title", number_of_views: 0, thumbnail_image_name: "ImageName", channel: Channel(), duration: 10)
Is one being initialized and one only be declared?
Yes, meaning that the declared cannot be used. If you tried to set a value for it, you would get a compile-time error:
variable 'title' passed by reference before being initialized
Which is more correct?
There is no rule of thumb to determine which is more correct, that would be depends on is there a need to initialize title directly.
On another hand, when it comes to declare properties for a class, saying var title = String() means that you are give title an initial value ("") which means that you are able to create an instance of this class directly, example:
class Foo {
var title = String()
}
let myFoo = Foo()
However, if title declared as var title: String, you will have to implement the init for Foo:
class Foo {
var title: String
init(title: String) {
self.title = title
}
}
let myFoo = Foo(title: "")
Also, you have an option to declare it as lazy:
lazy var title = String()
which means:
A lazy stored property is a property whose initial value is not
calculated until the first time it is used. You indicate a lazy stored
property by writing the lazy modifier before its declaration.
Properties - Lazy Stored Properties

Should I use optional for properties of object models that will be parsed from JSON?

My iOS app has a pretty common setup: it makes HTTP queries to an API server that responds with JSON objects. These JSON objects are then parsed to appropriate Swift objects.
Initially I divided properties into required properties and optional properties, mostly based on my API server's database requirements. For example, id, email, and name are require fields so they use non-optional types. Others can be NULL in database, so they are optional types.
class User {
let id: Int
let email: String
let profile: String?
let name: String
let motive: String?
let address: String?
let profilePhotoUrl: String?
}
Recently, I started wondering whether this was a good setup at all. I found out that although some properties might be always in the database, that does not mean that those properties will always be included in the JSON response.
For example, in the User profile page, all these fields are needed to properly display the view. Therefore, JSON response will include all these fields. For a view that lists users' names, however, I would not need email or id, and JSON response should probably not include those properties either. Unfortunately, this will cause error and crash the app when parsing JSON response into Swift object since the app expects id, email, name to be always not-nil.
I'm thinking of changing all properties of Swift objects into optionals, but it feels like throwing away all the benefits of this language-specific feature. Moreover, I will have to write many more lines of code to unwrap all these optionals somewhere else in the app anyway.
On the other hand, JSON objects are by their nature not very interoperable with strict static typing and nil-checking of Swift so it might be better to simply accept that annoyance.
Should I transition to models with every property as optionals? Or is there a better way? I'd appreciate any comment here.
There are three ways you can go with this:
Always send all the JSON data, and leave your properties non-optional.
Make all the properties optional.
Make all the properties non-optional, and write your own init(from:) method to assign default values to missing values, as described in this answer.
All of these should work; which one is "best" is opinion-based, and thus out of the scope of a Stack Overflow answer. Choose whichever one is most convenient for your particular need.
The first thing to do is ask: Does an element of the “view that lists users' names” need to be the same kind of object as the model object behind a “User profile page”? Perhaps not. Maybe you should create a model specifically for the user list:
struct UserList: Decodable {
struct Item: Decodable {
var id: Int
var name: String
}
var items: [Item]
}
(Although the question said the JSON response might not include id, it doesn't seem like a user list without ids with be particularly useful, so I made it required here.)
If you really want them to be the same kind of object, then maybe you want to model a user as having core properties that the server always sends, and a “details” field that might be nil:
class User: Decodable {
let id: Int
let name: String
let details: Details?
struct Details: Decodable {
var email: String
var profile: String?
var motive: String?
var address: String?
var profilePhotoUrl: String?
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
name = try container.decode(String.self, forKey: .name)
details = container.contains(.email) ? try Details(from: decoder) : nil
}
enum CodingKeys: String, CodingKey {
case id
case name
case email // Used to detect presence of Details
}
}
Note that I create the Details, if it's present, using Details(from: decoder), instead of the usual container.decode(Details.self, forKey: .details). I do it using Details(from: decoder) so that the properties of the Details come out of the same JSON object as the properties of the User, instead of requiring a nested object.
The premise:
Partial representing is a common pattern in REST. Does that mean all
properties in Swift need to be optionals? For example, the client
might just need a list of user ids for a view. Does that mean that all
the other properties (name, email, etc) need to be marked as optional?
Is this good practice in Swift?
Marking properties optional in a model only indicates that the key may or may not come. It allows the reader to know certain things about the model in the first look itself.
If you maintain only one common model for different API response structures and make all the properties optional, whether that's good practice or not is very debatable.
I have done this and it bites. Sometimes it's fine, sometimes it's just not clear enough.
Keeping one model for multiple APIs is like designing one ViewController with many UI elements and depending on particular cases, determining what UI element should be shown or not.
This increases the learning curve for new developers as there's more understanding-the-system involved.
My 2 cents on this:
Assuming we are going ahead with Swift's Codable for encoding/decoding models, I would break it up into separate models rather than maintaining a common model with all optionals &/or default values.
Reasons for my decision are:
Clarity of Separation
Each model for a specific purpose
Scope of cleaner custom decoders
Useful when the json structure needs a little pre-processing
Consideration of API specific additional keys that might come later on.
What if this User list API is the only one requiring more keys like, say, number of friends or some other statistic?
Should I continue to load a single model to support different cases with additional keys that come in only one API response but not another?
What if a 3rd API is designed to get user information but this time with a slightly different purpose? Should I over-load the same model with yet more keys?
With a single model, as the project continues to progress, things could get messy as key availability in now very API-case-based. With all being optionals we will have alot of optional bindings & maybe some shortcut nil coalescings here and there which we could have avoided with dedicated models in the first place.
Writing up a model is cheap but maintaining cases is not.
However, if I was lazy and I have a strong feeling crazy changes aren't coming up ahead, I would just go ahead making all the keys optionals and bear the associated costs.
I typically make all non-critical properties optional, and then have a failable initializer. This allows me to better handle any changes in the JSON format or broken API contracts.
For example:
class User {
let id: Int
let email: String
var profile: String?
var name: String?
var motive: String?
var address: String?
var profilePhotoUrl: String?
}
This means that I will never have a user object without an id or email (let's assume those are the two that always need to be associated with a user). If I get a JSON payload without an id or email, the Initializer in the User class will fail and won't create the user object. I then have error handling for failed initializers.
I'd much rather have a swift class with optional properties than a bunch of properties with an empty string value.
This really depends on the way you are handling your data. If you are handling your data through a "Codable" class, then you have to write a custom decoder to throw an exception when you don't get certain expected values. Like so:
class User: Codable {
let id: Int
let email: String
let profile: String?
let name: String
let motive: String?
let address: String?
let profilePhotoUrl: String?
//other methods (such as init, encoder, and decoder) need to be added below.
}
Because I know that I'm going to need to return an error if I don't get the minimum required parameters, you would need something like an Error enum:
enum UserCodableError: Error {
case missingNeededParameters
//and so on with more cases
}
You should be using coding keys to keep things consistent from the server. A way to do that inside of the User Object would be like so:
fileprivate enum CodingKeys: String, CodingKey {
case id = "YOUR JSON SERVER KEYS GO HERE"
case email
case profile
case name
case motive
case address
case profilePhotoUrl
}
Then, you need to write your Decoder. A way to do that would be like so:
required init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
guard let id = try? values.decode(Int.self, forKey: .id), let email = try? values.decode(String.self, forKey: .email), let name = try? values.decode(String.self, forKey: .name) else {
throw UserCodableError.missingNeededParameters
}
self.id = id
self.email = email
self.name = name
//now simply try to decode the optionals
self.profile = try? values.decode(String.self, forKey: .profile)
self.motive = try? values.decode(String.self, forKey: .motive)
self.address = try? values.decode(String.self, forKey: .address)
self.profilePhotoUrl = try? values.decode(String.self, forKey: .profilePhotoUrl)
}
SOMETHING TO NOTE: You should write your own encoder as well to stay consistent.
All of that can go, simply to a nice calling statement like this:
if let user = try? JSONDecoder().decode(User.self, from: jsonData) {
//do stuff with user
}
This is probably the safest, swift-ist, and most object oriented way to handle this type of issue.
I recommend to keep all non-scalar(String, Custom Types etc) properties as optional, scalar(Int, Float, Double etc) as non-optional(with some exceptions) by assigning a default value and collections with empty array. e.g,
class User {
var id: Int = 0
var name: String?
var friends: [User] = []
var settings: UserSettings?
}
This assures you a crash free app no matter what happens to server. I would prefer abnormal behavior over a crash.
If the server is giving Null value for the other properties, you can go for optionals and safe unwrap. Or while unwrapping you can assign empty string to property if the value is nil
profile = jsonValue ?? ""
Other case since the other properties are String data type you can assign default value as a empty string
class User {
let id: Int
let email: String
let profile: String = ""
let name: String
let motive: String = ""
let address: String = ""
let profilePhotoUrl: String = ""
}
Yes, you should use optional if the property is not necessary in API and if you want some value in the mandatory property then assign blank value:
class User {
let id: Int?
let email: String? = ""
let profile: String?
let name: String? = ""
let motive: String?
let address: String?
let profilePhotoUrl: String?
}
In my opinion, I will choose 1 of 2 solutions:
Edit my init func from JSON to object, init with default object values for all props (id = -1, email = ''), then read JSON with optional checking.
Create a new class/struct for that specific case.
I would prefer using failable Initializer its neat compared to other options.
So keep the required properties as non-optionals and create object only if they are present in the response (you can use if-let or gaurd-let to check this in response), else fail the creation of the object.
Using this approach we avoid making non-optionals as optionals and having a pain to handle them throughout the program.
Also optionals are not meant for defensive programming so don't abuse optionals by making "non-optional" properties as optionals.
I would prefer optional properties because you can not promise JSON values to be there all the time and any change on response property name would crash your app.
If you do not use optional values, you have to control parameters while parsing and add a default value if you want a crash free app. And you wouldn't know if it was nil or empty string from server.
Optional values is your best friends.
object mapper for mutable and non-mutable properties.
realm-swift for default non-optional values.

Extra argument in call error mystery

I had some older Swift code that used to compile and work where I was using the .append to build out a data structure dynamically. After upgrading to a few compiler versions newer I am getting the dreaded "Extra Argument ' ' in call" error. I reduced the code down to this:
struct EHSearch {
let EHcategory : String = ""
let EHname : String = ""
}
var myEHSearch = [EHSearch]()
// Call to dynamically append the results
// Extra argument: 'EHcategory' in call
myEHSearch.append(EHSearch(EHcategory: "Food", EHname: "Joes Crab Shack"))
I can't see anything so far in searching on what has changed to cause this one so seeking guidance here.
Because you have let in your struct.
Define your structure like this:
struct EHSearch {
var EHcategory : String = ""
var EHname : String = ""
}
If you have constants in your struct, you can not provide them initial value while creating new structure instances. The automatically-generated member-wise initializer doesn't accept let members as parameters of the initializer of struct.
It depends on your intentions with the struct's properties. Do you want them to be mutable or not?
If yes, then #sasquatch's answer will do.
If not, then you need to ensure a value is assigned to them only once. As you already do that in the struct declaration (the default values), you can't assign new values to them. But being a struct, they don't need to have default values - moreover, struct automatically receive a memberwise initializer. https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html
So here is the variant for immutable properties:
struct EHSearch {
let EHcategory : String
let EHname : String
}
var myEHSearch = [EHSearch]()
// Call to dynamically append the results
// Extra argument: 'EHcategory' in call
myEHSearch.append(EHSearch(EHcategory: "Food", EHname: "Joes Crab Shack"))
The "Extra Argument" error you're seeing is because the compiler already has values for the properties so it doesn't expect any new ones. Here is the "middle" way - one property has a default value whilst the other doesn't - which should make it clearer:
struct EHSearch {
let EHcategory : String = ""
let EHname : String
}
var myEHSearch = [EHSearch]()
// Call to dynamically append the results
// Extra argument: 'EHcategory' in call
myEHSearch.append(EHSearch(EHname: "Joes Crab Shack"))

Resources