12

This seems like a super basic question, but I just can't seem to find the answer anywhere :-( I am able to do this in Objective C, but I am getting stuck in Swift.

What I need to do:

  1. Take an Integer value
  2. Format it into a localized string
  3. Inject the value into another string using the stringWithFormat equivalent method (since the other string is localized as well, which is not shown in simplified examples below)

How it's easily done in Objective C -- this works:

    // points is of type NSNumber *

    NSNumberFormatter *formatter = [NSNumberFormatter new];
    formatter.locale             = [NSLocale currentLocale];
    formatter.numberStyle        = NSNumberFormatterDecimalStyle;

    NSString *ptsString = [formatter stringFromNumber:points];
    NSString *message   = [NSString stringWithFormat:@"You've earned %@ points", ptsString];

My best attempt at doing this in Swift -- compiler error on last line:

    // points is of type Int

    let formatter         = NSNumberFormatter()
    formatter.locale      = NSLocale.currentLocale()
    formatter.numberStyle = NSNumberFormatterStyle.DecimalStyle

    let ptsString = formatter.stringFromNumber(points)!
    let message   = String(format: "You've earned %@ points", arguments: ptsString)

I'm getting the following error in Xcode on that last line:

"Cannot convert value of type 'String' to expected argument type '[CVarArgType]'"

(In my actual code, the message into which I want to insert the points value is itself localized as well, but I have simplified this example, as I'm getting the exact same error in both cases.)

What am I missing here..?

Thanks so much for any help,

Erik

Erik van der Neut
  • 2,245
  • 2
  • 22
  • 21
  • That's indeed pretty basic. Please read the chapter [Strings and Characters](https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html) in the Swift Language Guide, paragraph String Interpolation – vadian Mar 04 '16 at 08:50
  • Thanks. Reading now... – Erik van der Neut Mar 04 '16 at 08:52
  • Ah, yes, I know about that. I should clarify in my question that I cannot make use of String Interpolation because the message with %@ in it is actually localized, translated in 8 different languages. – Erik van der Neut Mar 04 '16 at 08:55
  • 2
    You were almost there. In this line `let message = String(format: "You've earned %@ points", arguments: ptsString)`, just remove `arguments:`... I.e. it should be `let message = String(format: "You've earned %@ points", ptsString)` – 0x416e746f6e Mar 04 '16 at 08:55
  • Ah, thanks Anton! I see now... Indeed works when I take that off. If I leave it on, then I need to wrap the variable in a collection (as pointed out by totiG below). – Erik van der Neut Mar 04 '16 at 09:01
  • 1
    Just to clarify - if you use the String Format initializer with the arguments parameter then you need to wrap it in an array. If you leave out the arguments parameter you do not need to wrap this in an array. The reason is that there are two initializers for the arguments - the one accepts a CVarArgType array, the other accepts variadic arguments. By specifying the arguments parameter you are trying to use the initializer that requires the array. – totiDev Mar 04 '16 at 09:11

2 Answers2

23

You need to wrap the arguments in a collection. Like this:

let message   = String(format: "You've earned %@ points", arguments: [ptsString])

You can also use this method:

let message   = "You've earned \(ptsString) points"

Additionally you can create an extension method to do this:

extension String {
    func format(_ parameters: CVarArg...) -> String {
        return String(format: self, arguments: parameters)
    }
}

Now you can do this:

let message = "You've earned %@ points".format("test")
let message2params = "You've earned %@ points %@".format("test1", "test2")
Joannes
  • 2,569
  • 3
  • 17
  • 39
totiDev
  • 5,189
  • 3
  • 30
  • 30
  • Ah, thank you! Indeed wrapping it in a collection fixes the problem: `String(format: "You've earned %@ points", arguments: [ptsString])`. How come that is necessary in this case? From other examples I have seen online I did not see the `[` and `]` brackets around the argument variable... – Erik van der Neut Mar 04 '16 at 08:58
  • 1
    You don't need to wrap the arguments into an array, see Anton's comment to the question. – Martin R Mar 04 '16 at 09:01
  • Brilliant! Thanks everyone for the super fast response. Wow... Thanks so much! – Erik van der Neut Mar 04 '16 at 09:04
20

Sometimes, you need a little more control - so if you need to have leading zeros, you could use 'stringWithFormat' just like in objective-C

let ptsString = String(format: "%02d", points)
Russell
  • 5,436
  • 2
  • 19
  • 27