【问题标题】:Using Currency in Swift在 Swift 中使用货币
【发布时间】:2020-02-11 15:45:45
【问题描述】:

我正在尝试在 Swift 中创建一个函数,该函数接受一个整数作为参数,并以本地货币返回一个双精度值,例如:

输入:1250

产量:12.50 英镑

输入:12500

产出:125.00 英镑

我注意到有一个第三方库支持此功能,但不幸的是,该 repo 已存档。使用的单位类型是最小的货币类型,即minorUnits。

网络通话

/// GET - Retrive a feed of transactions during a certain period
static func get(accountUID: String, categoryUID: String, queryStartDate: String, queryEndDate: String , completionHandler: @escaping ([FeedItem], Error?) -> Void) {
    let sessionObject : URLSession = URLSession.shared
    let taskObject = sessionObject.dataTask(with: URLS().feedURLObject(accountUID,categoryUID,queryStartDate,queryEndDate)) { (Data, Response, Error) in
        guard Error == nil else {
            return
        }
        guard let Data = Data else {
            return
        }
        do {
            let feed = try JSONDecoder().decode(Feed.self, from: Data).feedItems.filter({ (item) -> Bool in
                item.direction == Direction.out
            })
            completionHandler(feed, nil)
        } catch let error  {
            print(error.localizedDescription)
        }
    }
    taskObject.resume()
}

模型 Feed 结构数量

struct Amount: Codable {
let currency: Currency
let minorUnits: Int}

单项 JSON 响应

FeedItem(feedItemUid: "0651afe9-f568-4623-ad26-31974e26015c", categoryUid: "a68f9445-4d59-44e5-9c3f-dce2df0f53d2", amount: Banking_App.Amount(currency: Banking_App.Currency.gbp, minorUnits: 551), sourceAmount: Banking_App.Amount(currency: Banking_App.Currency.gbp, minorUnits: 551), direction: Banking_App.Direction.out, updatedAt: "2020-02-04T14:09:49.072Z", transactionTime: "2020-02-04T14:09:48.743Z", settlementTime: "2020-02-04T14:09:48.992Z", source: Banking_App.Source.fasterPaymentsOut, status: Banking_App.Status.settled, counterPartyType: Banking_App.CounterPartyType.payee, counterPartyUid: Optional("fed4d40b-9ccc-411d-81c7-870164876d04"), counterPartyName: Banking_App.CounterPartyName.mickeyMouse, counterPartySubEntityUid: Optional("d6d444c0-942f-4f85-b076-d30c2f745a6f"), counterPartySubEntityName: Banking_App.CounterPartySubEntityName.ukAccount, counterPartySubEntityIdentifier: "204514", counterPartySubEntitySubIdentifier: "00000825", reference: Banking_App.Reference.externalPayment, country: Banking_App.Country.gb, spendingCategory: Banking_App.SpendingCategory.payments)

谢谢

【问题讨论】:

  • 这能回答你的问题吗? How to format a Double into Currency - Swift 3
  • 不,该值已经从 API 调用中预先确定,并且底层值是 Int 类型,我尝试使用数字格式化程序,但它为 1250 的值返回的值是 1,250 英镑应该返回 12.50 英镑。
  • 分享你的代码和json响应
  • @FurqanAgwan 您可以将值转换为Double 并除以100
  • @Sulthan 可能并非所有货币都有 100 的细分。

标签: ios swift currency


【解决方案1】:

货币应该never be represented using floating point 类型,如双。简短的原因是浮点数是以 2 为底的数字,使用它们来表示以 10 为底的数字会导致舍入错误。

要使用的货币的正确数据类型是INCurrencyAmount 或类似的东西。它的属性是 NSDecimalNumber 代表金额和 NSString 代表货币。

您可以使用 Swift 类型 Decimal 而不是 NSDecimalNumber。它还定义了常见的数学运算符,如+* 等。

【讨论】:

  • 在 Swift 中它被称为Decimal。无论如何,当金额表示为整数时,它也会起作用。我更担心没有将货币与金额一起存储。
【解决方案2】:

货币代码在标准 ISO 4217 中定义,作为标准的一部分是用于每种货币的小数位数,由于 swift 的 NumberFormatter 具有该 ISO 标准的样式,我们可以在这种情况下使用它。

创建实例并设置样式

let currencyFormatter = NumberFormatter()
currencyFormatter.numberStyle = .currencyISOCode

设置货币代码

currencyFormatter.currencyCode = "SEK"

现在currencyFormatter.minimumFractionDigitcurrencyFormatter.maximumFractionDigits 都将包含为给定货币定义的小数位数

所以现在我们可以把它放在一个函数中

func convertMinorUnits(_ units: Int, currencyCode: String) -> Decimal {
    let currencyFormatter = NumberFormatter()
    currencyFormatter.numberStyle = .currencyISOCode
    currencyFormatter.currencyCode = currencyCode.uppercased()

    return Decimal(units) / pow(10, currencyFormatter.minimumFractionDigits)
}

例子

print(convertMinorUnits(551, currencyCode: "GBP")

5.51

【讨论】:

  • 虽然这是一个有效的解决方案,但如果在这种情况下我想四舍五入到最接近的磅数,那么四舍五入的 20.64 应该产生 21。我曾尝试使用 NSDecimalRounding 但没有运气,结果应该还可以计算原始值和舍入值的差值。如果这可以通过算术来实现,例如 21 - 20.64 = 0.36
  • @FurqanAgwan 您没有说要在您的问题中四舍五入,并且像这样扩展您的问题是不可接受的,请尊重我们试图帮助您并发布新问题。
【解决方案3】:

正如其他人正确指出的那样,十进制数字应用于表示金额的任何数字。网站0.30000000000000004.com 很好地解释了它。

在将以美分或美分表示的整数值转换为以英镑或美元表示的值时,您需要知道乘数,即主要货币单位中有多少个小数货币单位(例如 1 GBP = 100 pennies 和在这种情况下,乘数为 100)。对于世界上大多数货币来说,它是 100,但有些货币有 1000 甚至没有任何小数单位(请参阅 Wikipedia)。

您通常从 API 中获取乘数,但如果它不提供,您可能会在您的应用中存储已知货币及其乘数的字典,但您有责任使其保持最新。

为了避免您的金额除以乘数,我建议计算指数并初始化十进制数,如下所示:

let amountInCents = 55123
let multiplicator = 100
let exponent = -1 * Int(log10(Double(multiplicator)))
let sign: FloatingPointSign = amountInCents < 0 ? .minus : .plus
let decimalAmount = Decimal(sign: sign, exponent: exponent, significand: Decimal(amountInCents))

然后,如果您需要格式化十进制数字以便在 UI 中显示它,您需要使用需要传递适当语言环境的数字格式化程序:

let formatter = NumberFormatter()
formatter.locale = Locale(identifier: "en_GB")
formatter.numberStyle = .currency
let formattedAmount = formatter.string(from: NSDecimalNumber(decimal: decimalAmount)) ?? decimalAmount.description
print(formattedAmount) // prints "£551.23"

【讨论】:

  • 这也是有效的解决方案,如果在这种情况下我想四舍五入到最接近的磅,说 20.64 向上取整应该产生 21。我尝试使用 NSDecimalRounding 但没有运气,结果也应该能够计算原始值和舍入值的差异。如果可以使用算术,例如 21 - 20.64 = 0.36,则向上舍入到最接近的磅并向下舍入显示是可能的。
  • 如果您在四舍五入方面需要帮助(问题中未提及),我建议您在 SO 上搜索NSDecimalRound,如果您在此处找不到解决方案,请提交一个新问题,我相信社区会很乐意提供帮助。您可以在此处提及您的新问题的链接,我也会尽力提供帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-03-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多