Использование валюты в Swift - PullRequest
1 голос
/ 11 февраля 2020

Я пытаюсь создать функцию в Swift, которая принимает целое число в качестве параметра и возвращает двойное число в валюте региона, например:

ввод: 1250

вывод: £ 12.50

ввод: 12500

вывод: £ 125,00

Я заметил, что есть сторонняя библиотека, которая поддерживает это, но, к сожалению, репозиторий заархивирован. Тип используемых единиц - это наименьший тип валюты, который является 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()
}

Сумма структуры подачи модели

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)

Спасибо

Ответы [ 3 ]

2 голосов
/ 11 февраля 2020

Валюта не должна быть представлена ​​ с использованием типов с плавающей точкой , таких как double. Короткая причина в том, что числа с плавающей запятой являются числами с основанием 2, и использование их для представления чисел с основанием 10 приведет к ошибкам округления.

Правильный тип данных для используемых валют: INCurrencyAmount или что-то в этом роде. нравится. Это свойства NSDecimalNumber для суммы и NSString для представления валюты.

Вместо NSDecimalNumber вы можете использовать тип Swift Decimal. Он также имеет общие математические операторы, определенные как +, *, et c ..

0 голосов
/ 11 февраля 2020

Коды валют определены в стандарте ISO 4217, и в качестве части стандарта используется количество десятичных цифр, используемых для каждой валюты, и поскольку у NumberFormatter swift есть стиль для этого стандарта ISO, мы можем использовать его для этого случая.

Создайте экземпляр и установите стиль

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

Установите код валюты

currencyFormatter.currencyCode = "SEK"

Теперь currencyFormatter.minimumFractionDigit и currencyFormatter.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

0 голосов
/ 11 февраля 2020

Как правильно отметили другие, десятичные числа должны использоваться для любых чисел, представляющих денежные суммы. Веб-сайт 0.30000000000000004.com прекрасно объясняет это.

Когда дело доходит до преобразования целочисленных значений в копейках или центах в значения в фунтах или долларах, вам необходимо знать множитель, т.е. сколько в основной денежной единице есть дробные денежные единицы (например, 1 фунт стерлингов = 100 копеек, а в данном случае мультипликатор равен 100). Для большинства мировых валют это 100, но есть валюты, которые имеют 1000 или даже не имеют дробных единиц (см. Википедия ).

Обычно вы получаете мультипликатор из 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))

Затем, если вам нужно отформатировать десятичное число, чтобы отобразить его в пользовательском интерфейсе, вы хотите чтобы использовать средство форматирования чисел, которому нужно передать соответствующий языковой стандарт:

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"
...