Can't seem to use picker

I've just started learning Swift UI, and I've been using the Hacking with Swift series to learn. For a challenge, I was told to make a conversion app that utilized pickers to select values, and takes a single input value. I have a computed property that converts the input. The problem is, the input value never makes it into the var, and I get a weird debug message. I tried to print out the input value inside the var and nothing prints out. I've tried this technique in the past and it's worked fine, so I don't know what gives. Can anyone help me out? I'm attaching my code, and the error message I get (not during compiling).
Code Block //
// ContentView.swift
// weConvert
//
// Created by Aaron Goldgewert on 7/26/20.
// Copyright © 2020 Aaronthetechguy. All rights reserved.
//
import SwiftUI
struct ContentView: View {
  @State private var units = ["Farenheit", "Celcius", "Kelvin"]
  @State private var unitFrom = 0
  @State private var unitTo = 0
  @State private var input = ""
  @State var convertedValue: Double = 0
  var conversion: Double{
    let doubleInput = Double(input) ?? 0
    print(input)
    print(doubleInput)
    let stringUnitFrom = units[unitFrom]
    if stringUnitFrom == "Farenheit"{
      let stringUnitTo = units[unitTo]
      print(stringUnitTo)
      if stringUnitTo == "Celcius"
      {
        
        convertedValue = doubleInput * 1.8 + 32
        
      }
    }
     return convertedValue
  
  }
   
  var body: some View {
    NavigationView{
    Form{
      Section(header: Text("Input:")){
        Picker("Convert from:", selection: $unitFrom){
          ForEach(0..<units.count){
            Text("\(self.units[$0])")
          }
        }
         
        Picker("Convert to:", selection: $unitTo) {
          ForEach(0 ..< units.count) {
            Text("\(self.units[$0])")
          }
        }
        TextField("Input Value:", text: $input)
          .keyboardType(.decimalPad)
         
          
          
         
       
         
     
      Section{
         
        Text("Converted value: \(convertedValue)")
         
      }
  }
  }
}
}
}
struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView()
  }
}

Error message: "2020-07-27 18:36:37.893045-0400 weConvert[4885:168065] Can't find keyplane that supports type 8 for keyboard iPhone-PortraitChoco-DecimalPad; using 25793PortraitChocoiPhone-Simple-Pad_Default"


Answered by Documentation Engineer in 624576022
BabyJ is correct that you don't want to declare a State variable for your computed property. Only create state when you need to store a source of truth in your view. In this case, you want to compute a new value whenever certain other sources of truth change. There's no need to define storage for that separately.

I would also say that your units state variable would be better represented as an enumeration:

Code Block
enum Unit: String, Identifiable, CaseIterable {
    case farenheit = "Farenheit"
    case celcius = "Celcius"
    case kelvin = "Kelvin"
    var id: Unit { self }
}


You make the enum Identifiable, which makes it easier to use in a ForEach, by including the conformance in the declaration and providing the id computed property. You make an enum CaseIterable, which lets you use the allCases property, by simply declaring the conformance. With this change, your "Convert from" picker now looks like this:

Code Block
Picker("Convert from:", selection: $unitFrom) {
ForEach(Unit.allCases) { unit in
Text(unit.rawValue)
}
}


The "Convert to" picker looks similar, except it uses a binding to unitTo. These two properties, by the way, are now of type Unit, making it easier to understand what they do when you read the code:

Code Block
@State private var unitFrom: Unit = .farenheit
@State private var unitTo: Unit = .celcius


Your convertedValue also gets a little more compact, easier to read, and better type-checked because you can switch on the case names:

Code Block
var convertedValue: Double {
    guard let doubleInput = Double(input) else { return 0 }
    switch unitFrom {
    case .farenheit:
        switch unitTo {
        case .farenheit: return doubleInput
        case .celcius: return (doubleInput - 32) / 1.8
        case .kelvin: return ((doubleInput - 32) / 1.8) + 273.15
        }
    case .celcius:
        switch unitTo {
        case .farenheit: return (doubleInput * 1.8) + 32
        case .celcius: return doubleInput
        case .kelvin: return doubleInput + 273.15
        }
    case .kelvin:
        switch unitTo {
        case .farenheit: return (doubleInput * 1.8) + 32 + 273.15
        case .celcius: return doubleInput - 273.15
        case .kelvin: return doubleInput
        }
    }
}


For more information about state, see Managing User Interface State.
This works for me.
Not sure about the error though as it’s talking about the decimal pad keyboard type.

Change your conversion variable to:

Code Block Swift
var convertedValue: Double {
let doubleInput = Double(self.input) ?? 0
let stringUnitFrom = self.units[self.unitFrom]
if stringUnitFrom == "Farenheit" {
let stringUnitTo = self.units[self.unitTo]
if stringUnitTo == "Celcius" {
return doubleInput * 1.8 + 32
}
}
return 0
}


And remove
Code Block Swift
@State var convertedValue: Double = 0


This worked perfectly (except for the conversion factor). Thanks so much.
Accepted Answer
BabyJ is correct that you don't want to declare a State variable for your computed property. Only create state when you need to store a source of truth in your view. In this case, you want to compute a new value whenever certain other sources of truth change. There's no need to define storage for that separately.

I would also say that your units state variable would be better represented as an enumeration:

Code Block
enum Unit: String, Identifiable, CaseIterable {
    case farenheit = "Farenheit"
    case celcius = "Celcius"
    case kelvin = "Kelvin"
    var id: Unit { self }
}


You make the enum Identifiable, which makes it easier to use in a ForEach, by including the conformance in the declaration and providing the id computed property. You make an enum CaseIterable, which lets you use the allCases property, by simply declaring the conformance. With this change, your "Convert from" picker now looks like this:

Code Block
Picker("Convert from:", selection: $unitFrom) {
ForEach(Unit.allCases) { unit in
Text(unit.rawValue)
}
}


The "Convert to" picker looks similar, except it uses a binding to unitTo. These two properties, by the way, are now of type Unit, making it easier to understand what they do when you read the code:

Code Block
@State private var unitFrom: Unit = .farenheit
@State private var unitTo: Unit = .celcius


Your convertedValue also gets a little more compact, easier to read, and better type-checked because you can switch on the case names:

Code Block
var convertedValue: Double {
    guard let doubleInput = Double(input) else { return 0 }
    switch unitFrom {
    case .farenheit:
        switch unitTo {
        case .farenheit: return doubleInput
        case .celcius: return (doubleInput - 32) / 1.8
        case .kelvin: return ((doubleInput - 32) / 1.8) + 273.15
        }
    case .celcius:
        switch unitTo {
        case .farenheit: return (doubleInput * 1.8) + 32
        case .celcius: return doubleInput
        case .kelvin: return doubleInput + 273.15
        }
    case .kelvin:
        switch unitTo {
        case .farenheit: return (doubleInput * 1.8) + 32 + 273.15
        case .celcius: return doubleInput - 273.15
        case .kelvin: return doubleInput
        }
    }
}


For more information about state, see Managing User Interface State.
This is super helpful. Thanks so much for your help.
Can't seem to use picker
 
 
Q