Swift3 & Xcode8: ’subscript' is unavailable: cannot subscript String with a CountableClosedRange<Int>, - swift

Error 1:
When I am trying get the stringValue from Metadata shows above error in Swift3:
let myMetadata: AVMetadataMachineReadableCodeObject = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
// take out the system and check-digits
let myBarcode = myMetadata.stringValue[1...11] //error
Error 2:
In extensions of String I write these to get right(x) and left(x) function to get substring:
extension String {
// length of string
var length: Int {
return self.characters.count
}
// right(x) and left(x) function to get substring
func right(_ i: Int) -> String?
{
return self[self.length-i ... self.length-1 ] //error
}
func left(_ i: Int) -> String?
{
return self[0 ... i-1] //error
}
}

Use this extension for the countable closed range [0...4] subscripting
extension String {
subscript (r: CountableClosedRange<Int>) -> String {
get {
let startIndex = self.index(self.startIndex, offsetBy: r.lowerBound)
let endIndex = self.index(startIndex, offsetBy: r.upperBound - r.lowerBound)
return self[startIndex...endIndex]
}
}
}
or a safer version which checks the bounds and returns nil rather than an out-of-range exception:
extension String {
subscript (r: CountableClosedRange<Int>) -> String? {
get {
guard r.lowerBound >= 0, let startIndex = self.index(self.startIndex, offsetBy: r.lowerBound, limitedBy: self.endIndex),
let endIndex = self.index(startIndex, offsetBy: r.upperBound - r.lowerBound, limitedBy: self.endIndex) else { return nil }
return self[startIndex...endIndex]
}
}
}
Swift 4 change: You need to create a new string from the result
return String(self[startIndex...endIndex])

I took inspiration from #vadian's answer and created a set of (Swift 4) extensions that make pulling substrings trivially easy. These do not bounds check, which is generally my preference since I shouldn't be deferring sanity checking to lower level utilities like these.
extension String {
subscript (_ index: Int) -> String {
return String(self[self.index(startIndex, offsetBy: index)])
}
subscript (_ range: CountableRange<Int>) -> String {
let lowerBound = index(startIndex, offsetBy: range.lowerBound)
let upperBound = index(startIndex, offsetBy: range.upperBound)
return String(self[lowerBound..<upperBound])
}
subscript (_ range: CountableClosedRange<Int>) -> String {
let lowerBound = index(startIndex, offsetBy: range.lowerBound)
let upperBound = index(startIndex, offsetBy: range.upperBound)
return String(self[lowerBound...upperBound])
}
subscript (_ range: CountablePartialRangeFrom<Int>) -> String {
return String(self[index(startIndex, offsetBy: range.lowerBound)...])
}
subscript (_ range: PartialRangeUpTo<Int>) -> String {
return String(self[..<index(startIndex, offsetBy: range.upperBound)])
}
subscript (_ range: PartialRangeThrough<Int>) -> String {
return String(self[...index(startIndex, offsetBy: range.upperBound)])
}
}

Related

Type 'MatchedValue' does not conform to protocol 'Decodable'

I have a struct that must conform to Codable protocol.
However, I get the error:
Type 'MatchedValue' does not conform to protocol 'Decodable'**
How can I make String.Index conform to Codable?
Thanks
struct MatchedValue: Codable {
let value: String
let range: Range<String.Index>
}
Try using Int instead of String.Index.
First, extensions to get the position of an element or string as Int and the ability to use integer ranges:
extension StringProtocol {
func distance(of element: Element) -> Int? { firstIndex(of: element)?.distance(in: self) }
func distance<S: StringProtocol>(of string: S) -> Int? { range(of: string)?.lowerBound.distance(in: self) }
func substring(with range: Range<Int>) -> String? {
guard range.lowerBound >= 0 && range.upperBound <= self.count else { return nil }
let lowerBoundStringIndex = self.index(self.startIndex, offsetBy: range.lowerBound)
let upperBoundStringIndex = self.index(lowerBoundStringIndex, offsetBy: range.upperBound - range.lowerBound)
return String(self[lowerBoundStringIndex..<upperBoundStringIndex])
}
subscript(r: Range<Int>) -> String? { substring(with: r) }
func substring(with range: ClosedRange<Int>) -> String? {
guard range.lowerBound >= 0 && range.upperBound < self.count else { return nil }
if range.lowerBound == range.upperBound { return "" }
let lowerBoundStringIndex = self.index(self.startIndex, offsetBy: range.lowerBound)
let upperBoundStringIndex = self.index(lowerBoundStringIndex, offsetBy: range.upperBound + 1 - range.lowerBound)
return String(self[lowerBoundStringIndex..<upperBoundStringIndex])
}
subscript(r: ClosedRange<Int>) -> String? { substring(with: r) }
}
extension Collection {
func distance(to index: Index) -> Int { distance(from: startIndex, to: index) }
}
extension String.Index {
func distance<S: StringProtocol>(in string: S) -> Int { string.distance(to: self) }
}
Now you can use this new implementation:
let letters = "My string"
letters.count // 9
// get range
let lowerBound: Int? = letters.distance(of: "M")
let upperBound: Int? = letters.distance(of: "g")
let intRange: Range<Int> = lowerBound!..<upperBound!
let intClosedRange: ClosedRange<Int> = lowerBound!...upperBound!
// get substring
letters.substring(with: intRange) // "My strin"
letters.substring(with: intClosedRange) // "My string"
// or
letters[intRange] // "My strin"
letters[intClosedRange] // "My string"
I also include a comparison using String.Index and other tests.
// For comparison purposes only
let lowerIndex = letters.firstIndex(of: "M")
let upperIndex = letters.firstIndex(of: "g")
let range: Range<String.Index> = lowerIndex!..<upperIndex!
let closedRange: ClosedRange<String.Index> = lowerIndex!...upperIndex!
letters[range] // "My strin"
letters[closedRange] // "My string"
// Additional implementation tests
letters.substring(with: 3...5) // "str"
letters.substring(with: 3..<5) // "st"
letters.substring(with: 0...9) // nil
letters.substring(with: 0..<9) // "My string"
letters.substring(with: 2...2) // ""
letters.substring(with: 2..<2) // ""
Here is my
gist.

Error handling special characters using utfOffset16

I have a function that searches and returns the index within the string of the first occurrence of searchStr, however I keep getting a fatal error whenever the string contains any special charactsers (such as ç or é). The error seems to be occuring at the utf16Offset call and I can't seem to figure out why.. here is the code I'm using:
func index(of aString: String, startingFrom position: Int? = 0) -> String.Index? {
guard let position = position else {
return nil
}
if self.startIndex.utf16Offset(in: aString) + position > self.endIndex.utf16Offset(in: aString) {
return nil
} // produces fatal error when special character encountered
let start: String.Index = self.index(self.startIndex, offsetBy: position)
let range: Range<Index> = Range<Index>.init(uncheckedBounds: (lower: start, upper: self.endIndex))
return self.range(of: aString, options: .literal, range: range, locale: nil)?.lowerBound
}
This part seems problematic to me
if self.startIndex.utf16Offset(in: aString) + position > self.endIndex.utf16Offset(in: aString) {
return nil
}
You're taking the start index on self and convert it to its UTF-16 offset in aString. self and aString are two unrelated strings though so this is probably undefined behavior (which might be why you see it crashing in some cases).
The intent of this if statement seems to be to ensure that this produces a valid range (lower <= upper)
let start: String.Index = self.index(self.startIndex, offsetBy: position)
let range: Range<Index> = Range<Index>.init(uncheckedBounds: (lower: start, upper: self.endIndex))
You can actually do that by just comparing the Indexes directly like this
let start: String.Index = self.index(self.startIndex, offsetBy: position)
guard start < self.endIndex else {
return nil
}
// Range is guaranteed to have valid boundaries now
let range: Range<Index> = Range<Index>.init(uncheckedBounds: (lower: start, upper: self.endIndex))
Full example:
extension String {
func index(of aString: String, startingFrom position: Int? = 0) -> String.Index? {
guard let position = position else {
return nil
}
let start: String.Index = self.index(self.startIndex, offsetBy: position)
guard start < self.endIndex else {
return nil
}
let range: Range<Index> = Range<Index>.init(uncheckedBounds: (lower: start, upper: self.endIndex))
return self.range(of: aString, options: .literal, range: range, locale: nil)?.lowerBound
}
}
// Doesn't crash anymore
"aaç".distance(from: foobar.startIndex, to: foobar.index(of: "ç", startingFrom: 0)!)

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

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)
}
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"
In place of substring use suffix. Use like below :
cell.detailTextLabel?.text = String(thisRecord.pubDate.suffix(from: index))
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...]))
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])
}
}
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)
}
}
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.
str[..<index]
str[index...]
The code above is "partial range from"
Look at this How can I use String slicing subscripts in Swift 4?

range function and crash in swift 3

My below code crashes:
func getrange(_ from: Int, length: Int) -> Range<String.Index>? {
guard let fromU16 = utf16.index(utf16.startIndex, offsetBy: from, limitedBy: utf16.endIndex), fromU16 != utf16.endIndex else {
return nil ----->crashes here
}
let toU16 = utf16.index(fromU16, offsetBy: length, limitedBy: utf16.endIndex) ?? utf16.endIndex
guard let from = String.Index(fromU16, within: self),
let to = String.Index(toU16, within: self) else { return nil }
return from ..< to
}
This code is crashing with swift 3 migration.
Can someone help debugging the issue.
Below is the sequence of events:
//input for below function is: text “123456789”, string “0”, nsrange = location =9, length=0
1) function 1
static func numericText(_ text: String, replacedBy string: String, in nsrange: NSRange) -> String {
guard let range = text.range(for: nsrange) else {
//assertionFailure("Should never reach here")
return text.numericString()
}
// Apply Replacement String to the textField text and extract only the numeric values
return text.replacingCharacters(in: range, with: string)
.numericString()
}
2) function 2
func range(for nsrange: NSRange) -> Range<String.Index>? {
return range(nsrange.location, length: nsrange.length)
}
3) function 3
func range(_ from: Int, length: Int) -> Range<String.Index>? {
guard let fromU16 = utf16.index(utf16.startIndex, offsetBy: from, limitedBy: utf16.endIndex), fromU16 != utf16.endIndex else {
return nil
}
let toU16 = utf16.index(fromU16, offsetBy: length, limitedBy: utf16.endIndex) ?? utf16.endIndex
guard let from = String.Index(fromU16, within: self),
let to = String.Index(toU16, within: self) else { return nil }
return from ..< to
}
Sorry, I didn't update during the weekend.
I reviewed your question.
I can implement your function 1:
extension String {
func getrange(_ from: Int, length: Int) -> Range<String.Index>? {
guard let fromU16 = utf16.index(utf16.startIndex, offsetBy: from, limitedBy: utf16.endIndex), fromU16 != utf16.endIndex else {
return nil
}
let toU16 = utf16.index(fromU16, offsetBy: length, limitedBy: utf16.endIndex) ?? utf16.endIndex
guard let from = String.Index(fromU16, within: self),
let to = String.Index(toU16, within: self) else { return nil }
return from ..< to
}
}
But I cant implement your function 2, is that converted to Swift3 syntax yet?
My question is this,
Below is the sequence of events:
//input for below function is: text “123456789”, string “0”, nsrange = location =9, length=0
your input, your location shouldn't be 9. As your string length is 9, the max location your can replace should be 8?
Just replacing utf with unicodeScalars in the code fixed the issue.

Cannot increment beyond endIndex

I'm looking for the "Swift 3" way of handling an error where I try to increment the position of a string to an out of bounds index. I have an extension that looks like the following:
extension String {
func substring(from: Int) -> String {
let fromIndex = index(from: from)
return substring(from: fromIndex)
}
}
In implementation code, I have a loop which periodically takes chunks of a string and moves the index further in the string. My problem is I'm not sure what the Swift 3 way is of handling "End of String, do not proceed if we've reached the end"
Implementation code is something as trivial as this:
myStr = myStr.substring(from: pos + 1)
if pos + 1 is the end of the string, it shouldn't error out, but should instead just exit/return from my loop. What's the best way of doing that?
You can write something like this
extension String {
func substring(from offset: Int) -> String {
let fromIndex = index(self.startIndex, offsetBy: offset)
return substring(from: fromIndex)
}
}
Examples
"Hello world".substring(from: 0) // "Hello world"
"Hello world".substring(from: 1) // "ello world"
"Hello world".substring(from: 2) // "llo world"
What does happen if you pass the wrong param?
Something like this will generate a fatal error.
"Hello world".substring(from: 12)
fatal error: cannot increment beyond endIndex
You can make you code safer adding a guard statement like this
extension String {
func substring(from: Int) -> String? {
guard from < self.characters.count else { return nil }
let fromIndex = index(self.startIndex, offsetBy: from)
return substring(from: fromIndex)
}
}
You can use the index(_, offsetBy:, limitedBy:) method
to ensure that the index is not advanced beyond the end index:
extension String {
func substring(from: Int) -> String? {
guard let fromIndex = index(startIndex, offsetBy: from, limitedBy: endIndex) else {
return nil
}
return substring(from: fromIndex)
}
}
extension String {
func substring(from index: Int) -> String {
guard index < characters.count else { return "" }
return substring(from: characters.index(startIndex, offsetBy: index))
}
}
"12345".substring(from: 3) // "45"
"12345".substring(from: 9) // ""
Alternatively, you might want to return nil if index is out of bounds when you change the function's return type to String?