~/Portfolio$Jude Mawad Selected Projects

[002] Project / IGNITE

IGNITE

IGNITE is a native iOS app for tracking nutrition, workouts, activity, hydration, supplements, weight, and long-term progress.

I built it with SwiftUI, Core Data, HealthKit, Supabase, and OpenFoodFacts.

Product render / iOSApp Store

[002]

The idea

Why I built IGNITE

The information I wanted to track every day was split between nutrition apps, Apple Health, workout apps, and notes.

I built IGNITE to keep nutrition, activity, hydration, workouts, supplements, weight, and goals in one place. The same daily data also feeds the app's progress views.

[003]

Local data

Most data stays on the device

Meals, nutrition, hydration, supplements, weight, goals, and progress are stored on the device with Core Data.

The app only goes online when it needs food search or barcode results, subscription data, or another external service.

Existing logs and progress remain available without a network connection.

01SwiftUIUI
02View ModelsScreen state
03Repositories / ServicesData access and external services
04Core Data · HealthKit · Remote APIsDevice storage, Apple Health, and online lookups
How data moves through the app

[004]

Food search

How food search works

Manual searches and barcode scans use the same lookup flow.

IGNITE checks Core Data first, then Supabase, and uses OpenFoodFacts when it still needs a match. Results are normalized and deduplicated before useful foods are saved locally.

INPUTSearch or barcode
FOOD SEARCHHybridFoodRepository
01 / FIRSTCore DataFoods stored on the device
02 / NEXTSupabaseApp food database
03 / IF NEEDEDOpenFoodFactsPublic food database
RESULTClean up resultsSave locally
Food search order and local caching
  1. 01

    Local first

    IGNITE checks Core Data before making a network request.

  2. 02

    Supabase

    If there is no local match, the app searches its Supabase food database.

  3. 03

    OpenFoodFacts

    OpenFoodFacts is used when Supabase does not return a useful match.

  4. 04

    Save locally

    Useful results can be stored in Core Data for future searches and barcode scans.

[005]

Barcode scanning

Scan a barcode and log it

IGNITE uses AVFoundation to detect barcodes with the iPhone camera.

A scanned code goes through the same food search as a manual query: Core Data first, followed by Supabase and OpenFoodFacts when needed. Once matched, the food can be logged and saved for later.

  1. 01AVFoundationCamera and barcode detection.
  2. 02Food searchUses the same food search as manual search.
  3. 03Save locallyMatched foods can be saved for future use.

[006]

Apple Health

HealthKit integration

IGNITE reads steps, active calories, weight, and workouts from Apple Health.

It can also write supported nutrition, hydration, weight, and workout data back to HealthKit.

HealthKit code lives in dedicated services rather than SwiftUI views. This keeps permissions, queries, writes, observers, and background updates in one place.

System dataApple Health
ApplicationIGNITE
Read from HealthKit
Steps, active calories, weight, and workouts appear alongside IGNITE's other daily data.
Write to HealthKit
Supported nutrition, hydration, weight, and workout data can be saved to Apple Health.
Background updates
Observers and background delivery keep supported health data current when it changes outside IGNITE.
Dedicated service
SwiftUI views call a HealthKit service instead of handling permissions and queries themselves.
IGNITE progress view showing weekly calories, protein, weight, and macro charts
Swift ChartsHistorical progress

[007]

Progress

See progress over time

Swift Charts turns daily tracking data into trends for nutrition, activity, hydration, and weight.

Users can switch between time periods to see changes beyond a single day.

Nutrition
Calorie intake, net calories, macros, and protein.
Activity
Steps and active energy across historical periods.
Hydration
Daily water intake against established targets.
Weight
Body-weight changes and progress toward a target.

[008]

Goals

One set of goals across the app

During onboarding, IGNITE uses profile information and a weight goal to calculate calorie and macro targets with the Mifflin–St Jeor equation.

It also creates hydration, step, active-calorie, and body-weight goals.

The same values are used in daily trackers and progress views instead of being configured separately in each part of the app.

INPUTBody data + weight objective
CALCULATIONMifflin–St Jeor + goal logic
OUTPUTGoals used across the appCalories · Macros · Hydration · Steps · Active calories · Body weight

[009]

Architecture

How IGNITE is structured

SwiftUI views handle presentation, while view models manage screen state.

Repositories handle data access. Services handle HealthKit, StoreKit 2, networking, and other external APIs.

Dependencies are created centrally and passed to the parts of the app that need them.

UISwiftUI ViewsInterface and navigation
STATEView ModelsScreen state and UI logic
DATA ACCESSRepositoriesCore Data, food search, and caching
SERVICESServicesHealthKit · Networking · StoreKit · System APIs
Core Data
HealthKit
Supabase
OpenFoodFacts
StoreKit 2
Dependencies are created in the application environment.

[010]

Concurrency

Keeping the UI responsive

IGNITE uses Swift concurrency for network requests, HealthKit reads and writes, StoreKit purchases, and other asynchronous work.

That work runs outside the main UI flow and updates the interface when results are ready.

If a remote food service fails, locally stored meals, tracking, and progress continue to work.

[011]

Core Data

Storing application data

Core Data stores foods, meals, supplements, hydration, weight, goals, and related tracking records.

I added migrations as the data model changed so app updates could preserve existing data instead of rebuilding the store.

  • Food
  • Meals
  • Supplements
  • Hydration
  • Weight
  • Goals
  • Related records

[012]

StoreKit 2

Subscriptions and premium access

StoreKit 2 loads products, verifies purchases, restores previous purchases, listens for transaction updates, and controls access to premium features.

The entitlement state is managed in one place and shared with the views that need it.

ProductsVerified purchasesEntitlement statePremium features

[013]

Localization

Four supported languages

IGNITE supports English, German, Spanish, and French.

Language selection and locale handling are managed centrally.

Supporting four languages also affected the layout because translated labels and navigation text can be much longer than English.

  1. 01EnglishEN
  2. 02DeutschDE
  3. 03EspañolES
  4. 04FrançaisFR

[014]

Testing

Testing the logic behind the UI

I mainly use XCTest for the parts of the app that carry business and data logic.

Tests cover Core Data repositories, food search and fallback behavior, caching, pagination, deduplication, network failures, translations, and view models.

In-memory Core Data stores and test services make those tests independent from production data and live APIs.

[015]

Tech stack

Tools and frameworks

Language
Swift
UI
SwiftUI · UIKit
Storage
Core Data · UserDefaults
Apple frameworks
HealthKit · StoreKit 2 · AVFoundation · Swift Charts
Networking
URLSession · REST
Data
Supabase · OpenFoodFacts
Architecture
MVVM · Repositories · Services · Dependency Injection
Testing
XCTest

[016]

Reflection

What I learned building IGNITE

IGNITE started as a relatively simple tracking app. As I added HealthKit, remote food data, subscriptions, and more local data, the difficult part became managing how those systems worked together.

I had to decide where each piece of data should come from, where it should be stored, and what the app should do when one source was unavailable.

That pushed me to separate SwiftUI views from persistence, networking, and platform integrations instead of putting everything directly inside the interface.

The main lesson was how much architecture starts to matter once an app grows beyond its first few features.