Swift NumberFormatter is giving compiler errors
Solution 1:
We don't format strings ourselves anymore, we let SwiftUI do it for us. The reason for this is so it can update the labels on screen if the user changes their region settings. With UIKit most of us never bothered to listen to the locale changed notification to do it.
We can simply tell SwiftUI what format we'd like:
struct AccountCard: View {
let netWorth: NetWorth
var body: some View {
VStack {
Text(netWorth.name)
Text(netWorth.investmentTotal, format: .currency(code: "USD"))
Text(netWorth.cashBalance, format: .currency(code: "USD"))
}
}
}
Or we can pass a formatter object in to the string interpolation:
let currencyFormatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
return formatter
}()
struct AccountCard: View {
let netWorth: NetWorth
var body: some View {
VStack {
Text(netWorth.name)
Text("$\(netWorth.investmentTotal, formatter: currencyFormatter)")
Text("$\(netWorth.cashBalance, formatter: currencyFormatter)")
}
}
}
Solution 2:
SwiftUI does not allow statements in its setup such as formatter.numberStyle = .currency
. There are places
in a view, such init(),
or .onAppear{}
for example, and of course any custom functions,
where such statements are allowed.
To achieve what you want you could try something like this:
struct AccountCard: View {
var netWorth: NetWorth
let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
return formatter
}()
@State var investmentTotalString: String?
var body: some View {
VStack {
Text(netWorth.name)
Text(String(format: "%.2f", netWorth.investmentTotal))
Text(String(format: "%.2f", netWorth.cashBalance))
}
.onAppear {
investmentTotalString = formatter.string(for: netWorth.investmentTotal)
}
}
}
Solution 3:
Building on what workingdog wrote, creating NumberFormatter
is an expensive operation, You should therefore make it e.g. static so it wouldn’t have to be recreated all the time. It’s also questionable whether you need State, you can always declare it as only var with getter.
struct AccountCard: View {
var netWorth: NetWorth
static let formatter: NumberFormatter = {
let formatter = NumberFormatter()
formatter.numberStyle = .currency
return formatter
}()
var investmentTotalString: String {
AccountCard.formatter.string(for: netWorth.investmentTotal)
}
var body: some View {
VStack {
Text(netWorth.name)
Text(String(format: "%.2f", netWorth.investmentTotal))
Text(String(format: "%.2f", netWorth.cashBalance))
}
}
}