Home > Software engineering >  SwiftUI - Spell Checking Multi-line String
SwiftUI - Spell Checking Multi-line String

Time:02-18

I am trying to create a function where a multi-line string is spell checked and a single SwiftUI Text() view is returned with any misspelled words highlighted in red.

I have almost cracked it by splitting the string by newlines, then splitting the lines by whitespace and then checking each word.

My main issue is that I am getting an extra new line added at the end of the resulting Text view. Is there anyway I can trim the last Text("\n") or prevent it from being added to the last line?

Also, if there is any way to make it more efficient as there is a slight lag introduced as a lot of text in an array gets checked and so the function is called many times?

Many thanks in advance

func formatText(multiLineText: String) -> Text {
    
    let lineArray = multiLineText.components(separatedBy: .newlines)
    let stringToTextView = lineArray.reduce(Text(""), {
        return $0   formatLineText(singleLineText: $1)   Text("\n")
    })
    return stringToTextView
}

func formatLineText(singleLineText: String) -> Text {
    
    let stringArray = singleLineText.components(separatedBy: .whitespaces)
    let stringToTextView = stringArray.reduce(Text(""), {
        if !wordIsValid(word: $1) {
            return $0   Text($1).foregroundColor(Color.red).underline()   Text(" ")
        }
        else {
            return $0   Text($1)   Text(" ")
        }
    })
    return stringToTextView
}

func wordIsValid(word: String) -> Bool {
    
    let checker = UITextChecker()
    let range = NSRange(location: 0, length: word.utf16.count)
    let misspelledRange = checker.rangeOfMisspelledWord(in: word, range: range, startingAt: 0, wrap: false, language: "en_GB")

    return misspelledRange.location == NSNotFound
}

CodePudding user response:

You could use .enumerated in your reduce to check to see if the item is the last one or not -- if it is, don't return the \n.

func formatText(multiLineText: String) -> Text {
    let lineArray = multiLineText.components(separatedBy: .newlines)
    let stringToTextView = lineArray.enumerated().reduce(Text(""), { (acc,item) in
        return acc   formatLineText(singleLineText: item.1)   Text(item.0 != lineArray.endIndex - 1 ? "\n" : "")
    })
    return stringToTextView
}

In terms of performance, I'd move the let checker = UITextChecker() to somewhere where it doesn't get recreated on every single call to wordIsValid

  • Related