Search code examples
swiftstringcharacterunicodescalar

Get unicodeScalars Character from String in a specific index in Swift


I'm using this extension that gets the character in a specific index; However currently it doesn't recognize unicodeScalars, can you help me fix the extension to get the results that I need?

I've put some tests at the end that shows the result I want to get (Test1 and Test2).

extension String {

var length: Int {
    return count
}

subscript (i: Int) -> String {
    return self[i ..< i + 1]
}

func substring(fromIndex: Int) -> String {
    return self[min(fromIndex, length) ..< length]
}

func substring(toIndex: Int) -> String {
    return self[0 ..< max(0, toIndex)]
}

subscript (r: Range<Int>) -> String {
    let range = Range(uncheckedBounds: (lower: max(0, min(length, r.lowerBound)),
                                        upper: min(length, max(0, r.upperBound))))
    let start = index(startIndex, offsetBy: range.lowerBound)
    let end = index(start, offsetBy: range.upperBound - range.lowerBound)
    return String(self[start ..< end])
  }

}


//How to Use the Extension:
//let str = "abcdef"
//str[1 ..< 3] // returns "bc"
//str[5] // returns "f"
//str[80] // returns ""
//str.substring(fromIndex: 3) // returns "def"
//str.substring(toIndex: str.length - 2) // returns "abcd"



var Test1 = "دب"
var Test2 = "دُبْ"

print(Test1[1]) //returns " ب " which is correct in this case
print(Test2[1]) // returns " بْ " where I want it to return the mark which is " ُ "

Test1 is the text without marks, it gets the position right " ب ", however Test2 contains marks that only can be recognized as characters with unicodeScalars, In Test2 it gets the position wrong and it shows the whole character with the mark " بْ " where I only want it to show the mark " ُ "

Thank you.


Solution

  • If you want to be subscripting String.unicodeScalars instead of the String itself, declare your extension on String.UnicodeScalarView instead of String.

    extension String.UnicodeScalarView {
    
        var length: Int {
            return count
        }
    
        subscript (i: Int) -> String {
            return self[i ..< i + 1]
        }
    
        func substring(fromIndex: Int) -> String {
            return self[Swift.min(fromIndex, length) ..< length]
        }
    
        func substring(toIndex: Int) -> String {
            return self[0 ..< Swift.max(0, toIndex)]
        }
    
        subscript (r: Range<Int>) -> String {
            let range = Range(uncheckedBounds: (lower: Swift.max(0, Swift.min(length, r.lowerBound)),
                                                upper: Swift.min(length, Swift.max(0, r.upperBound))))
            let start = index(startIndex, offsetBy: range.lowerBound)
            let end = index(start, offsetBy: range.upperBound - range.lowerBound)
            return String(self[start ..< end])
        }
    
    }
    

    And then simply do

    var test1 = "دب"
    var test2 = "دُبْ"
    
    test1.unicodeScalars[1] // "ب" 
    test2.unicodeScalars[1] // "ُ "