Swift: get self.description in self.description - ios

I'm trying to modify description output for ErrorProtocol. But this snippet, gives me infinite loop.
enum GeneralError: ErrorProtocol, CustomStringConvertible {
case NoMemory
var description: String {
return String(self).lowercased()
}
}
Changing to self.dynamic type gives me a "generalerror".
Is there any way how to get just "nomemory"? Without using conditionals.

Swift: Convert enum value to String? isn't an exact match for your question, but it really covers the guts of it. In your case, you'll want to change it up a little bit:
enum Error : String, ErrorProtocol, CustomStringConvertible {
case NoMemory
var description : String {
return self.rawValue.lowercaseString
}
}
This works because the default rawValue for enum cases where the associated type is String is the name of the case.

When you call String(self) in GeneralError.description, you're making the String initializer use your object (self) as a CustomStringConvertible to make a new string. Internally, it does this by calling description on it, hence the recursion
What exactly are you trying to achieve?

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.

Unclear use of Storage

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.

Trying to parse a string value from an API a JSON object into a struct as an Int

I am new to Swift and I have data I'd like to call from an API through JSON. For some strange reason some of the names are written with whitespaces making it difficult for me to declare variables that'll show the integer type value they hold. Here is a screenshot . I am trying to parse the Int value attached to the type "Anti-social behaviour" but don't seem to know how to declare it appropriately.
You can define your own CodingKeys inside the file. Note that the name should be CodingKeys.
struct Types: Codable {
let buglary: Int
let shoplifting: Int
let drugs: Int
let robbery: Int
let antiSocialBehavior: Int
// Other properties
enum CodingKeys: String, CodingKey {
case buglary = "Buglary"
case shoplifting = "Shoplifting"
case drugs = "Drugs"
case robbery = "Robbery"
case antiSocialBehavior = "Anti-social behavior"
// other coding keys
}
}
Also, note that properties in Swift are always camelcased and not capitalized. So, I also changed the name of your properties. Check the enum inside the struct which actually defines the mapping between property name and their encoding / decoding keys.

Enum Types: get raw value from key

How to get the raw value from a enum passing the key value? Must work for any enum types working like an extension of enum types.
Any mirror reflection, mappable or RawRepresentable solutions are welcome.
I would like something like this:
enum Test : String {
case One = "1"
}
Test.rawValueFromKey(keyValue: "One") // Must return "1"
// I don't want the solution below, I must get the rawValue through the key name as String.
Test.One.rawValue
I do need to get the rawValue passing the name of the key as a String. Otherwise, I will need to make a switch or many if/else conditions. I have a big Enum and I don't want to check the string passed in a switch for example and pass Test.One.rawValue. I would like to get the rawValue directly through the key as String, just like in a dictionary.
I also don't want the code below, because I have a Enum of 40 items and I think it is ugly to make a switch of 40 items.
switch keyValue {
case "One":
return Test.One.rawValue
break
}
I want something like this:
func rawValueFromKey (keyValue: String) -> Test {
// code here to get the rawValue through the keyValue as String
// return the proper Test Enum
}
I tried some possible solutions using Mirror reflection and enum iterations to find the rawValue through the keyValue but didn't work.
Please give the solution in both Swift 2 and 3.
Thanks
As far as I know, you can't reflect on types so I think you will be able to achieve this only with CustomReflectable or maybe using localized strings for mapping the values/keys. Invariably you'll have to map somehow.
Why not something like this? Sure you are just comparing Strings so it's potentially unsafe (if your enum would conform to CustomStringConvertible, etc), but you can't really avoid that.
I think CaseIterable is available only from Swift 4 though...
protocol KeyRepresentable: CaseIterable {
}
extension KeyRepresentable {
static func fromKey(key: String) -> Self? {
return Self
.allCases
.first { "\($0)" == key }
}
}
Usage:
enum Option: Int, KeyRepresentable {
case a = 1, b = 2
}
print(Option.fromKey(key: "a")?.rawValue)

Trying to work with an enum that has duplicate values in Swift

I have an enum of type String which needs to have multiple variables that have the same value. My enum looks something like this:
class MyClass {
enum MyEnum: String {
case blahA = "blaha"
case blahB = "blahb"
...
static var blahD = "blah"
static var blahE = "blah"
}
}
The reason why I'm using static var's in the above construction is because both "blahD" and "blahE" need to reference the same String value, used in different places (don't ask me why, it just has to be this way). However, I have a method where I need to pass in the value of the enum as follows:
if let testString = myString(foo: MyEnum.blahD) {...}
I unfortunately am getting the following compilation error:
Cannot convert value of type "String" to expected argument type "MyClass.MyEnum".
How do I get around passing the above variable which has duplicate values in my enum in the method, but cast it to the type of "MyClass.MyEnum"?
You can do this if you make the extra case reference the other enum case directly instead of just assigning them the same string value:
class MyClass {
enum MyEnum: String {
case blahA = "blaha"
case blahB = "blahb"
...
case blahD = "blah"
static var blahE = MyEnum.blahD
}
}
Then you can pass MyEnum.blahE the same way you would pass MyEnum.blahD
If the function takes a value of type MyEnum, you cannot do this. The type properties blahD and blahE are simply not of that type. Only the cases of the enum are of type MyEnum.
The function parameter's type must be changed to String.
The only other way around it would be to add a case to the enum that has a raw value matching the value of those two properties: case blahDOrE = "blah". Then you could construct that case: MyEnum(rawValue: MyEnum.blahD), but I can't see that being very useful.

Resources