In our last article, we walked through the experience of building the UI part of a production app, with SwiftUI. In this article (Part 2), we’ll cover how the UI module is interacting with the services: MeasurementService, HistoryDataService.
Initiating MeasurementService
MeasurementService is at the heart of the app’s data flow:
- It initiates hardware camera
- It takes the raw readings from camera, and passes raw readings to HeartRateCalculator, to actually calculate heart rate
- It periodically reads output from HeartRateCalculator, and update the stored latestHeartRate value (a state)
- Its states are observed by UI module
This is how we config MeasurementService, to start the hardware camera. Note that we marked progress, latestHeartRate, and state as @Published, so that UI module can observe them.
Start and Stop Measuring
We created dedicated functions, to start and stop a measurement.
1. In startMeasuring(), capture session is started, flashlight is turned on, idle timer is disabled, and a heart rate reading timer is scheduled. The state is moved to WaitingForFinger.
2. In stopMeasuring(), the config process is reversed. If the cancellation is initiated by the user, we move into NotMeasuring state; if the cancellation is called by the service itself, when enough data has been collected to conclude the bpm value, we move to Success state.
Process Camera Outputs
Camera outputs drive some of the states.
1. If we can’t find a finger on the camera (based on HSV value of the camera output), we’re still in WaitingForFinger state.
2. If the user’s finger is on the camera, we move to Measuring state.
3. If we’re in the middle of Measuring, and still need more camera data: we don’t change the state, and update progress value instead. This will update the ring widget’s progress.
4. Once we measured long enough, we call stopMeasuring(), which will update state to Success.
HistoryDataService
As the name suggests, HistoryDataService allows writing and reading history data of measurement.
- It maintains a few states for UI module to observe: todayMin, todayMax, todayAverage, todayLatest.
- It has couple functions, to read and write heart rate history data. These functions are used to update the states.
- Underneath it, is the Realm database, a very easy-to-use mobile database.
Part 2 Summary
In this part, we walked through how to create the service layer, that keeps and updates states values for UI layer to render. In this service layer, we have a MeasurementService and a HistoryDataService; they serves different purposes; but from the perspective of data flow, they both serve states data for UI layer. States are the bridges between raw data (from camera or from database) and UI.
In the next part, I’ll talk about how we integrate existing UIViewController subclasses into out SwiftUI app. You can follow me on Twitter for more iOS dev tips.