range function and crash in swift 3 - swift

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.

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?

distance(from:to:)' is unavailable: Any String view index conversion can fail in Swift 4; please unwrap the optional indices

I was trying to migrate my app to Swift 4, Xcode 9. I get this error. Its coming from a 3rd party framework.
distance(from:to:)' is unavailable: Any String view index conversion can fail in Swift 4; please unwrap the optional indices
func nsRange(from range: Range<String.Index>) -> NSRange {
let utf16view = self.utf16
let from = range.lowerBound.samePosition(in: utf16view)
let to = range.upperBound.samePosition(in: utf16view)
return NSMakeRange(utf16view.distance(from: utf16view.startIndex, to: from), // Error: distance(from:to:)' is unavailable: Any String view index conversion can fail in Swift 4; please unwrap the optional indices
utf16view.distance(from: from, to: to))// Error: distance(from:to:)' is unavailable: Any String view index conversion can fail in Swift 4; please unwrap the optional indices
}
You can simply unwrap the optional indices like this:
func nsRange(from range: Range<String.Index>) -> NSRange? {
let utf16view = self.utf16
if let from = range.lowerBound.samePosition(in: utf16view), let to = range.upperBound.samePosition(in: utf16view) {
return NSMakeRange(utf16view.distance(from: utf16view.startIndex, to: from), utf16view.distance(from: from, to: to))
}
return nil
}
The error says that the distances you are generating are optionals and need to be unwrapped. Try this:
func nsRange(from range: Range<String.Index>) -> NSRange {
let utf16view = self.utf16
guard let lowerBound = utf16view.distance(from: utf16view.startIndex, to: from), let upperBound = utf16view.distance(from: from, to: to) else { return NSMakeRange(0, 0) }
return NSMakeRange(lowerBound, upperBound)
}
However the return could be handled better in the guard statement. I'd recommend making the return type of the function NSRange? and checking for nil wherever you call the function to avoid inaccurate values being returned.
Please check :
let dogString = "Dog‼🐶"
let range = dogString.range(of: "🐶")!
// This is using Range
let strRange = dogString.range(range: range)
print((dogString as NSString).substring(with: strRange!)) // 🐶
extension String {
func range(range : Range<String.Index>) -> NSRange? {
let utf16view = self.utf16
guard
let from = String.UTF16View.Index(range.lowerBound, within: utf16view),
let to = String.UTF16View.Index(range.upperBound, within: utf16view)
else { return nil }
let utf16Offset = utf16view.startIndex.encodedOffset
let toOffset = to.encodedOffset
let fromOffset = from.encodedOffset
return NSMakeRange(fromOffset - utf16Offset, toOffset - fromOffset)
}
}
// This is using NSRange
let strNSRange = dogString.range(nsRange: NSRange(range, in: dogString))
print((dogString as NSString).substring(with: strNSRange!)) // 🐶
extension String {
func range(nsRange: NSRange) -> NSRange? {
guard
let from16 = utf16.index(utf16.startIndex, offsetBy: nsRange.location, limitedBy: utf16.endIndex),
let to16 = utf16.index(utf16.startIndex, offsetBy: nsRange.length, limitedBy: utf16.endIndex),
let from = from16.samePosition(in: self),
let to = to16.samePosition(in: self)
else { return nil }
return NSMakeRange(from.encodedOffset, to.encodedOffset)
}
}

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

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)])
}
}