2

I'm trying to use the below NumberFormatter but can't figure out how to use it in a Text view? Everything online relates to a TextView.

 static let numberFormat: NumberFormatter =  {
        let numberFormatter = NumberFormatter()
        numberFormatter.numberStyle = .none
        numberFormatter.positivePrefix = "+"
        numberFormatter.negativePrefix = "-"
        return numberFormatter
    }()

My attempted usage: but I am getting the error Instance method 'appendInterpolation(_:formatter:)' requires that 'Int' inherit from 'NSObject'

Text("\(450, formatter: Self.numberFormat)")
GarySabo
  • 5,806
  • 5
  • 49
  • 124

2 Answers2

4

Swift needs to be told to cast the Int to an NSNumber for the formatter.

Text("\(450 as NSNumber, formatter: Self.numberFormat)")

No other changes to your code necessary.

grg
  • 5,023
  • 3
  • 34
  • 50
1

Here is a simple way:

With this way, you would keep your @State as clean as possible. Also, the formatterFunction(number: Int) is reusable now and accessible from everywhere in comparison to your way, which would not be reusable or accessible.


extension Formatter {
    static let number: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.numberStyle = .none
        formatter.positivePrefix = "+"
        formatter.negativePrefix = "-"
        return formatter
    }()
}

func formatterFunction(number: Int) -> String {
    Formatter.number.string(for: number) ?? ""
}

struct ContentView: View {
    @State var number: Int = 450
    var body: some View { 
        Text(formatterFunction(number: number))
    }
}

If you just want to add + and - to your Text, then use simply this function below:

func formatterFunction(number: Int) -> String {
    if number > 0 {
        return "+" + String(number)
    }
    else if number < 0 {
        return "-" + String(abs(number))
    }
    else {
        return String(number)
    }
}
jnpdx
  • 45,847
  • 6
  • 64
  • 94
ios coder
  • 1
  • 4
  • 31
  • 91
  • 1
    No need to create a `NSNumber` object from your integer. You can use `Formatter`'s method `string(for: Any)` instead. Btw returning a "magic" string is bad practice IMO. Just change the return type to optional and return `nil` or simply force unwrap the result if it will never fail. Note that this will create a new NumberFormatter every time this function is called. And the direct access to the `description` property is discourage by Apple. Just use the String initializer. – Leo Dabus Apr 16 '21 at 19:12
  • 1
    https://developer.apple.com/documentation/swift/customstringconvertible/1539130-description – Leo Dabus Apr 16 '21 at 19:18
  • @LeoDabus: about returning `nil`, this is my personal use way I do not want deal with optional in View, the way that you said is also a good way, but i would return solid String rather than optional String or even force unwrapping. – ios coder Apr 16 '21 at 19:45
  • 1
    @swiftPunk just return an empty string – Leo Dabus Apr 16 '21 at 19:45
  • @swiftPunk yes this would always be a possibility. You can circumvent this creating a new Swift file create an extension on BinaryInteger and declare that formatter extension using file private on the same file – Leo Dabus Apr 16 '21 at 20:10
  • @LeoDabus: your code is perfect, I got this idea, how about using like this: `let numberFormatter: NumberFormatter = NumberFormatter() func function1() { numberFormatter.numberStyle = .decimal } func function2() { numberFormatter.numberStyle = .none }` we update as we want. – ios coder Apr 16 '21 at 20:16
  • @swiftPunk regarding the number style it depends on the desired output format. You can check the difference between the number styles [here](https://developer.apple.com/documentation/foundation/numberformatter/style) – Leo Dabus Apr 16 '21 at 20:33
  • @LeoDabus: I think I explained my meaning in wrong way to you, I know the difference between them, in my last comment to you I wanted to point out of creating just one instance of `NumberFormatter()` and updating it in functions if we need new style, that was the point. – ios coder Apr 16 '21 at 20:37
  • 1
    @swiftPunk this is not a god idea as you might create a racing condition depending on how you are using the formatter – Leo Dabus Apr 16 '21 at 20:39
  • 1
    it is better to have two formatters and never change its properties – Leo Dabus Apr 16 '21 at 20:42
  • interesting! So you are saying maybe function1 set the `numberStyle` to `decimal` and in the same time would function2 try set the `numberStyle` to `none` in kind of worse scenario, So in this case I would use deferent instance of `NumberFormatter()` for each functions to stop racing issue, is it ok? I think you just said it when I was typing – ios coder Apr 16 '21 at 20:45
  • 1
    @swiftPunk I mean if you are running asynchronous code sharing the same number formatter – Leo Dabus Apr 16 '21 at 20:47
  • @LeoDabus: everyday Smarter! thanks for information! :) – ios coder Apr 16 '21 at 20:48