WeSplit Challenge

 Apr '20 I'm sure this is terrible coding but this is how I achieved the Challenge, pointers are much appreciated: struct ContentView: View { @State private var checkAmount = "" @State private var numberOfPeople = 2 @State private var tipPercentage = "" let tipPercentages = [10, 15, 20, 25, 0] var totalPerPerson: Double { let peopleCount = Double(numberOfPeople + 2) let tipSelection = Double(tipPercentage) ?? 0 let orderAmount = Double(checkAmount) ?? 0 let tipValue = orderAmount / 100 * tipSelection let grandTotal = orderAmount + tipValue let amountPerPerson = grandTotal / peopleCount `````` return amountPerPerson } var totalAddition:Double { let amountTotal = Double(checkAmount) ?? 0 let percentageTotal = Double(tipPercentage) ?? 0 let addPercentage = amountTotal * percentageTotal / 100 let totalPercentage = amountTotal + addPercentage return totalPercentage } var body: some View { NavigationView { Form { Section { TextField("Amount", text: \$checkAmount) .keyboardType(.decimalPad) Picker("Number of people", selection: \$numberOfPeople) { ForEach(2 ..< 100) { Text("\(\$0) people") } } } Section(header:Text("How much tip do you want to leave?")) { TextField("Tip percentage", text: \$tipPercentage) .keyboardType(.decimalPad) } Section(header:Text("Amount per person")) { Text("£\(totalPerPerson, specifier:"%.2f")") } Section(header:Text("Total amount including tip")){ Text("£\(totalAddition, specifier:"%.2f")") } }.navigationBarTitle("WeSplit") } }`````` struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } } 3 Apr '20 I did something similar: ``````struct ContentView: View { @State private var checkAmount = "" @State private var numberOfPeople = "" @State private var tipPercentage = 2 let tipPercentages = [10, 15, 20, 25, 0] var totalPerPerson: Double { let peopleCount = Double(numberOfPeople) ?? 0 let tipSelection = Double(tipPercentages[tipPercentage]) let orderAmount = Double(checkAmount) ?? 0 let tipValue = orderAmount / 100 * tipSelection let grandTotal = orderAmount + tipValue let amountPerPerson = grandTotal / peopleCount return amountPerPerson } var totalWithTip: Double { let tipSelection = Double(tipPercentages[tipPercentage]) let orderAmount = Double(checkAmount) ?? 0 let tipValue = orderAmount / 100 * tipSelection let grandTotal = orderAmount + tipValue return grandTotal } var body: some View { NavigationView { Form { Section { TextField("Amount", text: \$checkAmount) .keyboardType(.decimalPad) TextField("Number of people", text: \$numberOfPeople) .keyboardType(.decimalPad) } Section(header: Text("How much tip would you like to leave?")) { Picker("Tip percentage", selection: \$tipPercentage) { ForEach(0 ..< tipPercentages.count) { Text("\(self.tipPercentages[\$0])%") } } .pickerStyle(SegmentedPickerStyle()) } Section(header: Text("Amount per person")) { Text("\$\(totalPerPerson, specifier: "%.2f")") } Section(header: Text("Total amount including tip")) { Text("\$\(totalWithTip, specifier: "%.2f")") } } .navigationBarTitle("WeSplit") } } }`````` Did you try the third challenge? Change the “Number of people” picker to be a text field, making sure to use the correct keyboard type. I tried as you can see in my code above, but that sets the Amount per person at \$-NaN until I enter a value into both text fields. Not sure how to fix that... Otherwise it works fine! 3 May '20 @joforsell I solved that small annoyance of showing infinity or -NaN `````` import SwiftUI struct ContentView: View { @State private var checkAmmount = "" @State private var numberOfPeople = "" @State private var tipPercentage = 2 let tipPercentages = [10,20,25,0] var totalPerPerson:Double{ let peopleCount = Double(numberOfPeople) ?? 1 let tipSelection = Double(tipPercentages[tipPercentage]) let orderAmmount = Double(checkAmmount) ?? 0 let tipValue = orderAmmount / 100 * tipSelection let grandTotal = orderAmmount + tipValue let ammountPerPerson = grandTotal / peopleCount return ammountPerPerson } var body: some View { NavigationView{ Form{ Section{ TextField("enter ammount", text: \$checkAmmount) .keyboardType(.numberPad) TextField("number of people", text: \$numberOfPeople) .keyboardType(.numberPad) } Section(header: Text("How much tip you wanna pay")){ Picker("Tip Percentage", selection: \$tipPercentage){ ForEach(0.. 0 ? totalPerPerson : 0 , specifier:"%.2f")") } Section(header: Text("Total ammount")){ Text(String((Int(checkAmmount) ?? 0) + tipPercentages[tipPercentage])) } }.navigationBarTitle("WeSplit") } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } `````` 4
 SPONSORED Superwall lets you build & test paywalls without shipping updates. Run experiments, offer sales, segment users, update locked features and more at the click of button. Best part? It's FREE for up to 250 conversions / mo and the Superwall team builds out 100% custom paywalls – free of charge. Sponsor Hacking with Swift and reach the world's largest Swift community!
 Jul '20 Hi All, Thanks to some of your help , I got inspired and solved the challenge!! Although, I see Amount per person as \$-NaN as default , everything else seems ok !! I am sure there must be many ways to fine tune it !! however , below is my code !! import SwiftUI struct ContentView: View { ``````@State private var checkAmount = "" @State private var numberOfPeople = "" // changed to take in STR now @State private var tipPercentage = 2 let tipPercentages = [10,15,20,25,0] var totalPerPerson: Double { let peopleCount = Double(numberOfPeople) ?? 0 // changed to have nil coelacing of str. let tipSelection = Double(tipPercentages[tipPercentage]) // instance to store section value from its array let orderAmount = Double(checkAmount) ?? 0 // nil coelacing to ensure only int instead of str is taken let tipValue = orderAmount / 100 * tipSelection // calculate tip value perctage from order amount let grandTotal = orderAmount + tipValue // order amount + tip value to divide let amountPerPerson = grandTotal / peopleCount // return amountPerPerson } var grandTotalValue: Double { let tipSelection1 = Double(tipPercentages[tipPercentage]) // instance to store section value from its array let orderAmount1 = Double(checkAmount) ?? 0 // nil coelacing to ensure only int instead of str is taken let tipValue1 = orderAmount1 / 100 * tipSelection1 let grandtotalval = tipValue1 + orderAmount1 return grandtotalval } var body: some View { NavigationView { Form { Section { TextField("Amount", text:\$checkAmount) .keyboardType(.decimalPad) // There is an issue here decimal pad is not showing up`````` // Picker("Number of people", selection: \$numberOfPeople) { // ForEach(2 ..< 100) { // Text("(\$0) people") // } // } TextField("Number of people", text:\$numberOfPeople) // changed picker to text. .keyboardType(.decimalPad) `````` } 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 for the Check")) { Text("\$\(grandTotalValue, specifier: "%.2f")") } Section(header: Text("Amount per person")) { Text("\$\(totalPerPerson, specifier: "%.2f")") } } .navigationBarTitle("WeSplit") } } }`````` struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } 3 Oct '20 Hello @pradeephoskote, Change the following code: ``let peopleCount = Double(numberOfPeople) ?? 0`` to this: ``let peopleCount = Double(numberOfPeople) ?? 2 // Default value for 2 people`` Hope that solves your problem. 3 Nov '20 I thought users might want to know how much their tip is, so I added that section, but I get an error "cannot find tipValue in scope". same with grandTotal, even though they are declared near the beginning. Not sure what's going on. P.S. I also fixed the -NaN issue with an if statement ``````import SwiftUI struct ContentView: View { @State private var checkAmount = "" @State private var numberOfPeople = "" @State private var tipPercentage = 2 let tipPercentages = [10, 15, 18, 20, 25, 0] var totalPerPerson: Double { let peopleCount = Double(numberOfPeople) ?? 0 let tipSelection = Double (tipPercentages[tipPercentage]) let orderAmount = Double(checkAmount) ?? 0 let tipValue = orderAmount / 100 * tipSelection <--------- Here it's declared let grandTotal = orderAmount + tipValue <----------Here too var amountPerPerson = grandTotal / peopleCount if peopleCount == 0 { <-------------------------- Fixes the -NaN issue amountPerPerson = 0 } else { return amountPerPerson } return amountPerPerson } var body: some View { NavigationView { Form { Section { TextField ("Check Amount", text: \$checkAmount) .keyboardType(.decimalPad) TextField("Number of People", text: \$numberOfPeople) .keyboardType(.decimalPad) } 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("Tip Amount")){ Text("\$\(tipValue, specifier: "%.2f")") <------------------------------Error } Section (header: Text("Total + Tip ")){ Text("\$\(grandTotal, specifier: "%.2f")") <---------------------------Error } Section (header: Text("Amount Per Person")){ Text("\$\(totalPerPerson, specifier: "%.2f")") } } .navigationBarTitle("WeSplit") } } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }`````` 3 Nov '20 You need to declare them outside of `totalPerPerson`. They are currently declared within the computed property instead of being properties of the struct. 3 Nov '20 Where/How do I declare it? If I copy it under tipPercentages, I get the following error: "Cannot use instance member 'numberOfPeople' within property initializer; property initializers run before 'self' is available" ``````struct ContentView: View { @State private var checkAmount = "" @State private var numberOfPeople = "" @State private var tipPercentage = 2 let tipPercentages = [10, 15, 18, 20, 25, 0] let peopleCount = Double(numberOfPeople) ?? 0 let tipSelection = Double (tipPercentages[tipPercentage]) let orderAmount = Double(checkAmount) ?? 0 let tipValue = orderAmount / 100 * tipSelection let grandTotal = orderAmount + tipValue var totalPerPerson: Double { let peopleCount = Double(numberOfPeople) ?? 0 let tipSelection = Double (tipPercentages[tipPercentage]) let orderAmount = Double(checkAmount) ?? 0 let tipValue = orderAmount / 100 * tipSelection let grandTotal = orderAmount + tipValue var amountPerPerson = grandTotal / peopleCount`````` 3 Nov '20 Yes, it is under `tipPercentage` but it also needs to be an `@State` property, since it is mutating the struct. 3 Nov '20 I made those variables @State private var (not sure if they needed to be private var or not) but I still get the same error message. "Cannot use instance member 'numberOfPeople' within property initializer; property initializers run before 'self' is available" for all 3 variables ``````@State private var peopleCount = Double(numberOfPeople) ?? 0 @State private var tipSelection = Double (tipPercentages[tipPercentage]) @State private var orderAmount = Double(checkAmount) ?? 0`````` 3 Nov '20 A couple things, now that you tried it: 1- You need to initialize them first in the declaration, then you can use them as per your first example, as part of the computed property. This will stop the error from showing within the section. (referring to the comment of where you show the declaration and error locations). 2- Alternatively you could create new computed properties: ``````private var grandTotal: Double { return totalPerPerson * (Double(numberOfPeople) ?? 0)`````` which you can declare after `totalPerPerson` 3- Or if you prefer you can use functions that return a double. I personally prefer that approach as it allows me to see things clearly and perform any safety checks I want without cluttering my code... but it's not "better"... just a preference. `````` func orderTotal() -> Double { guard let peopleCount = Double(numberOfPeople) else { return 0 } return totalPerPerson * peopleCount }`````` PS: I apologize for not clarifying it earlier, but the error you were getting first, is in reference to scope. Essentially, your properties were declared inside the scope of your computed property and therefore are not accessible outside of it. So when you declare properties, they can be accessed by anything within the same scope or deeper. The use of `private` is to make sure that no other part of the code that is outside the scope (in this example the ContentView struct) has access to them. This is done for safety reasons. The second error (which I am apologizing for) is due to the usage of other properties to initialize them. So `grandTotal` for instance, is initializing itself using another property. Since these are not yet initialized, they can not be used. Sorry for not clarifying that when I suggested you move them up one step. I hope this explains things clearly. 3 Nov '20 I'm sorry, but I'm a beginner. I don't understand your vocabulary when you say "You need to initialize them first in the declaration" or "computed property". And sorry this is a lot to ask, but: I've tried to put the code you provided but I keep getting errors. 1. Do I delete the @State private vars of peopleCount, tipSelection, and orderAmount? 2. If I don't, I still get the error I've describe in my earlier posts, but if I delete those then I get an error on let tipValue and grandTotal: "Cannot find 'orderAmount' in scope" ``````import SwiftUI struct ContentView: View { @State private var checkAmount = "" @State private var numberOfPeople = "" @State private var tipPercentage = 2 let tipPercentages = [10, 15, 18, 20, 25, 0] @State private var peopleCount = Double(numberOfPeople) ?? 0 @State private var tipSelection = Double (tipPercentages[tipPercentage]) @State private var orderAmount = Double(checkAmount) ?? 0 let tipValue = orderAmount / 100 * tipSelection let grandTotal = orderAmount + tipValue var totalPerPerson: Double { let peopleCount = Double(numberOfPeople) ?? 0 let tipSelection = Double (tipPercentages[tipPercentage]) let orderAmount = Double(checkAmount) ?? 0 let tipValue = orderAmount / 100 * tipSelection let grandTotal = orderAmount + tipValue var amountPerPerson = grandTotal / peopleCount if peopleCount == 0 { amountPerPerson = 0 } else { return amountPerPerson } return amountPerPerson } func orderTotal() -> Double { guard let peopleCount = Double(numberOfPeople) else { return 0 } return totalPerPerson * peopleCount }`````` 3 Nov '20 There's no need to apologize, we're all here learning. By initialize I mean similar to what you are doing with tipPercentage. You are initializing it with the value 2. Basically you are giving it some initial value. The computed property in this example, which I recommend you go over the tutorial for that again, means it is calculated instead of having a set value... hope that explains it. What you have in your example, is duplicates. You have the @State outside of your totalPerPerson and then you have them again inside. You can remove the ones inside now. Then make tipValue and grandTotal @State too so you can update the UI accordingly, and remove them from inside totalPerPerson. You also don't need the orderTotal() function if you are using grandTotal. This was a preference. You use either the function or the property. Your code should look like this: ``````import SwiftUI struct ContentView: View { @State private var checkAmount = "" @State private var numberOfPeople = "" @State private var tipPercentage = 2 let tipPercentages = [10, 15, 18, 20, 25, 0] @State private var peopleCount = Double(numberOfPeople) ?? 0 @State private var tipSelection = Double (tipPercentages[tipPercentage]) @State private var orderAmount = Double(checkAmount) ?? 0 @State let tipValue = orderAmount / 100 * tipSelection @State let grandTotal = orderAmount + tipValue var totalPerPerson: Double { var amountPerPerson = grandTotal / peopleCount if peopleCount == 0 { amountPerPerson = 0 } else { return amountPerPerson } return amountPerPerson } // A different way to approach totalPerPerson (Use your version or this one... not Both) var totalPerPerson: Double { guard peopleCount > 0 else { return 0 } return grandTotal / peopleCount } }`````` Important Note: The only reason for placing tipValue, orderAmount and grandTotal outside of totalPerPerson is in order for you to access them for your additional feature. You could have kept everything as per the tutorial, and then use totalPerPerson as your baseline, in order to calculate the new details you want, which is why I provided the example of the function... if you notice it uses totalPerPerson, and multiplies it by peopleCount (which is declared inside the function again)... but this is only if you keep the format of the tutorial... Sorry it takes me time to reply, it all depends on free time and memory. Let me know if you have any more questions on this. 3 Nov '20 I totally understand that your time is limited and I truly appreciate you taking the time to reply to all of my threads. I copied your code and pasted into mine and I still get the "Cannot use instance member 'tipPercentage' within property initializer; property initializers run before 'self' is available". I also get the error "Cannot find '\$tipPercentage' in scope" in my picker and cannot find 'numberOfPeople' in scope" in my grandTotal var even though we declared them at the top with @State(s) 3 Nov '20 I really should stop checking code late at night. Sorry about that... I don't know what I was thinking. That was the reason I was suggesting the use of functions. But anyway, here's what I should have pasted with comments for clarity. Again sorry. One more thing: you cannot use `@State` with a computed property, but the UI will still update because as the `@State` properties the change the UI will refresh. ``````struct ContentView: View { @State private var checkAmount = "" @State private var numberOfPeople = "" @State private var tipPercentage = 2 let tipPercentages = [10, 15, 18, 20, 25, 0] //We turn this into a computed property so we can access the //checkAmount, numberOfPeople and tipPercentage //when these change the computed property will change too var tipValue: Double { let orderAmount = Double(checkAmount) ?? 0 let tipSelection = Double(tipPercentages[tipPercentage]) return orderAmount / 100 * tipSelection } //Same with this one. var grandTotal: Double { let orderAmount = Double(checkAmount) ?? 0 return orderAmount + tipValue } //Same here var totalPerPerson: Double { let peopleCount = Double(numberOfPeople) ?? 0 let amountPerPerson = grandTotal / peopleCount //Notice how we just use a simpple if statement if peopleCount == 0 { return 0 } else { return amountPerPerson } } //This is where your body should be. //Make sure you are calling the right @State in the right place var body: some View { ... } } // closing brace of ContentView`````` This was a good lesson for me too. 😉. Always check and refactor code when you are well rested, otherwise the above mistake happens. We could have turned `orderAmount` into a computed property or `tipSelection` and even `peopleCount`, but there's no need, we are not repeating enough times to warrant a separate property, and this is clean enough. 3 Nov '20 This seems like it works even though it rounds up is some cases. struct ContentView: View { ``````@State private var checkAmount = "" @State private var numberOfPeople = "" @State private var tipPercentage = 2 let tipPercentages = [10, 15, 20, 25, 0] var totalPerPerson: Double { let peopleCount = Double(numberOfPeople) ?? 0 let tipSelection = Double(tipPercentages[tipPercentage]) let orderAmount = Double(checkAmount) ?? 0 let tipValue = orderAmount / 100 * tipSelection let grandTotal = orderAmount + tipValue let amountPerPerson = grandTotal / peopleCount return amountPerPerson } var peopleNumber: Double { let personCount = Double(numberOfPeople) ?? 0 //reused peopleCount from totalPerson return personCount } var body: some View { NavigationView { Form { Section { TextField("Amount", text: \$checkAmount) .keyboardType(.decimalPad) 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 { TextField("Number of People", text: \$numberOfPeople) .keyboardType(.numberPad) } } Section(header: Text("Amount per person")) { Text("\$\(totalPerPerson, specifier: "%.2f")") } /*multiply totalPerPerson and peopleNumber to get result, it rounds up in certain situtaions though*/ Section(header: Text("Total of Check Plus Tip")) { Text("\$\(totalPerPerson * peopleNumber, specifier: "%.2f")") } } .navigationTitle("WeSplit") } }`````` } 3 Dec '20 Try this: import SwiftUI struct ContentView: View { @State private var checkAmount = "" @State private var numberOfPeople = "" @State private var tipPercentage = 2 ``````let tipPercentages = [10, 15, 20, 25, 0] var totalPerPerson : Double { let peopleCount = Double(numberOfPeople) ?? 2 let tipSelection = Double(tipPercentages[tipPercentage]) let orderAmount = Double(checkAmount) ?? 0 let tipValue = orderAmount / 100 * tipSelection let grandTotal = orderAmount + tipValue let amountPerPerson = grandTotal / peopleCount return amountPerPerson } var totalAmountBill : Double { let tipSelection = Double(tipPercentages[tipPercentage]) let orderAmount = Double(checkAmount) ?? 0 let tipValue = orderAmount / 100 * tipSelection let grandAmount = orderAmount + tipValue return grandAmount } var body: some View { NavigationView { Form { Section { TextField("Amount", text: \$checkAmount) .keyboardType(.decimalPad) TextField("Number of people", text: \$numberOfPeople) .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("Amount per person")) { Text("\$\(totalPerPerson, specifier: "%.2f")") } Section(header: Text("Total amound")) { Text("\$\(totalAmountBill, specifier: "%.2f")") } } .navigationBarTitle("WeSplit") } }`````` } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } } 3 Dec '20 @MarcusKay I think your code is working. The only thing is, there is a large space at the top and so users have to scroll down a tiny bit to see the amount per person textfield instead of everything viewable on one page. Is there anything that can be done about that? thanks 3 Dec '20 That space on top has to do with `body` If you emebedded your view inside a `NavigationView` without giving it a title, that can appear as a white space. You could make the title displayMode `.inline` I can't really know why your code is doing that... check the layout. 3

SPONSORED Superwall lets you build & test paywalls without shipping updates. Run experiments, offer sales, segment users, update locked features and more at the click of button. Best part? It's FREE for up to 250 conversions / mo and the Superwall team builds out 100% custom paywalls – free of charge.

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.

You are not logged in