How can I use String substring in Swift 4? 'substring(to:)' is deprecated: Please use String slicing subscript with a 'partial range from' operator
Asked Answered
P

21

367

I have the following simple code written in Swift 3:

let str = "Hello, playground"
let index = str.index(of: ",")!
let newStr = str.substring(to: index)

From Xcode 9 beta 5, I get the following warning:

'substring(to:)' is deprecated: Please use String slicing subscript with a 'partial range from' operator.

How can this slicing subscript with partial range from be used in Swift 4?

Portecochere answered 8/8, 2017 at 8:4 Comment(1)
var str = "Hello, playground" let indexcut = str.firstIndex(of: ",") print(String(str[..<indexcut!])) print(String(str[indexcut!...]))Dendrology
G
438

You should leave one side empty, hence the name "partial range".

let newStr = str[..<index]

The same stands for partial range from operators, just leave the other side empty:

let newStr = str[index...]

Keep in mind that these range operators return a Substring. If you want to convert it to a string, use String's initialization function:

let newStr = String(str[..<index])

You can read more about the new substrings here.

Grams answered 8/8, 2017 at 8:7 Comment(9)
str[..<index] returns a Substring. If you want newStr to be a Stringyou have to write: let newStr = "\(str[..<index])"Godson
Using string interpolation with a lone Substring is probably slightly confusing, since what you are really trying to accomplish is String initialization: let newStr = String(str[..<index]).Accompaniment
Updating code where my 'index' value was simply an integer yields an error message, Cannot subscript a value of type 'String' with an index of type 'PartialRangeUpTo<Int>' . What types of values have to be used there?Jandel
@ConfusionTowers, Swift string indices are not integers, and that hasn't changed with version 4. You'll probably need str[..<str.index(str.startIndex, offsetBy: 8)] or something like that. Keep in mind that str.index is a linear-time operation in function of offsetBy.Maintop
@Maintop You probably want str.prefix(8)Uninterrupted
Ugly syntax. Hate Swift for such expressions: ?? !! a!!a ?a a? [..<index] [index...] SUPER UNREADABLE!!!Discontinuance
Is there an operator for substring from index to end but without the sign at the index? str[index>..] does not work :( Or do I need to offset the index?Refuse
how to create a index from a int ?Bulley
Why Swift got to make things so complicated? Python's string manipulation are so much better str = "Hello Python" print(str[0:2]) result is "He"Guilford
P
297

Convert Substring (Swift 3) to String Slicing (Swift 4)

Examples In Swift 3, 4:

let newStr = str.substring(to: index) // Swift 3
let newStr = String(str[..<index]) // Swift 4

let newStr = str.substring(from: index) // Swift 3
let newStr = String(str[index...]) // Swift 4 

let range = firstIndex..<secondIndex // If you have a range
let newStr = = str.substring(with: range) // Swift 3
let newStr = String(str[range])  // Swift 4
Pusan answered 7/10, 2017 at 5:51 Comment(2)
Perfecto, and thanks for giving the 3 and 4 examples side by side, makes updating my project way easier!Toad
This seems so needlessly complicated.Jelsma
A
134

Swift 5, 4

Usage

let text = "Hello world"
text[0] // H
text[...3] // "Hell"
text[6..<text.count] // world
text[NSRange(location: 6, length: 3)] // wor

Code

import Foundation

public extension String {
  subscript(value: Int) -> Character {
    self[index(at: value)]
  }
}

public extension String {
  subscript(value: NSRange) -> Substring {
    self[value.lowerBound..<value.upperBound]
  }
}

public extension String {
  subscript(value: CountableClosedRange<Int>) -> Substring {
    self[index(at: value.lowerBound)...index(at: value.upperBound)]
  }

  subscript(value: CountableRange<Int>) -> Substring {
    self[index(at: value.lowerBound)..<index(at: value.upperBound)]
  }

  subscript(value: PartialRangeUpTo<Int>) -> Substring {
    self[..<index(at: value.upperBound)]
  }

  subscript(value: PartialRangeThrough<Int>) -> Substring {
    self[...index(at: value.upperBound)]
  }

  subscript(value: PartialRangeFrom<Int>) -> Substring {
    self[index(at: value.lowerBound)...]
  }
}

private extension String {
  func index(at offset: Int) -> String.Index {
    index(startIndex, offsetBy: offset)
  }
}
Assiniboine answered 8/10, 2017 at 18:20 Comment(10)
this functionality should be default swift behavior.Sartorius
Definitely PR this into Swift lib :DBung
This is a fix of the ugliest part of the Swift.Catwalk
@Catwalk somebody didn't like it, as this answer has -2. :DAssiniboine
This is a bad answer since the subscript operator hides the O(n) complexity of the index-function. A good programming API only use subscript to denote fast lookup.Feat
@Feat - In that respect it is no worse than the core libraryHaroldson
How does this interface not exist with Swift already? hard to believeTachylyte
@Tachylyte Apple ignores my submits to work in Apple.Assiniboine
UTF engineer ...Tessatessellate
@Tessatessellate <3 ......Assiniboine
S
82

Shorter in Swift 4/5:

let string = "123456"
let firstThree = String(string.prefix(3)) //"123"
let lastThree = String(string.suffix(3)) //"456"
Simmers answered 23/3, 2018 at 10:44 Comment(4)
This only works if your string has integers, you can't do this for actual string suffix argument, thus does not answer the question.Unfamiliar
Can you add more info about your case? I don't really understand what do you mean by saying "string has integers" and "actual suffix argument"? You can get index of whatever you want in your string and cut out what you need by using that index.Simmers
let string = "hello world" - will not work for this. The argument for prefix and suffix in your case is Integer. Methods don't have argument for String such as string.prefix("world")Unfamiliar
It's not "my case". It's how those methods work. let string = "Hello world" let firstThree = String(string.prefix(3)) //"Hel" let lastThree = String(string.suffix(3)) //"rld" Just find index that you need using another answers Maybe that helps: linkSimmers
N
32

Swift5

(Java's substring method):

extension String {
    func subString(from: Int, to: Int) -> String {
       let startIndex = self.index(self.startIndex, offsetBy: from)
       let endIndex = self.index(self.startIndex, offsetBy: to)
       return String(self[startIndex..<endIndex])
    }
}

Usage:

var str = "Hello, Nick Michaels"
print(str.subString(from:7,to:20))
// print Nick Michaels
Northern answered 24/8, 2018 at 20:52 Comment(2)
Still works in Swift 5. I was looking for a Java style substring (from to to-1, e.g. "Hello".substring(1,4) returns "ell"). With a small modification (startIndex..<endIndex) this is the best solution I've found so far that does exactly that with just a few lines of code.Bynum
This works well and is by far the easiest solution of all. Thanks!Mcphail
R
29

The conversion of your code to Swift 4 can also be done this way:

let str = "Hello, playground"
let index = str.index(of: ",")!
let substr = str.prefix(upTo: index)

You can use the code below to have a new string:

let newString = String(str.prefix(upTo: index))
Richmal answered 20/9, 2017 at 12:18 Comment(0)
C
21

substring(from: index) Converted to [index...]

Check the sample

let text = "1234567890"
let index = text.index(text.startIndex, offsetBy: 3)

text.substring(from: index) // "4567890"   [Swift 3]
String(text[index...])      // "4567890"   [Swift 4]
Codicil answered 28/9, 2017 at 1:39 Comment(2)
characters are deprecated in Swift 4, I think you have to change the example to get the index for Swift 4Hentrich
This helped me. tnxEnamelware
W
13

Some useful extensions:

extension String {
    func substring(from: Int, to: Int) -> String {
        let start = index(startIndex, offsetBy: from)
        let end = index(start, offsetBy: to - from)
        return String(self[start ..< end])
    }

    func substring(range: NSRange) -> String {
        return substring(from: range.lowerBound, to: range.upperBound)
    }
}
Wheen answered 3/10, 2017 at 8:20 Comment(1)
Interesting, I was thinking the same. Is String(line["http://".endIndex...]) clearer than the deprecated substring? line.substring(from: "http://".endIndex) maybe until they get the stated goal of amazing string handling, they should not deprecate substring.Hardeman
P
7

Example of uppercasedFirstCharacter convenience property in Swift3 and Swift4.

Property uppercasedFirstCharacterNew demonstrates how to use String slicing subscript in Swift4.

extension String {

   public var uppercasedFirstCharacterOld: String {
      if characters.count > 0 {
         let splitIndex = index(after: startIndex)
         let firstCharacter = substring(to: splitIndex).uppercased()
         let sentence = substring(from: splitIndex)
         return firstCharacter + sentence
      } else {
         return self
      }
   }

   public var uppercasedFirstCharacterNew: String {
      if characters.count > 0 {
         let splitIndex = index(after: startIndex)
         let firstCharacter = self[..<splitIndex].uppercased()
         let sentence = self[splitIndex...]
         return firstCharacter + sentence
      } else {
         return self
      }
   }
}

let lorem = "lorem".uppercasedFirstCharacterOld
print(lorem) // Prints "Lorem"

let ipsum = "ipsum".uppercasedFirstCharacterNew
print(ipsum) // Prints "Ipsum"
Proctology answered 8/8, 2017 at 21:33 Comment(1)
If you mean substring(with: range) -> self[range]Soapbark
C
7

You can create your custom subString method using extension to class String as below:

extension String {
    func subString(startIndex: Int, endIndex: Int) -> String {
        let end = (endIndex - self.count) + 1
        let indexStartOfText = self.index(self.startIndex, offsetBy: startIndex)
        let indexEndOfText = self.index(self.endIndex, offsetBy: end)
        let substring = self[indexStartOfText..<indexEndOfText]
        return String(substring)
    }
}
Corene answered 26/12, 2017 at 15:48 Comment(1)
If this is going to take in an endIndex, it needs to be able to handle out of bounds as well.Broadminded
J
5

Creating SubString (prefix and suffix) from String using Swift 4:

let str : String = "ilike"
for i in 0...str.count {
    let index = str.index(str.startIndex, offsetBy: i) // String.Index
    let prefix = str[..<index] // String.SubSequence
    let suffix = str[index...] // String.SubSequence
    print("prefix \(prefix), suffix : \(suffix)")
}

Output

prefix , suffix : ilike
prefix i, suffix : like
prefix il, suffix : ike
prefix ili, suffix : ke
prefix ilik, suffix : e
prefix ilike, suffix : 

If you want to generate a substring between 2 indices , use :

let substring1 = string[startIndex...endIndex] // including endIndex
let subString2 = string[startIndex..<endIndex] // excluding endIndex
Jowers answered 8/12, 2017 at 2:45 Comment(2)
let prefix = str[...<index>] instead of str[..<index>] single dot is missing.Hentrich
@AbuTaareq, which one you are talking about? let prefix = str[..<index], it is perfect. (prefix string). Try in play ground to get more context.Jowers
S
4

I have written a string extension for replacement of 'String: subString:'

extension String {
    
    func sliceByCharacter(from: Character, to: Character) -> String? {
        let fromIndex = self.index(self.index(of: from)!, offsetBy: 1)
        let toIndex = self.index(self.index(of: to)!, offsetBy: -1)
        return String(self[fromIndex...toIndex])
    }
    
    func sliceByString(from:String, to:String) -> String? {
        //From - startIndex
        var range = self.range(of: from)
        let subString = String(self[range!.upperBound...])
        
        //To - endIndex
        range = subString.range(of: to)
        return String(subString[..<range!.lowerBound])
    }
    
}

Usage : "Date(1511508780012+0530)".sliceByString(from: "(", to: "+")

Example Result : "1511508780012"

PS: Optionals are forced to unwrap. Please add Type safety check wherever necessary.

Solidago answered 24/11, 2017 at 12:39 Comment(1)
Caution : optionals are forced to unwrap. please add Type safety check if necessary.Solidago
E
4

If you are trying to just get a substring up to a specific character, you don't need to find the index first, you can just use the prefix(while:) method

let str = "Hello, playground"
let subString = str.prefix { $0 != "," } // "Hello" as a String.SubSequence
Edacity answered 22/7, 2021 at 11:0 Comment(1)
Thanks! I learned a lot from this. If anyone else struggles to understand the syntax, it is well explained in docs.swift.org/swift-book/LanguageGuide/Closures.html Read about "Closure Expressions" and "Trailing Closures".Suchta
R
3

When programming I often have strings with just plain A-Za-z and 0-9. No need for difficult Index actions. This extension is based on the plain old left / mid / right functions.

extension String {

    // LEFT
    // Returns the specified number of chars from the left of the string
    // let str = "Hello"
    // print(str.left(3))         // Hel
    func left(_ to: Int) -> String {
        return "\(self[..<self.index(startIndex, offsetBy: to)])"
    }

    // RIGHT
    // Returns the specified number of chars from the right of the string
    // let str = "Hello"
    // print(str.left(3))         // llo
    func right(_ from: Int) -> String {
        return "\(self[self.index(startIndex, offsetBy: self.length-from)...])"
    }

    // MID
    // Returns the specified number of chars from the startpoint of the string
    // let str = "Hello"
    // print(str.left(2,amount: 2))         // ll
    func mid(_ from: Int, amount: Int) -> String {
        let x = "\(self[self.index(startIndex, offsetBy: from)...])"
        return x.left(amount)
    }
}
Rachellerachis answered 13/10, 2017 at 17:28 Comment(1)
Great idea!! Sad though that there is a need to abstract Swift-features just because they're constantly changing how things work. Guess they want us all to focus on constructs instead of productivity. Anyway, I made some minor updates to your fine code: pastebin.com/ManWmNnWSub
S
2

This is my solution, no warning, no errors, but perfect

let redStr: String = String(trimmStr[String.Index.init(encodedOffset: 0)..<String.Index.init(encodedOffset: 2)])
let greenStr: String = String(trimmStr[String.Index.init(encodedOffset: 3)..<String.Index.init(encodedOffset: 4)])
let blueStr: String = String(trimmStr[String.Index.init(encodedOffset: 5)..<String.Index.init(encodedOffset: 6)])
Spawn answered 7/11, 2017 at 5:37 Comment(1)
The use of encodedOffset is considered harmful and will be deprecated.Dhow
F
2

Hope this will help little more :-

var string = "123456789"

If you want a substring after some particular index.

var indexStart  =  string.index(after: string.startIndex )// you can use any index in place of startIndex
var strIndexStart   = String (string[indexStart...])//23456789

If you want a substring after removing some string at the end.

var indexEnd  =  string.index(before: string.endIndex)
var strIndexEnd   = String (string[..<indexEnd])//12345678

you can also create indexes with the following code :-

var  indexWithOffset =  string.index(string.startIndex, offsetBy: 4)
Fettling answered 15/2, 2018 at 7:48 Comment(0)
S
2

with this method you can get specific range of string.you need to pass start index and after that total number of characters you want.

extension String{
    func substring(fromIndex : Int,count : Int) -> String{
        let startIndex = self.index(self.startIndex, offsetBy: fromIndex)
        let endIndex = self.index(self.startIndex, offsetBy: fromIndex + count)
        let range = startIndex..<endIndex
        return String(self[range])
    }
}
Smuts answered 24/5, 2018 at 10:26 Comment(0)
L
2

the simples way that I use is :

String(Array(str)[2...4])
Laubin answered 21/9, 2021 at 13:6 Comment(0)
D
1
var str = "Hello, playground"
let indexcut = str.firstIndex(of: ",")
print(String(str[..<indexcut!]))
print(String(str[indexcut!...]))

You can try in this way and will get proper results.

Dendrology answered 24/7, 2019 at 12:9 Comment(0)
D
0

Swift 4, 5, 5+

Substring from Last

let str = "Hello World"
let removeFirstSix = String(str.dropFirst(6))
print(removeFirstSix) //World

Substring from First

let removeLastSix = String(str.dropLast(6))
print(removeLastSix) //Hello
Dunleavy answered 25/11, 2022 at 13:47 Comment(0)
H
-1

Hope it would be helpful.

extension String {
    func getSubString(_ char: Character) -> String {
        var subString = ""
        for eachChar in self {
            if eachChar == char {
                return subString
            } else {
                subString += String(eachChar)
            }
        }
        return subString
    }
}


let str: String = "Hello, playground"
print(str.getSubString(","))
Hectocotylus answered 31/5, 2018 at 12:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.