0

I have a function in Swift that returns Any? value and I would like to cast the result (based on the value type) to Bool, String or Int.

The function is this one:

static func value(forKey: SettingKeys) -> Any? {
    return settingManager.settings.filter({$0.key == forKey.rawValue}).first?.value
}

and then when I do:

let printingEnabled = AppSettings().value(forKey:"printingEnabled") as? Int

i get NIL. Without the casting, the value is Optional("1"). Similar when I try casting results to String or Int. Any idea of how I could cast the result to a type of my choosing?

Thank you.

Silviu B.
  • 43
  • 8
  • `if let pritingEnabledAsString = AppSettings().value(forKey:"printingEnabled") as? String { let printEnabledAsInt = Int(pritingEnabledAsString) }`? Simply said: a cast doesn't do magic. It doesn't interpret the value as another type if it can't. – Larme Sep 07 '18 at 14:24
  • Thank you. It works as an Int, but it doesn't as Bool. – Silviu B. Sep 07 '18 at 14:28

1 Answers1

2

You don't want a function to return Any?. It is a nightmare of a type. Instead, you want to use generics to make sure the value is the type you want. For example:

static func value<T>(ofType: T.Type, forKey: SettingKeys) -> T? {
    return settingManager.settings.filter({$0.key == forKey.rawValue}).first?.value as? T
}

Then this would be:

let printingEnabled = AppSettings().value(ofType: Int.self, forKey:"printingEnabled")

printingEnabled will in this case be Int?.

You can also, of course, wrap this up in helpers like:

static func int(forKey: SettingKeys) -> Int? {
    return value(ofType: Int.self, forKey: forKey)
}

(BTW, while you definitely want to avoid Any? and use this kind of approach, I can't actually reproduce your issue. printingEnabled is Int? for me, and is Optional(1) as expected. I don't see any case where it's nil. But I can't make your code compile as written, either, since it's not clear what SettingKeys and settings are, so my code is probably significantly different than yours.)

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • Thank you for your answer. I tried with String and it works just perfect. Problem is it returns nil for int and Bool. Here's my code: 'let enablePrintString = AppSettings.value(ofType: String.self, forKey: SettingKeys.enablePrinting) let enablePrintInt = AppSettings.value(ofType: Bool.self, forKey: SettingKeys.enablePrinting) let enablePrintBool = AppSettings.value(ofType: Bool.self, forKey: SettingKeys.enablePrinting)' – Silviu B. Sep 07 '18 at 14:43
  • And only returns Optional("1") for String – Silviu B. Sep 07 '18 at 14:44
  • 1
    I suspect that you have Strings in your settings, then. (I specifically suspect that the strings is literally `"Optional(1)"`.) So the bug is in how you're storing this data, not how you retrieve it. – Rob Napier Sep 07 '18 at 14:44
  • Yes, the data is in a database, stored as String – Silviu B. Sep 07 '18 at 14:47
  • 1
    So there's your issue. The string `"Optional(1)"` is not an Int, so `as? Int` is correctly `nil`. – Rob Napier Sep 07 '18 at 14:48
  • I'm using GRDB as database and storing inside a table values for keys. – Silviu B. Sep 07 '18 at 14:48
  • The values can differ (string, int, bool) and that is why I was hoping to convert those values after retrieving them from the database. – Silviu B. Sep 07 '18 at 14:49
  • OK, but if you're storing the string `Optional(1)`, you're going to need to do a lot of parsing on that to get it down to just the string `"1"`. Then you can can use the `Int.init?(string:)` initializer to convert it to an int. You don't use `as?` for that. But the point is that these aren't integers. They're strings. You need to convert them to strings before you can start parsing them into other types. – Rob Napier Sep 07 '18 at 14:51
  • Yes, your code is correct. And I will mark it as an answer to the question because it actually answers it. I just have to find a way to sort out my problem, which is how to store my data into the database in the correct format.. – Silviu B. Sep 07 '18 at 15:06