Stuart Breckenridge

Dev Notes - Questions and Answers

As a little side project, I am rebuilding my long forgotten app Amazing Flag Quiz. I don’t have the original source code, so it’s all being done from scratch. My current task is working on the question and answer model. There are few things it needs to do:

  • generate an array of questions (each element a Question) based on the user’s selected continent (e.g. Europe, Africa…)
  • each Question must contain the correct answer and three wrong answers
  • the array of questions and each Question’s answers must be randomised.

Before getting into coding the app properly, I’ve thrown the below together in a playground.

I have an array of all countries:

let allCountries = ["Afghanistan", "Akrotiri", "Albania", "Algeria",...]

My Question class contains a correct answer and three wrong answers, held together in an Array:

final class Question {
    let correctAnswer:String
    var questionOptions = [String]()
    
    init(cAnswer:String, qOptions:[String]) {
        self.correctAnswer = cAnswer
        self.questionOptions = qOptions
    }
}

To track which game option the user picks, I’ve created an enum:

public enum GameOptions
{
    case All, Africa, Americas, Asia, Australasia, Europe, BritishEmpire, USStates
}

Finally, in order to create a list of questions, I’ve created a protocol with a default implementation:

protocol Questionable
{
    func questionsForOption(option:GameOptions) -> [Question]
}
extension Questionable
{
    /*
     Based on option, will generate and return an array of Question objects.
     
     - parameter option: GameOptions selected by user.
     
     - returns: [Question]
     */
    func questionsForOption(option:GameOptions) -> [Question]
    {
        switch option
        {
        case .All:
            var allQuestions = [Question]()
            for q in allCountries
            {
                allQuestions.append(Question(cAnswer: q, qOptions: populateOptions(q)))
            }
            
            // Shuffle 
            return GKRandomSource().arrayByShufflingObjectsInArray(allQuestions) as! [Question]
            
            /* ... */
        default:
            return []
        }
        
    }
    
    /*
     Populate answers for question.
     
     - parameter correctAnswer: String
     
     - returns: [String]
     */
    func populateOptions(correctAnswer:String) -> [String]
    {
        var countrySet = Set<String>()
        countrySet.insert(correctAnswer)
        
        // Get four unique random answers
        repeat {
            let i = GKRandomDistribution(lowestValue: 0, highestValue: allCountries.count - 1).nextIntWithUpperBound(allCountries.count - 1)
            countrySet.insert(allCountries[i])
        } while countrySet.count < 4
        
        // Add answers to an array
        var options = [String]()
        for c in countrySet{
            options.append(c)
        }
        
        // Shuffle answers
        return GKRandomSource().arrayByShufflingObjectsInArray(options) as! [String]
    }
}

In terms of usage, I need to generate questions and answers when my GameViewController appears on screen, as part of viewDidLoad, as shown below:

class GameViewController:UIViewController, Questionable {
    
    var list = [Question]()
    
    /* ... */
    
    override func viewDidLoad() {
        super.viewDidLoad()
        list = questionsForOption(.All)
    }
}

I’m satisfied with this solution: it’s less than 100 lines of code and is easy to follow.

I seem to remember this being much harder when I wrote Amazing Flag Quiz originally and I’m mindful of what Henri Cartier-Bresson once said:

Your first 10,000 photographs are your worst.

That applies to your first 10,000 lines of code as well.

Next up: presentation logic.


Parse to Close

Kevin Lacker:

We have a difficult announcement to make. Beginning today we’re winding down the Parse service, and Parse will be fully retired after a year-long period ending on January 28, 2017. We’re proud that we’ve been able to help so many of you build great mobile apps, but we need to focus our resources elsewhere.

We understand that this won’t be an easy transition, and we’re working hard to make this process as easy as possible. We are committed to maintaining the backend service during the sunset period, and are providing several tools to help migrate applications to other services.

While Parse will be releasing a migration tool to assist with moving existing data to any other MongoDB database, the fact that it is closing will come as a huge blow to developers who make use of the platform.


New DeLoreans Coming Soon

Jordan Golson:

The DeLorean Motor Company, which acquired what was left of the original manufacturer 30 years ago, currently focuses on repairing and restoring all the DeLorean vehicles that are floating around the world. But now, with this specialty legislation, the company can use its millions of factory parts (and some that have been recreated from the original blueprints) to build new, 2017 model year DeLoreans complete with a crate engine from an outside supplier. Pricing is expected to run between $80,000 and $100,000.

I wonder how much the certificate of entitlement would be for a DeLorean in Singapore?


Twitter Stops Serving Ads to VIPs

Peter Kafka:

Twitter makes its money by showing ads to its users.

But not all of its users: For the past few months, the social media company has stopped displaying ads, or has dramatically reduced the number of ads it displays, to a small group of some of its most prominent and active users.

For those people, Twitter is an ad-free, or nearly ad-free, experience.

Advertisers must be curious about this move. They are paying for their ad to reach customers and Twitter is arbitrarily taking that opportunity away from them based on the particular user’s valuableness?

Twitter sources say the company doesn’t select the no-ad or low-ad group purely by star power, but by a variety of criteria, including the volume and reach of the tweets they generate.

Fortuitously, for the rest of the us, there’s a solution to obtaining an ad-free Twitter experience: use a third party Twitter app. I recommend Tweetbot 4.

Note: I have previously experimented with Twitter ads in the past and discovered they are very good at enshittening a user's timeline while not being very useful.


The Best Email App for iPhone

Sean O’Kane via The Verge:

More than anything, a good email app should be fast. Refreshing your inbox, loading messages — none of these things should take more than a second or two. It should also give you plenty of different ways to deal with the daily email deluge. That means letting you do things like reply, archive, delete, or schedule messages with swipes or taps, while also giving you easy access to other services like calendars and files.

Beside powerful options, a good email app should also have a great design — not just so that it’s easy on the eyes, but so it’s easy to use. If an app makes it too hard to glance at your inbox and know what’s important, it’s time to try a different email client.

It seems strange to me that the review fails to consider a vitally important part of email: Security. No third party mail app supports S/MIME which has been baked into iOS since 2011. I accept that email security is unwieldy and causes issues with webmail, but third party apps should, at the very least, support S/MIME.