October 20, 2019

Get Nth character in string with Swift

Easily get Nth character from string with Swift. In this post I will show you multiple ways to get the Nth character from a string with Swift

Get Nth character in string with Swift

This is something that annoys me to this day with Swift. This is a simple task with other languages. JavaScript and Java can use charAt and other languages like Python(actually JavaScript too) you can get a character using a simple index like you would with an array, eg, myString[2].

Apple has a reason for doing this as you can see here but even so, I find it annoying and I can never remember how to do it.

After researching how to do this I came across an StackOverflow post. This post has the correct answer, but I learned more than just getting a Character from a String. In this post I will go through the ways listed in the SO post to better understand it.

It seems that there was a way suggested in the String manifesto to do this, but unfortunately, that does not work anymore. This is the suggestion from the manifesto:

extension String : BidirectionalCollection {
    subscript(i: Index) -> Character { return characters[i] }
}

String Extension

This solution creates a String extension. This is what I would normally do, and what I was planning on writing this post about. Below is an example.

To get the nth character using an extension, you can use the following code:

extension String {
    subscript (characterIndex: Int) -> Character {
        return self[index(startIndex, offsetBy: characterIndex)]
    }
}

This is a simple extension that will get the Character at a specific index and it works well, the issue I have with this is that it returns a Character. Normally if I want to get the character at an index, I actually want the value to be a String and not a Character.

I updated it by creating a new instance of a string using the Character that we would have returned in the above example. This is the updated code:

extension String {
    subscript(characterIndex: Int) -> Self {
        return String(self[index(startIndex, offsetBy: characterIndex)])
    }
}

Substring

I must say, before today I didn't even know about Substring in Swift. I have never really needed to use Substring in Swift.

From research it seems that a Substring might be more efficient. This is a snippet from the documentation on Substring

Operating on substrings is fast and efficient because a substring shares its storage with the original string

It seems like it might be a good idea to get the character as a Substring. The other benefit is that a Substring has the same interface as a String, this is what the Apple documentation says:

The `Substring` type presents the same interface as `String`, so you can avoid or defer any copying of the string's contents

Now that we have this information we can do this:

extension String {
    subscript(characterIndex: Int) -> Substring {
        let start = index(startIndex, offsetBy: characterIndex)
        let end = start
        
        return self[start...end]
    }
}

From my testing this gave me the same results as the previous two examples. There is one thing with using a Substring and that can also be found in the documentation.

Important: Don't store substrings longer than you need them to perform a specific operation. A substring holds a reference to the entire storage of the string it comes from, not just to the portion it presents, even when there is no other reference to the original string. Storing substrings may, therefore, prolong the lifetime of string data that is no longer otherwise accessible, which can appear to be memory leakage.

StringProtocol

As I said, most of these solutions are based on solutions provided by the StackOverflow article I referenced in the beginning of this post.

Unlike Substring, I had heard of StringProtocol before, but I had never worked with it. According to StackOverflow, this is the best way to do it. So I took a look at the documentation for StringProtocol and the first and only line of documentation says this:

A type that can represent a string as a collection of characters

Sounds like the perfect solution, so lets see what it looks like.

extension StringProtocol {
    subscript(characterIndex: Int) -> String {
        return String(self[index(startIndex, offsetBy: characterIndex)])
    }
}

It is pretty much the same as the other solutions. I am showing the String version, but you could return a character in the same way we did when we made the String extension.

You could also do the Substring method that we have gone through above, but you will need to make the Substring return type an optional or force unwrap it when returning.

This is the code with the StringProtocol returning a Substring:

extension StringProtocol {
    subscript(characterIndex: Int) -> Substring? {
        let start = index(startIndex, offsetBy: characterIndex)
        let end = start

        return self[start...end] as? Substring
    }
}

I personally prefer keeping the return value as optional, but if you want you could force unwrap it.

Conclusion

This was an interesting post for me. I never thought there would be so many techniques to get the nth character from a string.