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))
        }
    }
}