gpbl
gpbl

Reputation: 4814

Extending type aliases in swift

I am dealing with different units, i.e. distance, altitude, speed, volume, etc.

My goal is to have an elegant, unique way to format them in the app, like for example calling myValue.formatted:

let mySpeed: Speed = 180
println(mySpeed.formatted) // 5.0 km/h

let myAltitude: Altitude = 4000
println(myAltitude.formatted) // 4000 m

I thought this was a good case for using type aliases.

typealias Distance = Float
typealias Altitude = Float
typealias Speed = Float

For the formatted property, I tried with an extension of the type Float:

extension Float {
    var formatted: String {
        get {
            switch self {
            case is Altitude:
                return "\(self) m"
            case is Speed:
                return "\(self * 3.6) km/h"
            default:
                return "\(self)"
            }
        }
    }
}

But the compiler says that my case blocks are always true.

Then i tried to extend the single type:

extension Speed {
  var formatted: String {
    return "\(self * 3.6) km/h"
  }
}
extension Altitude {
  var formatted: String {
    return "\(self) m"
  }
}

the compiler now clearly states Invalid redeclaration of 'formatted'

OK, it's clear now how type aliases work. But how would I get my .formatted property for different types of Floats in swift?

Upvotes: 8

Views: 4183

Answers (2)

codester
codester

Reputation: 37189

typealias just change or rename the type. It does not create another user type for you. You are actually extending Float for Speed, Altitude again.

You can pass 180 to your custom struct by conforming Literals types.

let mySpeed: Speed = 180

FloatLiteralConvertible and IntegerLiteralConvertible will give you same functionality you want and you can directly assign values to your custom struct types as you assign to Float

struct Speed: FloatLiteralConvertible,IntegerLiteralConvertible {

    var distance:Float
    init(floatLiteral value: Float) {
        distance = value
    }

    init(integerLiteral value: Int){
        distance = Float(value)
    }

    var formatted: String {
        return "\(distance * 3.6) km/h"
    }
}

let mySpeed: Speed = 180.0
println(mySpeed.formatted) // 5.0 km/h

Upvotes: 7

Kirsteins
Kirsteins

Reputation: 27345

Distance, Altitude, and Speed is always the same type - Float and share the same formatted property. This is how compiler sees your code:

extension Float {
    var formatted: String {
        get {
            switch self {
            case is Float:
                return "\(self) m"
            case is Float:
                return "\(self * 3.6) km/h"
            default:
                return "\(self)"
            }
        }
    }
}

I guess you need to create small wrappers for your functionality:

struct Distance {
    var value: Float
    var formatted: String {
        return "\(value) m"
    }

    init(_ value: Float) {
        self.value = value
    }
}

let myDistance = Distance(123)
myDistance.formatted

Upvotes: 1

Related Questions