100DaysofSwiftUI - Project 1 - Challenge 1: Convert temperature units: Celsius, Fahrenheit and Kelvin
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(...)
// ...