How can I concatenate multiple optional strings in swift 3.0?

Bug report filed by OP:

  • SR-1122: Failure to typecheck chain of binary operators on force-unwrapped values

Which has been resolved (fix commited to master Jan 3 2017), and should hence no longer be an issue in upcoming Swift 3.1.


This seems to be a bug (not present in Swift 2.2, only 3.0) associated with the case of:

  • Using the forced unwrapping operator (!) for at least 3 terms in an expression (tested using at least 2 basic operators, e.g. + or -).
  • For some reason, given the above, Swift messes up type inference of the expression (specifically, for the x! terms themselves, in the expression).

For all the examples below, let:

let a: String? = "a"
let b: String? = "b"
let c: String? = "c"

Bug present:

// example 1
a! + b! + c!
    /* error: ambiguous reference to member '+' */

// example 2
var d: String =  a! + b! + c!
    /* error: ambiguous reference to member '+' */

// example 3
var d: String? =  a! + b! + c!
    /* error: cannot convert value of type 'String' 
       to specified type 'String?' */

// example 4
var d: String?
d =  a! + b! + c!
    /* error: cannot assign value of type 'String' 
       to specified type 'String?' */

// example 5 (not just for type String and '+' operator)
let a: Int? = 1
let b: Int? = 2
let c: Int? = 3
var d: Int? = a! + b! + c!
    /* error: cannot convert value of type 'Int' 
       to specified type 'Int?' */
var e: Int? = a! - b! - c! // same error

Bug not present:

/* example 1 */
var d: String? = a! + b!

/* example 2 */
let aa = a!
let bb = b!
let cc = c!
var d: String? = aa + bb + cc
var e: String = aa + bb + cc

/* example 3 */
var d: String? = String(a!) + String(b!) + String(c!)

However as this is Swift 3.0-dev, I'm uncertain if this is really a "bug", as well as what's the policy w.r.t. reporting "bugs" in a not-yet-production version of code, but possibly you should file radar for this, just in case.

As for answering your question as how to circumvent this issue:

  • use e.g. intermediate variables as in Bug not present: Example 2 above,
  • or explicitly tell Swift all terms in the 3-term expression are strings, as in Bug not present: Example 3 above,
  • or, better yet, use safe unwrapping of your optional, e.g. using optional binding:

    var d: String? = nil
    if let a = a, b = b, c = c {
        d = a + b + c
    } /* if any of a, b or c are 'nil', d will remain as 'nil';
         otherwise, the concenation of their unwrapped values   */
    

Swift 3

let q: String? = "Hello"
let w: String? = "World"
let r: String? = "!"
var array = [q, w, r]

print(array.flatMap { $0 }.reduce("", {$0 + $1}))
// HelloWorld!


let q: String? = "Hello"
let w: String? = nil
let r: String? = "!"
var array = [q, w, r]

print(array.flatMap { $0 }.reduce("", {$0 + $1}))
// Hello!

func getSingleValue(_ value: String?..., seperator: String = " ") -> String? {

    return value.reduce("") {
        ($0) + seperator + ($1 ?? "")
        }.trimmingCharacters(in: CharacterSet(charactersIn: seperator) )
}