0

I have two textboxes. When the user clicks on a button, I need to check if the values entered into the textfields are positive integers.

I try to do this using the following statement:

if ((Int(txta.stringValue))! < 0 ||(Int(txtb.stringValue))! < 0)
{
            // Display error message
}

But it seems there is an issue with the if statement syntax. Expected ',' separator is the error produced.

What I'm I doing wrong?

Please advise.

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
techno
  • 6,100
  • 16
  • 86
  • 192

2 Answers2

3

There are two problems:

  • Binary operators (such as ||) must have whitespace on both sides (or on none), that's what causes the syntax error. (See what are the rules for spaces in swift.)
  • if (Int(txta.stringValue))! < 0 cannot be used to check for integer input because the forced unwrap crashes at runtime if the string is not an integer.

Int(txta.stringValue) returns nil if the string is not an integer, so a possible check for integer input would be

if Int(txta.stringValue) == nil || Int(txtb.stringValue) == nil {
    // display error message
}

But most probably you need the integer values themselves (in the case of valid input), that's what optional binding is for (see also When should I compare an optional value to nil?):

if let a = Int(txta.stringValue), let b = Int(txtb.stringValue) {
    // Valid input, values are in `a` and `b` ...
} else {
    // display error message
}

Additional conditions can be added to check the valid range, e.g.

if let a = Int(txta.stringValue), let b = Int(txtb.stringValue), a > 0, b > 0 {
    // Valid positive input, values are in `a` and `b` ...
} else {
    // display error message
}

Or with guard:

guard let a = Int(txta.stringValue), let b = Int(txtb.stringValue), a > 0, b > 0 else {
    // display error message
    return
}
// Valid positive input, values are in `a` and `b` ...
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Thanks ..will check and get back tomorrow. – techno Oct 01 '17 at 18:09
  • Thanks,your solution works perfect.I will learn more about `guard` statements. – techno Oct 04 '17 at 08:36
  • If I need to set a Boolean variable if the values are integers .. I can do like this .. I guess `guard .......... { // display error message return } myvar=true` – techno Oct 04 '17 at 08:48
0

This is an ideal case for Optional.map and Optional.flatMap:

if (Int(textA.stringValue).map{ 0 <= $0 } ?? false) &&
   (Int(textB.stringValue).map{ 0 <= $0 } ?? false) {

}

To break it down:

  1. textA is a NSTextField.
  2. textA.stringValue is some non-optional String value
  3. Int(textA.stringValue) converts the text field's non-optional string value into an Int? value (a.k.a. Optional<Int>). It's optional, since the conversion from String to Int can fail (when the string doesn't encode an integer)
  4. map{ 0 <= $0 } is called on that Int?. This is Optional.map, not to be confused with more common map functions, like Array.map. If the optional has some value, then the provided closure is applied to it. Otherwise, the nil is propagated onwards. Thus, the result of Int(textA.stringValue).map{ 0 <= $0 } is Bool?, with three possible values:
    1. true (Optional.some(true)), meaning the text field encodes a positive (non-negative) integer.
    2. false (Optional.some(false)), meaning the text field encodes a negative integer.
    3. nil (Optional.none), meaning the text field doesn't encode an integer at all.
  5. The nil coalescing operator is used (??), to merge the nil case into the false case. Thus, the result of Int(textA.stringValue).map{ 0 <= $0 } ?? false has 2 values:
    1. true meaning the text field encodes a positive integer.
    2. false meaning the text field encodes a negative integer, or doesn't encode a number at all. In either case, it doesn't encode a positive integer
  6. The process is repeated for textB, and the two expressions are anded together (&&)
Alexander
  • 59,041
  • 12
  • 98
  • 151
  • @techno Hold on, there's a mistake, working on fixing it now – Alexander Oct 01 '17 at 17:59
  • 1
    stringValue on OSX it is not optional – Leo Dabus Oct 01 '17 at 18:02
  • why not simply `if let valueA = Int(txta.stringValue), let valueB = Int(txtb.stringValue), valueA > 0 && valueB > 0` – Leo Dabus Oct 01 '17 at 18:09
  • @LeoDabus Totally possible, too. I would even prefer it, but that gets more complicated with more intermediate steps. E.g. on iOS where `UITextField.text` is Optional, or when there's a problem involving more conversions. You end up needing lots of non-descript but different names for the intermediate values – Alexander Oct 01 '17 at 18:16
  • You should always prefer readability. look at how you should do it at Martin’s answer – Leo Dabus Oct 01 '17 at 18:18
  • @LeoDabus I'm aware. I'm merely conveying an alternative that introduces people to the idea of mapping optionals. – Alexander Oct 01 '17 at 18:21
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/155715/discussion-between-alexander-and-leo-dabus). – Alexander Oct 01 '17 at 18:21
  • @Alexander you are over complicating such an easy task. I have already seen you posting to use map on optionals but thats definitely not the case. – Leo Dabus Oct 01 '17 at 18:28
  • @LeoDabus Read what I wrote in chat ^ – Alexander Oct 01 '17 at 18:30