Extend All Number Types in Swift

Extend all number types in Swift

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

How can I make a generic extension on all number types in Swift?

You can extend Numeric protoocol and return Self:

extension Numeric {
var powered: Self { self * self }
}


CGFloat(3).powered // CGFloat 9
2.powered // Int 4
2.5.powered // Double 6.25
Decimal(5).powered // Decimal 25

Note that this is possible because Numeric protocol requires that the types that conform to it to implement * and *= operator functions.

Extend all types in Swift?

First of all, it does not extend all types. It extends T!, which is ImplicitlyUnwrappedOptional<T>. So it extends the implicitly unwrapped optional type, not all types.

The "header" is not really Swift code; it is automatically generated. There may be bugs in the automatic header generator that makes it not generate true Swift code in some cases. Don't take it too literally.

One weird thing that you notice in the automatically-generated "header" is that certain built-in syntax contractions are inconsistently applied -- the type declaration doesn't use the contraction, but the extension does:

struct Array<T>
extension T[]

enum Optional<T>
extension T?

struct ImplicitlyUnwrappedOptional<T>
extension T!

Probably some code in the automatic header generator too greedily replaces the above types with their contracted syntax. So first we pretend that extension T! actually says extension ImplicitlyUnwrappedOptional<T>.

However, extension ImplicitlyUnwrappedOptional<T> doesn't compile either, with the error "Use of undeclared type 'T'". In fact, in the automatically-generated "header", we wee many instances of extension declarations with type parameters, e.g. extension Dictionary<KeyType, ValueType>, that do not compile in Swift. This is another bug with the automatic header generation.

But removing the type parameter works:

extension ImplicitlyUnwrappedOptional : MyProtocol {
// ...
}

This is exactly what is shown in this answer.

how to extend Double and Int in Swift with the same function

You can't really since there is no common protocol involved here but as far as I understand the types that has the method random(in:) can be grouped into types conforming to two protocols so you need only two implementations

// Double & Float
extension BinaryFloatingPoint {
static func random(in range: ClosedRange<Self>, count: Int) -> [Self] where Self.RawSignificand: FixedWidthInteger {
var values = [Self]()
if count > 0 {
for _ in 0..<count {
values.append(Self.random(in: range))
}
}
return values
}
}

// Int & UInt
extension FixedWidthInteger {
static func random(in range: ClosedRange<Self>, count: Int) -> [Self] {
var values = [Self]()
if count > 0 {
for _ in 0..<count {
values.append(Self.random(in: range))
}
}
return values
}
}

Extending Generic Integer Types in Swift

For Swift 2, see Andriy Gordiychuk's answer, which will be correct then. If you require Swift 1, then this cannot be done with extensions, and must be done with free functions. This is why there are so many free functions in stdlib that became extensions in Swift 2.

For Swift 1, what you have to do is:

func clamp<T:Comparable>(value: T, #minimum:T, #maximum:T) -> T {
if value < minimum { return minimum }
if value > maximum { return maximum }
return value
}

If you prefer modifying the value (as Andriy's example does), you can do it this way:

func clamp<T:Comparable>(inout value: T, #minimum:T, #maximum:T) {
if value < minimum { value = minimum }
else if value > maximum { value = maximum }
}

Otherwise you have to write an extension on every type. It's the only other answer in Swift 1. Swift 2 is much better.

Generic extension on Numeric protocol

There's no need for T.

extension Numeric {
func percentage<Self>(of whole: Self) -> Self {
return self / whole
}
}

how to extend optional chaining for all types

is there no way to make this generic function work as a protocol method?

No, you must "explicitly apply the protocol to the types I care about".

However, you are in fact reinventing the wheel. This is the use case of flatMap/map. If both foo and bar are optional, you can write:

bar.flatMap { foo?.doSomething($0) }

Note the lack of ? after bar. You are calling flatMap on Optional, rather than bar's type. If doSomething returns T, the above expression will return T?.

If only bar is optional, use map:

bar.map { foo.doSomething($0) }

Double Extension Swift

Looks like you got a little confused about what to unwrap in your guard statement and what needed to be turned from a String into a Double. In your current code, you try to turn self, which is already a Double into a Double? -- then, you try to add that Double? to a String.

Here's a fixed version:

extension Double {
func add(_ value: String) -> Double? {
guard let numValue = Double(value) else {
return nil
}
return self + numValue
}
}

Extentions for more than one protocol at once

In general, it is not possible to write a single extension on multiple protocols. This would require the compiler to figure out the set intersection of all the members in all those protocols (those are the members you have access to in the extensions), which the compiler cannot do.

To avoid code duplication, you would need to work out the members that you need - in your case init(integerLiteral:) and <=, put those in your own protocol, and make the concrete types you want the extension to apply to, conform to your own protocol:

// inheriting from Comparable and ExpressibleByIntegerLiteral gives you the members you need
protocol BinaryNumbers : Comparable & ExpressibleByIntegerLiteral {

}

extension BinaryNumbers {
func atMost127() -> Bool {
self <= 127
}
}

extension Int: BinaryNumbers {}
extension Int8: BinaryNumbers {}
extension Int16: BinaryNumbers {}
extension Int32: BinaryNumbers {}
extension Int64: BinaryNumbers {}
// ... plus the unsigned versions, if you need them

extension Float16: BinaryNumbers {}
extension Float32: BinaryNumbers {}
extension Float64: BinaryNumbers {}
extension Float80: BinaryNumbers {}

Now you might ask, why don't we just make an extension of Comparable & ExpressibleByIntegerLiteral then, as they provide all the members we are using? Well, because that's not a nominal type, and you can't write extensions on non-nominal types :(

However, you could write it in this way:

// now you don't need your own "BinaryNumbers"!
extension Comparable where Self: ExpressibleByIntegerLiteral {
func atMost127() -> Bool {
self <= 127
}
}

You are only able to do this because the two members you need all came from protocols. If for some reason you need a member that both BinaryFloatingPoint and BinaryInteger has, but isn't provided by any of the protocols they conform to, then you need to write your own protocol with those members, and manually conform everything to that protocol.



Related Topics



Leave a reply



Submit