2

I am trying to validate a form to make sure the user has entered an integer number and not a string. I can check if the number is an integer as follows:

 var possibleNumber = timeRetrieved.text
    convertedNumber = possibleNumber.toInt()
    // convertedNumber is inferred to be of type "Int?", or "optional Int"

    if convertedNumber != nil {

        println("It's a number!")

        totalTime = convertedNumber!


    }

My problem is I want to make sure the user has not entered any text, doubles etc. I only want integer numbers. The following code does not work because it evaluates true if the variable is an integer. What code should I use to evaluate if variable is not an integer?

if convertedNumber != nil  {


        let alertController = UIAlertController(title: "Validation Error", message: "You must enter an integer number!", preferredStyle: .Alert)
        let alertAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Destructive, handler: {(alert : UIAlertAction!) in
            alertController.dismissViewControllerAnimated(true, completion: nil)
        })
        alertController.addAction(alertAction)
        presentViewController(alertController, animated: true, completion: nil)
Tom
  • 790
  • 2
  • 9
  • 32

6 Answers6

5

Swift 2 changes this: as both Int("abc") and Int("0") return 0, integer conversion can't be used. You could use this:

class Validation {
    static func isStringNumerical(string : String) -> Bool {
        // Only allow numbers. Look for anything not a number.
        let range = string.rangeOfCharacterFromSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet)
        return (range == nil)
    }
}

It uses a decimalDigitCharacterSet, and can be changed to use whatever character set you want.

func testIsStringNumerical() {
    XCTAssertEqual(SignUpLoyaltyViewController.isStringNumerical("123"), true)
    XCTAssertEqual(SignUpLoyaltyViewController.isStringNumerical(""), true)
    XCTAssertEqual(SignUpLoyaltyViewController.isStringNumerical("12AA"), false)
    XCTAssertEqual(SignUpLoyaltyViewController.isStringNumerical("123.4"), false)
}

This is dramatically faster than the Regex answer. (2000 runs, 0.004s vs regex 0.233s)

Timing from device

Graham Perks
  • 23,007
  • 8
  • 61
  • 83
  • I am not sure when this changed but in Swift 2.2, `Int("abc")` and `Int("0")` return `nil`. I gleaned this from Minhal Khan's answer. I posted an answer which also uses this initializer. – Mobile Dan Apr 07 '16 at 20:57
4

If the number the user has entered is not an integer, convertedNumber will be nil. Just add an else clause in which you can show the alert.

Marnix999L
  • 81
  • 3
  • Thanks, I couldn't get my code to work until I put my convertToInt() function within my validate function. – Tom Dec 04 '14 at 22:45
2

Int initializer

This works in Swift 2.2 and above. It is based on Minhal Khan's answer which illustrates that Int has an initializer with this signature: init?(_ text: String, radix: Int = default). Since radix has a default value, it can be left out. *more info on this initializer is found here.

var totalTime: Int?
let possibleInt = timeRetrieved.text ?? ""
if let convertedNumber = Int(possibleInt) {
    print("'\(possibleInt)' is an Int")
    totalTime = convertedNumber
}
else {
    print("'\(possibleInt)' is not an Int")
}

print("totalTime: '\(totalTime)'")

Note: I assumed timeRetrieved is a UITextField. The UITextField text property is an optional string (though programmatically not allowed to be nil). Therefore, the compiler requires it be unwrapped. I used the nil coalescing operator (??) to substitute a nil for empty string which does not yield an integer as desired. Here's a post that discusses the optionality of UITextfield.text.

Mobile Dan
  • 6,444
  • 1
  • 44
  • 44
1

What i had done was get the value and check if it could convert it, works for me

var enteredText = Int(textfield.text) 
if enteredText == nil{
    //String entered 
} 
else{
//Int entered
}
Minhal Khan
  • 301
  • 3
  • 11
1

Based on @Graham Perks answer a Swift 3 Version as string extension:

extension String
{
    var isNumeric: Bool
    {
        let range = self.rangeOfCharacter(from: CharacterSet.decimalDigits.inverted)
        return (range == nil)
    }
}

Usage:

"123".isNumeric // true
"abc".isNumeric // false
Mario
  • 3,339
  • 2
  • 22
  • 41
0

I really recommend using a REGEX, I was recently trying to validate 10 digit phone numbers using if let _ = Int(stringToTest)... and on 32 bit hardware, I faced range issues.

func validate(value: String) -> Bool {
  let PHONE_REGEX = "\\d{10}"
  let phoneTest = NSPredicate(format: "SELF MATCHES %@", PHONE_REGEX)
  let result =  phoneTest.evaluateWithObject(value)
  if result == true {
    log.info("'\(self.text!)' is a valid number.")
  } else {
    log.info("'\(self.text!)' is an invalid number.")
  }
  return result
}
JohnVanDijk
  • 3,546
  • 1
  • 24
  • 27