Search code examples
swiftmeasurement

Why is my kg/lb conversion returning 0?


My function is used to convert between kilograms and pounds. For some unknown reason, the converted(to:) method seems to be either returning 0 (which I think is unlikely) or the assignment of the returned value to liftWeight isn't working.

func convertUnit(liftEvent: LiftEventRepresentable ) -> LiftEventRepresentable {
    let unit = liftEvent.liftWeight.unit.symbol
    switch unit {
    case "kg":
        let weight = liftEvent.liftWeight
        print("Event weight is \(weight)") // 110.0 kg
        let liftWeight = weight.converted(to: .pounds) // this is returning 0 or the assignment isn't working
        print("The new weight is \(liftWeight)") 0.0 lb
        liftEvent.updateWeightLifted(liftWeight.value)
        liftEvent.updateWeightUnit(liftEvent.liftWeight.unit.symbol)
        return liftEvent
    case "lb":

        // I'll finish this case after I get case "kg": working

    default:
        fatalError("Oops, this should not happen")
    }
}

I thought it might be similar to a problem I had in the past which involved trying to assign a Measurement to an optional but I don't have any optional values in this method so I assume I must be doing something else wrong.

LiftEvent:

extension LiftEvent {
    @NSManaged var date: Date
    @NSManaged var repetitions: NSNumber
    @NSManaged var uid: NSNumber?
    @NSManaged var weightLifted: NSNumber
    @NSManaged var weightUnit: String
    @NSManaged var formula: Formula
    @NSManaged var lift: Lift
}

The LiftEventRepresentable protocol is:

@objc protocol LiftEventRepresentable {
    var date: Date { get }
    var formula: Formula { get }
    var repetitions: NSNumber { get set }
    var liftWeight: Measurement<UnitMass> { get }
    var maxWeight: Double { get }
    var lift: Lift { get }
    var context: NSManagedObjectContext { get }
    var userDidChangeUnits: Bool { get set }

    func updateWeightLifted(_ weight: Double)
    func updateRepetitions(_ repetitions: Int)
    func updateMaxAmount(_ maxAmount: Measurement<UnitMass>) -> ()
    func updateLiftEventFormula(_ newFormulaUuid: String) -> ()
    func updateLift(_ newLiftUuid: String) -> ()
    func updateWeightUnit(_ newUnit: String)
    func calculateOneRepMax() -> Measurement<UnitMass>
    func updateLiftEventWithFinalValues()
}

Can anyone tell me why liftEvent.liftWeightis 0.0 lb after the conversion?

EDIT 1: This is how liftEvent.liftWeight is produced (which I should have included in my question originally). It's a computed property and I've now learning it's what causes the conversion to always return 0):

class LiftEvent: NSManagedObject, LiftEventRepresentable {

    var liftWeight: Measurement<UnitMass> {
        let defaultUnit = UserDefaults.weightUnit() // returns a string ("kg" or "lb")
        let unitSymbol = UnitMass.init(symbol: defaultUnit) // the wrong way
        let liftWeight = Measurement<UnitMass>(value: Double(weightLifted), unit: unitSymbol)
    return liftWeight
    }

EDIT 2: The root problem was, indeed, the liftEvent.liftWeight being invalid.

This is my new code which now works:

var liftWeight: Measurement<UnitMass> {
    let defaultUnit = UserDefaults.weightUnit() // returns a string ("kg" or "lb")
    let unit: UnitMass
    if defaultUnit == "kg" {
        unit = UnitMass.kilograms // the right way
        } else {
        unit = UnitMass.pounds // also the right way
    }
    let liftWeight = Measurement<UnitMass>(value: Double(weightLifted), unit: unit)
    return liftWeight
}

Solution

  • Something is wrong with your Measurement's unit. It's a UnitMass so it has the correct dimension, but its converter is missing. This is probably because you created it using something like UnitMass(symbol: "kg") instead of UnitMass.kilograms.

    // right way
    let goodUnit = UnitMass.kilograms
    let goodWeight:Measurement = Measurement(value: 110.0, unit: goodUnit)
    print("Good pounds: ", goodWeight.converted(to: .pounds)) // 242.508686220216 lb
    
    // wrong way (results in 0.0 output)
    let badUnit = UnitMass(symbol: "kg")
    let badWeight:Measurement = Measurement(value: 110.0, unit: badUnit)
    print("Bad pounds: ", badWeight.converted(to: .pounds)) // 0.0 lb
    

    Creating a unit with UnitMass(symbol: "kg") is actually valid code, so there's no error. You can use this syntax to create your own mass units with any symbol you want and a custom converter. For some reason the converter: parameter is optional, so UnitMass(symbol: "kg") just makes a new mass unit with the symbol "kg" and a default UnitLinearConverter with a coefficient of 0.

    Using UnitMass.kilograms instead gives you a predefined kilogram unit that's already setup with the correct symbol and converter. Easy mistake to make, especially since there's no error.

    I recommend taking a look at where you create liftEvent.liftWeight, as that is likely where the problem lies.