Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type 'String.Index' does not conform protocol 'IntegerLiteralConvertible'

Tags:

xcode

ios

swift

With Beta 3 all worked fine, now I get a strange error, and I have no clue how to fix it. Tried all the solutions for similiar problems.

Here is my code:

if !name.isEmpty {
        var splitted: [String] = name.componentsSeparatedByString(" ")

        for curPart in splitted {
            if !curPart.isEmpty {
                acronym += curPart.substringToIndex(1) //Error
            }
        }
        if (acronym as NSString).length > 2 {
            acronym = acronym.substringToIndex(2) //Error
        }
    }

Both marked lines gave me the same error:

Type 'String.Index' does not conform protocol 'IntegerLiteralConvertible'

Can someone help me? Or is Beta 4 bugged? Thanks!

like image 361
mort3m Avatar asked Jul 22 '14 06:07

mort3m


3 Answers

In beta 4, Swift's String.Index handling changed yet again -- you now can't supply an Int when a String.Index is expected. The way to handle it is by creating the String.Index you need using the advance method:

if !name.isEmpty {
    var splitted: [String] = name.componentsSeparatedByString(" ")

    for curPart in splitted {
        if !curPart.isEmpty {
            acronym += curPart.substringToIndex(advance(curPart.startIndex, 1))
        }
    }
    if countElements(acronym) > 2 {
        acronym = acronym.substringToIndex(advance(acronym.startIndex, 2))
    }
}

This is all based on making sure Unicode strings are handled properly - since different Unicode characters can have different sizes, pure integer indexing would hide the fact that Strings aren't random access.

like image 95
Nate Cook Avatar answered Nov 11 '22 18:11

Nate Cook


Swift's notion of string components and iteration has changed in Beta 4. From the guide, we see:

Every instance of Swift’s Character type represents a single extended grapheme cluster. An extended grapheme cluster is a sequence of one or more Unicode scalars that (when combined) produce a single human-readable character.

This has some interesting side effects:

let str1 = "abc"
let str2 = "\u{20DD}def"

countElements(str1)      // 3 
countElements(str2)      // 4
countElements(str1+str2) // 6 ≠ 3+4 !!!

That's because the c and \u{20DD} combine to form   c⃝. Also notice that we're using countElements. In order to figure out the length of the string, Swift actually has to iterate through the whole string and figure out where the actual grapheme divisions are, so it takes O(n) time.

We can also see the effect on different encodings:

Array((str1+str2).utf8)  // [97, 98, 99, 226, 131, 157, 100, 101, 102]
Array((str1+str2).utf16) // [97, 98, 99, 8413, 100, 101, 102]

Another issue, as your error says, is that String's IndexType is no longer convertible from an integer literal: you can't perform random access on the string by specifying an offset. Instead, you can use startIndex and advance to move forward some distance in the string, for example str[str.startIndex] or str[advance(str.startIndex, distance)].

Or you can define your own helper functions in the meantime:

func at<C: Collection>(c: C, i: C.IndexType.DistanceType) -> C.GeneratorType.Element {
    return c[advance(c.startIndex, i)]
}

func take<C: protocol<Collection, Sliceable>>(c: C, n: C.IndexType.DistanceType) -> C.SliceType {
    return c[c.startIndex..<advance(c.startIndex, n)]
}

at(str1+str2, 3)   // d

take(str1+str2, 2) // ab

Obviously there are some improvements that could (and probably will) be made in future updates. You may want to file a bug with your concerns. In the long run, supporting grapheme clusters correctly was probably a good decision, but it makes string access a little more painful in the meantime.

like image 21
jtbandes Avatar answered Nov 11 '22 17:11

jtbandes


For Swift 2.0

Using the example above:

curPart.substringToIndex(curPart.startIndex.advancedBy(1)) 
like image 6
Kelly Avatar answered Nov 11 '22 17:11

Kelly