Deep Breathing & Mindfulness on iOS and Android
"A language-independent meditation app to help ease the world's anxiety"
Lead Developer / Cofounder
Team Size: 2
Myself + 1 Product Designer
November 2020 - March 2021:
> 200 Hours
5.9 Million views on Tiktok
- React Native
- Google Firebase
- Apple Developer Program
- Sign In With Facebook
This mobile app was developed to help ease the world's stress and anxiety.
- Memoir promotes mindfulness and serenity through deep breathing and meditation exercises. Users can sign in with their Facebook, Apple ID, or email to save their favorite exercises and track their personal exercise progress.
- Users have a clean and simple-to-use UI to play and pause exercise videos, set timers, and save favorites.
- Users have access to a variety of breathing exercises and daily recommendations.
- Currently in beta testing on TestFlight for iOS, and works on Android via Expo.
- Memoir breathing exercises use visual cues to be followed by anyone in the world, regardless of language.
- Beautiful designs and gestures enhance the overall customer experience.
- Personalized user progress tracking add to the user experience and help to build better habits.
Caching the Downloaded Image and Video Assets from Firebase Storage using Expo FileSystem API
The following code gist was shrunken from the original code for simplification. The original uses a regular expression checker to categorize assets as "images" or "videos".
- Firstly, the specifically requested
videoFileassets are passed down as props to the individual
useState()hooks are then initialized to set the actual image and videos to be rendered for later on. The
useEffect()hook will run the initial caching function on mount.
asyncfunction, that first checks if the file already exists within the cache to prevent unnecessary download requests, saving bandwidth.
- If it wasn’t cached, the download URL of the file is retrieved from Firebase Storage. The URL is used to download the file to the device’s cache using the Expo FileSystem API. The
useState()states are then stored using the newly downloaded assets.
- Finally, with the stored states, the
cachedImageis used to render the exercise image. When you press on that image, it navigates to the
ExerciseVideoscreen along with the
cachedVideopassed down as a
UI: User Stats Scroll-Up Numbers Animation
How I implemented the custom animation for the numbers scrolling up:
- I first made a function to render a short vertical bar of numbers by dividing each specific stat number by 5.
- Then, I used a
MaskedViewcomponent from Expo to create a mask for the bar to hide behind.
- Whenever this specific screen was focused by the user, a
setTimeouttriggers the transform animation using
- With wild cases such as integers ranging from 0 to long-digit integers like 1,000,000 or beyond, I implemented a custom width solution using conditionals for responsive widths.
Dynamically Programmed “Moving Target” Goals & Congratulation Alerts
An alert dialog pops up whenever the user hits their "moving target" goals, which would always increase as the current benchmarks were met.
- The practice time goals increase from a 30 minute goal, to a 2 hour goal, to a 5 hour goal, and then increments by 5 hours indefinitely.
- Those conditional rules were stored in a variable and used to trigger the alerts whenever the goals were met and the user visited the stats page.
- The "dismissed" state of the alert begins as 'false' and is then set to 'true' when the user dismisses it.
Used Google Firebase’s Realtime Database to Store Personalized Favorites and Progress Objects
Users can keep track of their exercise goals and progress such as hours completed, sessions completed, and best streaks achieved.
- The number of seconds a user spends on any timed exercises are incremented, which are then converted to minutes and hours as the user progresses.
- The user's "Sessions Completed" tracker increments whenever a user finishes a timed exercise.
- The user's "Current Streak" and "Best Streak" are incremented whenever they start any exercise on the next calendar day. This is determined by comparing the last tracked date with a current
UI/UX Solution: Horizontal ScrollViews used for Custom Option Selectors
In order to set timers for breathing exercises, I implemented a horizontal options selector to choose timed intervals. My function renders an array of
TouchableOpacity's onto the screen and is hidden/revealed when the user presses the timer icon. Selecting a time option starts the timer and hides the options automatically.
Another horizontal ScrollView was used to set the timer for the Meditation Timer Screen.
I created a custom "scroll wheel" by gathering the measurements for the
contentSize.width, and the
layoutMeasurement.width and calculating the percentage of space used within the entire
This homemade "scroll wheel" includes haptic feedback to ensure the user is interacting with the adjuster.