NEW: Nominations are now open for the 2019 Swift Community Awards! >>

Calculating the total per person

Paul Hudson    @twostraws   

So far the final section has shown a simple text view with whatever check amount the user entered, but now it’s time for the important part of this project: we want that text view to show how much each person needs to contribute to the payment.

There are a few ways we could solve this, but the easiest one also happens to be the cleanest one, by which I mean it gives us code that is clear and easy to understand: we’re going to add a computed property that calculates the total.

This needs to do a small amount of mathematics: the total amount payable per person is equal to the value of the order, plus the tip percentage, divided by the number of people.

But before we can get to that point, we first need to pull out the values for how many people there are, what the tip percentage is, and the value of the order. That might sound easy, but there are some small wrinkles:

  • As you’ve already seen, numberOfPeople is off by 2 – when it stores the value 3 it means 5 people.
  • The tipPercentage integer stores an index inside the tipPercentages array rather than the actual tip percentage.
  • The checkAmount property is a string that the user entered, which might be a valid number like 20, it might be a string like “fish”, or it might even be empty.

So, we’re going to create a new computed property called totalPerPerson that will be a Double, and it will start by getting those three values above.

First, add the computed property itself, just before the body property:

var totalPerPerson: Double {
    // calculate the total per person here
    return 0
}

That sends back 0 so your code doesn’t break, but we’re going to replace the // calculate the total per person here comment with our calculations.

Next, we can figure out how many people there are by reading numberOfPeople and adding 2 to it. Remember, this thing has the range 2 to 100, but it counts from 0, which is why we need to add the 2.

So, start by replacing // calculate the total per person here with this:

let peopleCount = Double(numberOfPeople + 2)

You’ll notice that converts the resulting value to a Double so we can add everything up and divide it without losing accuracy.

Next we need to figure out the actual tip percentage. Our tipPercentage property stores the value the user chose, but that’s actually just a position in the tipPercentages array. As a result, we need to look in tipPercentages to figure out what they chose, and again convert it to a Double so we can keep all our precision – add this below the previous line:

let tipSelection = Double(tipPercentages[tipPercentage])

The final number we need for our calculation is the value of their check. Now, if you remember this is actually a string right now, because it’s used as a two-way binding to a text field. Although we wrote code to show only a decimal pad keyboard, there’s nothing stopping creative users from entering invalid values in there, so we need to be careful how we handle it.

What we want to have is another Double of the check amount. We actually have a string that may or may not contain a valid Double: it might be 22.50, it might be an empty string, or it might be the complete works of Shakespeare. It’s a string – it can be pretty much anything.

Fortunately, Swift has a simple way of converting a string to a Double, and it looks like this:

let stringValue = "0.5"
let doubleValue = Double(stringValue)

That might look easy enough, but there’s a catch: the type of doubleValue ends up being Double? and not Double – yes, it’s an optional. You see, Swift can’t know for sure that the string contains something that can be safely be converted into a Double, so it uses optionality: if the conversion was successful then our optional will contain the resulting value, but if the string was something invalid (“Fish”, the complete works of Shakespeare, etc) then the optional will be set to nil.

There are several ways we could handle the optionality here, but the easiest is to use the nil coalescing operator, ??, to ensure there’s always a valid value.

Please add this code to the totalPerPerson computed property, below the previous two:

let orderAmount = Double(checkAmount) ?? 0

That will attempt to convert checkAmount into a Double, but if that fails for some reason will use 0 instead.

Now that we have our three input values, it’s time do our mathematics. This takes another three steps:

  • We can calculate the tip value by dividing orderAmount by 100 and multiplying by tipSelection.
  • We can calculate the grand total of the check by adding the tip value to orderAmount.
  • We can figure out the amount per person by dividing the grand total by peopleCount.

Once that’s done, we can return the amount per person and we’re done.

Replace return 0 in the property with this:

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

return amountPerPerson

If you’ve followed everything correctly your code should look like this:

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
}

Now that totalPerPerson gives us the correct value, we can change the final section in our table so it shows the correct text.

Replace this:

Section {
    Text("$\(checkAmount)")
}

With this:

Section {
    Text("$\(totalPerPerson)")
}

Try running the app now, and see what you think. You should find that because all the values that make up our total are marked with @State, changing any of them will cause the total to be recalculated automatically.

Hopefully you’re now seeing for yourself what it means that SwiftUI’s views are a function of their state – when the state changes, the views automatically update to match.

Before we’re done, we’re going to fix a small issue with our interface, which is the way the total price is shown. We’re using a Double for our amount calculations, which means Swift is giving us much more precision than we need – we expect to see something like $25.50, but instead see $25.500000.

We can solve this by using a neat string interpolation feature that SwiftUI adds: the ability to decide how a number ought to be formatted inside the string. This actually dates way back to the C programming language, so the syntax is a little odd at first: we write a string called a specifier, giving it the value "%.2f”. That’s C’s syntax to mean “a two-digit floating-point number.”

Very roughly, “%f” means “any sort of floating-point number,” which in our case will be the entire number. An alternative is “%g”, which does the same thing except it removes insignificant zeroes from the end – $12.50 would be written as $12.5. Putting “.2” into the mix is what asks for two digits after the decimal point, regardless of what they are. SwiftUI is smart enough to round those intelligently, so we still get good precision.

You can read more about these C-style format specifiers on Wikipedia: https://en.wikipedia.org/wiki/Printf_format_string – we aren’t going to any others, so it’s just there if you want to satisfy your curiosity!

Anyway, we want the amount per person to use our new format specifier, so please modify the total text view to this:

Text("$\(totalPerPerson, specifier: "%.2f")")

Now run the project for the last time – we’re done!

SAVE 20% ON iOS CONF SG The largest iOS conference in Southeast Asia is back in Singapore for the 5th time in January 2020, now with two days of workshops plus two days of talks on SwiftUI, Combine, GraphQL, and more! Save a massive 20% on your tickets by clicking on this link.

MASTER SWIFT NOW
Buy Testing Swift Buy Practical iOS 12 Buy Pro Swift Buy Swift Design Patterns Buy Swift Coding Challenges Buy Server-Side Swift (Vapor Edition) Buy Server-Side Swift (Kitura Edition) Buy Hacking with macOS Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with Swift Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Beyond Code

Was this page useful? Let us know!

Average rating: 5.0/5