|  |  |  | // | 
					
						
							|  |  |  | //  Copyright (c) 2018 Open Whisper Systems. All rights reserved. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import Foundation | 
					
						
							|  |  |  | import UIKit | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | @objc public class TextFieldHelper: NSObject { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Used to implement the UITextFieldDelegate method: `textField:shouldChangeCharactersInRange:replacementString` | 
					
						
							|  |  |  |     // Takes advantage of Swift's superior unicode handling to append partial pasted text without splitting multi-byte characters. | 
					
						
							|  |  |  |     @objc public class func textField(_ textField: UITextField, shouldChangeCharactersInRange editingRange: NSRange, replacementString: String, byteLimit: UInt) -> Bool { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let byteLength = { (string: String) -> UInt in | 
					
						
							|  |  |  |             return UInt(string.utf8.count) | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let existingString = textField.text ?? "" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Given an NSRange, we need to interact with the NS flavor of substring | 
					
						
							|  |  |  |         let removedString = (existingString as NSString).substring(with: editingRange) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let lengthOfRemainingExistingString = byteLength(existingString) - byteLength(removedString) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let newLength = lengthOfRemainingExistingString + byteLength(replacementString) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (newLength <= byteLimit) { | 
					
						
							|  |  |  |             return true | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Don't allow any change if inserting a single char is already over the limit (typically this means typing) | 
					
						
							|  |  |  |         if (replacementString.count < 2) { | 
					
						
							|  |  |  |             return false | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // However if pasting, accept as much of the string as possible. | 
					
						
							|  |  |  |         let availableSpace = byteLimit - lengthOfRemainingExistingString | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         var acceptableSubstring = "" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (_, char) in replacementString.enumerated() { | 
					
						
							|  |  |  |             var maybeAcceptableSubstring = acceptableSubstring | 
					
						
							|  |  |  |             maybeAcceptableSubstring.append(char) | 
					
						
							|  |  |  |             if (byteLength(maybeAcceptableSubstring) <= availableSpace) { | 
					
						
							|  |  |  |                 acceptableSubstring = maybeAcceptableSubstring | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         textField.text = (existingString as NSString).replacingCharacters(in: editingRange, with: acceptableSubstring) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // We've already handled any valid editing manually, so prevent further changes. | 
					
						
							|  |  |  |         return false | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |