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

SOLVED: Day 18: Project 1 Wrap Up: Extra Challenge

Forums > 100 Days of SwiftUI

It has been very exciting to get started with SwiftUI, make an actual app and run it in the simulator! I think I did OK and I solved the three challenges without much trouble.

However, I'm completely lost with the extra challenge, that is storing the currency formatter inside a property. Any hints on how I should approach this? Or is there a certain topic I should revisit to get closer to the answer?

5      

SPOILERS Ahead!

This is a four part answer. Each part should take you closer to a solution. Read one at a time and see if you can solve on your own. Or return for more hints.

Part I

You should be quite comfortable with basic properties such as Strings and Integers, yes?

let answerToTheUltimateQuestion = 42  // integer property
var lastName = "Dolphins"  // string property

You're assigning a value to a name. This way, in your code whenever you need a user's last name you use the variable "lastName" rather than hand typing it each time. There's a danger that 99 times you will type "Dolphins", but late one night you will type "D⚬lphins" instead.

So @twostraw's lesson seems to be: Review your code and Remove redundancies. Remove the parts of your code that are pure duplicates and are used elsewhere. Here's the example:

// See the redundant code?
Text(checkAmount, format: .currency(code: Locale.current.currencyCode ?? "USD"))
// and
Text(totalPerPerson, format: .currency(code: Locale.current.currencyCode ?? "USD"))

He asks the question Can you make a new property to store the currency formatter?

The goal then is to create a property, let's call it dollarFormatter, so you can re-use it everywhere in your Tip calculator where you want to format a Double value to look like currency.

// Goal:
Text(checkAmount, format: dollarFormat))
// and
Text(totalPerPerson, format: dollarFormat))

So you might ask yourself, "Self? What is format? Is it a String? Is it an Integer?" How do you as a Day 18 coder find out this answer? This is an IMPORTANT skill for you to have!

6      

Part II

Move your mouse over the .currency key word in the original code and hold down the option key. The cursor changes to a question mark. ❓

Click the .currency keyword and open the Developer Documentation.

This is another skill for you to develop. Learn how to read these signatures!

static func currency<Value>(code: String) -> FloatingPointFormatStyle<Value>.Currency where Value : BinaryFloatingPoint

This tells you that currency is a function that modifies some value, where the value is a BinaryFloatingPoint. How does it modify the value? It needs a code (a string) to tell it how to modify the value. The documentation also tells you the function returns a type. (Note: the type is not something simple, like a string!)

The type is complex and named FloatingPointFormatStyle<Value>.Currency In fact this is a Struct. See the documentation for more detail! The other hint @twostraws gives is that <Value> is a placeholder for any type of value. However, in your solution, you really want a Double.

@twostraws gives you this hint. So with this you can at least stub out your new property.

// Creating a new property with a special type. For this app, we want it to work with Doubles.
var dollarFormat : FloatingPointFormatStyle<Double>.Currency {
   // Magic spell here
   // not sure what goes here, but we KNOW we need to return a FloatingPointFormatStyle.
   return FloatingPointFormatStyle<Double>.Currency( === something goes here === )
}

Getting close!

8      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

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

Part III

Notice that in the original solution, the format code in Text requires a parameter named code?

// the currency func has a required parameter named code. 
// Dig into the documentation and see this is a String.
format: .currency(code: Locale.current.currencyCode ?? "USD"))

You could just hard code the string "USD". But generally this is a Bad Practice™. You should really let your users decide what currency they use, and they will log their preference in their device. So @twostraws shows how to grab the user's preference.

Locale.current.currencyCode But this returns an optional (after all, your user maybe didn't actually record a preference.) So use the nil coalescing operator to default to "USD".

Fine, this is one more step you can put into your new reusable computed property.

// Creating a new property
var dollarFormat : FloatingPointFormatStyle<Double>.Currency {
   let currencyCode = Local.current.currencyCode ?? "USD"
   // more magic here.
   return FloatingPointFormatStyle<Double>.Currency( === something goes here === )
}

5      

Part IV. Final Step.

You've used Apple's documentation to research the pieces. You broke a bigger problem into several smaller steps. And you're almost done. What part is missing?

Your goal is to return a format style initialized to be some sort of currency. How do you initialize that Currency struct?

var dollarFormat : FloatingPointFormatStyle<Double>.Currency {
   let currencyCode = Local.current.currencyCode ?? "USD"
   // How do you initialize the Currency struct?
   return FloatingPointFormatStyle<Double>.Currency( === something goes here === )  // HOW do you initialize this?
}

Again, you need to dive into the documentation. Get comfortable with this! Make this skill a GOAL.

Let your mouse hover over the word Currency in your code and hold down the option key. When your cursor turns into a ❓click the word and open the documentation.

You'll see more information about this struct, scroll down until you find the initializers. Do you see any that use currency codes? Yes?

Voila! You can now finish your new computed property.

var dollarFormat : FloatingPointFormatStyle<Double>.Currency {
   let currencyCode = Local.current.currencyCode ?? "USD"  // Get user's preference, or use default
   return FloatingPointFormatStyle<Double>.Currency( code: currencyCode )
}

Now you have a reusable property you can use in your solution. Try it!

// Day 18. Extra credit challenge
// Make a new property to store the currency formatter.
// for: @hyperDolphins
// by:  @Obelix
// Copy-paste into Playground
import SwiftUI
import PlaygroundSupport
var dollarFormatter : FloatingPointFormatStyle<Double>.Currency {
    let currencyCode = Locale.current.currencyCode ?? "USD"
    return FloatingPointFormatStyle<Double>.Currency(code: currencyCode)
}

struct dollarView : View {
    var checkAmount = 7132.42  // note! using a Double. Will this work with an Integer?
    var body: some View {
        Section {
            // Which version is more declarative?
            Text(checkAmount, format: dollarFormatter)
            // verify against previous version...
            Text(checkAmount, format: .currency(code: Locale.current.currencyCode ?? "USD"))
        }
    }
}

// Show SwiftUI views in Playground
PlaygroundPage.current.setLiveView( dollarView() )

// expected results:   $7,132.42
// Try with different currencies!

8      

Hi @hyperdolphins

In the extra challenge you were given this FloatingPointFormatStyle<Double>.Currency as a hint this is type. So you could do

let localCurrency: FloatingPointFormatStyle<Double>.Currency = 

then when you put the dot your will get.currency(code: String) and you then can replace the String with Locale.current.currencyCode ?? "USD" so end up with

let localCurrency: FloatingPointFormatStyle<Double>.Currency = .currency(code: Locale.current.currencyCode ?? "USD")

Then use that any where require localized currency

Text(grandTotal, format: localCurrency)

5      

Thanks for the replies to both of you! It seems I was already on the right track yesterday but messed up something in the syntax. I managed to write a crude but working version after Obelix's second hint.

And @Obelix yes, I did study the quick help and developer docs, actually I have been checking the official Swift documentation regularly for some time now. :) But as a beginner I often find it hard to understand. It's highly technical and English is not my native language so many of the terms are something I need to look up. And it has been very hard to find an up-to-date dictionary for programming and computer science, between English and my language. Paul's Swift glossary sometimes helps, though.

And thanks for revealing how to handle and display SwiftUI stuff in the Playground! Earlier today I was wondering if it's possible.

Anyway, I'll play around with this for a while and then I'm off to tackle Challenge 1.

3      

Thanks to you both!

2      

You guys are awesome. I was really confused by this hard challenge! Thanks for sharing your knowledge!

2      

@tomn  

This is how I tackled the extra challenge for WeSplit Day 18. I'm sure there are better ways to do it, but for now, it seems to meet the requirements.

struct ContentView: View {
    @State private var checkAmount = 0.0
    @State private var numberOfPeople = 2
    @State private var tipPercentage = 20
    @State private var tipPercentageEntry = 20
    let tipPercentages = [10, 15, 20, 25, 0]
    var totalPerPerson: [Double] {
        let peopleCount = Double(numberOfPeople + 2)
        let tipSelection = Double(tipPercentageEntry)
        let tipValue = checkAmount / 100 * tipSelection
        let grandTotal = checkAmount + tipValue
        let amountPerPerson = grandTotal / peopleCount
        return [grandTotal, amountPerPerson]
    }
    @FocusState private var amountIsFocused: Bool
    var currencyFormatter: FloatingPointFormatStyle<Double>.Currency {
      return .currency(code: Locale.current.currency?.identifier ?? "EUR")
    }
    var body: some View {
        NavigationView {
            Form {
                Section {
                    TextField("Amount", value: $checkAmount, format: currencyFormatter)
                        .keyboardType(.decimalPad)
                        .focused($amountIsFocused)
                    Picker("Number of people", selection: $numberOfPeople) {
                        ForEach(2 ..< 100) {
                            Text("\($0) people")
                        }
                    }
                } header: {
                    Text("Check")
                }

                Section {
                    Picker("Tip percentage", selection: $tipPercentageEntry) {
                        ForEach(0..<101) {
                            Text($0, format: .percent)
                        }
                    }
                } header: {
                    Text("How much tip do you want to leave?")
                }
                Section {
                    Text(totalPerPerson[1], format: .currency(code: Locale.current.currency?.identifier ?? "EUR"))
                } header: {
                    Text("Amount per person")
                }
                Section {
                    Text(totalPerPerson[0], format: currencyFormatter)
                } header: {
                    Text("Total amount")
                }
            }
            .navigationTitle("WeSplit")
            .toolbar {
                ToolbarItemGroup(placement: .keyboard) {
                    Spacer()
                    Button("Done") {
                        amountIsFocused = false
                    }
                }
            }

        }
    }
}

WeSplit

2      

Thanks everyone for sharing your knowledge.

You know when running the app in the simulator, I cannot get the keyboard to come up like Paul did, let alone make it dissapear. The "Done" button is shown on the bottom Right just like the screenshot of @tomn above.

Any explanation, please.

2      

@Obelix - Thanks, that was an amazing explanation, it helped me a lot. :)

3      

@yoid passes Day 18!

@Obelix - Thanks, that was an amazing explanation

That's why I like contributing to this forum! Others helped me on my journey. I'd like to pay it back.

Keep coding!

3      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

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.