Separating CamelCase string into space-separated words in Swift

Brian Arnold picture Brian Arnold · Dec 22, 2016 · Viewed 8.9k times · Source

I would like to separate a CamelCase string into space-separated words in a new string. Here is what I have so far:

var camelCaps: String {
    guard self.count > 0 else { return self }
    var newString: String = ""

    let uppercase = CharacterSet.uppercaseLetters
    let first = self.unicodeScalars.first!
    newString.append(Character(first))
    for scalar in self.unicodeScalars.dropFirst() {
        if uppercase.contains(scalar) {
            newString.append(" ")
        }
        let character = Character(scalar)
        newString.append(character)
    }

    return newString
}

let aCamelCaps = "aCamelCaps"
let camelCapped = aCamelCaps.camelCaps // Produce: "a Camel Caps"

let anotherCamelCaps = "ÄnotherCamelCaps"
let anotherCamelCapped = anotherCamelCaps.camelCaps // "Änother Camel Caps"

I'm inclined to suspect that this may not be the most efficient way to convert to space-separated words, if I call it in a tight loop, or 1000's of times. Are there more efficient ways to do this in Swift?

[Edit 1:] The solution I require should remain general for Unicode scalars, not specific to Roman ASCII "A..Z".

[Edit 2:] The solution should also skip the first letter, i.e. not prepend a space before the first letter.

[Edit 3:] Updated for Swift 4 syntax, and added caching of uppercaseLetters, which improves performance in very long strings and tight loops.

Answer

Augustine P A picture Augustine P A · Apr 4, 2017

Here another method to do the same thing for Swift 2.x

extension String {

    func camelCaseToWords() -> String {

        return unicodeScalars.reduce("") {

            if NSCharacterSet.uppercaseLetterCharacterSet().characterIsMember(uint_least16_t($1.value)) {
                return ($0 + " " + String($1))
            }
            else {

                return ($0 + String($1))
            }
        }
    }
}

Swift 3.x

extension String {

    func camelCaseToWords() -> String {

        return unicodeScalars.reduce("") {

            if CharacterSet.uppercaseLetters.contains($1) {

                return ($0 + " " + String($1))
            }
            else {

                return $0 + String($1)
            }
        }
    }
}

May be helpful for someone :)