swiftfloating-pointdecimalnscharacterset

Avoid crash from double period during restrained CharacterSet


I have a GPA calculator project I'm close to perfecting. I need to prevent the user from inputting a non-float number despite ".0123456789" being the only allowed characters. For example, if a user inputs 2.3. for whatever reason, my program crashes since this "input" is not a float. I have included the base of my code below.

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

    var allowed = CharacterSet.decimalDigits
    let period = CharacterSet.init(charactersIn: ".")
    allowed.formUnion(period)

    //UNCOMMENT when isSuperset is working
    //let characterSet = CharacterSet(charactersIn: string)
    //return allowed.isSuperset(of: characterSet)

    // Swift 3 appropriate solution
    let isSuperset = string.rangeOfCharacter(from: allowed.inverted) == nil
    return isSuperset
}


@IBAction func Calculate(_ sender: AnyObject) {

    // if not filled in
    if Credits1TF.text == "" || Grade1TF.text == "" || CumCredits.text == "" || CumGPA.text == "" || Credits1TF.text == "." || Grade1TF.text == "." || CumCredits.text == "." || CumGPA.text == "."
    {
 print...         
}            else 
        {
            //credits
            let credit1 = Float(Credits1TF.text!)!
            //grades
            let grade1 = Float(Grade1TF.text!)!
            //math
            let firstgrade = credit1 * grade1

How would I go about adding the acceptance of only one decimal point in a text field most practically as it relates to the textfield function? Or is there a simpler/straightforward way?

Update 1:

   let credit1 = Float(Credits1TF.text!)!
            if credit1 != nil
            {
                let newcredits1 = credit1

            //grades
            let grade1 = Float(Grade1TF.text!)!
                if grade1 != nil
                { let newgrade1 = grade1

Unless I am going about this incorrectly, I am receiving the warning that "comparing non-optional value of type 'Float' to nil always returns true". I was under the impression that if credits1 or grade1 != nil, my numerical value is fine and wont cause a crash later on.


Solution

  • func textField(_ textField: UITextField, shouldChangeCharactersIn     range: NSRange, replacementString string: String) -> Bool {
    
        var allowed = CharacterSet.decimalDigits
        let period = CharacterSet.init(charactersIn: ".")
        allowed.formUnion(period)
    
    
        if (textField.text?.contains("."))! && string.contains(".")
        {
            return false
        }
        else
        {
            let isSuperset = string.rangeOfCharacter(from: allowed.inverted) == nil
    
            return isSuperset
    
        }
    

    }

    This code limits the accepted characters to "0-9" and also allows one period/decimal point to be entered. The return of the 0-9 characters is seen in the else portion of the if statement. This answer was inspired from the answer by Raj in this previous question How can I limit the number of decimal points in a UITextField?.