Decode string or int value in structure will return string("1") in Swift 4 - swift

I have a structure that some of the values will get Integer or string so I used this structure in my structure to parse JSON in the correct way but the problem is that when I want to print the value it will print string("1") instead of 1.
public struct player1_result_sheet : Decodable {
let ans_1 : QuantumValue?
let ans_2 : QuantumValue?
let ans_3 : QuantumValue?
let ans_4 : QuantumValue?
}
enum QuantumValue: Decodable {
case int(Int), string(String)
init(from decoder: Decoder) throws {
if let int = try? decoder.singleValueContainer().decode(Int.self) {
self = .int(int)
return
}
if let string = try? decoder.singleValueContainer().decode(String.self) {
self = .string(string)
return
}
throw QuantumError.missingValue
}
enum QuantumError:Error {
case missingValue
}
}
Here is the printing after decode:
(self.res?.response?.detailData?[indexPath.row].player1_result_sheet?.ans_1!)!

If you want to print the value without the enum wrapper, just implement description:
extension QuantumValue: CustomStringConvertible {
public var description: String {
switch self {
case let .string(string):
return string
case let .int(number):
return "\(number)"
}
}
}

Your enum case is string(String) so it prints case(value)
string("1")
You can solve it by creating varible inside enum which returns you a value depends on case of QuantumValue
var value: Any {
switch self {
case .int(let value):
return value
case .string(let value):
return value
}
}
then you can use it like this:
...ans_1!.value)!
1
Also note that type of value is Any so if you want to work with it as String, you have to downcast it
if let string = ...ans_1.value)! as? String {
...
}

QuantumValue is declared as enum, both good cases have associated values.
So printing a value prints both, the case and the associated value.
You could add two properties intValue and stringValue inside QuantumValue
var intValue : Int? {
guard case .int(let num) = self else { return nil }
return num
}
var stringValue : String? {
guard case .string(let string) = self else { return nil }
return string
}
Then you can print
player1_result_sheet?.ans_1?.intValue
By the way the name player1_result_sheet is pretty php-ish.
Please conform to the naming convention.
Structs, classes and enums are UpperCamelCased → Player1ResultSheet
Variables and functions are lowerCamelCased → ans1
And please consolidate your optionals, 6 (six) question and exclamation marks in one line is pretty weird
(self.res?.response?.detailData?[indexPath.row].player1_result_sheet?.ans_1!)!

Related

Accessing decodable enum after parsing in SwiftUI [duplicate]

In this code I've written a really useless enum that defines a possible Number with Int or Float.
I can't understand how can I access the value that I set with the association. If I try to print it I get just (Enum Value)
enum Number {
case int (Int)
case float (Float)
}
let integer = Number.int(10)
let float = Number.float(10.5)
println("integer is \(integer)")
println("float is \(float)")
For sake of completeness, enum's association value could be accesed also using if statement with pattern matching. Here is solution for original code:
enum Number {
case int (Int)
case float (Float)
}
let integer = Number.int(10)
let float = Number.float(10.5)
if case let .int(i) = integer {
print("integer is \(i)")
}
if case let .float(f) = float {
print("float is \(f)")
}
This solution is described in detail in: https://appventure.me/2015/10/17/advanced-practical-enum-examples/
The value is associated to an instance of the enumeration. Therefore, to access it without a switch, you need to make a getter and make it available explicitly. Something like below:
enum Number {
case int(Int)
case float(Float)
func get() -> NSNumber {
switch self {
case .int(let num):
return num
case .float(let num):
return num
}
}
}
var vInteger = Number.int(10)
var vFloat = Number.float(10.5)
println(vInteger.get())
println(vFloat.get())
Maybe in the future something like that may be automatically created or a shorter convenience could be added to the language.
It surprises me that Swift 2 (as of beta 2) does not address this. Here's an example of a workaround approach for now:
enum TestAssociatedValue {
case One(Int)
case Two(String)
case Three(AnyObject)
func associatedValue() -> Any {
switch self {
case .One(let value):
return value
case .Two(let value):
return value
case .Three(let value):
return value
}
}
}
let one = TestAssociatedValue.One(1)
let oneValue = one.associatedValue() // 1
let two = TestAssociatedValue.Two("two")
let twoValue = two.associatedValue() // two
class ThreeClass {
let someValue = "Hello world!"
}
let three = TestMixed.Three(ThreeClass())
let threeValue = three. associatedValue() as! ThreeClass
print(threeValue.someValue)
If your enum mixes cases with and without associated values, you'll need to make the return type an optional. You could also return literals for some cases (that do not have associated values), mimicking raw-value typed enums. And you could even return the enum value itself for non-associated, non-raw-type cases. For example:
enum TestMixed {
case One(Int)
case Two(String)
case Three(AnyObject)
case Four
case Five
func value() -> Any? {
switch self {
case .One(let value):
return value
case .Two(let value):
return value
case .Three(let value):
return value
case .Four:
return 4
case .Five:
return TestMixed.Five
}
}
}
let one = TestMixed.One(1)
let oneValue = one.value() // 1
let two = TestMixed.Two("two")
let twoValue = two.value() // two
class ThreeClass {
let someValue = "Hello world!"
}
let three = TestMixed.Three(ThreeClass())
let threeValue = three.value() as! ThreeClass
print(threeValue.someValue)
let four = TestMixed.Four
let fourValue = four.value() // 4
let five = TestMixed.Five
let fiveValue = five.value() as! TestMixed
switch fiveValue {
case TestMixed.Five:
print("It is")
default:
print("It's not")
}
// Prints "It is"
like #iQ. answer, you can use property in enum also
enum Number {
case int (Int)
var value: Int {
switch self {
case .int(let value):
return value
}
}
}
let integer = Number.int(10)
println("integer is \(integer.value)")
I have used something like this:
switch number {
case .int(let n):
println("integer is \(n)")
case .float(let n):
println("float is \(n)")
}
If you're using guard, you can write like below:
enum Action {
case .moveTab(index: Int)
}
guard let case .moveTab(index) = someAction else { return }
You can access enum associated value not only through switch! Mirrors come to our aid
Let's create a protocol
protocol MirrorAssociated {
var associatedValues: [String: Any] { get }
}
extension MirrorAssociated {
var associatedValues: [String: Any] {
var values = [String: Any]()
if let associated = Mirror(reflecting: self).children.first {
let children = Mirror(reflecting: associated.value).children
for case let item in children {
if let label = item.label {
values[label] = item.value
}
}
}
return values
}
}
and use it like this:
enum Test: MirrorAssociated {
case test(value: String, anotherValue: Int)
}
Now we can access any associated value without using switch:
let test: Test = .test(value: "Test String", anotherValue: 1337)
if let value = test.associatedValues["value"] as? String {
print("\(value)") // "Test String"
}
if let intValue = test.associatedValues["anotherValue"] as? Int {
print("\(intValue)") // 1337
}
Swift 5
enum Directory {
case accountImages(URL)
case accountData(URL)
var url: URL {
switch self {
case .accountImages(let url):
return url
case .accountData(let url):
return url
}
}
}
func save(to directory: Directory) {
let dir = directory.url
}
Swift 4,
I have created a simple enum with associated values for handling firebase database reference paths
import Firebase
enum FirebaseDBConstants {
case UserLocation(database : DatabaseReference, userID :String)
case UserRequest(database : DatabaseReference, requestID :String)
func getDBPath() -> DatabaseReference {
switch self {
case .UserLocation(let database,let userID):
return database.root.child(FirebaseDBEnvironmentEnum.getCurrentEnvioronMent()).child("Location").child(userID).child("JSON")
case .UserRequest(let database,let requestID):
return database.root.child(FirebaseDBEnvironmentEnum.getCurrentEnvioronMent()).child("Request").child(requestID)
default:
break
}
}
}
Use it like as shown
//Pass Database refenence root as parameter with your request id
let dbPath = FirebaseDBConstants.UserRequest(database: database, requestID: requestId).getDBPath()

Getter Setter With Type of Any Swift

Is it possible to do a getter and setter for an attribute that has a type of 'Any'
Here is my thought:
private var _valueObject: Any?
public var valueObject: Any? {
set {
if newValue is String {
self._valueObject = newValue as? String
} else if newValue is BFSignature {
self._valueObject = newValue as? BFSignature
}
}
get {
if self._valueObject is String {
return self._valueObject as? String
} else if self._valueObject is BFSignature {
return self._valueObject as? BFSignature
} else {
return self._valueObject
}
}
}
When I try to use it through out my code though I get errors stating:
Cannot compare String to type Any
Is there a way to use something like this without casting the 'valueObject' to a string whenever I need it. A way to use it and it already knows its a 'String' or 'BFSignature' instead of 'Any'.
Here is an example of the error:
I would rather it just know that cellValue is a 'String.' Instead of casting it each time I use it.
You shouldn't use Any
In my opinion, you should make a common representation of the API call result instead of using Any. You know exactly what the API is going to return, don't you? It's either a String or something that you turn into your custom object BFSignature.
Therefore, you can make an enum to represent your API call result:
enum APIResult {
case signature(BFASignature)
case justString(String)
}
and use it like
private var _valueObject: APIResult?
if let stringValue = newValue as? String {
self._valueObject = .justString(stringValue)
}
if let signatureValue = newValue as? BFSignature {
self._valueObject = .signature(signatureValue)
}
If there are a fixed number of types that you need to use here, you can use an enum:
struct BFSignature {
var a: Int
}
enum Either {
case bfSig(BFSignature)
case string(String)
}
var a: Either
var b: Either
a = .bfSig(BFSignature(a: 7))
b = .string("Stack Overflow")
a = b
Usage:
switch (b) {
case Either.bfSig(let signature):
print(signature.a) // Output integeral value
case Either.string(let str):
print(str) //Output string value
}

Can you initialize an Enum value from the name of its case (*not* its RawValue?)

Consider this enumeration (note its type is Int)
enum MyTestEnum : Int{
case one = 1
case eight = 8
case unknown = -1
}
You can easily initialize a version of this based on the raw value, like so...
let value = MyTestEnum(rawValue:-1)
I'm trying to find out if it can be initialized with a string representation of the case name itself (again, not the raw value, but the word after 'case') like so...
let value = MyTestEnum(caseName:"eight")
Note: I want this to work with any enum if possible, regardless of its raw value type. For instance, this one...
enum MyOtherEnum{
case xxx
case yyy
case zzz
}
let value = MyOtherEnum(caseName:"xxx")
So can this be done?
Thoughts:
I think Swift has a way to instantiate a class given a string representing the fully-qualified class-name. Perhaps something similar can be used here.
In Swift 4.2, this is quite easy to do now using CaseIterable.
enum MyOtherEnum: CaseIterable {
case xxx
case yyy
case zzz
init?(caseName: String) {
for value in MyOtherEnum.allCases where "\(value)" == caseName {
self = value
return
}
return nil
}
}
enum MyTestEnum: Int, CaseIterable{
case one = 1
case eight = 8
case unknown = -1
init?(caseName: String) {
for value in MyTestEnum.allCases where "\(value)" == caseName {
self = value
return
}
return nil
}
}
What I am doing here is creating a failable initializer which iterates through all potential cases, testing to see if "\(value)" (which returns the name for that potential case) matches the caseName argument passed in to the initializer.
When a match is found, self is set and the loop ends. Otherwise, nil is returned for the call.
Below, are two working and two failing examples:
let myOtherEnum = MyOtherEnum(caseName:"xxx")
print(myOtherEnum) // MyOtherEnum.xxx
let myTestEnum = MyTestEnum(caseName:"eight")
print(myTestEnum?.rawValue) // 8
let myOtherEnumFail = MyOtherEnum(caseName:"aaa")
print(myOtherEnumFail) // nil
let myTestEnumFail = MyTestEnum(caseName:"ten")
print(myTestEnumFail) // nil
Based on CodeBender's solution here is a nice extension for this case
extension CaseIterable {
///Note: case value and name can be different
init?(caseName: String) {
for v in Self.allCases where "\(v)" == caseName {
self = v
return
}
return nil
}
}
You can go with custom initializer
extension MyTestEnum {
public static func allValues() -> [MyTestEnum] {
let retVal = AnySequence { () -> AnyIterator<MyTestEnum> in
var raw = 0
return AnyIterator {
let current = withUnsafePointer(to: &raw) {
$0.withMemoryRebound(to: MyTestEnum.self, capacity: 1) { $0.pointee }
}
guard current.hashValue == raw else { return nil }
raw += 1
return current
}
}
return [MyTestEnum](retVal)
}
init?(caseName: String){
for v in MyTestEnum.allValues() {
if "\(v)" == caseName {
self = v
return
}
}
self = MyTestEnum.unknown
}
}
let test = MyTestEnum(caseName: "eight")
or simple manually all your case :)
extension MyTestEnum {
init?(caseName: String){
switch caseName {
case "eight": self.init(rawValue: 8)
case "one": self.init(rawValue: 1)
default: self.init(rawValue: -1)
}
}
}
let test1 = MyTestEnum(caseName: "eight")
let test2 = MyTestEnum(rawValue: 1)
Hope this helps !!
It sounds like your want your enum cases to have raw Int values and raw String values. It's not strictly possible to do this, but perhaps this comes close:
enum MyTestEnum : String {
case one
case eight
case unknown
var intValue: Int {
switch self {
case one: return 1
case eight: return 8
case unknown: return -1
}
}
}
Now you can initialize from case names, but you can also retrieve an intValue when needed. (If needed, you could easily add a failable initializer to allow initialization from integers as well.)

In Swift, is it possible to convert a string to an enum?

If I have an enum with the cases a,b,c,d is it possible for me to cast the string "a" as the enum?
Sure. Enums can have a raw value. To quote the docs:
Raw values can be strings, characters, or any of the integer or
floating-point number types
— Excerpt From: Apple Inc. “The Swift Programming Language.” iBooks. https://itun.es/us/jEUH0.l,
So you can use code like this:
enum StringEnum: String
{
case one = "value one"
case two = "value two"
case three = "value three"
}
let anEnum = StringEnum(rawValue: "value one")!
print("anEnum = \"\(anEnum.rawValue)\"")
Note: You don't need to write = "one" etc. after each case. The default string values are the same as the case names so calling .rawValue will just return a string
EDIT
If you need the string value to contain things like spaces that are not valid as part of a case value then you need to explicitly set the string. So,
enum StringEnum: String
{
case one
case two
case three
}
let anEnum = StringEnum.one
print("anEnum = \"\(anEnum)\"")
gives
anEnum = "one"
But if you want case one to display "value one" you will need to provide the string values:
enum StringEnum: String
{
case one = "value one"
case two = "value two"
case three = "value three"
}
All you need is:
enum Foo: String {
case a, b, c, d
}
let a = Foo(rawValue: "a")
assert(a == Foo.a)
let 💩 = Foo(rawValue: "💩")
assert(💩 == nil)
In Swift 4.2, the CaseIterable protocol can be used for an enum with rawValues, but the string should match against the enum case labels:
enum MyCode : String, CaseIterable {
case one = "uno"
case two = "dos"
case three = "tres"
static func withLabel(_ label: String) -> MyCode? {
return self.allCases.first{ "\($0)" == label }
}
}
usage:
print(MyCode.withLabel("one")) // Optional(MyCode.one)
print(MyCode(rawValue: "uno")) // Optional(MyCode.one)
In case with an enum with Int type you can do it so:
enum MenuItem: Int {
case One = 0, Two, Three, Four, Five //... as much as needs
static func enumFromString(string:String) -> MenuItem? {
var i = 0
while let item = MenuItem(rawValue: i) {
if String(item) == string { return item }
i += 1
}
return nil
}
}
And use:
let string = "Two"
if let item = MenuItem.enumFromString(string) {
//in this case item = 1
//your code
}
Riffing on djruss70's answer to create highly generalized solution:
extension CaseIterable {
static func from(string: String) -> Self? {
return Self.allCases.first { string == "\($0)" }
}
func toString() -> String { "\(self)" }
}
Usage:
enum Chassis: CaseIterable {
case pieridae, oovidae
}
let chassis: Chassis = Chassis.from(string: "oovidae")!
let string: String = chassis.toString()
Note: this will unfortunately not work if the enum is declared #objc. As far as I know as of Swift 5.3 there is no way to get this to work with #objc enum's except brute force solutions (a switch statement).
If someone happens to know of a way to make this work for #objc enums, I'd be very interested in the answer.
Swift 4.2:
public enum PaymentPlatform: String, CaseIterable {
case visa = "Visa card"
case masterCard = "Master card"
case cod = "Cod"
var nameEnum: String {
return Mirror(reflecting: self).children.first?.label ?? String(describing: self)
}
func byName(name: String) -> PaymentPlatform {
return PaymentPlatform.allCases.first(where: {$0.nameEnum.elementsEqual(name)}) ?? .cod
}
}
Extending Duncan C's answer
extension StringEnum: StringLiteralConvertible {
init(stringLiteral value: String){
self.init(rawValue: value)!
}
init(extendedGraphemeClusterLiteral value: String) {
self.init(stringLiteral: value)
}
init(unicodeScalarLiteral value: String) {
self.init(stringLiteral: value)
}
}
For Int enum and their String representation, I declare enum as follows:
enum OrderState: Int16, CustomStringConvertible {
case waiting = 1
case inKitchen = 2
case ready = 3
var description: String {
switch self {
case .waiting:
return "Waiting"
case .inKitchen:
return "InKitchen"
case .ready:
return "Ready"
}
}
static func initialize(stringValue: String)-> OrderState? {
switch stringValue {
case OrderState.waiting.description:
return OrderState.waiting
case OrderState.inKitchen.description:
return OrderState.inKitchen
case OrderState.ready.description:
return OrderState.ready
default:
return nil
}
}
}
Usage:
order.orderState = OrderState.waiting.rawValue
let orderState = OrderState.init(rawValue: order.orderState)
let orderStateStr = orderState?.description ?? ""
print("orderStateStr = \(orderStateStr)")
I used this:
public enum Currency: CaseIterable, Codable {
case AFN = 971 // Afghani (minor=2)
case DZD = 012 // Algerian Dinar (minor=2)
...
private static var cachedLookup: [String: Currency] = [:]
init?(string: String) {
if Self.cachedLookup.isEmpty {
Self.cachedLookup = Dictionary(uniqueKeysWithValues: Self.allCases.map { ("\($0)", $0) })
}
if let currency = Self.cachedLookup[string] {
self = currency
return
} else {
return nil
}
}
}
I found the other answers make this way more complicated then it needs to be. Here is a quick and concise example.
enum Gender: String {
case male, female, unspecified
}
Simple enum, note that I added ": String" to the enum itself to declare the type as string.
Now all you have to do is:
let example: Gender = Gender(rawValue: "male")
And thats it, 'example' is now an enum of type Gender with the value of .male
There is literally nothing else you need to do in Swift 4+.

Accessing an Enumeration association value in Swift

In this code I've written a really useless enum that defines a possible Number with Int or Float.
I can't understand how can I access the value that I set with the association. If I try to print it I get just (Enum Value)
enum Number {
case int (Int)
case float (Float)
}
let integer = Number.int(10)
let float = Number.float(10.5)
println("integer is \(integer)")
println("float is \(float)")
For sake of completeness, enum's association value could be accesed also using if statement with pattern matching. Here is solution for original code:
enum Number {
case int (Int)
case float (Float)
}
let integer = Number.int(10)
let float = Number.float(10.5)
if case let .int(i) = integer {
print("integer is \(i)")
}
if case let .float(f) = float {
print("float is \(f)")
}
This solution is described in detail in: https://appventure.me/2015/10/17/advanced-practical-enum-examples/
The value is associated to an instance of the enumeration. Therefore, to access it without a switch, you need to make a getter and make it available explicitly. Something like below:
enum Number {
case int(Int)
case float(Float)
func get() -> NSNumber {
switch self {
case .int(let num):
return num
case .float(let num):
return num
}
}
}
var vInteger = Number.int(10)
var vFloat = Number.float(10.5)
println(vInteger.get())
println(vFloat.get())
Maybe in the future something like that may be automatically created or a shorter convenience could be added to the language.
It surprises me that Swift 2 (as of beta 2) does not address this. Here's an example of a workaround approach for now:
enum TestAssociatedValue {
case One(Int)
case Two(String)
case Three(AnyObject)
func associatedValue() -> Any {
switch self {
case .One(let value):
return value
case .Two(let value):
return value
case .Three(let value):
return value
}
}
}
let one = TestAssociatedValue.One(1)
let oneValue = one.associatedValue() // 1
let two = TestAssociatedValue.Two("two")
let twoValue = two.associatedValue() // two
class ThreeClass {
let someValue = "Hello world!"
}
let three = TestMixed.Three(ThreeClass())
let threeValue = three. associatedValue() as! ThreeClass
print(threeValue.someValue)
If your enum mixes cases with and without associated values, you'll need to make the return type an optional. You could also return literals for some cases (that do not have associated values), mimicking raw-value typed enums. And you could even return the enum value itself for non-associated, non-raw-type cases. For example:
enum TestMixed {
case One(Int)
case Two(String)
case Three(AnyObject)
case Four
case Five
func value() -> Any? {
switch self {
case .One(let value):
return value
case .Two(let value):
return value
case .Three(let value):
return value
case .Four:
return 4
case .Five:
return TestMixed.Five
}
}
}
let one = TestMixed.One(1)
let oneValue = one.value() // 1
let two = TestMixed.Two("two")
let twoValue = two.value() // two
class ThreeClass {
let someValue = "Hello world!"
}
let three = TestMixed.Three(ThreeClass())
let threeValue = three.value() as! ThreeClass
print(threeValue.someValue)
let four = TestMixed.Four
let fourValue = four.value() // 4
let five = TestMixed.Five
let fiveValue = five.value() as! TestMixed
switch fiveValue {
case TestMixed.Five:
print("It is")
default:
print("It's not")
}
// Prints "It is"
like #iQ. answer, you can use property in enum also
enum Number {
case int (Int)
var value: Int {
switch self {
case .int(let value):
return value
}
}
}
let integer = Number.int(10)
println("integer is \(integer.value)")
I have used something like this:
switch number {
case .int(let n):
println("integer is \(n)")
case .float(let n):
println("float is \(n)")
}
If you're using guard, you can write like below:
enum Action {
case .moveTab(index: Int)
}
guard let case .moveTab(index) = someAction else { return }
You can access enum associated value not only through switch! Mirrors come to our aid
Let's create a protocol
protocol MirrorAssociated {
var associatedValues: [String: Any] { get }
}
extension MirrorAssociated {
var associatedValues: [String: Any] {
var values = [String: Any]()
if let associated = Mirror(reflecting: self).children.first {
let children = Mirror(reflecting: associated.value).children
for case let item in children {
if let label = item.label {
values[label] = item.value
}
}
}
return values
}
}
and use it like this:
enum Test: MirrorAssociated {
case test(value: String, anotherValue: Int)
}
Now we can access any associated value without using switch:
let test: Test = .test(value: "Test String", anotherValue: 1337)
if let value = test.associatedValues["value"] as? String {
print("\(value)") // "Test String"
}
if let intValue = test.associatedValues["anotherValue"] as? Int {
print("\(intValue)") // 1337
}
Swift 5
enum Directory {
case accountImages(URL)
case accountData(URL)
var url: URL {
switch self {
case .accountImages(let url):
return url
case .accountData(let url):
return url
}
}
}
func save(to directory: Directory) {
let dir = directory.url
}
Swift 4,
I have created a simple enum with associated values for handling firebase database reference paths
import Firebase
enum FirebaseDBConstants {
case UserLocation(database : DatabaseReference, userID :String)
case UserRequest(database : DatabaseReference, requestID :String)
func getDBPath() -> DatabaseReference {
switch self {
case .UserLocation(let database,let userID):
return database.root.child(FirebaseDBEnvironmentEnum.getCurrentEnvioronMent()).child("Location").child(userID).child("JSON")
case .UserRequest(let database,let requestID):
return database.root.child(FirebaseDBEnvironmentEnum.getCurrentEnvioronMent()).child("Request").child(requestID)
default:
break
}
}
}
Use it like as shown
//Pass Database refenence root as parameter with your request id
let dbPath = FirebaseDBConstants.UserRequest(database: database, requestID: requestId).getDBPath()