How to replace emoji text using textDocumentProxy in Swift iOS custom keyboard extension

I am trying to replace user input text with some edited text. I have implemented the below function to do this:


func replaceText(replacementString string: String) {

  if let afterInput = textDocumentProxy.documentContextAfterInput {
 
  textDocumentProxy.adjustTextPosition(byCharacterOffset: afterInput.count)
  }

  if let word:String = textDocumentProxy.documentContextBeforeInput
  {
  
  for _: Int in 0 ..< word.count {
  textDocumentProxy.deleteBackward()
  }
  }

  textDocumentProxy.insertText(string)

  }


Thereafter, I will use this as follows to replace text in any user input string:

delegate?.replaceText(replacementString: "Test")

where delegate points to the keyboard viewcontroller.


Input -> Output ( | denotes the cursor position):

Zxzxzxzxzx| -> Test|

Zxzxzxzxzx😉zxc -> Test|xc


However, it doesn't seem to work as planned, as you can see from the above input -> output.

Eg. For normal text without emojis, "Zxzxzxzxzx" gets replaced by "Test" as expected. But with emoji, "Zxzxzxzxzx😉zxc" gets replaced by "Test|xc", and the cursor is now in the middle of the text.

It seems to break when emojis are present in the text. I have printed out the word counts and it seems consistent. Any ideas? Thanks in advance.

Accepted Reply

Thanks for trying.

You may need to adjust some other parts of your code based on utf16.count, but I cannot see where, as for now.

Hope you can fix your issue soon. Good luck.

Replies

It's so tough to make an environment to test custom keyboard that I have not tested, but you use `String.count` when counting characters.


It counts `Swift.Character` contained in the String, but many parts of iOS works with NSString which counts characters by UTF-16 code units.


Please try changing `afterInput.count` to `afterInput.utf16.count` and see what happens.

Thanks for the suggestion. I have changed .count to .utf16.count but it still gives the same result.

Thanks for trying.

You may need to adjust some other parts of your code based on utf16.count, but I cannot see where, as for now.

Hope you can fix your issue soon. Good luck.

You are right! I went to look at my previous parts of the code and I realised that I had a function that first grabs the user input text. I was using .lengthOfBytes(using: String.Encoding.utf8) instead if .count and after correcting them to .utf16.count if works fine now. Though I cannot see why that function would cause the problem, since it resets the cursor back to the correct last original position after grabbing user text.

Thanks much!