4

I have a Json (received as text) that I convert it like this:

if let jsonData = raw.data(using: String.Encoding.utf8) {
   if let json = try JSONSerialization.jsonObject(with: jsonData, options: .allowFragments) {
      let products = json["products"] as! [Dictionary<String, AnyObject>]

Products contains an array of products (also json), where I have keys which value can be String, Float, Double... one key has a value that it can be any kind of number.

This field when it is received on text from endpoint looks like this: \"min_size\":\"0.01\" but also can have values like \"min_size\":\"1\", or \"min_size\":\"0.001\". After my json conversion looks like this (printing using po):

▿ 1 : 2 elements
  - key : "min_size"
  - value : 1

Or

▿ 15 : 2 elements
  - key : "min_size"
  - value : 0.01

But then, when I'm trying to access it on my code (I want to read it as a float, independently if it is a 1 or a 0.01), I get this:

let k = item["min_size"] // contains > ▿ Optional<Any> - some : 0.01
let i = item["min_size"] as? Float // contains > nil
let j = item["min_size"] as? Int // contains > nil

let y = item["min_size"] as? NSString // contains > "0.01"
let z = y!.floatValue // contains > "0.009999997"

I'm new on Swift, and I don't understand what's going on here.

  • Why I cannot cast directly the value (1 or 0.01) to float?
  • If not, what can I do to get a float number for this field?
  • And the most weird for me... why having a NSString that it is "0.01" converting value with floatValue, result is different? Rounding is not an option, because it could be such a long decimal too. It can be any decimal or integer number there.

Any idea what I'm doing wrong?

Pataquer
  • 283
  • 1
  • 14
  • 2
    You can't _cast_ from String to some numeric type, you need to _convert_ to a numeric type, something like `let i = Float(item["min_size"] ?? "")` – Joakim Danielson Feb 19 '21 at 08:47
  • 1
    It has double quotes around it, so it's a `String`. You can't cast it as such, you need to read how to go from `String` to `Float`. For the rest it's about precision issue, like there: https://stackoverflow.com/questions/40959254/why-are-doubles-printed-differently-in-dictionaries – Larme Feb 19 '21 at 08:48
  • 1
    swift can't allow one variable to assign 2 data type. so you need to use CGFloat in both side and assume value as 1.0 & 0.01. –  Feb 19 '21 at 08:48
  • 1
    My example should contain a `as? String` before the `??`. – Joakim Danielson Feb 19 '21 at 08:53

2 Answers2

2

Based on suggestions that are on comments, solution that I implemented is:

let min_size = (item["min_size"] as? String ?? "").convertFromJSONStringToFloat()

where I have two extensions for String and Float

extension String {
    func convertFromJSONStringToFloat() -> Float? {
        let partNumber = self.split(separator: ".")
        return (Float(self)?.roundToDecimal((partNumber.count > 1) ? partNumber[1].count : 1))!
    }
}

extension Float {
    func roundToDecimal(_ fractionDigits: Int) -> Float {
        let multiplier = pow(10, Double(fractionDigits))
        return Float((Double(self) * multiplier) / multiplier)
    }
}
Pataquer
  • 283
  • 1
  • 14
2

Your dictionary has a value type of AnyObject and your JSON min_size value is a String, you could convert String into a Float and if it does not exist provide a default value:

let y = Float(item["min_size"] as? String ?? "0") ?? 0
Eahzo
  • 21
  • 4