Lambda Labs Week I

9 minute read

The first week of Labs was a bit of an adjustment for me. The whole paradigm of iOS development we’ve learned so far is basically doing as much on the device as possible and then just using the web to sync data between devices. But Labs is definitely set up for most of the people to be building a web-first product that has some sort of iOS component. So I have had to shift my focus to things that make sense in that context.

Individual Accomplishments this Sprint

I don’t feel like I accomplished a whole lot this week, but I think that is because most of it is kind of non-tangible. I met my team. I spent a lot of time in Zoom meetings and Slack calls with my team. We worked our way through the Technical Development Document and got a decent idea of what we are going to build over the (then) next five weeks, although a lot of it doesn’t mean much to me because it is web-stuff. We got a Trello board set up with some semblance of order. I went from not really understanding how we were going to track motion – even wanting to push it to be a stretch goal – to having a pretty good plan for it. I laid out a basic login/register view, and wrote the logic for registering a user. I built some helpers that will make styling the app easier. I built an custom date picker view that will let us style it how we want and also only let the user set alarms within the parameters of our app.

Detailed Analysis

Probably the most interesting code I worked on this week was the date picker for the alarm. I originally intended to just use the built-in date picker for the this, but there were a couple of problems with that:

  • It lets you set dates for whenever, which is not really what we want for our use case. You can set minimum and maximum dates, but that leads to weird quirks with the appearance.
  • It does not let you set change the appearance, which is a problem because our team wanted to do a dark theme since this will be an app people are primarily using at night.

From Apple Documentation: image-center

I did find a workaround that would let you set the text color using KVC, but it isn’t officially supported, it may not pass App Store Review, and it lead to unexpected appearances in my minimal testing.

1
datePicker.setValue(UIColor.white, forKeyPath: "textColor")

My solution instead was to make a subclass of UIPickerView that acts as it’s own data source and delegate. It has a date value that is publicly readable, a setter method to set that date to some time period from now, and two computed properties that will return the hours and minutes from now. It only allows you to set the date to some time within the next 24 hours, making the assumption that no one will want to sleep for more than 24 hours at a time.

It has two main internal functions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
private func dateFromComponents() -> Date {
        // Get the current components, and adjust based on modifier
        var hour = Int(hours[selectedRow(inComponent: 0)])!
        if hour == 12 { hour -= 12 }
        let minute = Int(minutes[selectedRow(inComponent: 1)])!
        let modifier = modifiers[selectedRow(inComponent: 2)]
        if modifier == "pm" { hour += 12 }

        // Grab the components from the current date and set the hour and minute
        var components = calendar.dateComponents([.day, .year, .month, .calendar, .era, .timeZone], from: Date())
        components.hour = hour
        components.minute = minute

        // Make a date from the components and add a day if it is before the current time
        var date = calendar.date(from: components)!
        let comparison = calendar.compare(date, to: Date(), toGranularity: .minute)
        if comparison == .orderedAscending {
            date = calendar.date(byAdding: .day, value: 1, to: date)!
        }

        return date
    }

    private func componentsToDate(_ date: Date) {
        // Get the components from the date
        let components = calendar.dateComponents([.hour, .minute], from: date)
        guard var hour = components.hour, var minute = components.minute else { return }

        // Make adjustments
        minute = roundMinute(minute)

        let modifierIndex: Int
        if hour < 12 {
            modifierIndex = 0
        } else {
            hour -= 12
            modifierIndex = 1
        }

        // Turn them into strings and get their indexes
        let hourString = String(hour)
        let minuteString = String(minute)
        guard let hourIndex = hours.index(of: hourString), let minuteIndex = minutes.index(of: minuteString) else { return }

        // Select the right rows and set the date
        selectRow(hourIndex, inComponent: 0, animated: true)
        selectRow(minuteIndex, inComponent: 1, animated: true)
        selectRow(modifierIndex, inComponent: 2, animated: true)
        self.date = dateFromComponents()
    }

    private func roundMinute(_ minute: Int) -> Int {
        if minute % 5 == 0 {
            return minute
        } else {
            return minute + (5 - minute % 5)
        }
    }

Milestone Reflections

Honestly I do not feel that I was able to contribute to analyzing the product specification or writing the TDD very much. Most of it was about how the web front end and back ends would be organized, which I can’t speak to very much. I did research various other sleep tracking apps like SleepCycle, SleepScore and Sleep++. From that investigation I got some good examples of how the UI might be laid out, and learned that the three primary ways of tracking the quality of sleep on iOS without some additional physical hardware are: using the microphone (and I’m assuming some ML model), using an AppleWatch to track heart rate and motion, using the phone itself to track motion, placed under the pillow. The first is obviously outside of the scope of our Labs project, and the second requires that the user has an AppleWatch, which is much less likely than that they have an iPhone, so we will be using the third method. We also added as a stretch goal to incorporate the heart rate data if the user is “premium” and has an AppleWatch.

I was really nervous about being asked to track the quality of sleep at first because I was fairly certain I could get a bunch of motion data points from the phone over the course of the night, but I wasn’t sure how we would translate that into something describing sleep quality. After we presented our TDD, I was told that it would be good enough for now to just present a graph of the motion over time and let the user decide if the quality was good or not. Then I started looking at CoreMotion more closely and started thinking about the best way to summarize that data over the course of the night. I don’t have any specific solutions yet, but I have started putting together a test app that will let me start collecting some of my own sleep data so that I have some numbers to look at.

Reservations

Something that has been holding me back is not being able to work on disparate things until the pull request gets merged. (I think this is a problem with our GitHub flow). If I am working on networking code and realize a problem with the UI, I can’t fix that until after the networking code has been merged, because, even if I make a separate commit, that commit gets added to the current pull request. So I have to remember to go back and fix that UI thing later, instead of just adding a small change now. I am sure there is some way to deal with this in Git, but not in a way that was described in the Github flow given to us. Separate branches? This also is probably a problem that I am more likely to run into in iOS development than the web guys.

The thing that is making me the most nervous for next week is the login/authentication flow, especially with OAuth. That is not really something that we learned in the iOS course. I can probably piece something together that works, but I would rather not do that in the problem-space of privacy/security. I would much rather know the best practices and how I should organize that in my app and/or GitHub repository.