Bronze Challenge

At first I was not entirely happy with this; it seemed like overkill to turn the newly typed character into a set, but I didn’t see a direct way of checking to see if an individual character was a member of a set. (Although with a bit more digging I found the contains method which would do the trick.)

But if you take cut & paste into account, you could have multiple characters being inserted all at the same time, so turning the replacement string into a set makes more sense in that light.

func textField(_ textField: UITextField,
               shouldChangeCharactersIn range: NSRange,
               replacementString string: String) -> Bool
{
    let existingTextHasDecimalSeparator = textField.text?.range(of: ".")
    let replacementTextHasDecimalSeparator = string.range(of: ".")
        
    let replacementTextCharacterSet = CharacterSet(charactersIn: string)
    let replacementTextIsAlphabetic = (replacementTextCharacterSet.isDisjoint(with: CharacterSet.letters) == false)
        
    if (existingTextHasDecimalSeparator != nil &&
        replacementTextHasDecimalSeparator != nil) ||
        replacementTextIsAlphabetic
    {
        return false
    }
    else
    {
        return true
    }
}

I did this a bit differently, without touching the existing checks for the double decimal points, I used a guard at the start of the textField(_:shouldChangeCharactersIn:replacementString:), as follows:

var numbers = CharacterSet.decimalDigits
numbers.insert(charactersIn: ".")
        
guard (string.rangeOfCharacter(from: numbers) != nil) || string.isEmpty else {
    return false
}

I created a CharacterSet of decimal digits (i.e. numbers only) and then also added the “.” to it to make it allowed, too. Then checked for the range of the first character found in the replacementString from the character set. If this is not nil, meaning it did find a valid numeric digit or “.”, OR if the replacementString is empty (which means the user is trying to delete numbers), then continue with the rest of the function, otherwise return false and prevent changes.

The “letters” group from CharacterSet does not include other symbols such as / [ ] ', etc. and these appear to be valid inputs in your solution.

The “letters” group from CharacterSet does not include other symbols such as / [ ] ', etc. and these appear to be valid inputs in your solution.

True. The challenge was to prohibit alphabetic characters so I chose to do only that, and not worry about symbols or other non-alphanumeric characters.

Also, there’s a problem with your implementation that you’re going to find out about in the next chapter.

1 Like

Spent several hours racking my brain on this one and reading additional articles involving characterSets, and it almost felt like I stumbled upon the solution I got which prevents letters like Jon pointed out from being valid. Is there any underlying issue with it in regards to other ways to go about it?

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    let existingTextHasDecimalSeparator = textField.text?.range(of:".")
    let replacementTexthasDecimalSeparator = string.range(of: ".")
    // character set containing numbers
    let numbers = CharacterSet.decimalDigits
    // character set containing letters
    let letters = CharacterSet.letters
    
    let currentText = textField.text?.rangeOfCharacter(from: numbers)
    let replacementText = string.rangeOfCharacter(from: letters)
   
    
    // if existingTextHasdecimalSeparator does not equal 0 and replacementTexthasDecimalSepartor does not equal 0, or currentText does not equal 0 and replacmentText does not equel 0 reject the change. Otherwise go ahead with the change
    if existingTextHasDecimalSeparator != nil && replacementTexthasDecimalSeparator != nil || currentText != nil && replacementText != nil
    {
        return false
    }else {
        return true
    }