Lets say i have something like this:
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)
}
}
I want that to support as most of swift/mac number types as possible eg. CGFLoat NSNumber Int Float etc. But i dont want to repeat myself (copy paste and extend everything) or cast everywhere i want to use that function.
I tried to extend protocols like FloatLiteralType/Convertible but needs also casting. It "should" be possible to extend basic types in a more convenient way..
I also thought of global functions but they are less discoverable and feel more hacky.
Is there a nice way to achieve this in swift?
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))
As Sogmeister
already said you will have to use Swift 2.0 to solve your problem.
Then you can do it like this:
// 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
Updated answer:
The basic idea is to extend MyProtocol
with default implementation of your functions and then extend IntegerType
and FloatingPointType
. But this won't happen in Swift 2.0 (see here). The reason why it's not working yet is here. Here is another solution, which is better then my first one.
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
We could create a protocol, extend it with a default implementation and make all our numeric types conform to it:
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.
Depending on the current locale, you can now get formatted numbers just so:
1000.localized // "1,000"
12_345_678.localized // "12,345,678"
(1_000_000 * Double.pi).localized // "3,141,592.65358979"
Of course, for more control over the formatting, we could also use NumberFormatter
in our implementation:
return NumberFormatter.localizedString(from: number, number: .decimal)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With