srikanth Kumar
srikanth Kumar

Reputation: 43

Extracting currency value from a formatted string in Swift

I have the following string, from which I want to remove the currency formatting and extract the numeric value for manipulation:

"Product Price":"\u00a3314.95",

I've tried using the following code:

let productvalue = model[indexPath.row].productPrice ?? ""
let prodval = productvalue.replacingOccurrences(of: "\u00a3", with: "")
let proqty = model[indexPath.row].quantity ?? ""

let totalprice = (Int(prodval)) * (Int(proqty))

However, when I run this code, I am getting the following response from an API:

Binary operator '*' cannot be applied to two 'Int?' operands

Upvotes: 0

Views: 1328

Answers (4)

Matic Oblak
Matic Oblak

Reputation: 16774

If you look closely the constructors of integers from string return optional values. So what you have is:

let totalprice = {
    let a: Int? = Int(prodval)
    let b: Int? = Int(proqty)
    return a*b // Error
}()

a quick fix is to force-unwrap it using ! resulting in let totalprice = (Int(prodval))! * (Int(proqty))! but I would not suggest it because it may crash your app.

A but more elegant solution is to use defaults:

let totalprice = {
    let a: Int = Int(prodval) ?? 0
    let b: Int = Int(proqty) ?? 0
    return a*b
}()

But on the other hand why are you even using integers here? What if the price is not a whole number? I suggest you rather use decimal numbers to handle these cases:

let a = NSDecimalNumber(string: prodval)
let b = NSDecimalNumber(string: proqty)

let totalprice = a.multiplying(by: b)

This is now working with decimal numbers directly. To get a double value or integer value you would simply need to use it's properties totalprice.doubleValue or totalprice.intValue. But there is no need for that either. If you need to convert it back to string simply use formatters:

let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.currencySymbol = "$"
let finalOutput: String = formatter.string(from: totalprice)

There are many possible solutions to this and if possible I would try to do it all with formatters and decimal numbers. For instance in your case something like the following might do the trick:

private func generateFormatter(currencySymbol: String = "$", decimalSeparator: String = ".") -> NumberFormatter {
    let formatter = NumberFormatter()
    formatter.currencySymbol = currencySymbol
    formatter.decimalSeparator = decimalSeparator
    formatter.numberStyle = .currency
    return formatter
}

private func parseValue(_ input: String?, formatterInfo: (currencySymbol: String, decimalSeparator: String)) -> NSNumber? {
    guard let input = input else { return nil }
    let formatter = generateFormatter(currencySymbol: formatterInfo.currencySymbol, decimalSeparator: formatterInfo.decimalSeparator)
    return formatter.number(from: input)
}

private func multiplyValues(_ values: [String?], formatterInfo: (currencySymbol: String, decimalSeparator: String)) throws -> NSNumber {
    return try values.reduce(NSDecimalNumber(value: 1.0)) { result, value in
        guard let parsedValue = parseValue(value, formatterInfo: formatterInfo) else {
            throw NSError(domain: "Parsing values", code: 400, userInfo: ["dev_message": "Could not parse a value \(value ?? "[Null value]")"])
        }
        return NSDecimalNumber(decimal: result.decimalValue).multiplying(by: NSDecimalNumber(decimal: parsedValue.decimalValue))
    }
}

let values = ["$1.2", "$1.6", "$2"]
let result = try? multiplyValues(values, formatterInfo: ("$", "."))

let parsedResult: String = {
    guard let result = try? multiplyValues(values, formatterInfo: ("$", ".")) else { return "Could not produce result" }
    return generateFormatter(currencySymbol: "$", decimalSeparator: ".").string(from: result) ?? "Could not format result"
}()

print(result ?? "No result")
print(parsedResult)

I hope the code speaks for itself and you can see it is easy to change/inject different formats/symbols.

Upvotes: 0

Same7Farouk
Same7Farouk

Reputation: 907

You can convert currency string to decimal

let str = "£300"

let formatter = NumberFormatter()
formatter.numberStyle = .currency
formatter.currencyCode = "GBP"

if let number = formatter.number(from: str) {
    let amount = number.decimalValue
    print(amount)
}

Upvotes: 0

Joakim Danielson
Joakim Danielson

Reputation: 52013

The problem is that the init methods you use returns an optional value so you need to include a value in case the conversion from String to Int returns nil like

let value = Int(someString) ?? 0

but in your case you are dealing with decimal values so you need to convert to Double

let totalprice = (Double(prodval) ?? 0.0) * (Double(proqty) ?? 0.0)

Upvotes: 1

PinkeshGjr
PinkeshGjr

Reputation: 8680

You can use this extension that will help you remove currency symbols from the amount.

Replace your desired currency with $ for example use € instead of unicode.

extension String {
func removeFormatAmount() -> Double {
    let formatter = NumberFormatter()

    formatter.locale = Locale(identifier: "en_US")
    formatter.numberStyle = .currency
    formatter.currencySymbol = "$"
    formatter.decimalSeparator = ","

    return formatter.number(from: self) as Double? ?? 0
 }
}

How to use this extension.

let currencyString = "$1,000.00"
let amount = currencyString.removeFormatAmount() // 1000.0

Upvotes: 1

Related Questions