Расширить все типы чисел в 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))