26

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!

3 Answers 3

29

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.

Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for the solution! It compiles and seem to work! Thanks. I also think this will change before swift 1.0
"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.". I thought swift getas around that by using the Charcater type (instead of, e.g. bytes) which is exactly one unicode character, and swift Strings could be thought of as arrays of such? What stands in the way of accessing the n-th (unicode) character of the string?
No, each Character is a single visible character in the string, which can be made up of one, two, or many code points. For example, 👩‍👩‍👧‍👧 is seven, I think.
In case you're looking for an extension that does use the Int to get all the substrings you may want, try this one: stackoverflow.com/questions/24044851/…
"What stands in the way of accessing the n-th (unicode) character of the string?" -- Swift's philosophy is that you should feel the runtime cost of Unicode string processing while writing your code. Expensive operations are painful to write.
12

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.

1 Comment

Ok, now I know whats going on under the hood :D Thanks!
6

For Swift 2.0

Using the example above:

curPart.substringToIndex(curPart.startIndex.advancedBy(1)) 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.