Search code examples
swiftprintfstringwithformat

Swift, stringWithFormat, %s gives strange results


I searched for an answer the entire day but nothing really came close to answering my issue. I am trying to use stringWithFormat in Swift but while using printf format strings. The actual issue I have is with the %s. I can't seem to get to the original string no matter how I try this.

Any help would be much much appreciated (or workarounds). Things I already did: tried all available encodings for the cString, tried creating an ObjC function to use for this, but when I passed the arguments from Swift I ran into the same strange issue with the %s, even if when hardcoded in the ObjC function body it appears to print the actual correct String.

Please find bellow the sample code.

Many thanks!

var str = "Age %2$i, Name: %1$s"
let name = "Michael".cString(using: .utf8)!
let a = String.init(format: str, name, 1234)

Expected result is quite clear I presume, however I get something like this instead of the correct name:

"Age 1234, Name: ÿQ5"

Solution

  • Use withCString() to invoke a function with the C string representation of a Swift string. Also note that %ld is the correct format for a Swift Int (which can be a 32-bit or 64-bit integer).

    let str = "Age %2$ld, Name: %1$s"
    let name = "Michael"
    
    let a = name.withCString { String(format: str, $0, 1234) }
    print(a) // Age 1234, Name: Michael
    

    Another possible option would be to create a (temporary) copy of the C string representation (using the fact a Swift string is automatically converted to a C string when passed to a C function taking a const char * parameter, as explained in String value to UnsafePointer<UInt8> function parameter behavior):

    let str = "Age %2$ld, Name: %1$s"
    let name = "Michael"
    
    let nameCString = strdup(name)!
    
    let a = String(format: str, nameCString, 1234)
    print(a)
    
    free(nameCString)
    

    I assume that your code does not work as expected because name (which has type [CChar] in your code) is bridged to an NSArray, and then the address of that array is passed to the string formatting method.