Расширить все типы чисел в Swift

Допустим, у меня есть что-то вроде этого:

extension NSNumber{
    func toLocalCurrency(fractDigits:Int = 2)->String{
        let formatter = NSNumberFormatter()
        formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle
        let userSettings:UserInfo? = UserInfo.first(sortDescriptors: nil, context: AERecord.defaultContext) as? UserInfo
        if let code = userSettings?.currency.name_short {
            formatter.currencyCode = code
        }
        formatter.maximumFractionDigits = fractDigits
        return formatter.stringFromNumber(self)!
    }
    func toLocalCurrencyWithoutFractionDigits()->String{
        return self.toLocalCurrency(fractDigits: 0)
    }
}

Я хочу, чтобы он поддерживал как можно больше типов чисел swift/mac, например. CGFLoat NSNumber Int Float и т. Д. Но я не хочу повторяться (копировать, вставлять и расширять все) или приводить везде, где я хочу использовать эту функцию.

Я пытался расширить протоколы, такие как FloatLiteralType/Convertible, но также нуждается в приведении. "Должно быть" возможно расширить базовые типы более удобным способом.

Я также думал о глобальных функциях, но они менее открыты и чувствуют себя более хакерскими.

Есть ли хороший способ добиться этого в кратчайшие сроки?

1 ответ

Решение

Как Sogmeister уже сказал, что вам придется использовать Swift 2.0 для решения вашей проблемы.

Тогда вы можете сделать это так:

// the solution right now is to implement it twice, I'll explain why
extension IntegerType {

    func toLocalCurrency(fractDigits:Int = 2) -> String {

        let formatter = NSNumberFormatter()
        formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle

        /* ... */

        formatter.maximumFractionDigits = fractDigits
        return formatter.stringFromNumber(self as! NSNumber)! // probably like this
    }

    func toLocalCurrencyWithoutFractionDigits() -> String {

        return self.toLocalCurrency(0)
    }
}

extension FloatingPointType {
    // second implementation goes here
}

// some example
let someUInt = UInt(12340)

someUInt.toLocalCurrency() // returns "12.340,00 €" for me

Обновленный ответ:

Основная идея заключается в расширении MyProtocol с реализацией по умолчанию ваших функций, а затем расширить IntegerType а также FloatingPointType, Но этого не произойдет в Swift 2.0 ( см. Здесь). Причина, по которой он еще не работает, здесь. Вот еще одно решение, которое лучше моего первого.

protocol MyProtocol {}

extension MyProtocol {

    func toLocalCurrency(fractDigits:Int = 2) -> String {

        let formatter = NSNumberFormatter()
        formatter.numberStyle = NSNumberFormatterStyle.CurrencyStyle

        /* ... */

        formatter.maximumFractionDigits = fractDigits
        guard let newNumber = self as? NSNumber else { fatalError("this type is not convertable to NSNumber") }
        return formatter.stringFromNumber(newNumber)!
    }

    func toLocalCurrencyWithoutFractionDigits() -> String {

        return self.toLocalCurrency(0)
    }
}

/* extend your number types you need */
extension Int : MyProtocol {} 
extension Double : MyProtocol {} // done

Мы могли бы создать протокол, расширить его реализацией по умолчанию и привести в соответствие все наши числовые типы:

protocol FormattableNumeric {}
extension FormattableNumeric {
    var localized: String {
        guard let number = self as? NSNumber else { return "NaN" }
        return number.description(withLocale: Locale.current)
    }
}
extension Int: FormattableNumeric {}
extension UInt: FormattableNumeric {}
extension Float: FormattableNumeric {}
extension Double: FormattableNumeric {}
// etc.

В зависимости от текущей локали вы можете теперь получить отформатированные числа просто так:

1000.localized // "1,000"
12_345_678.localized // "12,345,678"
(1_000_000 * Double.pi).localized // "3,141,592.65358979"

Конечно, для большего контроля над форматированием мы могли бы также использовать NumberFormatter в нашей реализации:

return NumberFormatter.localizedString(from: number, number: .decimal)

подробности

  • Xcode 10.1 (10B61)
  • Swift 4.2

Решение

extension NSNumber {
    func toLocalCurrency(fractDigits: Int = 2) -> String? {
        let formatter = NumberFormatter()
        formatter.numberStyle = .currency
        formatter.maximumFractionDigits = fractDigits
        return formatter.string(from: self)
    }
    func toLocalCurrencyWithoutFractionDigits() -> String? {
        return toLocalCurrency(fractDigits: 0)
    }
}

extension Numeric {
    func toLocalCurrency(fractDigits: Int = 2) -> String? {
        return (self as? NSNumber)?.toLocalCurrency(fractDigits: fractDigits)
    }
    func toLocalCurrencyWithoutFractionDigits() -> String? {
        return toLocalCurrency(fractDigits: 0)
    }
}

Образец

let value = 34.234

func test<T: Numeric>(value: T) {
    let toLocalCurrency = value.toLocalCurrency()
    let result = toLocalCurrency != nil ? "\(toLocalCurrency!)" : "nil"
    print(" type: \(type(of: value)), toLocalCurrency: \(result)")
}

func test<T: NSNumber>(value: T) {
    let toLocalCurrency = value.toLocalCurrency()
    let result = toLocalCurrency != nil ? "\(toLocalCurrency!)" : "nil"
    print(" type: \(type(of: value)), toLocalCurrency: \(result)")
}

test(value: Int8(value))
test(value: Int16(value))
test(value: Int32(value))
test(value: Int(value))
test(value: Float(value))
test(value: Int16(value))
test(value: Int32(value))
test(value: Int8(value))
test(value: Double(value))
test(value: CGFloat(value))
test(value: NSNumber(value: value))

Результат

Другие вопросы по тегам