71

How do I get the string before a certain character in swift? The code below is how I did it in Objective C, but can't seem to perform the same task in Swift. Any tips or suggestions on how to achieve this? rangeOfString seems to not work at all in swift (although Swift has been acting up for me again).

NSRange range = [time rangeOfString:@" "];
NSString *startDate =
[time substringToIndex:range.location];

As you can see from the code above I am able to get the string before the space character in Objective C.

Edit : If I try something like this

 var string = "hello Swift"
 var range : NSRange = string.rangeOfString("Swift")

I get the following error.

Cannot convert the expression's type 'NSString' to type '(String, options: NSStringCompareOptions, range: Range?, locale: NSLocale?)'

Not sure what I did wrong exactly or how to resolve it correctly.

jscs
  • 63,694
  • 13
  • 151
  • 195
Danger Veger
  • 1,117
  • 1
  • 9
  • 16
  • In [this answer](http://stackoverflow.com/a/24161872/2240769) they also use rangeOfString in Swift and it seems to work. – Tom Spee Apr 02 '15 at 20:25
  • I edited my question to better explain what is happening when I try to apply your solution. – Danger Veger Apr 02 '15 at 20:30
  • You can use `Range` instead of `NSRange`: `let range : Range? = string.rangeOfString("Swift")`. You can also cast `String` to `NSString`: `var range : NSRange = (string as NSString).rangeOfString("Swift")` – albertamg Apr 02 '15 at 22:07

11 Answers11

144

Use componentsSeparatedByString() as shown below:

var delimiter = " "
var newstr = "token0 token1 token2 token3"
var token = newstr.components(separatedBy: delimiter)
print (token[0])

Or to use your specific case:

var delimiter = " token1"
var newstr = "token0 token1 token2 token3"
var token = newstr.components(separatedBy: delimiter)
print (token[0])
Marián Černý
  • 15,096
  • 4
  • 70
  • 83
Syed Tariq
  • 2,878
  • 3
  • 27
  • 37
  • 1
    Fantastic answer. I was trying to figure out how to accomplish this task w/ rangeOfString, but this is MUCH more efficient. – Adrian Feb 07 '16 at 04:08
31

You can do the same with rangeOfString() provided by String class

let string = "Hello Swift"
if let range = string.rangeOfString("Swift") {
   let firstPart = string[string.startIndex..<range.startIndex]
   print(firstPart) // print Hello
}

You can also achieve it with your method substringToIndex()

let string = "Hello Swift"
if let range = string.rangeOfString("Swift") {
   firstPart = string.substringToIndex(range.startIndex)
   print(firstPart) // print Hello
}

Swift 3 UPDATE:

let string = "Hello Swift"
if let range = string.range(of: "Swift") {
    let firstPart = string[string.startIndex..<range.lowerBound]
    print(firstPart) // print Hello
}

Hope this can help you ;)

Kevin Machado
  • 4,141
  • 3
  • 29
  • 54
19

Following up on Syed Tariq's answer: If you only want the string before the delimiter (otherwise, you receive an array [String]):

var token = newstr.components(separatedBy: delimiter).first
Tim Newton
  • 1,463
  • 1
  • 13
  • 13
16

Swift 5 extension

extension String {
    func before(first delimiter: Character) -> String {
        if let index = firstIndex(of: delimiter) {
            let before = prefix(upTo: index)
            return String(before)
        }
        return ""
    }
    
    func after(first delimiter: Character) -> String {
        if let index = firstIndex(of: delimiter) {
            let after = suffix(from: index).dropFirst()
            return String(after)
        }
        return ""
    }
}

let str = "n1:lolz"
str.before(first: ":") // n1
str.after(first: ":") // lolz
str.after(first: "z") // empty string
str.before(first: "n") // empty string
str.before(first: "g") // empty string

I think it makes as much sense to return an optional String for preference (returning nil if there isn't anything before or after the delimiter).

trndjc
  • 11,654
  • 3
  • 38
  • 51
  • Alternatively you might want to return self instead of empty string when you don't find the character. It's subtle, but it kind of makes sense the prefix until you reach a character that isn't found is the whole string! – Evan Moran Mar 03 '23 at 23:13
11

You could use the method prefix(upTo:) in Swift 4 or above

var string = "hello Swift"
if let index = string.firstIndex(of: " ") {
    let firstPart = string.prefix(upTo: index)
    print(firstPart) // print hello
}
Michael Wang
  • 9,583
  • 1
  • 19
  • 17
5

My 2 cents :-) using Swift 3.0, similar to PHP strstr

extension String {

    func strstr(needle: String, beforeNeedle: Bool = false) -> String? {
        guard let range = self.range(of: needle) else { return nil }

        if beforeNeedle {
            return self.substring(to: range.lowerBound)
        }

        return self.substring(from: range.upperBound)
    }

}

Usage1

"Hello, World!".strstr(needle: ",", beforeNeedle: true) // returns Hello

Usage2

"Hello, World!".strstr(needle: " ") // returns World!
AamirR
  • 11,672
  • 4
  • 59
  • 73
4

To mutate a String into the part until the first appearance of a specified String you could extend String like this:

extension String {

    mutating func until(_ string: String) {
        let components = self.components(separatedBy: string)
        self = components[0]
    }

}

This can be called like this then:

var foo = "Hello Swift"
foo.until(" Swift") // foo is now "Hello"
Alex Giatrakis
  • 365
  • 3
  • 16
Benno Kress
  • 2,637
  • 27
  • 39
3

If you want a solution that doesn't involve pulling in foundation, you can do it with find and slicing:

let str = "Hello, I must be going."

if let comma = find(str, ",") {
    let substr = str[str.startIndex..<comma]
    // substr will be "Hello"
}

If you explicitly want an empty string in the case where no such character is found, you can use the nil-coalescing operator:

let str = "no comma"
let comma = find(str, ",") ?? str.startIndex
let substr = str[str.startIndex..<comma]  // substr will be ""

Note, unlike the componentsSeparatedByString technique, this does not require the creation of an array, and only requires scanning up to the first occurrence of the character rather than breaking the entire string up into the character-delimited array.

Airspeed Velocity
  • 40,491
  • 8
  • 113
  • 118
1

You can use rangeOfString, but it returns a Range<String.Index> type, not a NSRange:

let string = "hello Swift"
if let range = string.rangeOfString("Swift") {
    print(string.substringToIndex(range.startIndex))
}
Rob
  • 415,655
  • 72
  • 787
  • 1,044
1
let string = "Hello-world"
if let range = string.range(of: "-") {
let firstPart = string[(string.startIndex)..<range.lowerBound]
 print(firstPart) 
}

Output is: Hello

Sai kumar Reddy
  • 1,751
  • 20
  • 23
  • hello, are you still there? How to fetch world output without - Thank you in advance – Ali Aug 18 '23 at 03:04
1

Below is kind of a whole combo

let string = "This a string split using * and this is left."
if let range = string.range(of: "*") {
    let lastPartIncludingDelimiter = string.substring(from: range.lowerBound)
    print(lastPartIncludingDelimiter) // print * and this is left.

    let lastPartExcludingDelimiter = string.substring(from: range.upperBound)
    print(lastPartExcludingDelimiter) // print and this is left.

    let firstPartIncludingDelimiter = string.substring(to: range.upperBound)
    print(firstPartIncludingDelimiter) // print This a string split using *

    let firstPartExcludingDelimiter = string.substring(to: range.lowerBound)
    print(firstPartExcludingDelimiter) // print This a string split using
}
anoop4real
  • 7,598
  • 4
  • 53
  • 56