Sofia Chevrolat (August 2022)
A sample app to visualize a workout described by data points in a JSON file on a mapview, along with the heart rate. Supports dynamic type, voice over and dark mode.
Light mode | Dark mode |
From a JSON file containing workout data points (latitude, longitude, heart rate):
-
Visualize a workout summary - date, title, subtitle and duration of your workout - , along with a plot of your route on a map.
-
The plot is colored according to your heart rate during the workout. A minimalist legend displays the lowest and highest values.
-
Supports:
- Accessibility Features:
- Dynamic Type and Bold Text
- Voice Over (only for the Summary View. A custom rotor would be best for the map view)
- Dark mode
- Accessibility Features:
- The sample app follows an MVC architecture
- The views have been created to be as reusable as possible
- The
WorkoutDataPoint
struct conforms toDecodable
and implements a custominit(from decoder: Decoder) throws
- The repository pattern is implemented for retrieving stored data, using:
- a
Repository
protocol - a generic
JSONRepository
class that can be used to retrieve any type stored using JSON.
- a
- MVC architecture
- Repository design pattern
- Protocols
- Generics
- Object Oriented Design (classes, inheritance)
- Composition
- Auto Layout with Safe Area
- Trait Collections
- Accessibiltiy
- Dark mode
- Programmatic UI
A full class diagram is provided in the Documentation folder. Below is a shortened version of the most important entities:
classDiagram
%% ============= ENTITIES ============= %%
%% CLASSES
class WorkoutViewController {
var workout: Workout
var workoutSummaryView: SummaryView
var mapView: MKMapView
var legendView: LegendView
func plotRoute()
}
class JSONRepository {
typealias T = StoredType
...
}
%% STRUCTS
class Workout {
...
type: WorkoutType
...
data: [WorkoutDataPoint]
}
class WorkoutDataPoint {
position: CLLocationCoordinate2D
heartRate: Int
init(from decoder: Decoder) throws
}
%% ENUMS
class WorkoutType {
case running
}
%% PROTOCOL
class Decodable {
init(from decoder: Decoder) throws
}
class Repository {
associatedtype T
...
}
class MKMapViewDelegate
%% ========== RELATIONSHIPS ========== %%
WorkoutViewController ..|> MKMapViewDelegate : \nConforms to \n(Conformance)
Workout *-- JSONRepository : \nCreates \n(Composition)
WorkoutViewController "1" *-- "1" Workout : \nCreates \n(Composition)
Workout "1" <.. "1" WorkoutType : \nReceives \n(Dependency)
Workout "1" <.. "1..*" WorkoutDataPoint : \nReceives \n(Dependency)
JSONRepository ..> WorkoutDataPoint : \nFetches
JSONRepository ..|> Repository : \n Conforms to \n(Conformance)
WorkoutDataPoint ..|> Decodable : \n Conforms to \n(Conformance)
The app comes with a few unit tests validating the behavior of the JSONRepository, the key component of the sample app.
The following 2 packages are included:
- SnapKit for easy view positioning.
- SwiftFormat for code formatting. Swift format is run at each build and enforces a set of rules declared in the accompanying
.swiftformat
file.
- Open the project in Xcode
- Run "Resolve Package Versions" (File > Packages) if needed
- Run the project
- Try different accessibility font sizes and see the UI update dynamically.
- Try switching between dark / light mode and see the UI update dynamically.
- Try using Voice Over to have a human-friendly read of the summary view.
- Run the tests
- Interface functions and variable are documented with DocC style comments.
These can be used to generate a documentation for the app (as of WWDC2022), that can be hosted on GitHub page or on any other website.