That's the intended behaviour.
When you test substring, you test if Kae is as such, inside name.
It is not (Kae letters are all in Kate is not a substring of Kate).
So, what is your spec ?
- get letters all present ? Then ake would match Kae, which you probably don't want
- find letters in this order ? Then Akyabe would match Kae. Is it what you want ?
- Would Kaa match ?
You could use this:
func isMatching(sub: String, inString: String) -> Bool {
let sub = sub.lowercased()
var inString = inString.lowercased()
// Let us find the positions of each character of sub in inString
var positions = [Int]()
for c in sub {
if let pos = inString.firstRange(of: String(c)) {
let startPos = inString.distance(from: inString.startIndex, to: pos.lowerBound)
positions.append(startPos)
inString.removeSubrange(pos) // Now that we have found c, let us remove it
} else {
return false
}
}
// If 0 or 1 character in sub, it is in correct order
if positions.count < 2 { return true }
// Now that we have found all letters, check if they are in correct order
for index in 0...positions.count-2 {
if positions[index] > positions[index+1] { return false }
}
// All OK
return true
}
Some tests:
var result = isMatching(sub: "Kae", inString: "Kate")
print("Kae", result)
result = isMatching(sub: "Kate", inString: "Kate")
print("Kate", result)
result = isMatching(sub: "Katy", inString: "Kate")
print("Katy", result)
result = isMatching(sub: "Kaa", inString: "Kate")
print("Kaa in Kate", result)
result = isMatching(sub: "Kaa", inString: "Katea")
print("Kaa in Katea", result)
result = isMatching(sub: "ake", inString: "Kate")
print("ake", result)
result = isMatching(sub: "kae", inString: "Akyabe")
print("kae in Akyabe", result)
Which results in:
Kae true
Kate true
Katy false
Kaa in Kate false
Kaa in Katea false
ake false
kae in Akyabe true
And replace the test
name.firstName.lowercased().contains(searchText.lowercased())
by
isMatching(sub: searchText, inString: name.firstName)