Peter G Peter G - 5 months ago 20
Swift Question

Random number with user defined range

I am teaching myself Swift and am stuck with a Random function.

What I want to achieve: User is able to specify the min and max of the Range.

Code I have now for the button, where I believe I am declaring the text as an integer:

@IBAction func generateNumbers(sender: AnyObject) {

let minRange:Int? = Int(lowValue.text!)
let maxRange:Int? = Int(highValue.text!)

func randomNumber(range: Range<Int> = minRange...maxRange) -> Int {
let min = range.startIndex
let max = range.endIndex
return Int(arc4random_uniform(UInt32(max - min))) + min
}
}


Obviously minRange and maxRange aren't valid integers for the Range, I am just wondering where I'm going wrong.

Thanks in advance for any assistance.

Edit: I have tested that min/maxRange are valid Ints with:

let results = minRange! + maxRange!

self.field1.text = String(results)


Edit 2: Full Code (sorry for spam but it was requested)

import UIKit


class ViewController: UIViewController {

@IBOutlet weak var lowValue: UITextField!
@IBOutlet weak var highValue: UITextField!
@IBOutlet weak var numberOfFields: UITextField!

@IBOutlet weak var field1: UITextField!
@IBOutlet weak var field2: UITextField!
@IBOutlet weak var field3: UITextField!
@IBOutlet weak var field4: UITextField!
@IBOutlet weak var field5: UITextField!
@IBOutlet weak var field6: UITextField!
@IBOutlet weak var field7: UITextField!
@IBOutlet weak var field8: UITextField!
@IBOutlet weak var field9: UITextField!
@IBOutlet weak var field10: UITextField!



override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?){
view.endEditing(true)
super.touchesBegan(touches, withEvent: event)
}

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

@IBAction func generateNumbers(sender: AnyObject) {

// let minRange:Int? = Int(lowValue.text!)
// let maxRange:Int? = Int(highValue.text!)

guard let
lowText = lowValue.text,
highText = highValue.text,
minRange = Int(lowText),
maxRange = Int(highText)
else {
print("Couldn't find valid integers inside lowValue and highValue UITextFields. So I'm giving up...")
return
}


func randomNumber(range: Range<Int> = minRange...maxRange) -> Int {
let min = range.startIndex
let max = range.endIndex
return Int(arc4random_uniform(UInt32(max - min))) + min
}


//test
let results = randomNumber()
self.field1.text = String(results)


}


}

Answer

As others have identified, your minRange and maxRange are Optionals. They need to be Int, not Int? in order to form the range.

Also, you should avoid using ! to unwrap the text fields, just in case they're nil.

If you have some reasonable defaults for minRange and maxRange (such as 1 and 10), you could use the nil coalescing operator to unwrap your text fields and replace the values with defaults if the text fields can't be converted to an Int:

@IBAction func generateNumbers(sender: AnyObject) {

    let minRange = Int(lowValue.text ?? "") ?? 1
    let maxRange = Int(highValue.text ?? "") ?? 10

    func randomNumber(range: Range<Int>) -> Int {
        let min = range.startIndex
        let max = range.endIndex
        return Int(arc4random_uniform(UInt32(max - min))) + min
    }

    // test
    let results = randomNumber(minRange...maxRange)
    self.field1.text = String(results)
}

Note: The compiler is not happy with using the values minRange and maxRange to set the default value for range, so I added them to the call of randomNumber.


There is another potential gotcha here as well. You can't form a range if minRange is larger than maxRange, so you should check for that as well:

var minRange = Int(lowValue.text ?? "") ?? 1
var maxRange = Int(highValue.text ?? "") ?? 10
if minRange > maxRange {
    // Swap 'em
    (minRange, maxRange) = (maxRange, minRange)
}

Why even torture yourself in creating the range anyway? Since randomNumber is declared inside of generateNumbers you can just use minRange and maxRange:

@IBAction func generateNumbers(sender: AnyObject) {

    var minRange = Int(lowValue.text ?? "") ?? 1
    var maxRange = Int(highValue.text ?? "") ?? 10
    if minRange > maxRange {
        // Swap 'em
        (minRange, maxRange) = (maxRange, minRange)
    }

    func randomNumber() -> Int {
        return Int(arc4random_uniform(UInt32(maxRange - minRange))) + minRange
    }

    //test
    let results = randomNumber()
    self.field1.text = String(results)
}
Comments