How does one generate a random number in Swift?
Swift 4.2+
Swift 4.2 shipped with Xcode 10 introduces new easy-to-use random functions for many data types.
You can call the random()
method on numeric types.
let randomInt = Int.random(in: 0..<6)
let randomDouble = Double.random(in: 2.71828...3.14159)
let randomBool = Bool.random()
Generate list of unique random numbers in Swift from range
There are 2 problems here:
You don't generate enough numbers. You need to keep generating random numbers until your set is large enough:
func getRandomNumbers(maxNumber: Int, listSize: Int)-> [Int] {
var randomNumbers = Set<Int>()
while randomNumbers.count < listSize {
let randomNumber = Int(arc4random_uniform(UInt32(maxNumber+1)))
randomNumbers.insert(randomNumber)
}
return randomNumbers
}You're biasing your random numbers by putting them in the ordering Set chooses, which is highly predictable. You should append your numbers to an array (to keep them in order that they are generated), while still leveraging a set in parallel, for its fast deduplication:
func getRandomNumbers(maxNumber: Int, listSize: Int)-> [Int] {
precondition(listSize < maxNumber, "Cannot generate a list of \(listSize) unique numbers, if they all have to be less than \(maxNumber)")
var randomNumbers = (array: [Int](), set: Set<Int>())
while randomNumbers.set.count < listSize {
let randomNumber = Int(arc4random_uniform(UInt32(maxNumber+1)))
if randomNumbers.set.insert(randomNumber).inserted { // If the number is unique
randomNumbers.array.append(randomNumber) // then also add it to the arary
}
}
return randomNumbers.array
}
How to generate different random number in a range in Swift?
A Set
is simpler, it guarantees unique values
var randomIndex = Set<Int>()
while randomIndex.count < 3 {
randomIndex.insert(Int.random(in: 1 ... 10))
}
Another approach is to create an array of the values, pick a random element and remove this element from the array
var randomValues = Set<Int>()
var valueArray = Array(1...10)
for _ in 0..<3 {
guard let randomElement = valueArray.randomElement() else { break }
randomValues.insert(randomElement)
valueArray.remove(at: valueArray.firstIndex(of: randomElement)!)
}
Regarding is there any way to optimize my approach the for
loop in your code is extremely expensive.
First of all the recommended syntax for an index based loop is
for i in 0 ..< generatedValue.count {
But actually you don't need the index so Fast Enumeration is better
for value in generatedValue {
if randomValue != value {
The most expensive part of the loop is that the entire array is always iterated even if the value was already found. At least add a break
statement to exit the loop
for value in generatedValue {
if randomValue != value {
randomIndex.append(randomValue)
c += 1
break
}
}
But there is still a more efficient syntax
if !generatedValue.contains(randomValue) {
randomIndex.append(randomValue)
c += 1
}
Generating random numbers with Swift
let randomIntFrom0To10 = Int.random(in: 1..<10)
let randomFloat = Float.random(in: 0..<1)
// if you want to get a random element in an array
let greetings = ["hey", "hi", "hello", "hola"]
greetings.randomElement()
How to Generate a New Random Number Once Every time Content View Loads?
Approach #1:
MyView
is created once, when ContentView
renders.
Inside MyView
, randomInt
is set when the view is first created and then never modified. When you press the button, myMessage
is set, but randomInt
is never changed. If you wanted randomInt
to change, you'd want to say something like:
randomInt = Int.random(in: 1...100)
inside your button action:
Approach #2:
You're creating a new randomInt
variable in the local scope by declaring let randomInt =
inside the view body.
Instead, if I'm reading your initial question correctly, I think you'd want something using onAppear
:
struct MyView: View {
@State var randomInt = 0
@State var myMessage: String = ""
var body: some View {
VStack {
Text(String(randomInt))
Button("Press Me") {
myMessage = String(randomInt)
}
Text(myMessage)
}.onAppear {
randomInt = Int.random(in: 1...100)
}
}
}
You'll see that with this, every time you go back in the navigation hierarchy and then revisit MyView, there's a new value (since it appears again). The button triggering a re-render doesn't re-trigger onAppear
Approach #3:
MyView
gets created on the first render of the parent view (ContentView
). Unless something triggers a refresh of ContentView
, you wouldn't generate a new random number here.
In conclusion, I'm a little unclear on what the initial requirement is (what does it mean for a View to 'load'? Does this just mean when it gets shown on the screen?), but hopefully this describes each scenario and maybe introduces the idea of onAppear
, which seems like it might be what you're looking for.
Addendum: if you want the random number to be generated only when ContentView
loads (as the title says), I'd create it in ContentView
instead of your MyView
and pass it as a parameter.
struct ContentView: View {
@State var randomInt = Int.random(in: 1...100)
var body: some View {
NavigationView {
NavigationLink(destination: MyView(randomInt: $randomInt)) {
Text("Link to MyView")
}
}
}
}
struct MyView: View {
@Binding var randomInt : Int
@State var myMessage: String = ""
var body: some View {
Text(String(randomInt))
Button("Press me to refresh only myMessage") {
myMessage = String(randomInt)
}
Button("Press me to change the randomInt as well") {
randomInt = Int.random(in: 1...100)
myMessage = String(randomInt)
}
Text(myMessage)
}
}
Generate Array of Unique Random Numbers within Inclusive Range
You could make your live much easier using a Set to store all random numbers until you reach the expected number of randoms:
func uniqueRandoms(numberOfRandoms: Int, minNum: Int, maxNum: UInt32) -> [Int] {
var uniqueNumbers = Set<Int>()
while uniqueNumbers.count < numberOfRandoms {
uniqueNumbers.insert(Int(arc4random_uniform(maxNum + 1)) + minNum)
}
return uniqueNumbers.shuffled()
}
print(uniqueRandoms(numberOfRandoms: 5, minNum: 0, maxNum: 10))
func uniqueRandoms(numberOfRandoms: Int, minNum: Int, maxNum: UInt32, blackList: Int?) -> [Int] {
var uniqueNumbers = Set<Int>()
while uniqueNumbers.count < numberOfRandoms {
uniqueNumbers.insert(Int(arc4random_uniform(maxNum + 1)) + minNum)
}
if let blackList = blackList {
if uniqueNumbers.contains(blackList) {
while uniqueNumbers.count < numberOfRandoms+1 {
uniqueNumbers.insert(Int(arc4random_uniform(maxNum + 1)) + minNum)
}
uniqueNumbers.remove(blackList)
}
}
return uniqueNumbers.shuffled()
}
uniqueRandoms(numberOfRandoms: 3, minNum: 0, maxNum: 10, blackList: 8) // [0, 10, 7]
how to generate random numbers from 1 to 4 and 8 in swift
Put your sides
into an array and use randomElement()
to perform the roll:
let sides = [1, 2, 3, 4, 8]
let roll = sides.randomElement()!
if possible I need 4 and 8 numbers to come 20% more often
That means, 4
and 8
should come up 6 times for every time the others come up 5. So put 5 of each of 1
-3
in your array, and 6 each of 4
and 8
:
let sides = [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8]
let roll = sides.randomElement()!
Note: It is perfectly safe and appropriate to force unwrap the result of randomElement()
with !
here because randomElement()
only returns nil
when the collection is empty, and we know it’s not.
Also, check out MartinR's randomNumber(probabilities:)
function.
You'd use his function like this:
let sides = [1, 2, 3, 4, 8]
let probabilities = [1.0, 1.0, 1.0, 1.2, 1.2]
let rand = randomNumber(probabilities: probabilities)
let roll = sides[rand]
Generate random number of 9 digits in Swift
import Foundation
func random9DigitString() -> String {
let min: UInt32 = 100_000_000
let max: UInt32 = 999_999_999
let i = min + arc4random_uniform(max - min + 1)
return String(i)
}
I'm trying to make basic random numbers..swift
The code appears to be trying to add a random number to an array as a side-effect of when a button is pressed to display the contents of the random number array.
The error message is unclear, but what is happening is the compiler has spotted a state mutation in the code whilst laying out the UI that could cause a race condition.
To get things working, the code needs to separate out UI layout from state update so that they occur in separate cycles.
Here's one example of how that might be done:
import SwiftUI
struct ContentView: View {
@State var randomArray: [Int] = []
var arrayString: String {
if randomArray.count == 0 {
return "No numbers"
} else {
return randomArray.map { "\($0)" }.reduce("") { $0 + " " + $1 }
}
}
var body: some View {
VStack {
Text("My random numbers = \(arrayString)")
Button("New random number") {
randomArray.append(Int.random(in: 1 ... 45))
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Good luck and have fun.
PS: If learning Swift, the Hacking With Swift website is an excellent reasource
How to make a list of random numbers that never repeat (consecutively) in swift ui
First, I'd move your numbers into their own model (I'm using NumberModel
) so that they can have unique IDs, which will make them more convenient to use in the List
/ForEach
(especially if they end up changing values over time). This will also allow you to just use ForEach(numbers)
and not deal with the indices at all.
You don't need @State
variables for previousNumber
and randomNumber
because they're just used within onAppear
. I've refactored your onAppear
a bit, but you could definitely go further with it.
struct NumberModel : Identifiable {
var id = UUID()
var number : Int
var stringValue : String {
number.description
}
}
struct ContentView: View {
@State var numbers: [NumberModel] = []
var body: some View {
List{
ForEach(numbers) { i in
Text(i.stringValue)
}
}
.navigationTitle("numbers")
.onAppear() {
var previousNumber : Int? = nil
for _ in 0 ..< 100 {
while true {
let randomNumber = Int.random(in: 0...9)
guard randomNumber != previousNumber else {
continue
}
previousNumber = randomNumber
numbers.append(NumberModel(number: randomNumber))
break
}
}
}
}
}
Related Topics
Distinction in Swift Between Uppercase "Self" and Lowercase "Self"
Swiftui: Global Overlay That Can Be Triggered from Any View
Swift Turn a Country Code into a Emoji Flag via Unicode
How to Go Back to the Initial View Controller in Swift
How to Export Dae Files for Use in Scene Kit Without Seeing "Untitled-Animations"
Closure Use of Non-Escaping Parameter May Allow It to Escape
Round Up Double to 2 Decimal Places
Swift Make Method Parameter Mutable
Is There a Method to Blur a Background in Swiftui
Swiftui. How to Change the Placeholder Color of the Textfield
Arkit - Get Current Position of Arcamera in a Scene
How to Add an Event in the Device Calendar Using Swift
Arkit - How to Export Obj from Iphone/iPad with Lidar