Monomeeth Monomeeth - 3 years ago 246
iOS Question

Displaying strings in iOS randomly without repeating them

I'm making a quiz app. The app uses a .json file as the 'database' of questions and answers. This .json file looks as follows...

"id" : "1",
"question": "Earth is a:",
"answers": [
"difficulty": "1"

...and just keeps going for over 500 questions.

I display the questions randomly by using the following code:

let randomNumber = Int(arc4random_uniform(UInt32(allEntries.count)))

This work fine, but because it selects the questions randomly I'm finding that questions are repeating too regularly (even though I have over 500 questions!).

I've researched this extensively and think perhaps I've read too much as I'm now quite confused. I've read about 'seeding', about saving an index of questions asked, and trying to use NSUserDefault.

In short, how can I modify my code to achieve one of the following outcomes instead:

  1. To not repeat any questions at all, but to stop asking questions when all questions have been asked once;

  2. Similar to 1 above, but have the number of questions being asked set by the user rather than asking all questions in the database;

  3. To not repeat any questions at all until all questions have been asked first; or,

  4. To not repeat any questions that have previously been answered correctly, but those that were answered incorrectly may be repeated.

Below is what I think are the relevant bits of code:


let randomNumber = Int(arc4random_uniform(UInt32(allEntries.count)))

func LoadAllQuestionsAndAnswers()
let path = NSBundle.mainBundle().pathForResource("content", ofType: "json")
let jsonData : NSData = NSData(contentsOfFile: path!)!
allEntries = (try! NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers)) as! NSArray


func LoadQuestion(index : Int)
let entry : NSDictionary = allEntries.objectAtIndex(index) as! NSDictionary
let question : NSString = entry.objectForKey("question") as! NSString
let arr : NSMutableArray = entry.objectForKey("answers") as! NSMutableArray


labelQuestion.text = question as String

let indices : [Int] = [0,1,2,3]
//let newSequence = shuffle(indices)
let newSequence = indices.shuffle()
var i : Int = 0
for(i = 0; i < newSequence.count; i++)
let index = newSequence[i]
if(index == 0)
// we need to store the correct answer index
currentCorrectAnswerIndex = i


let answer = arr.objectAtIndex(index) as! NSString
case 0:
buttonA.setTitle(answer as String, forState: UIControlState.Normal)

case 1:
buttonB.setTitle(answer as String, forState: UIControlState.Normal)

case 2:
buttonC.setTitle(answer as String, forState: UIControlState.Normal)

case 3:
buttonD.setTitle(answer as String, forState: UIControlState.Normal)


buttonNext.hidden = true
// we will need to reset the buttons to reenable them


@IBAction func PressedButtonNext(sender: UIButton) {
print("button Next pressed")
let randomNumber = Int(arc4random_uniform(UInt32(allEntries.count)))


Answer Source

If I understand your requirements correctly:

  • You want to persist the randomly generated numbers between View Controller instances and application launches
  • you never want to repeat any question

A quick way to achieve this is to use NSUserDefaults to keep track of the order and value of (pseudo)random numbers from your PRNG. This way, you can instantiate the array of avaiable questions on instantaition by replaying previous dice rolls, removing those questions from the pool alongside in the right order.

This also lets you handle the PRNG as a black box, not caring about seeding and replay.

Also, make sure to remove any chosen questions from the current pool for any dice roll.

Side note: You are not following naming conventions - functions are to be starting lower case, for example.

For your own good and readability, please follow prevalent conventions, at least when sharing your code with others. Better yet, all the time.


To be more verbose:

  • NSUserDefaults can save small amounts of data for you in an easy way. If you 'just want to remember an array of numbers', it's the go to place.
  • suppose your data is helt in an instance variable like so:

    var questions : [Question] = // load the questions - carefully, see below.

    you can easily remove the question your PSNG (pseudo-random number generator) selects like so:

    let randomNumber = Int(arc4random_uniform(UInt32(questions.count)))

    Thus the next dice roll (e.g. invocation of your PSNG) is guaranteed not to select the question already asked because it is no longer in the pool of avaiable questions it selects from.

  • This approach does need you to save all your random numbers in the right order to 'replay' what has happened before when you are instantiating a new instance (for example, because the app has been reopened). That's where NSUserDefaults' setObject:forKey: and objectForKey: come into play when loading the questions initially and 'remembering' every new dice roll.

I hope, this covers what you might didn't understand before.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download