SJ

{name}

Memoir

Deep Breathing & Mindfulness on iOS and Android

"A language-independent meditation app to help ease the world's anxiety"

Live | GitHub

{name}

My Role:

Lead Developer / Cofounder

Team Size: Two

Myself + 1 Product Designer

User Testing:

5.9 Million views on Tiktok
158,000 followers

Download:

iOS App Store

~1,000 downloads

Technologies Used:

  • React Native
  • Expo
  • JavaScript
  • CSS
  • Figma
  • Google Firebase
  • Apple Developer Program
  • Sign In With Facebook

TikTok Marketing

Viral TikTok “duet“ trend

Memoir Features

  • 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.
  • Personalized user progress tracking add to the user experience and help to build better habits.
memoir exercises
Variety of Breathing Exercises Content
Home Screen
Breathing Exercise Player
Setting a Timer
Favorites Library
Delayed Signup Prompts
Animated User Stats

User Journey

{name}

Guiding Customers to Create an Account

We delayed the account creation process to allow the customer to explore the app before committing to signing up.

We designed the app to allow customers to be able to try out the free exercises as they please, and would be prompted to create an account when visiting the favorites or profile pages.

This kept an incentive for the customer to create an account while reducing the chance of them leaving the app due to an early account creation request.

{name}

Challenges Faced:

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 image and videoFile assets are passed down as props to the individual Exercise components.
  • 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.
  • cacheAsset is an async function, 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 cachedImage is used to render the exercise image. When you press on that image, it navigates to the ExerciseVideo screen along with the cachedVideo passed down as a route.param.

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 MaskedView component from Expo to create a mask for the bar to hide behind.
  • Whenever this specific screen was focused by the user, a setTimeout triggers the transform animation using Animatable.
  • 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.
Animated User Stats

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.
Congratulations Alert!

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 new Date() Object.
memoir real time database google firebaserental property calculator mapbox geocoding address to coordinates feature
Favorites and progress correspond to each user‘s unique user ID

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 contentOffset.x, the contentSize.width, and the layoutMeasurement.width and calculating the percentage of space used within the entire ScrollView.

This homemade "scroll wheel" includes haptic feedback to ensure the user is interacting with the adjuster.

Horizontal 'Wheel' to Set Timer
Memoir Splash intro screen
Thanks for visiting!