511

How can I get the nth character of a string? I tried bracket([]) accessor with no luck.

var string = "Hello, world!"

var firstChar = string[0] // Throws error

ERROR: 'subscript' is unavailable: cannot subscript String with an Int, see the documentation comment for discussion

stackich
  • 3,607
  • 3
  • 17
  • 41
Mohsen
  • 64,437
  • 34
  • 159
  • 186
  • 1
    The error message “cannot subscript String with an Int, see the documentation comment for discussion” seems to be referring to https://github.com/apple/swift/blob/master/stdlib/public/core/UnavailableStringAPIs.swift.gyb – andrewdotn Aug 06 '17 at 19:55
  • use `var firstChar = string.index(string.startIndex, offsetBy: 0)` instead – Sazzad Hissain Khan May 22 '19 at 08:36
  • @SazzadHissainKhan this would result in a string index, not a character. Btw why not simply `string.startIndex`? For the first character `string[string.startIndex] ` or simply `string.first`. Note that the first approach you would need to check if the string is empty first the second returns an optional – Leo Dabus Jul 19 '19 at 23:20
  • https://www.simpleswiftguide.com/get-character-from-string-using-its-index-in-swift/ – Vivek Nov 22 '22 at 07:35
  • I've seen a lot of programming languages, but I swear this one is the most retarded. Hands down. The whole 2 pages of discussion of something that should've been `str[idx]`. Wow! – c00000fd Dec 10 '22 at 09:11

48 Answers48

610

Attention: Please see Leo Dabus' answer for a proper implementation for Swift 4 and Swift 5.

Swift 4 or later

The Substring type was introduced in Swift 4 to make substrings faster and more efficient by sharing storage with the original string, so that's what the subscript functions should return.

Try it out here

extension StringProtocol {
    subscript(offset: Int) -> Character { self[index(startIndex, offsetBy: offset)] }
    subscript(range: Range<Int>) -> SubSequence {
        let startIndex = index(self.startIndex, offsetBy: range.lowerBound)
        return self[startIndex..<index(startIndex, offsetBy: range.count)]
    }
    subscript(range: ClosedRange<Int>) -> SubSequence {
        let startIndex = index(self.startIndex, offsetBy: range.lowerBound)
        return self[startIndex..<index(startIndex, offsetBy: range.count)]
    }
    subscript(range: PartialRangeFrom<Int>) -> SubSequence { self[index(startIndex, offsetBy: range.lowerBound)...] }
    subscript(range: PartialRangeThrough<Int>) -> SubSequence { self[...index(startIndex, offsetBy: range.upperBound)] }
    subscript(range: PartialRangeUpTo<Int>) -> SubSequence { self[..<index(startIndex, offsetBy: range.upperBound)] }
}

To convert the Substring into a String, you can simply do String(string[0..2]), but you should only do that if you plan to keep the substring around. Otherwise, it's more efficient to keep it a Substring.

It would be great if someone could figure out a good way to merge these two extensions into one. I tried extending StringProtocol without success, because the index method does not exist there. Note: This answer has been already edited, it is properly implemented and now works for substrings as well. Just make sure to use a valid range to avoid crashing when subscripting your StringProtocol type. For subscripting with a range that won't crash with out of range values you can use this implementation


Why is this not built-in?

The error message says "see the documentation comment for discussion". Apple provides the following explanation in the file UnavailableStringAPIs.swift:

Subscripting strings with integers is not available.

The concept of "the ith character in a string" has different interpretations in different libraries and system components. The correct interpretation should be selected according to the use case and the APIs involved, so String cannot be subscripted with an integer.

Swift provides several different ways to access the character data stored inside strings.

  • String.utf8 is a collection of UTF-8 code units in the string. Use this API when converting the string to UTF-8. Most POSIX APIs process strings in terms of UTF-8 code units.

  • String.utf16 is a collection of UTF-16 code units in string. Most Cocoa and Cocoa touch APIs process strings in terms of UTF-16 code units. For example, instances of NSRange used with NSAttributedString and NSRegularExpression store substring offsets and lengths in terms of UTF-16 code units.

  • String.unicodeScalars is a collection of Unicode scalars. Use this API when you are performing low-level manipulation of character data.

  • String.characters is a collection of extended grapheme clusters, which are an approximation of user-perceived characters.

Note that when processing strings that contain human-readable text, character-by-character processing should be avoided to the largest extent possible. Use high-level locale-sensitive Unicode algorithms instead, for example, String.localizedStandardCompare(), String.localizedLowercaseString, String.localizedStandardRangeOfString() etc.

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
aleclarson
  • 18,087
  • 14
  • 64
  • 91
  • Right but it doesn't do the range adaptation for slicing a string that the above solution solves so elegantly. – John LaBarge May 26 '15 at 19:12
  • 4
    `Cannot find an overload for 'advance' that accepts an argument list of type '(String.Index, T)'` ... `String.Index` and `Int` aren't compatible. –  Jun 19 '15 at 02:33
  • @Barry That's more than likely because of the Swift 2.0 release. I'm not using Swift right now, so if someone could update this answer, I'm sure people would appreciate it. – aleclarson Jun 19 '15 at 07:10
  • 7
    If you see `Cannot subscript a value of type 'String'...` check this answer: http://stackoverflow.com/a/31265316/649379 – SoftDesigner Jul 07 '15 at 09:57
  • I am getting `fatal error: can not increment endIndex` – Aaron Sep 24 '15 at 20:28
  • 1
    the last operation could seriously improve performance by first getting the start index as `let start = startIndex.advancedBy(r.startIndex)` and using it as the starting point for end index `let end = start.advanceBy(r.endIndex - r.startIndex)`. – Sulthan Oct 11 '15 at 15:41
  • 1
    It seems you can replace `return substringWithRange(Range(start: startIndex.advancedBy(r.startIndex), end: startIndex.advancedBy(r.endIndex)))` by `return self[Range(start: startIndex.advancedBy(r.startIndex), end: startIndex.advancedBy(r.endIndex))]` – duthen Jan 08 '16 at 16:12
  • 24
    When I try to use this, I get `Ambiguous use of 'subscript'`. – jowie Mar 02 '16 at 16:55
  • @aleclarson Looks like this will have some issues with Swift 3. `init(start:end:)` is deprecated. – Craig.Pearce May 03 '16 at 04:13
  • Why are you using "==="? That's identity operator for objects not comparing values.. – nekonari May 18 '16 at 17:18
  • 24
    WARNING! The extension below is horribly inefficient. Every time a string is accessed with an integer, an O(n) function to advance its starting index is run. Running a linear loop inside another linear loop means this for loop is accidentally O(n2) — as the length of the string increases, the time this loop takes increases quadratically. Instead of doing that you could use the characters's string collection. – ignaciohugog Jun 18 '16 at 17:21
  • @ignaciohugog Do you think String.CharacterView.Index.successor() is O(1) or O(N)? I couldn't find any O() info for .successor() or .predecessor(). Thanks. – Randel S Jun 20 '16 at 18:43
  • 2
    `fatal error: Can't form a Character from an empty String` – Devin Brown Jun 22 '16 at 21:29
  • Can I add this to a utilities file I am making that I eventually wish to pass to GitHub? – Jack Aug 12 '16 at 02:07
  • I'm getting a couple errors when trying to use this with xCode 8 – Thomas Sep 14 '16 at 21:18
  • 1
    For those who get `Ambiguous use of 'subscript'` typehint to the variable type since `"someString"[5]` can either return a String or a Character with the shown subscripts. You need to tell him what you need. @jowie – bemeyer Mar 19 '17 at 17:24
  • I'm confused. So is there any case where the Swift3 answer won't work? If not what Apple did makes no sense – mfaani Apr 26 '18 at 13:31
  • Check out my answer, it uses the same thing (credit to [@alecarlson](https://stackoverflow.com/users/2228559/aleclarson)), but **you can `get` and `set` the subscript.** – Noah Wilder Apr 27 '18 at 22:31
  • 1
    This method as it is unnecessarily offsetting both bounds from the startIndex. What you should do to avoid that is offset the endIndex starting from the start (not the startIndex) offsetBy the range count. Regarding the StringProtocol subscript extension I have already posted it. – Leo Dabus Aug 31 '18 at 11:25
  • 1
    @LeoDabus I edited my answer to recommend yours. Good work! – aleclarson Aug 31 '18 at 13:01
  • @aleclarson Just change startIndex to start and upperBound to count when defining the end index. This is probably the reason of 20 downvotes. Also cast Character to Character in your Swift 3 version is pointless – Leo Dabus Aug 31 '18 at 13:04
  • 1
    @LeoDabus Feel free to make those edits yourself instead of telling me to do so. :) – aleclarson Aug 31 '18 at 13:05
  • @ignaciohugog wait. I think it ends up being more like O(n!) – Michael Feb 03 '19 at 00:56
437

Swift 5.2

let str = "abcdef"
str[1 ..< 3] // returns "bc"
str[5] // returns "f"
str[80] // returns ""
str.substring(fromIndex: 3) // returns "def"
str.substring(toIndex: str.length - 2) // returns "abcd"

You will need to add this String extension to your project (it's fully tested):

extension String {

    var length: Int {
        return count
    }

    subscript (i: Int) -> String {
        return self[i ..< i + 1]
    }

    func substring(fromIndex: Int) -> String {
        return self[min(fromIndex, length) ..< length]
    }

    func substring(toIndex: Int) -> String {
        return self[0 ..< max(0, toIndex)]
    }

    subscript (r: Range<Int>) -> String {
        let range = Range(uncheckedBounds: (lower: max(0, min(length, r.lowerBound)),
                                            upper: min(length, max(0, r.upperBound))))
        let start = index(startIndex, offsetBy: range.lowerBound)
        let end = index(start, offsetBy: range.upperBound - range.lowerBound)
        return String(self[start ..< end])
    }
}

Even though Swift always had out of the box solution to this problem (without String extension, which I provided below), I still would strongly recommend using the extension. Why? Because it saved me tens of hours of painful migration from early versions of Swift, where String's syntax was changing almost every release, but all I needed to do was to update the extension's implementation as opposed to refactoring the entire project. Make your choice.

let str = "Hello, world!"
let index = str.index(str.startIndex, offsetBy: 4)
str[index] // returns Character 'o'

let endIndex = str.index(str.endIndex, offsetBy:-2)
str[index ..< endIndex] // returns String "o, worl"

String(str.suffix(from: index)) // returns String "o, world!"
String(str.prefix(upTo: index)) // returns String "Hell"
nalexn
  • 10,615
  • 6
  • 44
  • 48
  • change `range.upperBound - range.lowerBound` to `range.count` – Leo Dabus Oct 25 '17 at 23:32
  • It's not part of the original question, but... it would be nice if this supported assignment too. E.g., s[i] = "a" :). – Chris Prince Nov 06 '17 at 23:57
  • 4
    I believe with Swift 4.2 subscripts are not available again. I get an error saying: 'subscript' is unavailable: cannot subscript String with an Int, see the documentation comment for discussion – C0D3 Oct 14 '18 at 15:14
  • 3
    @ChrisPrince `extension StringProtocol where Self: RangeReplaceableCollection { subscript(offset: Int) -> Element { get { return self[index(startIndex, offsetBy: offset)] } set { let start = index(startIndex, offsetBy: offset) replaceSubrange(start.. – Leo Dabus Dec 11 '18 at 03:08
  • 4
    This should be built-in functions – Ammar Mujeeb Jan 29 '20 at 07:31
  • Why Swift make string so hard to use? Only Swift support Unicode? I think modern languages such Java, Javascript should all support Unicode, why their String is way easier to use? – James Wang Feb 07 '20 at 22:27
  • 1
    @JamesWang this is done for compatibility with different String character systems. Characters may take a different number of bytes, based on encoding, and there is no simple solution if you want both convenience of the API and best performance and memory consumption. – nalexn Feb 08 '20 at 10:42
  • `i ..< i + 1` should be changed to `i...i`. – Peter Schorn Feb 09 '21 at 23:34
  • @PeterSchorn That would cause the error: `'subscript(_:)' is unavailable: cannot subscript String with an integer range, use a String.Index range instead` – John Oct 25 '22 at 17:33
  • This is exactly the reason why this is not built in. You hide the complexity this causes and create potentially much more expensive code in terms of runtime efficiency. E.g. image a very large unicode string were you want to get the 100000 character. This way the string would be parsed twice to get to this character instead of only once. – Alexander Jährling Jan 25 '23 at 12:18
  • Just tried to write first swift and be annoyed by substring, how come a easy task need an extension? – Adison Jul 13 '23 at 07:16
184

I just came up with this neat workaround

var firstChar = Array(string)[0]
Jens Wirth
  • 17,110
  • 4
  • 30
  • 33
  • 2
    This is a good quick work-around for the (common) case where you know you have UTF8 or ASCII encoded strings. Just be sure that the strings will never be in an encoding that uses more than one byte. – Jeff Hay Aug 19 '14 at 15:04
  • 59
    That seems extremely inefficient as you are copying the entire string just to get the first character. Use string[string. startIndex] instead of 0 as Sulthan pointed out. – Bjorn Jan 12 '15 at 03:25
  • 7
    Unwrap string: `var firstChar = Array(string!)[0]` Otherwise it will say `add arrayLiteral` – Mohammad Zaid Pathan Apr 09 '15 at 13:58
  • This should really be the accepted answer. It is efficient and clean, and works perfectly. – mginn May 03 '15 at 16:51
  • 2
    I don't believe this is clean, as a matter of fact it's a round about. I'm not quite sure which initializer in Array is used first causing this to happen (and I'm assuming it's the SequenceType initializer causing it to gather the characters of the string as individual components for the Array). This isn't explicit at all and may be corrected in the future without type casting. This also doesn't work if you use shorthand for the array via [string].first. @Sulthan's solution works best to use the baked in index values. It's far more clear on what is happening here. – TheCodingArt May 25 '15 at 18:22
  • 3
    Wow, compiler segment fault! – Frank Sep 26 '16 at 03:43
  • This doesn't work with Swift 3: `argument type 'String' does not conform to expected type 'Sequence'`. You would need to get the `CharacterView` for the string first, then index into its array: `Array(str.characters)[0]`, or `String(Array(str.characters)[0])` if you want to keep the String type. – Matt Le Fleur Apr 11 '17 at 08:52
  • Most time user use short string , so efficiency is not an issue at all. Swift shouldn't make Swift String so hard to use, just see How Java String handle Unicode so user-friendly – James Wang Feb 07 '20 at 22:32
  • 1
    Whatever you guys discussing it's an O(n) operation, just because it works doesn't mean you should use it . But it really depends on how frequently you are accessing the first character and how long really the string is. Anyways I wanted to throw it out there in case anyone is wondering or needs help . – Tilak Madichetti Mar 29 '20 at 21:20
  • This trick worked for me too . I am converting character into string back . Swift5 : var stringAtIndex = String(Array(inputString)[index]) – Abhishek B Aug 26 '20 at 04:54
  • Also `let stringArray: [String] = myString { String($0) }; stringArray[0]` works too. – Eric Apr 09 '22 at 11:13
163

Xcode 11 • Swift 5.1

You can extend StringProtocol to make the subscript available also to the substrings:

extension StringProtocol {
    subscript(_ offset: Int)                     -> Element     { self[index(startIndex, offsetBy: offset)] }
    subscript(_ range: Range<Int>)               -> SubSequence { prefix(range.lowerBound+range.count).suffix(range.count) }
    subscript(_ range: ClosedRange<Int>)         -> SubSequence { prefix(range.lowerBound+range.count).suffix(range.count) }
    subscript(_ range: PartialRangeThrough<Int>) -> SubSequence { prefix(range.upperBound.advanced(by: 1)) }
    subscript(_ range: PartialRangeUpTo<Int>)    -> SubSequence { prefix(range.upperBound) }
    subscript(_ range: PartialRangeFrom<Int>)    -> SubSequence { suffix(Swift.max(0, count-range.lowerBound)) }
}

extension LosslessStringConvertible {
    var string: String { .init(self) }
}

extension BidirectionalCollection {
    subscript(safe offset: Int) -> Element? {
        guard !isEmpty, let i = index(startIndex, offsetBy: offset, limitedBy: index(before: endIndex)) else { return nil }
        return self[i]
    }
}

Testing

let test = "Hello USA !!! Hello Brazil !!!"
test[safe: 10]   // ""
test[11]   // "!"
test[10...]   // "!!! Hello Brazil !!!"
test[10..<12]   // "!"
test[10...12]   // "!!"
test[...10]   // "Hello USA "
test[..<10]   // "Hello USA "
test.first   // "H"
test.last    // "!"

// Subscripting the Substring
 test[...][...3]  // "Hell"

// Note that they all return a Substring of the original String.
// To create a new String from a substring
test[10...].string  // "!!! Hello Brazil !!!"
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
  • May I ask what is "self[index(startIndex, offsetBy: i)]"? And how does "self[i]" work? – allenlinli Sep 22 '16 at 13:12
  • 1
    Hi Leo, thank you for the solution! I just (today) switched to from Swift 2.3 to 3 and your solution subscript(range: Range) gives the error "Extra argument 'limitedBy' in call". What do you think can be wrong? – Ahmet Akkök Oct 03 '16 at 14:56
  • @AhmetAkkök are you sure you didn't change the code? – Leo Dabus Oct 03 '16 at 15:26
  • 1
    @Leo it turned out I did not convert the whole project but on the app not the extension, I'have repeated the process for both app and the extension and it works OK now. Your help is very much appreciated! – Ahmet Akkök Oct 03 '16 at 15:57
  • this is very complicated code. What's the advantage over doing `return String(Array(characters)[range])` in Swift 3? – Dan Rosenstark Nov 07 '16 at 16:25
  • Why did you choose to return `SubSequence` rather than a value like `SubString` or `String`? – Noah Wilder Apr 25 '18 at 22:14
  • I am extending the protocol not the type (String) so you need to return SubSequence which in the case of a String is a Substring. – Leo Dabus Apr 25 '18 at 22:18
  • I want to use Java `charAt(i)` usage in my Swift string traverse within for loop without writing extension or another loop. How can I do ? – Emre Değirmenci Jan 24 '20 at 14:30
133

No indexing using integers, only using String.Index. Mostly with linear complexity. You can also create ranges from String.Index and get substrings using them.

Swift 3.0

let firstChar = someString[someString.startIndex]
let lastChar = someString[someString.index(before: someString.endIndex)]
let charAtIndex = someString[someString.index(someString.startIndex, offsetBy: 10)]

let range = someString.startIndex..<someString.index(someString.startIndex, offsetBy: 10)
let substring = someString[range]

Swift 2.x

let firstChar = someString[someString.startIndex]
let lastChar = someString[someString.endIndex.predecessor()]
let charAtIndex = someString[someString.startIndex.advanceBy(10)]

let range = someString.startIndex..<someString.startIndex.advanceBy(10)
let subtring = someString[range]

Note that you can't ever use an index (or range) created from one string to another string

let index10 = someString.startIndex.advanceBy(10)

//will compile
//sometimes it will work but sometimes it will crash or result in undefined behaviour
let charFromAnotherString = anotherString[index10]
CodeBender
  • 35,668
  • 12
  • 125
  • 132
Sulthan
  • 128,090
  • 22
  • 218
  • 270
  • 8
    `String` indexs are unique to a string. This is because different strings may have different multi-unit UTF-16 `Characters` and/or at different positions so the UTF-16 unit indexs will not match, may fall beyond the end or point inside a multi-unit UTF-16 `Character`. – zaph Aug 19 '14 at 15:34
  • @Zaph That's obvious. – Sulthan Aug 19 '14 at 15:58
  • 3
    Explaning why you say: "sometimes it will crash or result in undefined behaviour". Perhaps better to say just don't do it because ... – zaph Aug 19 '14 at 17:17
  • 1
    @Sulthan `..` is now `..<` (in your assignment to `range`) – Aaron Brager Oct 25 '14 at 04:41
  • On the shipping version of Swift, `string[string.endIndex]` results in EXC_BAD_INSTRUCTION apparently as `string.endIndex` is one place beyond the end of the string. `string[string.endIndex-1]` doesn't work as String.Index isn't IntegerConvertible. How do we get the last character of the string? – Cajunluke Oct 28 '14 at 19:37
  • 3
    @CajunLuke I know it's been a while since you posted this comment, but take a look at [this answer](http://stackoverflow.com/a/24122445/1203475). You can use `var lastChar = string[string.endIndex.predecessor()]` – David L Dec 14 '14 at 07:11
85

Swift 4

let str = "My String"

String at index

let index = str.index(str.startIndex, offsetBy: 3)
String(str[index])    // "S"

Substring

let startIndex = str.index(str.startIndex, offsetBy: 3)
let endIndex = str.index(str.startIndex, offsetBy: 7)
String(str[startIndex...endIndex])     // "Strin"

First n chars

let startIndex = str.index(str.startIndex, offsetBy: 3)
String(str[..<startIndex])    // "My "

Last n chars

let startIndex = str.index(str.startIndex, offsetBy: 3)
String(str[startIndex...])    // "String"

Swift 2 and 3

str = "My String"

**String At Index **

Swift 2

let charAtIndex = String(str[str.startIndex.advancedBy(3)])  // charAtIndex = "S"

Swift 3

str[str.index(str.startIndex, offsetBy: 3)]

SubString fromIndex toIndex

Swift 2

let subStr = str[str.startIndex.advancedBy(3)...str.startIndex.advancedBy(7)] // subStr = "Strin"

Swift 3

str[str.index(str.startIndex, offsetBy: 3)...str.index(str.startIndex, offsetBy: 7)]

First n chars

let first2Chars = String(str.characters.prefix(2)) // first2Chars = "My"

Last n chars

let last3Chars = String(str.characters.suffix(3)) // last3Chars = "ing"
Community
  • 1
  • 1
Warif Akhand Rishi
  • 23,920
  • 8
  • 80
  • 107
34

Swift 5.3

I think this is very elegant. Kudos at Paul Hudson of "Hacking with Swift" for this solution:

@available (macOS 10.15, * )
extension String {
    subscript(idx: Int) -> String {
        String(self[index(startIndex, offsetBy: idx)])
    }
}

Then to get one character out of the String you simply do:

var string = "Hello, world!"

var firstChar = string[0] // No error, returns "H" as a String

NB: I just wanted to add, this will return a String as pointed out in the comments. I think it might be unexpected for Swift users, but often I need a String to use in my code straight away and not a Character type, so it does simplify my code a little bit avoiding a conversion from Character to String later.

multitudes
  • 2,898
  • 2
  • 22
  • 29
  • what will be the time complexity of doing this ? – Surjeet Rajput Feb 26 '21 at 05:38
  • 2
    The time complexity is the same as doing it in code with `let firstChar = string[string.index(string.startIndex, offsetBy: 0)]` – multitudes Feb 26 '21 at 12:13
  • 1
    This is exactly what is being done at the accepted answer. The only difference is that you are extending String instead of the StringProtocol (which supports substrings as well) and returning a string instead of a character which is totally unexpected considering that it should return the collection element which in this case is a character – Leo Dabus Jun 25 '21 at 02:36
  • Fails if the string is empty `""[0]` causes fatal error. – Rob Jan 03 '23 at 07:51
24

If you see Cannot subscript a value of type 'String'... use this extension:

Swift 3

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

    subscript (i: Int) -> String {
        return String(self[i] as Character)
    }

    subscript (r: Range<Int>) -> String {
        let start = index(startIndex, offsetBy: r.lowerBound)
        let end = index(startIndex, offsetBy: r.upperBound)
        return self[start..<end]
    }

    subscript (r: ClosedRange<Int>) -> String {
        let start = index(startIndex, offsetBy: r.lowerBound)
        let end = index(startIndex, offsetBy: r.upperBound)
        return self[start...end]
    }
}

Swift 2.3

extension String {
    subscript(integerIndex: Int) -> Character {
        let index = advance(startIndex, integerIndex)
        return self[index]
    }

    subscript(integerRange: Range<Int>) -> String {
        let start = advance(startIndex, integerRange.startIndex)
        let end = advance(startIndex, integerRange.endIndex)
        let range = start..<end
        return self[range]
    }
}

Source: http://oleb.net/blog/2014/07/swift-strings/

SoftDesigner
  • 5,640
  • 3
  • 58
  • 47
24

Swift 2.0 as of Xcode 7 GM Seed

var text = "Hello, world!"

let firstChar = text[text.startIndex.advancedBy(0)] // "H"

For the nth character, replace 0 with n-1.

Edit: Swift 3.0

text[text.index(text.startIndex, offsetBy: 0)]


n.b. there are simpler ways of grabbing certain characters in the string

e.g. let firstChar = text.characters.first

Matt Le Fleur
  • 2,708
  • 29
  • 40
19

Swift 2.2 Solution:

The following extension works in Xcode 7, this is a combination of this solution and Swift 2.0 syntax conversion.

extension String {
    subscript(integerIndex: Int) -> Character {
        let index = startIndex.advancedBy(integerIndex)
        return self[index]
    }

    subscript(integerRange: Range<Int>) -> String {
        let start = startIndex.advancedBy(integerRange.startIndex)
        let end = startIndex.advancedBy(integerRange.endIndex)
        let range = start..<end
        return self[range]
    }
}
Dan Beaulieu
  • 19,406
  • 19
  • 101
  • 135
16

The swift string class does not provide the ability to get a character at a specific index because of its native support for UTF characters. The variable length of a UTF character in memory makes jumping directly to a character impossible. That means you have to manually loop over the string each time.

You can extend String to provide a method that will loop through the characters until your desired index

extension String {
    func characterAtIndex(index: Int) -> Character? {
        var cur = 0
        for char in self {
            if cur == index {
                return char
            }
            cur++
        }
        return nil
    }
}

myString.characterAtIndex(0)!
drewag
  • 93,393
  • 28
  • 139
  • 128
  • 4
    You can already loop through strings: for letter in "foo" { println(letter) } – Doobeh Jun 07 '14 at 01:56
  • @Doobeh I meant loop through and return the actual character like in my edit above – drewag Jun 07 '14 at 02:01
  • nice! It's funny how you can iterate through it, but not via an index. Swift's feeling pythonic but with harder edges. – Doobeh Jun 07 '14 at 02:10
  • I found using the `myString.bridgeToObjectiveC().characterAtIndex(0)` or `(string as NSString ).characterAtIndex(0)` returns an Int value of the character – markhunte Jun 07 '14 at 09:24
  • @markhunte That's a good point. Another argument in my mind to use the native method – drewag Jun 07 '14 at 15:11
  • 4
    **Don't** use `NSString` methods for accessing individual characters from a Swift-native `String` - the two use different counting mechanisms, so you'll get unpredictable results with higher Unicode characters. The first method should be safe (once Swift's Unicode bugs are handled). – Nate Cook Jun 09 '14 at 16:01
  • @NateCook you're right. I deleted the second option – drewag Jun 12 '14 at 14:24
  • How about "for (cur, char) in enumerate(self) {}"? – masakielastic Jul 14 '14 at 05:07
  • That looks like O(n). – iaomw Jul 22 '20 at 01:13
14

You can do it by convert String into Array and get it by specific index using subscript as below

var str = "Hello"
let s = Array(str)[2]
print(s)
Saravit Soeng
  • 491
  • 4
  • 8
  • 2
    Note that this solution will result in contents duplication, making it less performant memory wise and CPU-wise. – Cristik Dec 28 '19 at 19:42
11

As an aside note, there are a few functions applyable directly to the Character-chain representation of a String, like this:

var string = "Hello, playground"
let firstCharacter = string.characters.first // returns "H"
let lastCharacter = string.characters.last // returns "d"

The result is of type Character, but you can cast it to a String.

Or this:

let reversedString = String(string.characters.reverse())
// returns "dnuorgyalp ,olleH" 

:-)

Frederic Adda
  • 5,905
  • 4
  • 56
  • 71
11

Swift 4

String(Array(stringToIndex)[index]) 

This is probably the best way of solving this problem one-time. You probably want to cast the String as an array first, and then cast the result as a String again. Otherwise, a Character will be returned instead of a String.

Example String(Array("HelloThere")[1]) will return "e" as a String.

(Array("HelloThere")[1] will return "e" as a Character.

Swift does not allow Strings to be indexed like arrays, but this gets the job done, brute-force style.

haxgad
  • 387
  • 4
  • 9
  • 1
    Duplicating the whole string contents to another memory location is counter-performant, especially for large strings. We should not need extra memory allocations for simple tasks like direct memory access. – Cristik Dec 28 '19 at 19:47
8

My very simple solution:

Swift 4.1:

let myString = "Test string"
let index = 0
let firstCharacter = myString[String.Index(encodedOffset: index)]

Swift 5.1:

let firstCharacter = myString[String.Index.init(utf16Offset: index, in: myString)]
OhadM
  • 4,687
  • 1
  • 47
  • 57
Linh Dao
  • 1,305
  • 1
  • 19
  • 31
  • Works in Swift 4.1 – leanne Aug 25 '18 at 18:09
  • Simplest solution and now with Swift 5 example :) – OhadM Sep 21 '19 at 12:19
  • @Linh Dao Don't use encodedOffset. **encodedOffset is deprecated: encodedOffset has been deprecated as most common usage is incorrect.** – Leo Dabus Feb 22 '20 at 18:36
  • @OhadM simplest doesn't mean it is correct or at least it won't work as you expect Try `let flags = ""` `flags[String.Index(utf16Offset: 4, in: flags)] // ""` – Leo Dabus Feb 22 '20 at 18:38
  • @LeoDabus, There are many solutions for a question and each solution solves it differently and can help other devs for other problems. – OhadM Feb 23 '20 at 07:53
  • 1
    @OhadM My comment was just a warning. Fell free to use it if you think that it behaves how you expect. – Leo Dabus Feb 23 '20 at 07:58
7

I just had the same issue. Simply do this:

var aString: String = "test"
var aChar:unichar = (aString as NSString).characterAtIndex(0)
Glorfindel
  • 21,988
  • 13
  • 81
  • 109
user3723247
  • 111
  • 1
  • 4
  • This fails for many Emoji and other characters that actually take up more than once "character" in an `NSString`. – rmaddy May 25 '17 at 02:37
7

We have subscript which will very useful here

But String subscript will take param as String.Index so most of the people gets confuse here how to pass String.Index to get details how to form String.Index as per our requirement please look at below documentation Apple Documentation

Here i have created one extension method to get nth character in string

extension String {
    subscript(i: Int) -> String {
        return  i < count ? String(self[index(startIndex, offsetBy: i)]) : ""
    }
}

Usage

let name = "Narayana Rao Routhu"
print(name[11]) //o
print(name[1]) //a
print(name[0]) //N
print(name[30]) //""

if you pass index which is out of bounds of String count it will return empty String

Narayana Rao Routhu
  • 6,303
  • 27
  • 42
  • Note that a String is a collection of Characters. Returning a String instead of a Character through subscript for a index instead of a range doesn't make any sense – Leo Dabus Dec 28 '22 at 05:08
5

By now, subscript(_:) is unavailable. As well as we can't do this

str[0] 

with string.We have to provide "String.Index" But, how can we give our own index number in this way, instead we can use,

string[str.index(str.startIndex, offsetBy: 0)]
YodagamaHeshan
  • 4,996
  • 2
  • 26
  • 36
  • Please edit your answer and add some context by explaining how your answer solves the problem, instead of posting code-only answer. [From Review](https://stackoverflow.com/review/low-quality-posts/24962119) – Pedram Parsian Dec 28 '19 at 19:03
  • Why doing the unnecessary offset-ing? Why not simply `string[string.startIndex]`? BTW, the code would not correctly behave/compile, as you used two different variable names. – Cristik Dec 28 '19 at 19:40
5

In Swift 5 without extension to the String :

var str = "ABCDEFGH"
for char in str {
if(char == "C") { }
}

Above Swift code as same as that Java code :

int n = 8;
var str = "ABCDEFGH"
for (int i=0; i<n; i++) {
if (str.charAt(i) == 'C') { }
}
Emre Değirmenci
  • 715
  • 6
  • 22
4

My solution is in one line, supposing cadena is the string and 4 is the nth position that you want:

let character = cadena[advance(cadena.startIndex, 4)]

Simple... I suppose Swift will include more things about substrings in future versions.

4

Swift3

You can use subscript syntax to access the Character at a particular String index.

let greeting = "Guten Tag!"
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index] // a

Visit https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html

or we can do a String Extension in Swift 4

extension String {
    func getCharAtIndex(_ index: Int) -> Character {
        return self[self.index(self.startIndex, offsetBy: index)]
    }
}

USAGE:

let foo = "ABC123"
foo.getCharAtIndex(2) //C
Community
  • 1
  • 1
Hamed
  • 1,678
  • 18
  • 30
4

Swift 4.2 or later

Range and partial range subscripting using String's indices property

As variation of @LeoDabus nice answer, we may add an additional extension to DefaultIndices with the purpose of allowing us to fall back on the indices property of String when implementing the custom subscripts (by Int specialized ranges and partial ranges) for the latter.

extension DefaultIndices {
    subscript(at: Int) -> Elements.Index { index(startIndex, offsetBy: at) }
}

// Moving the index(_:offsetBy:) to an extension yields slightly
// briefer implementations for these String extensions.
extension String {
    subscript(range: Range<Int>) -> SubSequence {
        let start = indices[range.lowerBound]
        return self[start..<indices[start...][range.count]]
    }
    subscript(range: ClosedRange<Int>) -> SubSequence {
        let start = indices[range.lowerBound]
        return self[start...indices[start...][range.count]]
    }
    subscript(range: PartialRangeFrom<Int>) -> SubSequence {
        self[indices[range.lowerBound]...]
    }
    subscript(range: PartialRangeThrough<Int>) -> SubSequence {
        self[...indices[range.upperBound]]
    }
    subscript(range: PartialRangeUpTo<Int>) -> SubSequence {
        self[..<indices[range.upperBound]]
    }
}

let str = "foo bar baz bax"
print(str[4..<6]) // "ba"
print(str[4...6]) // "bar"
print(str[4...])  // "bar baz bax"
print(str[...6])  // "foo bar"
print(str[..<6])  // "foo ba"

Thanks @LeoDabus for the pointing me in the direction of using the indices property as an(other) alternative to String subscripting!

Community
  • 1
  • 1
dfrib
  • 70,367
  • 12
  • 127
  • 192
  • 1
    the only disadvantage is CountableClosedRange will offset both indexes from the startIndex – Leo Dabus Oct 25 '17 at 20:51
  • https://stackoverflow.com/a/38215613/4573247 now extends StringProtocol to support Substrings as well – Leo Dabus Mar 26 '18 at 23:38
  • Swift 4.2 `DefaultBidirectionalIndices` has been renamed to `DefaultIndices` – Leo Dabus Aug 31 '18 at 11:41
  • Not yet. May be in 10-14 days from now. Are you coding on linux? – Leo Dabus Aug 31 '18 at 15:37
  • 1
    @LeoDabus I see. Yes mostly linux but not much Swift these days :/ I use `swiftenv` when I do, though, I guess it will be updated with 4.2 also somewhat soon then. – dfrib Aug 31 '18 at 19:29
  • 1
    @LeoDabus thanks for updating this answer to modern Swift! – dfrib Jan 27 '20 at 08:06
  • I think you will like this one https://stackoverflow.com/questions/59887561/how-to-implement-a-mutable-ordered-set-generic-type-formerly-known-as-nsmutableo/59887562 – Leo Dabus Jan 27 '20 at 09:20
  • 1
    @LeoDabus nice work! Will have to look into the details at a later time, but I do recall that I never liked that we had to fall back on Foundation some of the ordered/countable set types. – dfrib Jan 27 '20 at 10:02
4

Swift 5.1.3:

Add a String extension:

extension String {

 func stringAt(_ i: Int) -> String { 
   return String(Array(self)[i]) 
 } 

 func charAt(_ i: Int) -> Character { 
  return Array(self)[i] 
 } 
}

let str = "Teja Kumar"
let str1: String = str.stringAt(2)  //"j"
let str2: Character = str.charAt(5)  //"k"
Teja Kumar Bethina
  • 3,486
  • 26
  • 34
  • 9
    This will convert the whole string into an array of characters every time you call this property to extract a single character from it. – Leo Dabus Feb 22 '20 at 18:19
3

Swift 3: another solution (tested in playground)

extension String {
    func substr(_ start:Int, length:Int=0) -> String? {
        guard start > -1 else {
            return nil
        }

        let count = self.characters.count - 1

        guard start <= count else {
            return nil
        }

        let startOffset = max(0, start)
        let endOffset = length > 0 ? min(count, startOffset + length - 1) : count

        return self[self.index(self.startIndex, offsetBy: startOffset)...self.index(self.startIndex, offsetBy: endOffset)]
    }
}

Usage:

let txt = "12345"

txt.substr(-1) //nil
txt.substr(0) //"12345"
txt.substr(0, length: 0) //"12345"
txt.substr(1) //"2345"
txt.substr(2) //"345"
txt.substr(3) //"45"
txt.substr(4) //"5"
txt.substr(6) //nil
txt.substr(0, length: 1) //"1"
txt.substr(1, length: 1) //"2"
txt.substr(2, length: 1) //"3"
txt.substr(3, length: 1) //"4"
txt.substr(3, length: 2) //"45"
txt.substr(3, length: 3) //"45"
txt.substr(4, length: 1) //"5"
txt.substr(4, length: 2) //"5"
txt.substr(5, length: 1) //nil
txt.substr(5, length: -1) //nil
txt.substr(-1, length: -1) //nil
Peter Kreinz
  • 7,979
  • 1
  • 64
  • 49
3

Best way which worked for me is:

var firstName = "Olivia"
var lastName = "Pope"

var nameInitials.text = "\(firstName.prefix(1))" + "\    (lastName.prefix(1))"

Output:"OP"

Jonas Deichelmann
  • 3,513
  • 1
  • 30
  • 45
2

Update for swift 2.0 subString

public extension String {
    public subscript (i: Int) -> String {
        return self.substringWithRange(self.startIndex..<self.startIndex.advancedBy(i + 1))
    }

    public subscript (r: Range<Int>) -> String {
        get {
            return self.substringWithRange(self.startIndex.advancedBy(r.startIndex)..<self.startIndex.advancedBy(r.endIndex))
        }
    }

}
YanSte
  • 10,661
  • 3
  • 57
  • 53
2

I think that a fast answer for get the first character could be:

let firstCharacter = aString[aString.startIndex]

It's so much elegant and performance than:

let firstCharacter = Array(aString.characters).first

But.. if you want manipulate and do more operations with strings you could think create an extension..here is one extension with this approach, it's quite similar to that already posted here:

extension String {
var length : Int {
    return self.characters.count
}

subscript(integerIndex: Int) -> Character {
    let index = startIndex.advancedBy(integerIndex)
    return self[index]
}

subscript(integerRange: Range<Int>) -> String {
    let start = startIndex.advancedBy(integerRange.startIndex)
    let end = startIndex.advancedBy(integerRange.endIndex)
    let range = start..<end
    return self[range]
}

}

BUT IT'S A TERRIBLE IDEA!!

The extension below is horribly inefficient. Every time a string is accessed with an integer, an O(n) function to advance its starting index is run. Running a linear loop inside another linear loop means this for loop is accidentally O(n2) — as the length of the string increases, the time this loop takes increases quadratically.

Instead of doing that you could use the characters's string collection.

ignaciohugog
  • 334
  • 2
  • 10
2

Swift 3

extension String {

    public func charAt(_ i: Int) -> Character {
        return self[self.characters.index(self.startIndex, offsetBy: i)]
    }

    public subscript (i: Int) -> String {
        return String(self.charAt(i) as Character)
    }

    public subscript (r: Range<Int>) -> String {
        return substring(with: self.characters.index(self.startIndex, offsetBy: r.lowerBound)..<self.characters.index(self.startIndex, offsetBy: r.upperBound))
    }

    public subscript (r: CountableClosedRange<Int>) -> String {
        return substring(with: self.characters.index(self.startIndex, offsetBy: r.lowerBound)..<self.characters.index(self.startIndex, offsetBy: r.upperBound))
    }

}

Usage

let str = "Hello World"
let sub = str[0...4]

Helpful Programming Tips and Tricks (written by me)

quemeful
  • 9,542
  • 4
  • 60
  • 69
2

Here's an extension you can use, working with Swift 3.1. A single index will return a Character, which seems intuitive when indexing a String, and a Range will return a String.

extension String {
    subscript (i: Int) -> Character {
        return Array(self.characters)[i]
    }
    
    subscript (r: CountableClosedRange<Int>) -> String {
        return String(Array(self.characters)[r])
    }
    
    subscript (r: CountableRange<Int>) -> String {
        return self[r.lowerBound...r.upperBound-1]
    }
}

Some examples of the extension in action:

let string = "Hello"

let c1 = string[1]  // Character "e"
let c2 = string[-1] // fatal error: Index out of range

let r1 = string[1..<4] // String "ell"
let r2 = string[1...4] // String "ello"
let r3 = string[1...5] // fatal error: Array index is out of range


n.b. You could add an additional method to the above extension to return a String with a single character if wanted:

subscript (i: Int) -> String {
    return String(self[i])
}

Note that then you would have to explicitly specify the type you wanted when indexing the string:

let c: Character = string[3] // Character "l"
let s: String = string[0]    // String "H"
Community
  • 1
  • 1
Matt Le Fleur
  • 2,708
  • 29
  • 40
  • Creating an `Array` just to access an element seems like over-engineering, and also has performance costs as the string contents will have to be duplicated in memory. – Cristik Dec 28 '19 at 19:50
2

Get & Set Subscript (String & Substring) - Swift 4.2

Swift 4.2, Xcode 10

I based my answer off of @alecarlson's answer. The only big difference is you can get a Substring or a String returned (and in some cases, a single Character). You can also get and set the subscript. Lastly, mine is a bit more cumbersome and longer than @alecarlson's answer and as such, I suggest you put it in a source file.


Extension:

public extension String {
    public subscript (i: Int) -> Character {
        get {
            return self[index(startIndex, offsetBy: i)]
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
    }
    public subscript (bounds: CountableRange<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[start ..< end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ..< end, with: s)
        }
    }
    public subscript (bounds: CountableClosedRange<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[start ... end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ... end, with: s)
        }
        
    }
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            return self[start ... end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            replaceSubrange(start ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeThrough<Int>) -> Substring {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[startIndex ... end]
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeUpTo<Int>) -> Substring {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[startIndex ..< end]
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }
    
    public subscript (i: Int) -> String {
        get {
            return "\(self[index(startIndex, offsetBy: i)])"
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            self.replaceSubrange(n...n, with: "\(c)")
        }
    }
    public subscript (bounds: CountableRange<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[start ..< end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ..< end, with: s)
        }
    }
    public subscript (bounds: CountableClosedRange<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[start ... end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ... end, with: s)
        }
        
    }
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            return "\(self[start ... end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            replaceSubrange(start ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeThrough<Int>) -> String {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[startIndex ... end])"
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeUpTo<Int>) -> String {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[startIndex ..< end])"
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }
    
    public subscript (i: Int) -> Substring {
        get {
            return Substring("\(self[index(startIndex, offsetBy: i)])")
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
    }
}
public extension Substring {
    public subscript (i: Int) -> Character {
        get {
            return self[index(startIndex, offsetBy: i)]
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
        
    }
    public subscript (bounds: CountableRange<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[start ..< end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ..< end, with: s)
        }
    }
    public subscript (bounds: CountableClosedRange<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[start ... end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ... end, with: s)
        }
    }
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> Substring {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            return self[start ... end]
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            replaceSubrange(start ... end, with: s)
        }
        
    }
    public subscript (bounds: PartialRangeThrough<Int>) -> Substring {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[startIndex ... end]
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }
    public subscript (bounds: PartialRangeUpTo<Int>) -> Substring {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return self[startIndex ..< end]
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }
    public subscript (i: Int) -> String {
        get {
            return "\(self[index(startIndex, offsetBy: i)])"
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
    }
    public subscript (bounds: CountableRange<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[start ..< end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ..< end, with: s)
        }
    }
    public subscript (bounds: CountableClosedRange<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[start ... end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(start ... end, with: s)
        }
        
    }
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> String {
        get {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            return "\(self[start ... end])"
        }
        set (s) {
            let start = index(startIndex, offsetBy: bounds.lowerBound)
            let end = index(endIndex, offsetBy: -1)
            replaceSubrange(start ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeThrough<Int>) -> String {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[startIndex ... end])"
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ... end, with: s)
        }
    }
    public subscript (bounds: PartialRangeUpTo<Int>) -> String {
        get {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            return "\(self[startIndex ..< end])"
        }
        set (s) {
            let end = index(startIndex, offsetBy: bounds.upperBound)
            replaceSubrange(startIndex ..< end, with: s)
        }
    }
    
    public subscript (i: Int) -> Substring {
        get {
            return Substring("\(self[index(startIndex, offsetBy: i)])")
        }
        set (c) {
            let n = index(startIndex, offsetBy: i)
            replaceSubrange(n...n, with: "\(c)")
        }
    }
}
Community
  • 1
  • 1
Noah Wilder
  • 1,656
  • 20
  • 38
  • This is unnecessarily offsets both indexes (start and end) from the startIndex. You could simply offset the end index using the range.count and offset the start index – Leo Dabus Feb 22 '20 at 18:42
2

Swift 4.2

This answer is ideal because it extends String and all of its Subsequences (Substring) in one extension

public extension StringProtocol {
    
    public subscript (i: Int) -> Element {
        return self[index(startIndex, offsetBy: i)]
    }

    public subscript (bounds: CountableClosedRange<Int>) -> SubSequence {
        let start = index(startIndex, offsetBy: bounds.lowerBound)
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return self[start...end]
    }
    
    public subscript (bounds: CountableRange<Int>) -> SubSequence {
        let start = index(startIndex, offsetBy: bounds.lowerBound)
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return self[start..<end]
    }
    
    public subscript (bounds: PartialRangeUpTo<Int>) -> SubSequence {
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return self[startIndex..<end]
    }
    
    public subscript (bounds: PartialRangeThrough<Int>) -> SubSequence {
        let end = index(startIndex, offsetBy: bounds.upperBound)
        return self[startIndex...end]
    }
    
    public subscript (bounds: CountablePartialRangeFrom<Int>) -> SubSequence {
        let start = index(startIndex, offsetBy: bounds.lowerBound)
        return self[start..<endIndex]
    }
}

Usage

var str = "Hello, playground"

print(str[5...][...5][0])
// Prints ","
Community
  • 1
  • 1
Noah Wilder
  • 1,656
  • 20
  • 38
  • This is unnecessarily offsets both indexes (`start` and `end`) from the `startIndex`. You could simply offset the `end` index using the range.count and offset the `start` index – Leo Dabus Dec 19 '18 at 00:24
1

Swift's String type does not provide a characterAtIndex method because there are several ways a Unicode string could be encoded. Are you going with UTF8, UTF16, or something else?

You can access the CodeUnit collections by retrieving the String.utf8 and String.utf16 properties. You can also access the UnicodeScalar collection by retrieving the String.unicodeScalars property.

In the spirit of NSString's implementation, I'm returning a unichar type.

extension String
{
    func characterAtIndex(index:Int) -> unichar
    {
        return self.utf16[index]
    }

    // Allows us to use String[index] notation
    subscript(index:Int) -> unichar
    {
        return characterAtIndex(index)
    }
}

let text = "Hello Swift!"
let firstChar = text[0]
Erik
  • 12,730
  • 5
  • 36
  • 42
  • This will fail for characters that need more storage than 16 bits. Basically, any Unicode character beyond U+FFFF. – rmaddy May 25 '17 at 02:50
1

In order to feed the subject and show swift subscript possibilities, here's a little string "substring-toolbox" subscript based

These methods are safe and never go over string indexes

extension String {
    // string[i] -> one string char
    subscript(pos: Int) -> String { return String(Array(self)[min(self.length-1,max(0,pos))]) }

    // string[pos,len] -> substring from pos for len chars on the left
    subscript(pos: Int, len: Int) -> String { return self[pos, len, .pos_len, .left2right] }

    // string[pos, len, .right2left] -> substring from pos for len chars on the right
    subscript(pos: Int, len: Int, way: Way) -> String { return self[pos, len, .pos_len, way] }

    // string[range] -> substring form start pos on the left to end pos on the right
    subscript(range: Range<Int>) -> String { return self[range.startIndex, range.endIndex, .start_end, .left2right] }

    // string[range, .right2left] -> substring start pos on the right to end pos on the left
    subscript(range: Range<Int>, way: Way) -> String { return self[range.startIndex, range.endIndex, .start_end, way] }

    var length: Int { return countElements(self) }
    enum Mode { case pos_len, start_end }
    enum Way { case left2right, right2left }
    subscript(var val1: Int, var val2: Int, mode: Mode, way: Way) -> String {
        if mode == .start_end {
            if val1 > val2 { let val=val1 ; val1=val2 ; val2=val }
            val2 = val2-val1
        }
        if way == .left2right {
            val1 = min(self.length-1, max(0,val1))
            val2 = min(self.length-val1, max(1,val2))
        } else {
            let val1_ = val1
            val1 = min(self.length-1, max(0, self.length-val1_-val2 ))
            val2 = max(1, (self.length-1-val1_)-(val1-1) )
        }
        return self.bridgeToObjectiveC().substringWithRange(NSMakeRange(val1, val2))

        //-- Alternative code without bridge --
        //var range: Range<Int> = pos...(pos+len-1)
        //var start = advance(startIndex, range.startIndex)
        //var end = advance(startIndex, range.endIndex)
        //return self.substringWithRange(Range(start: start, end: end))
    }
}


println("0123456789"[3]) // return "3"

println("0123456789"[3,2]) // return "34"

println("0123456789"[3,2,.right2left]) // return "56"

println("0123456789"[5,10,.pos_len,.left2right]) // return "56789"

println("0123456789"[8,120,.pos_len,.right2left]) // return "01"

println("0123456789"[120,120,.pos_len,.left2right]) // return "9"

println("0123456789"[0...4]) // return "01234"

println("0123456789"[0..4]) // return "0123"

println("0123456789"[0...4,.right2left]) // return "56789"

println("0123456789"[4...0,.right2left]) // return "678" << because ??? range can wear endIndex at 0 ???
Luc-Olivier
  • 3,715
  • 2
  • 29
  • 29
1

A python-like solution, which allows you to use negative index,

var str = "Hello world!"
str[-1]        // "!"

could be:

extension String {
    subscript (var index:Int)->Character{
        get {
            let n = distance(self.startIndex, self.endIndex)
            index %= n
            if index < 0 { index += n }
            return self[advance(startIndex, index)]
        }
    }
}

By the way, it may be worth it to transpose the whole python's slice notation

Community
  • 1
  • 1
Joseph Merdrignac
  • 3,510
  • 2
  • 19
  • 16
  • Do you mind writing something that compiles for Swift 4? last return ... line doesn't seem to work advance() function is not there I believe. – C0D3 Oct 11 '18 at 19:00
1

You can also convert String to Array of Characters like that:

let text = "My Text"
let index = 2
let charSequence = text.unicodeScalars.map{ Character($0) }
let char = charSequence[index]

This is the way to get char at specified index in constant time.

The example below doesn't run in constant time, but requires linear time. So If You have a lot of searching in String by index use the method above.

let char = text[text.startIndex.advancedBy(index)]
Marcin Kapusta
  • 5,076
  • 3
  • 38
  • 55
1

I wanted to point out that if you have a large string and need to randomly access many characters from it, you may want to pay the extra memory cost and convert the string to an array for better performance:

// Pay up front for O(N) memory
let chars = Array(veryLargeString.characters)

for i in 0...veryLargeNumber {
    // Benefit from O(1) access
    print(chars[i])
}
Joseph Chen
  • 1,520
  • 13
  • 21
1

In Swift 3 without extensions to the String class, as simple as I can make it!

let myString = "abcedfg"
let characterLocationIndex = myString.index(myString.startIndex, offsetBy: 3)
let myCharacter = myString[characterLocationIndex]

myCharacter is "3" in this example.

John Pavley
  • 5,366
  • 2
  • 14
  • 16
1

Using characters would do the job. You can quickly convert the String to an array of characters that can be manipulated by the CharacterView methods.

Example:

let myString = "Hello World!"
let myChars  = myString.characters

(full CharacterView doc)

(tested in Swift 3)

Stephane Paquet
  • 2,315
  • 27
  • 31
1

There's an alternative, explained in String manifesto

extension String : BidirectionalCollection {
    subscript(i: Index) -> Character { return characters[i] }
}
George Maisuradze
  • 1,953
  • 19
  • 16
0

Get the first letter:

first(str) // retrieve first letter

More here: http://sketchytech.blogspot.com/2014/08/swift-pure-swift-method-for-returning.html

Noah Wilder
  • 1,656
  • 20
  • 38
philippinedev
  • 1,220
  • 10
  • 9
0

You could use SwiftString (https://github.com/amayne/SwiftString) to do this.

"Hello, world!"[0] // H
"Hello, world!"[0...4] // Hello

DISCLAIMER: I wrote this extension

eurobrew
  • 77
  • 4
0

Allows Negative Indices

Its always useful not always having to write string[string.length - 1] to get the last character when using a subscript extension. This (Swift 3) extension allows for negative indices, Range and CountableClosedRange.

extension String {
    var count: Int { return self.characters.count }

    subscript (i: Int) -> Character {
        // wraps out of bounds indices
        let j = i % self.count
        // wraps negative indices
        let x = j < 0 ? j + self.count : j

        // quick exit for first
        guard x != 0 else {
            return self.characters.first!
        }

        // quick exit for last
        guard x != count - 1 else {
            return self.characters.last!
        }

        return self[self.index(self.startIndex, offsetBy: x)]
    }

    subscript (r: Range<Int>) -> String {
        let lb = r.lowerBound
        let ub = r.upperBound

        // quick exit for one character
        guard lb != ub else { return String(self[lb]) }

        return self[self.index(self.startIndex, offsetBy: lb)..<self.index(self.startIndex, offsetBy: ub)]
    }

    subscript (r: CountableClosedRange<Int>) -> String {
        return self[r.lowerBound..<r.upperBound + 1]
    }
}

How you can use it:

var text = "Hello World"

text[-1]    // d
text[2]     // l
text[12]    // e
text[0...4] // Hello
text[0..<4] // Hell

For the more thorough Programmer: Include a guard against empty Strings in this extension

subscript (i: Int) -> Character {
    guard self.count != 0 else { return '' }
    ...
}

subscript (r: Range<Int>) -> String {
    guard self.count != 0 else { return "" }
    ...
}
Jay Harris
  • 4,201
  • 17
  • 21
0

Swift 3:

extension String {
    func substring(fromPosition: UInt, toPosition: UInt) -> String? {
        guard fromPosition <= toPosition else {
            return nil
        }

        guard toPosition < UInt(characters.count) else {
            return nil
        }

        let start = index(startIndex, offsetBy: String.IndexDistance(fromPosition))
        let end   = index(startIndex, offsetBy: String.IndexDistance(toPosition) + 1)
        let range = start..<end

        return substring(with: range)
    }
}

"ffaabbcc".substring(fromPosition: 2, toPosition: 5) // return "aabb"
Daniele Ceglia
  • 849
  • 8
  • 12
-1

Include this extension in your project

  extension String{
func trim() -> String
{
    return self.trimmingCharacters(in: NSCharacterSet.whitespaces)
}

var length: Int {
    return self.count
}

subscript (i: Int) -> String {
    return self[i ..< i + 1]
}

func substring(fromIndex: Int) -> String {
    return self[min(fromIndex, length) ..< length]
}

func substring(toIndex: Int) -> String {
    return self[0 ..< max(0, toIndex)]
}

subscript (r: Range<Int>) -> String {
    let range = Range(uncheckedBounds: (lower: max(0, min(length, r.lowerBound)),
                                        upper: min(length, max(0, r.upperBound))))
    let start = index(startIndex, offsetBy: range.lowerBound)
    let end = index(start, offsetBy: range.upperBound - range.lowerBound)
    return String(self[start ..< end])
}

func substring(fromIndex: Int, toIndex:Int)->String{
    let startIndex = self.index(self.startIndex, offsetBy: fromIndex)
    let endIndex = self.index(startIndex, offsetBy: toIndex-fromIndex)

    return String(self[startIndex...endIndex])
}

An then use the function like this

let str = "Sample-String"

let substring = str.substring(fromIndex: 0, toIndex: 0) //returns S
let sampleSubstr = str.substring(fromIndex: 0, toIndex: 5) //returns Sample
AkilRajdho
  • 49
  • 1
  • 5
-1

Check this is Swift 4

let myString = "LOVE"

self.textField1.text = String(Array(myString)[0])
self.textField2.text = String(Array(myString)[1])
self.textField3.text = String(Array(myString)[2])
self.textField4.text = String(Array(myString)[3])
Sreekanth G
  • 658
  • 6
  • 13
-1

Swift 5.1

Here might be the easiest solution out of all these answers.

Add this extension:

extension String {
    func retrieveFirstCharacter() -> String? {
        guard self.count > 0 else { return nil }
        let numberOfCharacters = self.count
        return String(self.dropLast(numberOfCharacters - 1))
    }
}
Josh R
  • 104
  • 4
-1

This String extension works like a charm.

extension String {
   func getCharacter(_ index: Int) -> Character {
       let character = Array(self)[index]
       return character
   }
}
rBalmer
  • 67
  • 9
-3

prob one of the best and simpliest way

        let yourString = "thisString"
        print(Array(yourString)[8])

puts each letters of your string into arrrays and then you sellect the 9th one

  • Same solution already given by Sreekanth G, Matt Le Fleur and others. And your version is worse because it doesn't convert back the result to a string. – Eric Aya Mar 30 '19 at 21:33
  • but it doesnt have to thats for the person to decide what they want to do with the result – bibble triple Mar 30 '19 at 21:49
  • I understand what you say. But the second part of my comment was just an addition to the first part, which was the real important part: **this solution has already been given in other answers**. Please don't post what has already been posted. Don't post duplicate content, that's all I'm saying. `puts each letters of your string into arrrays` is already the solution given by Sreekanth G and others. Literally the same. – Eric Aya Mar 30 '19 at 21:52
  • 1
    For a simple task like this one, the solution poses performance issues, as the whole contents of the string will be duplicated in memory. – Cristik Dec 28 '19 at 19:44