33

i've just converted my little app but i've found this error: 'substring(from:)' is deprecated: Please use String slicing subscript with a 'partial range from' operator

my code is:

    let dateObj = dateFormatterFrom.date(from: dateStringa)


    if dateObj != nil {
        cell.detailTextLabel?.text = dateFormatterTo.string(from:(dateObj!))
    } else {
        let index = thisRecord.pubDate.index(thisRecord.pubDate.startIndex, offsetBy: 5)
        cell.detailTextLabel?.text = thisRecord.pubDate.substring(from: index)
    }
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
Raffaele Spadaro
  • 331
  • 1
  • 3
  • 3
  • 5
    This is **not** a duplicate of that question. That question is about “partial range upto”, and this question is about “partial range from”. They are not the same, they are constructed with different operators, and that question doesn't have an answer for either constructing a “partial range upto” or otherwise taking the suffix of a string. – rob mayoff Sep 26 '17 at 22:57
  • 2
    Possible duplicate of [How can I use String slicing subscripts in Swift 4?](https://stackoverflow.com/questions/45562662/how-can-i-use-string-slicing-subscripts-in-swift-4) – Kaibo Nov 01 '19 at 12:55

7 Answers7

48

Follow the below example to fix this warning: Supporting examples for Swift 3, 4 and 5.

let testStr = “Test Teja”

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

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

//Swift 3
let finalStr = testStr.substring(from: index(startIndex, offsetBy: 3)) 

//Swift 4 and 5
let reqIndex = testStr.index(testStr.startIndex, offsetBy: 3)
let finalStr = String(testStr[..<reqIndex])

//**Swift 5.1.3 - usage of index**

let myStr = "Test Teja == iOS"

let startBound1 = String.Index(utf16Offset: 13, in: myStr)
let finalStr1 = String(myStr[startBound1...])// "iOS"

let startBound2 = String.Index(utf16Offset: 5, in: myStr)
let finalStr2 = String(myStr[startBound2..<myStr.endIndex]) //"Teja == iOS"
Teja Kumar Bethina
  • 3,486
  • 26
  • 34
  • 2
    i got 'subscript' is unavailable: cannot subscript String with an integer range, see the documentation comm – famfamfam Nov 20 '18 at 10:34
  • @famfamfam Which XCode your using. It depends on the Swift version ur working. The above code if for SWIFT 3 and 4 only. – Teja Kumar Bethina Nov 21 '18 at 13:32
  • sorry im using 4.2 – famfamfam Nov 21 '18 at 14:55
  • Updated my answer to support Swift 5.1.3 – Teja Kumar Bethina Jul 09 '20 at 07:02
  • 1
    @TejaKumarBethina Your 5.1.3 update is wrong. You should not use String `Index(utf16Offset:)` initializer to offset your index as most usage of it is wrong. Try `let myStr = "iOS"` `let startBound1 = String.Index(utf16Offset: 3, in: myStr)` `let finalStr1 = String(myStr[startBound1...])` // would result in "iOS" instead of "iOS" and `let startBound2 = String.Index(utf16Offset: 5, in: myStr)` `let finalStr2 = String(myStr[startBound2.. – Leo Dabus Dec 30 '20 at 07:08
16

In place of substring use suffix. Use like below :

cell.detailTextLabel?.text = String(thisRecord.pubDate.suffix(from: index))
Vini App
  • 7,339
  • 2
  • 26
  • 43
  • 1
    0 down vote accept No work, say me: Cannot assign value of type 'String.SubSequence' (aka 'Substring') to type 'String?' Raf – Raffaele Spadaro Sep 21 '17 at 06:49
  • This worked for me, I just replaced subString for suffix, then Xcode prompted me to make some code changes and it worked!. The line now reads: return String(self.suffix(from: to)) – David_2877 Feb 07 '22 at 12:06
12

It means you should use the new partial range operator as your upperBound:

let str =  "Hello World !!!"
if let index = str.range(of: "Hello ")?.upperBound {
   let string = String(str[index...])  // "World !!!"
}

In your case

cell.detailTextLabel?.text = String(thisRecord.pubDate[index...]))
Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
5

In Swift 5, it is:

extension String {

    func index(from: Int) -> Index {
        return self.index(startIndex, offsetBy: from)
    }

    func substring(from: Int) -> String {
        let fromIndex = index(from: from)
        return String(self[fromIndex...])
    }

    func substring(to: Int) -> String {
        let toIndex = index(from: to)
        return String(self[..<toIndex])
    }

    func substring(with r: Range<Int>) -> String {
        let startIndex = index(from: r.lowerBound)
        let endIndex = index(from: r.upperBound)
        return String(self[startIndex..<endIndex])
    }
}
Ankit Thakur
  • 4,739
  • 1
  • 19
  • 35
  • 3
    It's so annoying they went for a syntax that was simple and elegant to one where you've gotta write extensions to get something workable. Super ****ing annoying when working with regular expressions.. Thank you for posting this :) – Adrian Feb 25 '20 at 03:36
  • 1
    @Adrian note that when using the last method (range) it will unnecessarily offset both indices from the startIndex. `let endIndex = index(startIndex, offsetBy: r.count)` Note that this will offset only the range count from the local startIndex thats been already offset – Leo Dabus Dec 30 '20 at 06:59
4

Most of my strings have A-Za-z and 0-9 content. No need for difficult Index handling. This extension of String is based on the familiar LEFT / MID and 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)
    }
}
Andrea Mario Lufino
  • 7,921
  • 12
  • 47
  • 78
Vincent
  • 4,342
  • 1
  • 38
  • 37
  • 1
    Note that this would crash if you pass an Int larger than the string count. Much easier to use Collection prefix method that wouldn't crash and return a Substring or initialise a new String with it before returning it `func left(_ to: Int) -> String { return String(prefix(to)) }` – Leo Dabus Dec 07 '17 at 18:38
  • https://stackoverflow.com/questions/24092884/get-nth-character-of-a-string-in-swift-programming-language/38215613#38215613 – Leo Dabus Dec 08 '17 at 16:14
0

If you wish to get substring with specific offset without upper bound do the following:

let index = thisRecord.pubDate.index(thisRecord.pubDate.startIndex, offsetBy: 5)
cell.detailTextLabel?.text = String(thisRecord.pubDate[index...]

This way you create a new String object from your existing String thisRecord.pubDate taking anything from specified index to the end index of original String.

0
str[..<index]
str[index...]

The code above is "partial range from" Look at this How can I use String slicing subscripts in Swift 4?

Kaibo
  • 111
  • 1
  • 4