100DaysofSwiftUI - Project 1 - Challenge 1: Convert temperature units: Celsius, Fahrenheit and Kelvin This is my code to solve the challenge in 100 Days of SwiftUI by Paul Hudson. This is the first challenge in the series and I chose to convert the temperature units from C° -> F°, F° -> C°, C° -> K° and K°-> C°

I need to ask, is this code readable by you and if anything needs to be changed/modified in order to make more efficient?

import SwiftUI

struct ContentView: View {
    @State private var inputTemp = ""
    @State private var inputUnit = 0
    @State private var outputUnit = 0

    let inputUnits = ["C", "F", "K"]
    let outputUnits = ["C", "F", "K"]

    var convertedDegreeC2F: Int {
        guard let inputDegree = Int(inputTemp) else { return 0 }
        let convertC2F = Int(inputDegree/5*9+32)
        let convertF2C = Int((inputDegree-32)*5/9)
        let convertC2K = Double(inputDegree) + 273.15
        let convertK2C = Double(inputDegree) - 273.15

        if inputUnit == 0 &amp;&amp; outputUnit == 1 {
           return convertC2F
        } else if inputUnit == 1 &amp;&amp; outputUnit == 0 {
            return convertF2C
        } else if inputUnit == 0 &amp;&amp; outputUnit == 2 {
            return Int(convertC2K)
        } else if inputUnit == 2 &amp;&amp; outputUnit == 0 {
            return Int(convertK2C)
        }

        return 0
    }

    var body: some View {
        NavigationView {
            Form {
                Section {
                    TextField("Input Temperature", text: $inputTemp)
                        .keyboardType(.decimalPad)
                }
                Section(header: Text("Select input unit:" )) {
                    Picker("Input Unit", selection: $inputUnit) {
                        ForEach(0..&lt;inputUnits.count) {
                            Text("\(self.inputUnits[$0])°")
                        }
                    }
                    .pickerStyle(SegmentedPickerStyle())
                }
                Section(header: Text("Select output unit:" )) {
                    Picker("Output Unit", selection: $outputUnit){
                        ForEach(0..&lt;outputUnits.count) {
                            Text("\(self.outputUnits[$0])°")
                        }
                    }
                        .pickerStyle(SegmentedPickerStyle())
                }
                Section(header: Text("Temperature after conversion is:")) {
                    Text("\(convertedDegreeC2F)°")
                }
            }
        .navigationBarTitle("UConvert")
        }
    }
}
The conversion function convertedDegreeC2F() handles only four of the nine possible conversions between the three units. In particular, the conversion between identical input/output unit is not handled. Also the function name is misleading because the function is not about conversion from Celsius to Fahrenheit only.

The conversion between temperature units can be greatly simplified using  Measurement from the Foundation framework:


  A numeric quantity labeled with a unit of measure, with support for unit conversion and unit-aware calculations.


Here is a standalone example for demonstration:

let t = Measurement(value: 10.0, unit: UnitTemperature.celsius)
print(t.converted(to: .fahrenheit).value) // 49.99999999999585
print(t.converted(to: .kelvin).value)     // 283.15


The Fahrenheit value is not “exact” due to floating point rounding issues. But that does not matter, because displaying the value should be done with a formatter anyway. Here we can use a MeasurementFormatter:


  A formatter that provides localized representations of units and measurements.


Again a standalone example for demonstration:

let formatter = MeasurementFormatter()
formatter.unitOptions = .providedUnit

let t = Measurement(value: 10.0, unit: UnitTemperature.celsius)
print(formatter.string(from: t)) //10°C
print(formatter.string(from: t.converted(to: .fahrenheit))) // 50°F
print(formatter.string(from: t.converted(to: .kelvin))) // 283.15 K


The measurement formatter adds the correct unit symbol automatically, and also localizes the output correctly (e.g. “283,15 K” with a comma as fraction separator, where appropriate).

The conversion from the input field value to a floating point number should be done with a NumberFormatter() to handle localized input correctly.

With these preparations, the correct conversion between arbitrary temperature units becomes simple:

func convert(temperature: String, from inputUnit: UnitTemperature, to outputUnit: UnitTemperature) -&gt; String? {
    let inputFormatter = NumberFormatter()
    guard let value = inputFormatter.number(from: temperature) else { return nil }

    let inputTemp = Measurement(value: value.doubleValue, unit: inputUnit)
    let outputTemp = inputTemp.converted(to: outputUnit)

    let outputformatter = MeasurementFormatter()
    outputformatter.unitOptions = .providedUnit
    return outputformatter.string(from: outputTemp)
}


In your ContentView you can then define the list of available units as

let units: [UnitTemperature] = [.celsius, .fahrenheit, .kelvin]


instead of using a list of strings. Both pickers can be created from this list (with the correct symbol for each unit), e.g.

Picker("Input Unit", selection: $inputUnit) {
    ForEach(0..&lt;units.count) {
        Text(self.units[$0].symbol)
    }
}
.pickerStyle(SegmentedPickerStyle())


Finally, converting the input value and displaying it is now simply done with

Text(convert(temperature: inputTemp, from: units[inputUnit],
            to: units[outputUnit]) ?? "Invalid input")


A small performance improvement might be to create both formatters only once, e.g. by storing them as private variables in the compilation unit:

import SwiftUI

fileprivate let inputFormatter = NumberFormatter()
fileprivate let outputformatter: MeasurementFormatter = {
    let fmt = MeasurementFormatter()
    fmt.unitOptions = .providedUnit
    return fmt
}()

func convert(...)
// ...