Episode Links Source Code Defining the Streak Data Structure First we need to define a structure to hold our streak data. enum MetricType { case Movement case Exercise case Standing } struct Streak { let metric: MetricType var numberOfDays: Int } Now we need a way of computing the streaks, given the activity logs we were provided. Computing Streaks lazy var streaks: [Streak] = { var streaks: [Streak] = [] var potentialStreaks: [MetricType: Int] = [:] var lookup: [MetricType : (ActivityLog) -> Bool] = [ .Movement : { $0.activityProgress >= 1.0 }, .Exercise : { $0.exerciseProgress >= 1.0 }, .Standing : { $0.standProgress >= 1.0 } ] for activity in self.activities { for metric in lookup.keys { let hasCompletedMetric = lookup[metric]!(activity) if hasCompletedMetric { let numberOfDays = (potentialStreaks[metric] ?? 0) + 1 potentialStreaks[metric] = numberOfDays } else { if let numberOfDays = potentialStreaks[metric] where numberOfDays > 1 { streaks.append(Streak(metric: metric, numberOfDays: numberOfDays)) } potentialStreaks[metric] = nil } } } return streaks }() Testing the Algorithm describe("streak detection") { var samples: [ActivityLog]! let calendar = NSCalendar.currentCalendar() var streaks: [Streak]! context("no streaks") { beforeEach { samples = FakeHealthData.randomSamplesWithNoStreaks(calendar: calendar, startingWithDate: NSDate()) var datasource = ActivityDataSource(calendar: calendar, activities: samples) streaks = datasource.streaks } it("returns no streaks") { expect(streaks).to(beEmpty()) } } context("known streaks") { beforeEach { samples = FakeHealthData.randomSampleWithStreaks(calendar: calendar, startingWithDate: NSDate()) var datasource = ActivityDataSource(calendar: calendar, activities: samples) streaks = datasource.streaks } it("should return 2 streaks") { expect(streaks).to(haveCount(2)) } it("should have a movement streak of 4 days") { let movementStreak = streaks.filter { $0.metric == .Movement }.first expect(movementStreak).notTo(beNil()) expect(movementStreak!.numberOfDays).to(equal(4)) } it("should have a standing streak of 5 days") { let standingStreak = streaks.filter { $0.metric == .Standing }.first expect(standingStreak).notTo(beNil()) expect(standingStreak!.numberOfDays).to(equal(5)) } } Adding the Date In the video I neglected to add an important piece: the date of the streak! The easiest solution would be to have the streak's date be the starting date of the streak: struct Streak { let metricType: MetricType let startingDate: NSDate let numberOfDays: Int } Then when we create the streak, we can use the date of the current log, which we've already guaranteed will be in reverse chronological order -- in other words the last element in a streak will be the earliest date. We can then create the streak like this: streaks.append(Streak(metric: metric, startingDate: activity.date, numberOfDays: numberOfDays))