# Let's Build Activity++ - Part 7

Episode #229 | 13 minutes | published on July 22, 2016 | Uses Nimble-4.1.0, Quick-0.9.2, Xcode-7.3, swift-2.3
Subscribers Only
We build our activity streak detection algorithm, testing it along the way with Quick and Nimble.

This episode is part of a series: Let's Build Activity++!.

 1. Let‘s Build Activity++! 38 min 2. Let's Build Activity++ - Part 2 16 min 3. Let's Build Activity++ - Part 3 10 min 4. Let's Build Activity++ - Part 4 12 min 5. Let's Build Activity++ - Part 5 28 min 6. Let's Build Activity++ - Part 6 14 min 7. Let's Build Activity++ - Part 7 13 min

## 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))
}
}
``````

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))
``````