274

I have not yet been able to figure out how to get a substring of a String in Swift:

var str = “Hello, playground”
func test(str: String) -> String {
 return str.substringWithRange( /* What goes here? */ )
}
test (str)

I'm not able to create a Range in Swift. Autocomplete in the Playground isn’t super helpful - this is what it suggests:

return str.substringWithRange(aRange: Range<String.Index>)

I haven't found anything in the Swift Standard Reference Library that helps. Here was another wild guess:

return str.substringWithRange(Range(0, 1))

And this:

let r:Range<String.Index> = Range<String.Index>(start: 0, end: 2)
return str.substringWithRange(r)

I've seen other answers (Finding index of character in Swift String) that seem to suggest that since String is a bridge type for NSString, the "old" methods should work, but it's not clear how - e.g., this doesn't work either (doesn't appear to be valid syntax):

let x = str.substringWithRange(NSMakeRange(0, 3))

Thoughts?

Community
  • 1
  • 1
Rob
  • 25,984
  • 32
  • 109
  • 155
  • What do you mean when you say the last example doesn't work? Does it cause an error or return an unexpected value? – 67cherries Jun 04 '14 at 18:40
  • Please dupe rdar://17158813 requesting subscript notation http://openradar.appspot.com/radar?id=6373877630369792 – Rob Napier Jun 04 '14 at 20:33

33 Answers33

259

You can use the substringWithRange method. It takes a start and end String.Index.

var str = "Hello, playground"
str.substringWithRange(Range<String.Index>(start: str.startIndex, end: str.endIndex)) //"Hello, playground"

To change the start and end index, use advancedBy(n).

var str = "Hello, playground"
str.substringWithRange(Range<String.Index>(start: str.startIndex.advancedBy(2), end: str.endIndex.advancedBy(-1))) //"llo, playgroun"

You can also still use the NSString method with NSRange, but you have to make sure you are using an NSString like this:

let myNSString = str as NSString
myNSString.substringWithRange(NSRange(location: 0, length: 3))

Note: as JanX2 mentioned, this second method is not safe with unicode strings.

matt---
  • 2,470
  • 2
  • 20
  • 20
Connor Pearson
  • 63,902
  • 28
  • 145
  • 142
  • Interesting. I'm surprised that it only accepts Range, I would have expected Range. – Rob Jun 04 '14 at 18:43
  • 5
    Yeah. For now I'd keep on bridging to NSString when necessary. It seems Swift's String is still incomplete – Connor Pearson Jun 04 '14 at 18:49
  • 16
    This is not safe! You are assuming that indexes into String and NSString are interchangeable. That is NOT the case. Indexes into a String refer to Unicode code points while indexes into an NSString refer to UTF-16 code units! Try it with emoji. – JanX2 Jun 09 '14 at 09:08
  • 3
    Please note that Indexes into a String do NOT refer to Unicode code points anymore. Think of them as referencing user-perceived characters. – JanX2 Aug 20 '14 at 10:05
  • It's worth noting that the `advance` function can be used to do things like `advance(str.startIndex, 10)` to get the 10th index into a string. – Charles A. Oct 08 '14 at 19:29
  • 2
    So what's the right answer here? Calling `advance` means we have to iterate the whole way through a possibly long string. But forming an NSRange and using NSString explicitly is dangerous? What's left? – matt Nov 18 '14 at 18:45
  • You can use this string extension I wrote to do it easily https://bit.ly/JString (string[0...3] or substringFrom(), etc...) – William Falcon Feb 16 '15 at 20:21
  • Stack Overflow's format is not doing a great job at providing much clarity on this situation because the Swift language changes frequently. This code works fine for me with Swift 2. It appears that there is a new, less verbose way to do it with Swift 2 (scroll, scroll, scroll down until you find it). By the time that answer gets enough upvotes, we'll be in Swift 3 :P – jeremywhuff Oct 21 '15 at 00:11
  • 1
    Thanks, m8. This transition to Swift is kind of messy after 4 years only doing Objective-C. – Felipe Nov 04 '15 at 18:53
  • 1
    Please update first line to: str.substringWithRange(Range(str.startIndex.. – Warpzit Jul 01 '16 at 09:25
  • New syntax compatible with swift 3 (Range(start,end) is deprecated) str[str.startIndex..< str.startIndex.advancedBy(5,limit: str.endIndex)] – valR Aug 31 '16 at 08:58
  • So weird, why didn't they just do ```subString(start:1, end:5)``` like all other languages. – Oliver Dixon Nov 19 '16 at 08:20
  • 1
    "So much work for such a common task. Shame" said xiefei. Yes but full compatible UTFn. – Luc-Olivier Nov 25 '16 at 20:12
167

Swift 2

Simple

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

Swift 3

let startIndex = str.index(str.startIndex, offsetBy: 3)
let endIndex = str.index(str.startIndex, offsetBy: 7)

str[startIndex...endIndex]       // "Strin"
str.substring(to: startIndex)    // "My "
str.substring(from: startIndex)  // "String"

Swift 4

substring(to:) and substring(from:) are deprecated in Swift 4.

String(str[..<startIndex])    // "My "
String(str[startIndex...])    // "String"
String(str[startIndex...endIndex])    // "Strin"
Warif Akhand Rishi
  • 23,920
  • 8
  • 80
  • 107
  • 4
    THIS. So confusing that you have to find your index before you can use it. No idea why it took me so long to realise, but thanks for your contribution to human kind. – Warpzit May 01 '17 at 13:44
  • 1
    In swift 4 substring operations return an instance of the Substring type, instead of String. Need to convert the result to a String for long-term storage. let newString = String(substring) – phnmnn Oct 31 '17 at 19:31
46

At the time I'm writing, no extension is perfectly Swift 4.2 compatible, so here is one that covers all the needs I could think of:

extension String {
    func substring(from: Int?, to: Int?) -> String {
        if let start = from {
            guard start < self.count else {
                return ""
            }
        }

        if let end = to {
            guard end >= 0 else {
                return ""
            }
        }

        if let start = from, let end = to {
            guard end - start >= 0 else {
                return ""
            }
        }

        let startIndex: String.Index
        if let start = from, start >= 0 {
            startIndex = self.index(self.startIndex, offsetBy: start)
        } else {
            startIndex = self.startIndex
        }

        let endIndex: String.Index
        if let end = to, end >= 0, end < self.count {
            endIndex = self.index(self.startIndex, offsetBy: end + 1)
        } else {
            endIndex = self.endIndex
        }

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

    func substring(from: Int) -> String {
        return self.substring(from: from, to: nil)
    }

    func substring(to: Int) -> String {
        return self.substring(from: nil, to: to)
    }

    func substring(from: Int?, length: Int) -> String {
        guard length > 0 else {
            return ""
        }

        let end: Int
        if let start = from, start > 0 {
            end = start + length - 1
        } else {
            end = length - 1
        }

        return self.substring(from: from, to: end)
    }

    func substring(length: Int, to: Int?) -> String {
        guard let end = to, end > 0, length > 0 else {
            return ""
        }

        let start: Int
        if let end = to, end - length > 0 {
            start = end - length + 1
        } else {
            start = 0
        }

        return self.substring(from: start, to: to)
    }
}

And then, you can use:

let string = "Hello,World!"

string.substring(from: 1, to: 7)gets you: ello,Wo

string.substring(to: 7)gets you: Hello,Wo

string.substring(from: 3)gets you: lo,World!

string.substring(from: 1, length: 4)gets you: ello

string.substring(length: 4, to: 7)gets you: o,Wo

Updated substring(from: Int?, length: Int) to support starting from zero.

Glenn Posadas
  • 12,555
  • 6
  • 54
  • 95
winterized
  • 3,324
  • 3
  • 19
  • 21
  • How does this handle emoji and extended grapheme clusters? (I should test it out myself but it's not convenient at the moment.) – Suragch Sep 28 '16 at 15:57
  • 2
    @Suragch It handles it OK: `"Hello∝ℵℶ≹".substring(from: 4, to: 14)` gives us: `o∝ℵℶ` – winterized Sep 29 '16 at 09:33
  • Those look like normal characters, though. Try using emoji like ‍‍‍ or – Ky - Apr 21 '17 at 10:50
44

NOTE: @airspeedswift makes some very insightful points on the trade-offs of this approach, particularly the hidden performance impacts. Strings are not simple beasts, and getting to a particular index may take O(n) time, which means a loop that uses a subscript can be O(n^2). You have been warned.

You just need to add a new subscript function that takes a range and uses advancedBy() to walk to where you want:

import Foundation

extension String {
    subscript (r: Range<Int>) -> String {
        get {
            let startIndex = self.startIndex.advancedBy(r.startIndex)
            let endIndex = startIndex.advancedBy(r.endIndex - r.startIndex)

            return self[Range(start: startIndex, end: endIndex)]
        }
    }
}

var s = "Hello, playground"

println(s[0...5]) // ==> "Hello,"
println(s[0..<5]) // ==> "Hello"

(This should definitely be part of the language. Please dupe rdar://17158813)

For fun, you can also add a + operator onto the indexes:

func +<T: ForwardIndex>(var index: T, var count: Int) -> T {
  for (; count > 0; --count) {
    index = index.succ()
  }
  return index
}

s.substringWithRange(s.startIndex+2 .. s.startIndex+5)

(I don't know yet if this one should be part of the language or not.)

Mabedan
  • 857
  • 8
  • 30
Rob Napier
  • 286,113
  • 34
  • 456
  • 610
  • 2
    We should probably work with String.Index instead of integer indexes as much as possible for performance reasons. Every conversion from an integer index into a String.Index needs to process the string to seek. – JanX2 Jun 09 '14 at 09:59
  • 11
    @AndrewDunn This is not premature optimization. Each conversion from an integer index into a String.Index is O(n), not O(1)! Please read the comments for `advance(…)` in the `Swift` namespace. – JanX2 Jul 11 '14 at 04:22
  • 1
    The whole of the String implementation is done using `String.Index`. In this case it's not as much optimization as it is keeping everything straightforward. Having to juggle between int and String.Index back and forth everywhere will leads to a mess. – chakrit Aug 06 '14 at 07:42
  • @chakrit Only if indexes you need to use aren't already integers (instead of being `String.Index`es) which is often the case. Than you can just wish all those string handling methods were working with `Int`s. And you are forced to conver it to `String.Indexes` which leads to the mess you mentioned. – Rasto Nov 19 '14 at 21:49
  • The interested reader should give a look at ExSwift https://github.com/pNre/ExSwift – AsTeR Dec 17 '14 at 14:34
  • Better fix those for loops, var function parameters and increment/decrement operators soon https://github.com/apple/swift-evolution#accepted-proposals-for-swift-30 – brimstone Jan 03 '16 at 04:46
  • extension String { subscript (r: Range) -> String { get { //let startIndex = advance(self.startIndex, r.startIndex) let startIndex = self.startIndex.advancedBy(r.startIndex) let endIndex = startIndex.advancedBy(r.endIndex - r.startIndex) return self[Range(start: startIndex, end: endIndex)] } } } // -----------This is for SWIFT 2.1 – uplearned.com Mar 10 '16 at 04:08
31

SWIFT 2.0

simple:

let myString = "full text container"
let substring = myString[myString.startIndex..<myString.startIndex.advancedBy(3)] // prints: ful

SWIFT 3.0

let substring = myString[myString.startIndex..<myString.index(myString.startIndex, offsetBy: 3)] // prints: ful

SWIFT 4.0

Substring operations return an instance of the Substring type, instead of String.

let substring = myString[myString.startIndex..<myString.index(myString.startIndex, offsetBy: 3)] // prints: ful

// Convert the result to a String for long-term storage.
let newString = String(substring)
phnmnn
  • 12,813
  • 11
  • 47
  • 64
19

It is much more simple than any of the answers here, once you find the right syntax.

I want to take away the [ and ]

let myString = "[ABCDEFGHI]"
let startIndex = advance(myString.startIndex, 1) //advance as much as you like
let endIndex = advance(myString.endIndex, -1)
let range = startIndex..<endIndex
let myNewString = myString.substringWithRange( range )

result will be "ABCDEFGHI" the startIndex and endIndex could also be used in

let mySubString = myString.substringFromIndex(startIndex)

and so on!

PS: As indicated in the remarks, there are some syntax changes in swift 2 which comes with xcode 7 and iOS9!

Please look at this page

Community
  • 1
  • 1
Lars Christoffersen
  • 1,719
  • 13
  • 24
  • Yes, I had the same issues with the debugger not showing the correct string several time? – Lars Christoffersen Oct 07 '14 at 16:32
  • i have a string like this /Date(147410000000)/. How can i get value instead of specifying the index. I need to get the value using rangeOfString("(") and rangeOfString(")"). The result should be 147410000000. Hoe can i get that – iPhone Guy Oct 28 '14 at 06:27
  • That is not so hard:var str = "/Date(147410000000)/." var range = str.rangeOfString( "(" )!.endIndex.. – Lars Christoffersen Oct 29 '14 at 00:22
  • 4
    since Xcode 7 beta 6 `advance(myString.endIndex, -1)` syntax has changed to `myString.endIndex.advancedBy(-1)` – marc-medley Aug 31 '15 at 22:06
16

For example to find the first name (up to the first space) in my full name:

let name = "Joris Kluivers"

let start = name.startIndex
let end = find(name, " ")

if end {
    let firstName = name[start..end!]
} else {
    // no space found
}

start and end are of type String.Index here and are used to create a Range<String.Index> and used in the subscript accessor (if a space is found at all in the original string).

It's hard to create a String.Index directly from an integer position as used in the opening post. This is because in my name each character would be of equal size in bytes. But characters using special accents in other languages could have used several more bytes (depending on the encoding used). So what byte should the integer refer to?

It's possible to create a new String.Index from an existing one using the methods succ and pred which will make sure the correct number of bytes are skipped to get to the next code point in the encoding. However in this case it's easier to search for the index of the first space in the string to find the end index.

Joris Kluivers
  • 11,894
  • 2
  • 48
  • 47
16

Since String is a bridge type for NSString, the "old" methods should work, but it's not clear how - e.g., this doesn't work either (doesn't appear to be valid syntax):

 let x = str.substringWithRange(NSMakeRange(0, 3))

To me, that is the really interesting part of your question. String is bridged to NSString, so most NSString methods do work directly on a String. You can use them freely and without thinking. So, for example, this works just as you expect:

// delete all spaces from Swift String stateName
stateName = stateName.stringByReplacingOccurrencesOfString(" ", withString:"")

But, as so often happens, "I got my mojo workin' but it just don't work on you." You just happened to pick one of the rare cases where a parallel identically named Swift method exists, and in a case like that, the Swift method overshadows the Objective-C method. Thus, when you say str.substringWithRange, Swift thinks you mean the Swift method rather than the NSString method — and then you are hosed, because the Swift method expects a Range<String.Index>, and you don't know how to make one of those.

The easy way out is to stop Swift from overshadowing like this, by casting explicitly:

let x = (str as NSString).substringWithRange(NSMakeRange(0, 3))

Note that no significant extra work is involved here. "Cast" does not mean "convert"; the String is effectively an NSString. We are just telling Swift how to look at this variable for purposes of this one line of code.

The really weird part of this whole thing is that the Swift method, which causes all this trouble, is undocumented. I have no idea where it is defined; it is not in the NSString header and it's not in the Swift header either.

Community
  • 1
  • 1
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • However, I suggest filing a bug. Apple should "fish or cut bait" - either give us a way to construct a `Range`, or get this undocumented method out of our way. – matt Jul 23 '14 at 17:32
  • Damn - this is useful. Despite what the documentation says, I didn't understand what was going wrong until I read this. Now sorted :) – Jon M Apr 13 '15 at 19:36
13

The short answer is that this is really hard in Swift right now. My hunch is that there is still a bunch of work for Apple to do on convenience methods for things like this.

String.substringWithRange() is expecting a Range<String.Index> parameter, and as far as I can tell there isn't a generator method for the String.Index type. You can get String.Index values back from aString.startIndex and aString.endIndex and call .succ() or .pred() on them, but that's madness.

How about an extension on the String class that takes good old Ints?

extension String {
    subscript (r: Range<Int>) -> String {
        get {
            let subStart = advance(self.startIndex, r.startIndex, self.endIndex)
            let subEnd = advance(subStart, r.endIndex - r.startIndex, self.endIndex)
            return self.substringWithRange(Range(start: subStart, end: subEnd))
        }
    }
    func substring(from: Int) -> String {
        let end = countElements(self)
        return self[from..<end]
    }
    func substring(from: Int, length: Int) -> String {
        let end = from + length
        return self[from..<end]
    }
}

let mobyDick = "Call me Ishmael."
println(mobyDick[8...14])             // Ishmael

let dogString = "This 's name is Patch."
println(dogString[5..<6])               // 
println(dogString[5...5])              // 
println(dogString.substring(5))        // 's name is Patch.
println(dogString.substring(5, length: 1))   // 

Update: Swift beta 4 resolves the issues below!

As it stands [in beta 3 and earlier], even Swift-native strings have some issues with handling Unicode characters. The dog icon above worked, but the following doesn't:

let harderString = "1:1️⃣"
for character in harderString {
    println(character)
}

Output:

1
:
1
️
⃣
Nate Cook
  • 92,417
  • 32
  • 217
  • 178
  • 1
    It's odd, the core Swift classes just seem to lack a lot of basic functions. I can't think Apple wants us to continually reference Foundation classes to perform simple tasks. – bluedevil2k Jun 05 '14 at 16:05
  • 1
    Totally. Keep in mind that Swift was basically developed in the dark, and everyone has a different definition of "simple tasks" - I bet we see some quick iteration on these issues. – Nate Cook Jun 05 '14 at 16:07
  • @JanX2 - Thanks, you're totally right. The seamless bridging with `NSString` made me think I was using only Swift-native `String` methods, since I wasn't using any `myString as NSString` calls. – Nate Cook Jun 09 '14 at 14:48
  • There are some Unicode bugs in Swift-native string handling right now, too: see my note at the end. – Nate Cook Jun 09 '14 at 14:52
  • @NateCook Can you please file a radar for that issue? – JanX2 Jun 09 '14 at 17:46
  • Done: rdar://17232358 (and the Bug Reporter choked on even the easier Unicode in my bug report, tellingly). – Nate Cook Jun 09 '14 at 17:53
  • This will be fixed in Swift 1.0 @NateCook. :) The reason the second one didn't work is because it is a composed character, made up of multiple code points. – Randy the Dev Jul 09 '14 at 17:44
13

In new Xcode 7.0 use

//: Playground - noun: a place where people can play

import UIKit

var name = "How do you use String.substringWithRange?"
let range = name.startIndex.advancedBy(0)..<name.startIndex.advancedBy(10)
name.substringWithRange(range)

//OUT:

enter image description here

Claudio Silva
  • 3,743
  • 1
  • 26
  • 27
11

You can use this extensions to improve substringWithRange

Swift 2.3

extension String
{   
    func substringWithRange(start: Int, end: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if end < 0 || end > self.characters.count
        {
            print("end index \(end) out of bounds")
            return ""
        }
        let range = Range(start: self.startIndex.advancedBy(start), end: self.startIndex.advancedBy(end))
        return self.substringWithRange(range)
    }

    func substringWithRange(start: Int, location: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if location < 0 || start + location > self.characters.count
        {
            print("end index \(start + location) out of bounds")
            return ""
        }
        let range = Range(start: self.startIndex.advancedBy(start), end: self.startIndex.advancedBy(start + location))
        return self.substringWithRange(range)
    }
}

Swift 3

extension String
{
    func substring(start: Int, end: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if end < 0 || end > self.characters.count
        {
            print("end index \(end) out of bounds")
            return ""
        }
        let startIndex = self.characters.index(self.startIndex, offsetBy: start)
        let endIndex = self.characters.index(self.startIndex, offsetBy: end)
        let range = startIndex..<endIndex

        return self.substring(with: range)
    }

    func substring(start: Int, location: Int) -> String
    {
        if (start < 0 || start > self.characters.count)
        {
            print("start index \(start) out of bounds")
            return ""
        }
        else if location < 0 || start + location > self.characters.count
        {
            print("end index \(start + location) out of bounds")
            return ""
        }
        let startIndex = self.characters.index(self.startIndex, offsetBy: start)
        let endIndex = self.characters.index(self.startIndex, offsetBy: start + location)
        let range = startIndex..<endIndex

        return self.substring(with: range)
    }
}

Usage:

let str = "Hello, playground"

let substring1 = str.substringWithRange(0, end: 5) //Hello
let substring2 = str.substringWithRange(7, location: 10) //playground
ChikabuZ
  • 10,031
  • 5
  • 63
  • 86
7

Sample Code for how to get substring in Swift 2.0

(i) Substring from starting index

Input:-

var str = "Swift is very powerful language!"
print(str)

str = str.substringToIndex(str.startIndex.advancedBy(5))
print(str)

Output:-

Swift is very powerful language!
Swift

(ii) Substring from particular index

Input:-

var str = "Swift is very powerful language!"
print(str)

str = str.substringFromIndex(str.startIndex.advancedBy(6)).substringToIndex(str.startIndex.advancedBy(2))
print(str)

Output:-

Swift is very powerful language!
is

I hope it will help you!

Umang Kothari
  • 3,674
  • 27
  • 36
7

Easy solution with little code.

Make an extension that includes basic subStringing that nearly all other languages have:

extension String {
    func subString(start: Int, end: Int) -> String {
        let startIndex = self.index(self.startIndex, offsetBy: start)
        let endIndex = self.index(startIndex, offsetBy: end)

        let finalString = self.substring(from: startIndex)
        return finalString.substring(to: endIndex)
    }
}

Simply call this with

someString.subString(start: 0, end: 6)
Oliver Dixon
  • 7,012
  • 5
  • 61
  • 95
6

This works in my playground :)

String(seq: Array(str)[2...4])
patrykens
  • 159
  • 1
  • 6
  • Actually this is more efficient than calling `advance` –  May 24 '15 at 06:50
  • 2
    It's not working in my Xcode 7 (Swift 2) playground, but this does... 'var str = "Hello, playground"' String(Array(str.characters)[7]) // "p" String(Array(str.characters)[7...10]) // "play" String(Array(str.characters)[7..<10]) // "pla" – Vince O'Sullivan Jul 09 '15 at 20:45
6

Updated for Xcode 7. Adds String extension:

Use:

var chuck: String = "Hello Chuck Norris"
chuck[6...11] // => Chuck

Implementation:

extension String {

    /**
     Subscript to allow for quick String substrings ["Hello"][0...1] = "He"
     */
    subscript (r: Range<Int>) -> String {
        get {
            let start = self.startIndex.advancedBy(r.startIndex)
            let end = self.startIndex.advancedBy(r.endIndex - 1)
            return self.substringWithRange(start..<end)
        }
    }

}
Firo
  • 15,448
  • 3
  • 54
  • 74
4

try this in playground

var str:String = "Hello, playground"

let range = Range(start:advance(str.startIndex,1), end: advance(str.startIndex,8))

it will give you "ello, p"

However where this gets really interesting is that if you make the last index bigger than the string in playground it will show any strings that you defined after str :o

Range() appears to be a generic function so that it needs to know the type it is dealing with.

You also have to give it the actual string your interested in playgrounds as it seems to hold all stings in a sequence one after another with their variable name afterwards.

So

var str:String = "Hello, playground"

var str2:String = "I'm the next string"

let range = Range(start:advance(str.startIndex,1), end: advance(str.startIndex,49))

gives "ello, playground�str���I'm the next string�str2�"

works even if str2 is defined with a let

:)

  • I agree it is verbose compared to Objective-C, however it all fits on one line which is a bonus. – Peter Roberts Jul 30 '14 at 08:15
  • Well there is still the one line missing that actually prints out the substring given the range you computed. I like some of the other solutions that create String class extensions using your logic, but in an extension. That is ultimately the solution I went with. – Roderic Campbell Jul 30 '14 at 17:16
  • They've fixed the bug where you can go beyond the end of the string. Now you can only put in a value up till str.endIndex – Peter Roberts Aug 17 '14 at 17:18
4

Rob Napier had already given a awesome answer using subscript. But i felt one drawback in that as there is no check for out of bound conditions. This can tend to crash. So i modified the extension and here it is

extension String {
    subscript (r: Range<Int>) -> String? { //Optional String as return value
        get {
            let stringCount = self.characters.count as Int
            //Check for out of boundary condition
            if (stringCount < r.endIndex) || (stringCount < r.startIndex){
                return nil
            }
            let startIndex = self.startIndex.advancedBy(r.startIndex)

            let endIndex = self.startIndex.advancedBy(r.endIndex - r.startIndex)

            return self[Range(start: startIndex, end: endIndex)]
        }
    }
}

Output below

var str2 = "Hello, World"

var str3 = str2[0...5]
//Hello,
var str4 = str2[0..<5]
//Hello
var str5 = str2[0..<15]
//nil

So i suggest always to check for the if let

if let string = str[0...5]
{
    //Manipulate your string safely
}
ipraba
  • 16,485
  • 4
  • 59
  • 58
3

In Swift3

For ex: a variable "Duke James Thomas", we need to get "James".

let name = "Duke James Thomas"
let range: Range<String.Index> = name.range(of:"James")!
let lastrange: Range<String.Index> = img.range(of:"Thomas")!
var middlename = name[range.lowerBound..<lstrange.lowerBound]
print (middlename)
Sandu
  • 436
  • 4
  • 8
2

Taking a page from Rob Napier, I developed these Common String Extensions, two of which are:

subscript (r: Range<Int>) -> String
{
    get {
        let startIndex = advance(self.startIndex, r.startIndex)
        let endIndex = advance(self.startIndex, r.endIndex - 1)

        return self[Range(start: startIndex, end: endIndex)]
    }
}

func subString(startIndex: Int, length: Int) -> String
{
    var start = advance(self.startIndex, startIndex)
    var end = advance(self.startIndex, startIndex + length)
    return self.substringWithRange(Range<String.Index>(start: start, end: end))
}

Usage:

"Awesome"[3...7] //"some"
"Awesome".subString(3, length: 4) //"some"
Albert Bori
  • 9,832
  • 10
  • 51
  • 78
2

This is how you get a range from a string:

var str = "Hello, playground"

let startIndex = advance(str.startIndex, 1)
let endIndex = advance(startIndex, 8)
let range = startIndex..<endIndex
let substr = str[range] //"ello, pl"

The key point is that you are passing a range of values of type String.Index (this is what advance returns) instead of integers.

The reason why this is necessary, is that strings in Swift don't have random access (because of variable length of Unicode characters basically). You also can't do str[1]. String.Index is designed to work with their internal structure.

You can create an extension with a subscript though, that does this for you, so you can just pass a range of integers (see e.g. Rob Napier's answer).

Community
  • 1
  • 1
User
  • 31,811
  • 40
  • 131
  • 232
2

I tried to come up with something Pythonic.

All the subscripts here are great, but the times I really need something simple is usually when I want to count from back, e.g. string.endIndex.advancedBy(-1)

It supports nil values, for both start and end index, where nil would mean index at 0 for start, string.characters.count for end.

extension String {
    var subString: (Int?) -> (Int?) -> String {
        return { (start) in
            { (end) in
                let startIndex = start ?? 0 < 0 ? self.endIndex.advancedBy(start!) : self.startIndex.advancedBy(start ?? 0)
                let endIndex = end ?? self.characters.count < 0 ? self.endIndex.advancedBy(end!) : self.startIndex.advancedBy(end ?? self.characters.count)

                return startIndex > endIndex ? "" : self.substringWithRange(startIndex ..< endIndex)
            }
        }
    }
}

let dog = "Dog‼"
print(dog.subString(nil)(-1)) // Dog!!

EDIT

A more elegant solution:

public extension String {
    struct Substring {
        var start: Int?
        var string: String

        public subscript(end: Int?) -> String {
            let startIndex = start ?? 0 < 0 ? string.endIndex.advancedBy(start!) : string.startIndex.advancedBy(start ?? 0)
            let endIndex = end ?? string.characters.count < 0 ? string.endIndex.advancedBy(end!) : string.startIndex.advancedBy(end ?? string.characters.count)

            return startIndex > endIndex ? "" : string.substringWithRange(startIndex ..< endIndex)
        }
    }

    public subscript(start: Int?) -> Substring {
        return Substring(start: start, string: self)
    }
}

let dog = "Dog‼"
print(dog[nil][-1]) // Dog!!
funct7
  • 3,407
  • 2
  • 27
  • 33
1

First create the range, then the substring. You can use fromIndex..<toIndex syntax like so:

let range = fullString.startIndex..<fullString.startIndex.advancedBy(15) // 15 first characters of the string
let substring = fullString.substringWithRange(range)
1

here is a example to get video-Id only .i.e (6oL687G0Iso) from the whole URL in swift

let str = "https://www.youtube.com/watch?v=6oL687G0Iso&list=PLKmzL8Ib1gsT-5LN3V2h2H14wyBZTyvVL&index=2"
var arrSaprate = str.componentsSeparatedByString("v=")
let start = arrSaprate[1]
let rangeOfID = Range(start: start.startIndex,end:start.startIndex.advancedBy(11))
let substring = start[rangeOfID]
print(substring)
Hardik Thakkar
  • 15,269
  • 2
  • 94
  • 81
1
let startIndex = text.startIndex
var range = startIndex.advancedBy(1) ..< text.endIndex.advancedBy(-4)
let substring = text.substringWithRange(range)

Full sample you can see here

Svitlana
  • 2,938
  • 1
  • 29
  • 38
0

http://www.learnswiftonline.com/reference-guides/string-reference-guide-for-swift/ shows that this works well:

var str = "abcd"
str = str.substringToIndex(1)
swiftmb
  • 17
  • 1
  • 3
    Playground execution failed: error: :23:5: error: type 'String.Index' does not conform to protocol 'IntegerLiteralConvertible' str = str.substringToIndex(1) ^ – Verbe Aug 01 '14 at 12:53
0

Well, I had the same issue and solved with the "bridgeToObjectiveC()" function:

var helloworld = "Hello World!"
var world = helloworld.bridgeToObjectiveC().substringWithRange(NSMakeRange(6,6))
println("\(world)") // should print World!

Please note that in the example, substringWithRange in conjunction with NSMakeRange take the part of the string starting at index 6 (character "W") and finishing at index 6 + 6 positions ahead (character "!")

Cheers.

0

You can use any of the substring methods in a Swift String extension I wrote https://bit.ly/JString.

var string = "hello"
var sub = string.substringFrom(3) // or string[3...5]
println(sub)// "lo"
William Falcon
  • 9,813
  • 14
  • 67
  • 110
0

If you have an NSRange, bridging to NSString works seamlessly. For example, I was doing some work with UITextFieldDelegate and I quickly wanted to compute the new string value when it asked if it should replace the range.

func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool {
    let newString = (textField.text as NSString).stringByReplacingCharactersInRange(range, withString: string)
    println("Got new string: ", newString)
}
joslinm
  • 7,845
  • 6
  • 49
  • 72
0

Simple extension for String:

extension String {

    func substringToIndex(index: Int) -> String {
        return self[startIndex...startIndex.advancedBy(min(index, characters.count - 1))]
    }
}
Bartłomiej Semańczyk
  • 59,234
  • 49
  • 233
  • 358
0

If you don't care about performance... this is probably the most concise solution in Swift 4

extension String {
    subscript(range: CountableClosedRange<Int>) -> String {
        return enumerated().filter{$0.offset >= range.first! && $0.offset < range.last!}
            .reduce(""){$0 + String($1.element)}
    }
}

It enables you to do something like this:

let myStr = "abcd"
myStr[0..<2] // produces "ab"
-1

Swift 3.0

I decided to have a little fun with this and produce an extension on String. I might not be using the word truncate properly in what I'm having the function actually do.

extension String {

    func truncate(from initialSpot: Int, withLengthOf endSpot: Int) -> String? {

        guard endSpot > initialSpot else { return nil }
        guard endSpot + initialSpot <= self.characters.count else { return nil }

        let truncated = String(self.characters.dropFirst(initialSpot))
        let lastIndex = truncated.index(truncated.startIndex, offsetBy: endSpot)

        return truncated.substring(to: lastIndex)
    }

}

let favGameOfThronesSong = "Light of the Seven"

let word = favGameOfThronesSong.truncate(from: 1, withLengthOf: 4)
// "ight"
  • You're posting a custom string extension? To a question asked, and answered (with a highly voted accepted answer) over two years ago? – David Makogon Jun 28 '16 at 21:16
  • 1
    Sorry about that. The new Game of Thrones got me all excited. Also, most of the solutions above didn't work in the new Xcode with Swift 3.0. – J. Campagno Jun 29 '16 at 23:32
-1

Swift 3.X+ , Xcode 8.X + Tested working easy solution;

Use simple ;

let myString = "12.12.2017 12:34:45"
let newString = myString?[(myString?.startIndex)!..<(myString?.index((myString?.startIndex)!, offsetBy: 10))!]
print(newString) 

Output = 12.12.2017

For customization easily change;

offsetBy: 10 // Change 10 to endIndex.

When you change offsetBy: 10 to 15,20 etc. Will cut your string..

Thank you !

SwiftDeveloper
  • 7,244
  • 14
  • 56
  • 85
-4

There's been a lot of good examples of how to attack this problem. The original question was about using the substringWithRange, but as has been pointed out that's a harder way to go than just doing your own extension.

The above range solution is good. You can also do this a dozen other ways. Here's yet another example of how you could do this:

extension String{
    func sub(start: Int, length: Int) -> String {
        assert(start >= 0, "Cannot extract from a negative starting index")
        assert(length >= 0, "Cannot extract a negative length string")
        assert(start <= countElements(self) - 1, "cannot start beyond the end")
        assert(start + length <= countElements(self), "substring goes past the end of the original")
        var a = self.substringFromIndex(start)
        var b = a.substringToIndex(length)
        return b
    }
}
var s = "apple12345"
println(s.sub(6, length: 4))
// prints "2345"