Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Formatting strings in Swift

In some languages, like C# for example, you can create a string in the following way:

"String {0} formatted {1} "

And then format it with String.format by passing in the values to format.

The above declaration is good, because you don't have to know of what type its parameters are when you create the string.

I tried to find similar approach in Swift, but what I found out was something like the following format:

"String %d formatted %d"

which requires you to format the string with String(format: , parameters). This is not good because you would also have to know parameter types when declaring the string.

Is there a similar approach in Swift where I wouldn't have to know the parameter types?

like image 801
Victor Sigler Avatar asked Mar 19 '15 20:03

Victor Sigler


2 Answers

Use this one:

let printfOutput = String(format:"%@ %2.2d", "string", 2)

It's the same as printf or the Obj-C formatting.

You can also mix it in this way:

let parm = "string"
let printfOutput = String(format:"\(parm) %2.2d", 2)

Edit: Thanks to MartinR (he knows it all ;-)

Be careful when mixing string interpolation and formatting. String(format:"\(parm) %2.2d", 2) will crash if parm contains a percent character. In (Objective-)C, the clang compiler will warn you if a format string is not a string literal.

This gives some room for hacking:

let format = "%@"
let data = "String"
let s = String(format: "\(format)", data) // prints "String"

In contrast to Obj-C which parses the format string at compile time, Swift does not do that and just interprets it at runtime.

like image 200
qwerty_so Avatar answered Sep 21 '22 02:09

qwerty_so


In Swift, types need to conform to the CustomStringConvertible protocol in order to be used inside strings. This is also a requirement for the types used in string interpolation like this:

"Integer value \(intVal) and double value \(doubleVal)"

When you understand the CustomStringConvertible, you can create your own function to fulfill your needs. The following function formats the string based on the given arguments and prints it. It uses {} as a placeholder for the argument, but you can change it to anything you want.

func printWithArgs(string: String, argumentPlaceHolder: String = "{}", args: CustomStringConvertible...) {
    var formattedString = string
    
    // Get the index of the first argument placeholder
    var nextPlaceholderIndex = string.range(of: argumentPlaceHolder)
    
    // Index of the next argument to use
    var nextArgIndex = 0

    // Keep replacing the next placeholder as long as there's more placeholders and more unused arguments
    while nextPlaceholderIndex != nil && nextArgIndex < args.count {
        
        // Replace the argument placeholder with the argument
        formattedString = formattedString.replacingOccurrences(of: argumentPlaceHolder, with: args[nextArgIndex].description, options: .caseInsensitive, range: nextPlaceholderIndex)
        
        // Get the next argument placeholder index
        nextPlaceholderIndex = formattedString.range(of: argumentPlaceHolder)
        nextArgIndex += 1
    }
    
    print(formattedString)
}

printWithArgs(string: "First arg: {}, second arg: {}, third arg: {}", args: "foo", 4.12, 100)
// Prints: First arg: foo, second arg: 4.12, third arg: 100

Using a custom implementation allows you to have more control over it and tweak its behavior. For example, if you wanted to, you could modify this code to display the same argument multiple times using placeholders like {1} and {2}, you could fill the arguments in a reversed order, etc.

For more information about string interpolation in Swift: https://docs.swift.org/swift-book/LanguageGuide/StringsAndCharacters.html#//apple_ref/doc/uid/TP40014097-CH7-ID292

like image 37
Aleksi Sjöberg Avatar answered Sep 21 '22 02:09

Aleksi Sjöberg