Exercises For Programmers: Exercise One-Saying Hello

A couple of months ago I bought Exercises for Programmers by my friend Brian Hogan. I was trying to find some mini challenges for myself like I had when I was a student when I would just work on a small, solvable problem.

When I think about programing, I usually get really overwhelmed by thinking about all of the components I need to make a minimum viable product. I start thinking about having to learn everything I have to do in order to put together a whole application. I wind up not doing anything because I feel like I have to learn twenty things before I can start coding rather than just diving in.

I have worked on other people’s projects and been given some rather explicit functionality that I have to have that is divorced from thinking about how to construct an actual program.

When I initially looked through this, I was disappointed because I felt like this was more targeted at people learning programming. There were tip calculators and Hello, World. So I got all sulky and emo teenager and decided this was not for me.

I was wrong.

I am planning to go through each exercise in this book and treat each exercise as a complete project. I am going to write unit tests for all of my code, make sure that the auto layout works, and try to think about what constraints I need rather than just trying to get something that works under specific circumstances.

User Interface and Autolayout

The first exercise in the book is a play on the usual “Hello world!” application. In this, you take user input and you output it to the screen.

To get user input from a user in iOS, you need to set up a bunch of UI elements: a label asking for your name, a text field for your name, a button to enter your name, and another label to display the message.

goodAutoLayout

This is a slightly embarrassing thing for me to admit, but I do not work much with user interface stuff. For the last two years I have worked exclusively on things like unit tests and network programming. The last time I worked a lot with user interfaces was around the time Storyboards were introduced. I have not mastered the Zen of Auto Layout.

Initially, I thought I had all of my elements laid out properly, but they didn’t show up where I wanted them to. I realized I had to go in and figure out a bunch of constraints to see what I wanted to on my screen.

autoLayoutFail

The top and bottom labels were easier because I knew I wanted them to be centered. The button and the text field were harder because I wanted them next to one another and I couldn’t center them the same way I did the labels.

autoLayoutFail2

I thought about giving up and just putting them on their own lines, but that kind of defeats the purpose of doing these exercises. I vaguely remembered that I can line things up by the leading or trailing edge. I liked the text field up with the leading edge of the top label and the button to the trailing edge of the top label. The text field rendered out to be about two characters wide, so I added one more constraint to maintain the spacing between the text field and the button.

HelloDelia

Programming Logic

Now that I have the layout working properly, I needed to think about what my failure cases are. Back when I was a student, I would just pull out whatever was entered in the text field and print it out on the screen. Since I want to write unit tests for this, I needed to put more thought into what would be a “bad” input.

The two that I could think about were either not entering anything at all, or entering in a number.

Since the text field is an optional, I needed to wrap it in an if-let. I thought about using a guard statement, but I didn’t feel like it would work in this case because I didn’t just want the result to be nil if you didn’t enter anything. I wanted to actually do something if it was nil, so I stuck with the if-let.

I thought that if there was nothing in the text field when you clicked on the button that it would be nil, so I had an else statement to state that if you just clicked on the button. That did not work because the text field returns an empty string. So that became a case I needed to check.

The last one was trying to figure out how check if the string contains a number. The ability to check a string for a number has changed a bit over the last two years in Swift. There used to be a toInt() function on the String class, but that was replaced with an initializer on the Int class. You pass a string into the Int initializer and if it can be converted to an int then you will have a value, otherwise, you get a nil.

Testing and Decoupling

I did my own testing on my application and I was able to trigger all three possible string outputs. But I want to get in the habit of writing unit tests for all of my personal applications. When I worked for Brad Larson, the main component of my job was to write unit tests for our code.

One way we were able to do that is because we made things very functional. We had very few classes. We had a lot of stand alone functions that could be tested thoroughly on their own. We created fake hardware objects in code.

That was harder to do with the view controller. I could make a function within the view controller to process the input from the text field, but I couldn’t do things as functionally as I wanted to.

Then I realized I could break my function up. Initially, I had this:

@IBAction func enterName(sender: AnyObject) {
    if let name = nameField.text {

        let myInt: Int? = Int(name)

        if name.characters.count == 0 {
            helloLabel.text = "You need to enter your name"
        } else if myInt != nil {
            helloLabel.text = "You entered numbers, not a name"
        } else {
            helloLabel.text = "Hello \(name), how are you?"
        }
    }
}

This coupled the code in such a way that it was impossible to pull that code out and make it testable. Everything in this function was talking to parts of the class, so I couldn’t pull it out and make it stand alone.

So I rethought was I was trying to do. This function needed to do one thing: Set the helloLabel. I was making it do two things. I was also making it determine what the helloLabel was set to.

I realized I could make the output from the text field into a parameter that could be passed into a stand alone function.

I changed that function from what I had to this:

@IBAction func enterName(sender: AnyObject) {
        if let name = nameField.text {
            let nameString:String = nameFunction(name)
            helloLabel.text = nameString
        }
}

func nameFunction(name:String) -> String {
    
    let myInt: Int? = Int(name)
    
    if name.characters.count == 0 {
        return "You need to enter your name"
    } else if myInt != nil {
        return "You entered numbers, not a name"
    } else {
        return "Hello \(name), how are you?"
    }
    
}

I pulled the nameFunction out of the View Controller and I put it in a Helper Function file. This function is the only part of this program that I need to unit test and I pulled it out from everything else. This makes testing my programming logic a snap.

    func testLabelOutput() {
        let emptyString = ""
        let numberString = "42"
        let normalString = "Delia"
        
        let firstTest = nameFunction(emptyString)
        let secondTest = nameFunction(numberString)
        let thirdTest = nameFunction(normalString)
        
        XCTAssertEqual(firstTest, "You need to enter your name")
        XCTAssertEqual(secondTest, "You entered numbers, not a name")
        XCTAssertEqual(thirdTest, "Hello \(normalString), how are you?")
    }

Conclusions

I am a little embarrassed that this took me longer than I thought it would. I have my name on a few programming books and I “know” this stuff. The Apple ecosystem is an incredibly vast and complex system of knowledge. If you don’t work with something for a while, it takes a while to remember how to do things and shake the cobwebs off.

There are a lot of considerations I put into this exercise that I would not have bothered with when I was a student. I would have been like, why bother making this testable? It works. No one is going to be stupid enough to post numbers and if they do, who cares?

That isn’t a good way to think.

It doesn’t matter what kind of application you are doing. You should try to make everything you code the best that it can be.

This was a good exercise for me to think about this differently than I would have, especially knowing I was going to write unit tests. I rewrote that function a few times and almost thought I couldn’t decouple it until I thought about it for like an hour.

I could have had this working in ten minutes, but the UI would have been screwed up and it would not have been safe.

I am still not happy with the final version of my code, but I feel better about it than I did before. I hope that someone will code review this and tell me how I could have done it better.

All in all, this was a really interesting thought experiment. There are lots of dark corner in my brain of things that I don’t know I don’t know. Shining some light into those corners and airing them out is a good thing to do every once and a while.

I am making a GitHub repository for my exercise if people have any interest in looking them over and comparing notes.

Catch you next time!

Also, if anyone can give me some suggestions about how to get code to format reliably in WordPress I would appreciate it. Sometimes it works and sometimes all of my formatting is gone.