Back to Hardware

I recently joined early-stage hardware start-up Grow, heading up their software engineering. This is a super exciting opportunity and a chance to work on a technically, very interesting product. What does my role as Head of Software Engineering mean for a hardware start-up? It’s probably going to be mean a lot of things. At a high level, I’ll be the first engineer focusing on the user’s digital experience with this product.

This means I’l focus more on off-device software that will serve as the primary means of user interaction with the device (when they aren’t physically handling it). It’s going to be a mobile-first experience.. so this means building the mobile apps the customer will use.. it also means building the back-end server related components that are needed to support these apps and give us the data we need to get a picture of what’s going on.

It also means I’ll be hiring and building out the future team who will scale this as the company grows!

So, what is this device? Grow is building a outdoor-based automated growing system. What does this mean? The vision is a box that you can plant vegetables in and not have to stay on top of of complex schedule of caring for them. They are watered on a schedule designed for the species and current stage of life of the the plant. There’s an app you use to monitor progress remotely… and let you know about tasks you do need to do such as pruning, harvesting, etc.

Here’s are some pictures of our first prototype.

Grow Prototype - Box & Board

Software wise, there a lot of really interesting problems to solve.

  1. Data retrieval from the bed. If we’re taking care of your plant, we need to pull relatively recent data from it giving us a picture of what’s happening. Soil moisture levels, temperature, etc.. How do we get this data? (Bluetooth, Cellular, Wifi?)
  2. Handling gardens with n number of beds.. i.e. scaling.
  3. Dynamic watering programs. Customizing the behavior of each bed to what is planted in it and it’s needs.
  4. Data capture/analysis of what’s getting grown successfully and funneling this learning back into product/software design.
  5. Early stage validations of hardware components. (Lab testing)
  6. Effective user testing. What do we put in front of users first? What do we need to capture when we do this.
Girl Develop It - iOS Intro Course

Today I am one week into the running/instructing of a 4 week introductory course on iOS and Swift. It’s been such an awesome experience so far planning and executing this course, I wanted to capture the highlights and discuss some of the interesting things I’ve encountered.

What Course?

The course I’m talking about is facilitated through Girl Develop It. The meet-up page is here.

It is meeting for 3 hours on Tuesday nights between 6:30-9:30 for the month of March.

Why?

So, full disclosure. I’m a woman. I’m a software engineer. There aren’t that many of us.. proportionally speaking! It’s long been a goal of mine to get more women into this field. I have many opinions on how this can be done. Convincing young girls that it is indeed a career option that makes sense for them is one of these. This, however, is a long game… Hugely important and we will continue to spread this message. But I won’t be coding alongside these individuals for ~10 years. How can I make a quicker impact?

I’ve been trying to find a way over the last 6 months. Early this year I had a discussion with a female colleague of mine who was telling me how she’s really like get into engineering.. specifically iOS app development. I said, that is awesome.. you should totally do it! She mentioned how it’s pretty hard to get off the ground with learning and that she’d really like a course-based setting. We started discussing sources for such courses Girl Develop It, Girls Who Code, etc… But she said they don’t frequently have iOS courses. I thought and said to her ‘I should teach one!’

I contacted Aurelia Moser of Girl Develop It the following week and was on my way to organizing their next iOS Intro course. (The last had been in early 2015.)

My Co-Instructors/Organizers.

As this is a course dedicated to women learning iOS and programming… it should be organized and taught by women right? I found two awesome engineers to help out.

Tamar Nachmany - iOS Engineer at Tumblr
Claire Davis - Recent Flat Iron iOS bootcamp graduate

Decisions

Prerequisites

In organizing likely any introductory course, it’s difficult to draw a line with the amount of programming experience attendees should have. Also, the way Girl Develop It works is attendees sign-up like they would for any meet-up. I had thought if we could screen attendees, we could perhaps offer two courses. One for somewhat experienced coders trying to get into iOS and another for total programming newbies, looking to get into programming via iOS.

As it stands, we decided to just open it up and tell attendees that they should have some (if minimal) programming experience and should understand basic concepts like classes, functions, booleans, etc..

After the course filled up, we sent out a questionnaire to try to get a feeling for who the attendees were and what background they had. It turns out, not surprisingly, that we had gotten a pretty wide range of experience levels. From virtually totally new to programming.. to coming to iOS from a C++ development career.

My conclusion with prerequisites is that if you can’t enforce them, they’re just more of recommendations and you can’t count on everyone to match-up to them. We decided to acknowledge this disparate range in background and to perform frequent check-ins with attendees to make sure they’re getting what they came to get.

Course Structure

Session 1 in action

Lecture vs. Lab

Lectures are boring right? We decided to try to minimize these. With 1 introductory lecture in the first session overviewing the iOS framework as a whole and how it’s really just like any operating system… connecting apps to system resources. Beyond this, we wanted to focus mostly on labs.

Labs are more fluid and dynamic, but can also get bogged down in complexity if you’re not careful. We are trying an approach of doing mini labs.. so we do a little coding (5 min) over projected screen share, then hand it off to the attendees to try the same, with perhaps a slight elaboration.

Project vs. Focused Examples

We were initially playing around with the idea of have a course project where attendees would pick the problem/solution and design and build an app to address it over the course of the project.

We felt this would be powerful because attendees would leave with a real iOS app that they build on their phones at the end of the course. It would also demonstrate the process on a end-to-end.

So, after getting into session planning, we identified that it would be harder and harder to tie the material into everyones different projects. We decided to adjust our approach.

Attendees are being encouraged to still work on an individual project (if they choose) with our support should they need it. In class, we’re going to be doing focused mini projects that will tie together the content being covered in that particular class. This will hopefully ensure that everyone is taking away exactly what we planned.

Course Size

The smaller the better! I had initially thought that the more attendees we can fit the better. Advice I received from other GDI instructors said keep it small.. especially since this is the first course I’m running. In a lab-based class, you apparently should target 4-5 attendees per lab instructor or assistant. This is the number we went with as we’ll have 5 instructors/lab assistants in total.

Moving Forward

In our first session, our coding was fairly limited. We achieved a ‘Hello World’ example and then built a two screen application that would use a few UI elements like labels/buttons to show information and move the user between screens.

For session 2, we had to decide between going further into screen building and UI elements or diving into the Swift language. We’ve decided to go into Swift as using it and understanding it’s strengths will really propel a new iOS developer forward and accelerate overall learning (we believe). We shall see. The intention is to get as much feedback from the course attendees as possible to both frame sessions 3 & 4 but to also hopefully improve this course and offer it again!

Swift - Optionals Mindset

Optionals are one of Swifts most unique features. They are powerful.. but take some getting used to when starting out with swift development. I for one spent about half of my development time figuring out what should and should not be an optional and how and when to pass them around and unwrap them.

There are a lof of posts out there explaining what optionals are and how they work. I want to talk today about how you program with them. Specifically, how you think differently about property, variable and paramter definition as you design your classes and functions (among other contexts).

Thinking Shift

In Objective-C (or other programming languages for that matter).. when working nils you can sort of just pass them around until you reach a case where operating on it with a nil value would cause a problem. At this point.. you check for whether or not it is nil.. and then you perform your operation if it isn’t.

Consider the following class definitions.

@interface Book : NSObject
@property (nonatomic) NSString *name;
@property (nonatomic) NSString *author;
@property (nonatomic) NSString *description;
@end

Code that crashes:

Book *book = [[Book alloc] init];
book.author = nil

NSRange *rangeOfAuthor = NSMakeRange(0, self.author.length)

This happens because we are attempting to create the NSRange with a nil length.

The safe thing to do is:

if(book.author != nil){
	NSRange *author = NSMakeRange(0, book.author.length)
}	

So basically, anywhere a nil causes a crash, you will have to check for the case of nil.

In Swift… I like to think about objects being allowed to be defined in two ways:

  • Those that can exist as a nil
  • Those that can not exist as a nil

When you define a property or variable.. you tell the compiler whether this thing is the first or the second. If the property or variable you’re defining can (ever) exist as nil… then you must declare it as an optional. Ahhhh!!! Optionals.

Ok, it is not so bad! Let’s consider our book example. With a book, a name and author are probably items that we can say must always be there. i.e. The can never be nil. A book’s description is something that might not be available, however.. meaning it could exist as a nil.

One thing that’s quite swifty is to represent Book as a struct. This will aid us in forcing that name and author be present in every definition of a book.

struct Book {
    var name: String
    var author: String
    var description: String? //This is indicating the description is a property that could be nil (an optional!)
}

var b = Book()

The above declaration of b gives you an error. Defining a book as struct means that everytime we instantiate one, we must provide the properties it contains. As follows:

var b = Book(name: "Jean", author:"Auel", description: nil)

Notice if you try to set author to nil, you get a compilation error.

var b = Book(name: "Jean", author:nil, description: nil)

So, this means everytime we access a books name or author, we can be sure it will never be nil. This is safer all around as we DO NOT have to remember to check for nil in the cases we might see a crash.

Takeaway

Optionals allow:

  • You to be explicit about whether a variable of property must be present at all times.
  • To not have to explicitly check for nil in cases when it may cause a crash.
  • In doing the above, minimize likelyhood of crashes in your app.

(BTW, also)

  • Structs go a little further to enforce you create your object with the properties it is defined to have.

Sometimes it’s fun to visit a classic CS problem when you’re learning a new language. Since I’m new to Swift, I wanted to tackle a pretty classic recursion problem, using unit tests. The Tower of Hanoi problem is a great introduction to recursion and once you get it.. it really demonstrates how recursion is really about breaking a larger problem down into smaller pieces.

The goal of the puzzle is to move all the disks from the leftmost peg to the rightmost peg, adhering to the following rules: Move only one disk at a time. A larger disk may not be placed ontop of a smaller disk.

Tackle by breaking the problem down

Often the problem is introduced for moving 3-5 disks. Consider the problem of moving 2 disks first.

You’re going to first move the top disk off to the middle tower, here called the buffer. Considering one tower to be a buffer is intrinsic to the solution so keep that idea in mind!

You’ll move the second, larger disk disk off to the final or destination tower.

You’ll finally move the original disk on top of the larger disk which is now on the destination tower.

A Unit Test

Unit Tests are obviously great because they let you know when you’ve finished. It makes validating an expected outcome so much easier. In this example, I’m using Quick and Nimble to unit test my Hanoi Tower problem

class HanoiTowerSpec: QuickSpec {
    override func spec() {
        describe("Hanoi tower") {
            
            it("should move 5 disks from tower A to tower C") {
                
                var towerA = Stack(name: "Tower A")
                var towerB = Stack(name: "Tower B")
                var towerC = Stack(name: "Tower C")
                
                //put the disks on the first tower
                for index in 1...5 {
                    towerA.push(index)
                }
                
                let hanoi = HanoiTower()
                hanoi.transfer(numDisks: 5,
                               origin: &towerA,
                               destination: &towerC,
                               buffer: &towerB)
                
                expect(towerC.contents.count).to(equal(5))
                expect(hanoi.numMoves).to(equal(31)). // a hanoi tower solution should be (2^n - 1) moves where n is the number of disks
            }
        }
    }
}

Solution Review

The key to turning this into a recursive code solution is to keep in mind that first move is really a buffer, on the way to the target of moving two disks from tower 1 to tower 3. Also, all towers can and should be used as buffers towers as the problem is broken down.

But, let’s first look at some psuedo code for the two disk problem.

Source tower: Tower 1

Target tower: Tower 3

Buffer tower: Tower 2

  1. Move disk count - 1 from source to the buffer.
  2. Move 1 disk from source to the destination.
  3. Move disk count - 1 from buffer to the destination.

Now, recursion always involves a function that calls itself until it has reached the end of it’s execution.. then returning from the inside calls to the earlier ones.. allowing any remaining executions to occur after the recursive call returns.

Let’s design our function.

class HanoiTower {    
    func transfer(numDisks: Int, origin: Stack, destination: Stack, buffer: Stack) {
        
    }   
}

This will serve our needs outlined above.. for each move of n number of disks.. we’ll need to specify an origin tower (here called Stack as these are essentially the data type Stack), a destination and a buffer.

Just for code completion. The Stack looks something like the following:

struct Stack {
    var name: String
    var contents: Array<Int>
    
    init(name: String, contents: Array<Int> = Array<Int>()) {
        self.name = name
        self.contents = contents
    }
    
    mutating func push(_ val: Int) {
        self.contents.append(val)
    }
    
    mutating func pop() -> Int {
        return self.contents.removeLast()
    }
}

Now let’s fill it out the transfer function to handle n number of disks.

class HanoiTower {    
    var numMoves = 0

    func transfer(numDisks: Int, origin: inout Stack, destination: inout Stack, buffer: inout Stack) {
        
        if numDisks > 1 {
            print("Transferring \(numDisks) from \(origin.name) to \(destination.name) with \(buffer.name) as buffer.")
            let nextDisks = numDisks - 1
            
            transfer(numDisks: nextDisks,
                     origin: &origin,
                     destination: &buffer,
                     buffer: &destination)
            
            transfer(numDisks: 1, origin: &origin, destination: &destination, buffer: &buffer)
            transfer(numDisks: nextDisks, origin: &buffer, destination: &destination, buffer: &origin)
        } else if numDisks == 1 {
            print("Moving single disk from \(origin.name) to \(destination.name)")
            numMoves += 1
            let disk = origin.pop()
            destination.push(disk)
        }
    }
}

Summing Up

Hope this all makes sense. Again, the key to to break the problem down. The print statements in the solution above help to realize that you may be starting out calling the function to move n=5 disks.. but it starts making calls to move n-1 disks and disks don’t really start moving until a call to move the first disk off the origin, which is many calls into the solution, upon which you return out to all the earlier method calls to move disks from your buffer to whatever the destination is.

Also, what’s essential conceptually is to consider that the buffer is shifting for each call.

Check out my solution for this in this Github repository.

I’ve been learning Swift for a few months now and wrote my first function using generics today for a real world usecase.

Use case: merge two dictionaries, skipping duplicates. You’ve got to check for the same key types and value types.

extension Dictionary {
    func merge<K, V>(dict: [K: V]) -> [Key: Value]{
        var new: Dictionary<Key, Value> = [:]

        for (k, v) in self {
            new[k] = v
        }

        for (k, v) in dict {
            if let key = k as? Key {
                new[key] = v as? Value
            }
        }
        return new
    }
}