UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Can't figure out what's wrong with my first challenge.

Forums > 100 Days of SwiftUI

First and foremost, a big, BIG "Thank you!" to Paul Hudson for creating such an extensive learning site for Swift. The last time I wrote an app was for MS-DOS in Turbo Pascal. I have what I think is a great idea for a Mac app, but first need to educate myself in Swift.

Here's the first challenge app with the final code. It includes the challenge changes made to WeSplit.

import SwiftUI

struct ContentView: View { @State private var checkAmount = "" @State private var numberOfPeople = 2 @State private var tipPercentage = 2 @State private var piggyPeople = ""

let tipPercentages = [10, 15, 20, 25, 0]

var totalPerPerson: Double {
    guard let numOfDiners = Double(piggyPeople) else { return 0 }
    let tipSelection = Double(tipPercentages[tipPercentage])
    let orderAmount = Double(checkAmount) ?? 0

    let tipValue = orderAmount / 100 * tipSelection
    let grandTotal = orderAmount + tipValue
    let amountPerPerson = grandTotal / numOfDiners

    return amountPerPerson
}

var body: some View {
    NavigationView {
    Form {
        Section {
            TextField("Amount", text: $checkAmount)
                .keyboardType(.decimalPad)

        Section {
            TextField("Number of people", text: $piggyPeople)
                .keyboardType(.numberPad)
            }
        }

        Section(header: Text("How much tip do you want to leave?")){
            Picker("Tip percentage", selection: $tipPercentage) {
                ForEach(0 ..< tipPercentages.count) {
                    Text("\(self.tipPercentages[$0])%")
                }
            }
            .pickerStyle(SegmentedPickerStyle())
        }

        Section(header: Text("Total amount of bill - (charge plus tip).")){
            let tipSelection = Double(tipPercentages[tipPercentage])
            let orderAmount = Double(checkAmount) ?? 0
            let totalBill = orderAmount + (orderAmount / 100 * tipSelection)
            Text("$\(totalBill, specifier: "%.2f")")
        }

        Section(header: Text("Amount per person.")){
                Text("$\(totalPerPerson, specifier: "%.2f")")
            }
        }
    .navigationBarTitle("WeSplit")
    }
}

}

struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }

This all works as expected. I went on then to the first challenge (Day 19). For the life of me, I can't figure out why the picker row doesn't work, or why the variable always remains as an empty string instead of being populated with "Celcius", "Fahrenheit" or "Kelvin". Because it's always empty, the func returns nothing. You can change any == in the func to "" and then the app will always return that calculated value, proving the variable tempType is always empty.

Here's my code. What's wrong with it that the picker won't work, and the variable is always empty? Thanks in advance for looking.

import SwiftUI

struct ContentView: View { @State private var currentTemp = "" @State private var convertValue = 0.0 @State private var tempType = "" @State private var chosenConversion = 0.0

let tempTypes = ["Celsius", "Fahrenheit", "Kelvin"]

func convertedValue(tempName :String) -> Double {
    guard let startingTemp = Double(currentTemp) else { return 0 }
    var finalTempurature = 0.0

    if tempType == "Celsius" {
        let finalCelsius = ((startingTemp - 32) / 1.8)
        finalTempurature = finalCelsius
    } else {
        if tempType == "Fahrenheit" {
        let finalFarneheit = (startingTemp)
            finalTempurature = finalFarneheit
    } else {
        if tempType == "Kelvin" {
        let finalKelvin = ((startingTemp - 32) * 5 / 9 + 273.15)
        finalTempurature = finalKelvin
            }
        }
    }
    return finalTempurature
}

var body: some View {
NavigationView {
    Form {
        Section(header: Text("Tempurature")) {
            TextField("Current tempurature", text: $currentTemp)
                .keyboardType(.decimalPad)
        }

        Section(header: Text("Select conversion")) {
            Picker("Current format", selection: $tempType) {
                ForEach(0 ..< tempTypes.count) {
                    Text("\(self.tempTypes[$0])")

                }
            }
            .pickerStyle(SegmentedPickerStyle())
        }

        Section(header: Text("Converted tempurature")) {
            let chosenConversion = convertedValue(tempName: self.tempType)
            Text("\(chosenConversion, specifier: "%.2f")")
        }
    }
    .navigationBarTitle("Tempurature Converter",displayMode: .inline)
    }
}

}

struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }

3      

Hi @JKLStudiosLLC

A few things

  1. The first set of code is a different project from the second set.
  2. Your ForEach(0 ..< tipPercentages.count) is picking a Int however your func convertedValue(tempName :String) require a string

I had tempType = "" to tempType = 0 then change the error to Int and then it worked Full Code

struct ContentView: View {
    @State private var currentTemp = ""
    @State private var convertValue = 0.0
    @State private var tempType = 0
    @State private var chosenConversion = 0.0

    let tempTypes = ["Celsius", "Fahrenheit", "Kelvin"]

    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("Tempurature")) {
                    TextField("Current tempurature", text: $currentTemp)
                        .keyboardType(.decimalPad)
                }

                Section(header: Text("Select conversion")) {
                    Picker("Current format", selection: $tempType) {
                        ForEach(0 ..< tempTypes.count) {
                            Text("\(self.tempTypes[$0])")

                        }
                    }
                    .pickerStyle(SegmentedPickerStyle())
                }

                Section(header: Text("Converted tempurature")) {
                    let chosenConversion = convertedValue(tempName: self.tempType)
                    Text("\(chosenConversion, specifier: "%.2f")")
                }
            }
            .navigationBarTitle("Tempurature Converter",displayMode: .inline)
            }
    }

    func convertedValue(tempName :Int) -> Double {
        guard let startingTemp = Double(currentTemp) else { return 0 }
        var finalTemperature = 0.0

        if tempType == 0 {
            let finalCelsius = ((startingTemp - 32) / 1.8)
            finalTemperature = finalCelsius
        } else {
            if tempType == 1 {
            let finalFarneheit = (startingTemp)
            finalTemperature = finalFarneheit
        } else {
            if tempType == 2 {
            let finalKelvin = ((startingTemp - 32) * 5 / 9 + 273.15)
            finalTemperature = finalKelvin
                }
            }
        }
        return finalTemperature
    }
}

PS it usually to put properties before the body and function below. It just make it easier to read.

4      

Thank you, Nigel!

I hate when the solution is looking you right in the face, but your brain is stuck on trying to do it one way. I spent three days trying to pull the string out of the array for my == conditions when your fix of using the much easier array position did the trick.

You can also see my Pascal conditioning in action there. The "body" in Pascal is the main app code, and all functions, structs (procedures in Pascal) must go above the body so the app can find them. I'm still not used to the ideas you can:

  1. Put structs, funcs, etc. pretty much anywhere you want.
  2. Can declare variables or constants on the fly anywhere in your code.

At least now I can move on to project 2, and also the knowledge this forum is a great place to avoid doing the "lone wolf" learning Paul suggests against. I just didn't personally know anyone else programming for the Mac. Not even Facebook friends.

3      

Hi @JKLStudiosLLC

I have been thinking about it and if you want string in the logic function you can also do by changing the ForEach(0 ..< tempTypes.count) { to ForEach(tempTypes, id: \.self) { which then uses the string.

struct ContentView: View {
    @State private var currentTemp = ""
    @State private var convertValue = 0.0
    @State private var tempType = "Celsius"
    @State private var chosenConversion = 0.0

    let tempTypes = ["Celsius", "Fahrenheit", "Kelvin"]

    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("Temperature")) {
                    TextField("Current temperature in Fahrenheit", text: $currentTemp) // changed the placeholder text
                        .keyboardType(.decimalPad)
                }

                Section(header: Text("Select conversion")) {
                    Picker("Current format", selection: $tempType) {
                        ForEach(tempTypes, id: \.self) { // <- use the array for the ForEach
                            Text($0)

                        }
                    }
                    .pickerStyle(SegmentedPickerStyle())
                }

                Section(header: Text("Converted temperature")) {
                    let chosenConversion = convertedValue(tempName: self.tempType)
                    Text("\(chosenConversion, specifier: "%.2f")")
                }
            }
            .navigationBarTitle("Temperature Converter",displayMode: .inline)
            }
    }

    func convertedValue(tempName :String) -> Double {
        guard let startingTemp = Double(currentTemp) else { return 0 }
        var finalTemperature = 0.0
        switch tempType {
        case "Celsius":
            let finalCelsius = ((startingTemp - 32) / 1.8)
            finalTemperature = finalCelsius
        case "Kelvin":
            let finalKelvin = ((startingTemp - 32) * 5 / 9 + 273.15)
            finalTemperature = finalKelvin
        default:
            let finalFahrenheit = (startingTemp)
            finalTemperature = finalFahrenheit
        }
        return finalTemperature
    }
}

PS i change the function to use switch because it look and read cleaner.

I noticed that you have to enter in Fahrenheit. Are you going to do so the user can enter which input unit he want and the output unit?

3      

OR you can use enum to take away from using strings where typo can occur

enum Types: String {
    case celsius = "Celsius"
    case fahrenheit = "Fahrenheit"
    case kelvin = "Kelvin"
}

Then

struct ContentView: View {
    @State private var currentTemp = ""
    @State private var convertValue = 0.0
    @State private var tempType: Types = .celsius // <- change the format from String to 'Types'
    @State private var chosenConversion = 0.0

    let tempTypes: [Types] = [.celsius, .fahrenheit, .kelvin] // <- change the format from [String] to '[Types]'

    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("Temperature")) {
                    TextField("Current temperature in Fahrenheit", text: $currentTemp)
                        .keyboardType(.decimalPad)
                }

                Section(header: Text("Select conversion")) {
                    Picker("Current format", selection: $tempType) {
                        ForEach(tempTypes, id: \.self) {
                            Text($0.rawValue) // <- need to use rawValue

                        }
                    }
                    .pickerStyle(SegmentedPickerStyle())
                }

                Section(header: Text("Converted temperature")) {
                    let chosenConversion = convertedValue(tempName: self.tempType)
                    Text("\(chosenConversion, specifier: "%.2f")")
                }
            }
            .navigationBarTitle("Temperature Converter",displayMode: .inline)
            }
    }

    func convertedValue(tempName :Types) -> Double { // <- change the format from String to 'Types'
        guard let startingTemp = Double(currentTemp) else { return 0 }
        var finalTemperature = 0.0
        switch tempType {  // <- using enum
        case .celsius: 
            let finalCelsius = ((startingTemp - 32) / 1.8)
            finalTemperature = finalCelsius
        case .kelvin:
            let finalKelvin = ((startingTemp - 32) * 5 / 9 + 273.15)
            finalTemperature = finalKelvin
        case .fahrenheit:
            let finalFahrenheit = (startingTemp)
            finalTemperature = finalFahrenheit
        }
        return finalTemperature
    }
}

The choise is yours!

As you can see more then one way to skin a code 😂

3      

Haha! Yes, I thought about making the func use a switch statement, too. Cleaner and faster to write than a bunch of "if this do that" choices. But I was already done writing the "ifs", so left it as is.

I did mull over the silliness of having a choice for Fahrenheight when that's what the beginning value represents. It would have made more sense for the first input field to ask for a Fahrenheight value, and then have buttons only for conversions to Celcius and Kelvin. Which I see you did in another version.

One other issue the first project didn't address was negative values. You were directed to use the proper numeric keypads, which makes it impossible to enter a negative symbol on an actual iOS device. Unless there's another besides .decimalPad and .numberPad that gives you one. And … yes, there is. I replaced .decimalPad with .numbersAndPunctuation. Puts more items you can tap on the screen that having nothing to do with a numeric value, but at least the user can then enter a negative value.

3      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.