During the 2020 lockdowns, I created The Bell app as a way to further explore watchOS development. Initially built using WatchKit, I later discovered that newer APIs were SwiftUI-only after the release of the Apple Watch Series 7.
In early 2023, I decided to revisit the app and update it by transitioning the UI layer to SwiftUI and implementing async/await APIs. With no intention of monetizing it, I chose to open-source The Bell as a functional proof-of-concept.
Throughout the process, I reworked approximately 70% of the app's codebase. Looking back, if I were to start from scratch today, I would likely opt for an Elm/redux-inspired architecture over MVVM.
- Xcode 15+
- watchOS 10+
- Clone the repository.
- (Optional) Install Mint and ensure it's available in the
PATH
loaded by Xcode.- Run
mint bootstrap
- Run
- Copy
main.xcconfig.example
and rename it tomain.xcconfig
- In
main.xccconfig
, setDEVELOPMENT_TEAM
to a valid identifier andDISAMBIGUATOR
to${DEVELOPMENT_TEAM}
. - Alternatively, if you plan to run the app on the simulator, you can leave
DEVELOPMENT_TEAM
andDISAMBIGUATOR
blank.
- In
- Copy the content of
Supporting Files/Assets/Sounds/cc0
toSupporting Files/Assets/Sounds/
, see Audio Assets for more information. - Open Xcode and run the project!
The Bell uses a three-tier architecture with MVVM in the presentation layer. While the layers are not completely separated, they could be easily decoupled with minimal work. For example:
- the simplicity of the data model in The Bell allows for the presentation layer to directly query repositories, as the data is often not converted or manipulated by the application layer;
UserPreference
andWorkout
are both plain structs, easily created and mocked and thus not abstracted;- application-level data models like
WorkoutSummary
are also freely passed around.
However, a more complex app would benefit from having different types encapsulating the data in the different layers, even if they match 1:1 (in that case, type aliases could be used).
The Bell relies heavily on protocols and dependency injection to ensure that view models, managers, and helpers can be tested in isolation.
- Presentation Layer: Contains all the UI-related code, including MVVM's Views and View Models.
- Standard suffixes:
-View
(suffixesButton
,Label
etc. are also possible),-ViewModel
- Directory:
Sources/Presentation
- Standard suffixes:
- Application Layer: Contains the application logic and abstractions to system components, such as HealthKit, AVFoundation and Haptic Feedback. HealthKit can be considered part of MVVM's Models.
- Standard suffixes:
-Manager
- Directory:
Sources/Application
- Standard suffixes:
- Data Layer: Contains repositories and the database logic, aka MVVM's Models.
- Standard suffixes:
-Repository
- Directory:
Sources/Data
- Standard suffixes:
All tiers may also contain -Helper
types.
The Bell has a very simple navigation pattern, therefore, NavigationPath
and/or Coordinators are not involved.
The audio assets used in the App Store version of the app cannot be freely distributed, so I had to replace them with public domain equivalents. I intentionally chose some silly sounds to keep things fun and lighthearted. Who knew that public-domain audio could be so fun?
Here are some tasks that I plan to work on for The Bell as time allows.
- Improve the test structure and split test cases between unit and integration tests, especially around time-sensitive code.
- Save the workout state in the database instead of using UserDefaults, as this is a remnant of early prototypes.
- Allow the creation and management of multiple workouts.
The codebase is licensed under the terms of the Apache License 2.0, see LICENSE for more information. It includes code inspired by DebouncedOnChange, licensed under the terms of the MIT license.
Image assets are licensed under the terms of the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License, see LICENSE.CC-BY-NC-ND for more information.
Audio assets are licensed under terms of the Creative Commons CC0 1.0 Universal License, see LICENSE.CC0. They were sourced from freesound.org and remixed: