Community-powered Β· Free forever Β· Built for Bangladesh
Finding a clean public washroom in Dhaka is hard.
Dead apps, outdated listings, zero community input.
Loo fixes that β open-source, OSM-powered, built by the community for the city.
| Feature | Description |
|---|---|
| πΊ Live OSM Map | OpenStreetMap tiles via MapLibre β no Google, no fees, always up to date |
| π§ Liquid Glass UI | iOS 26 floating Liquid Glass top bar β search, filter, profile pills over the map |
| π Locate Me | Tap to recenter on yourself anytime; auto-center on first GPS fix |
| π§ Compass Finder | Real-time rotating arrow guides you turn-by-turn with haptic pulses as you get closer |
| π Nearby Sheet | 5 closest washrooms sorted by live GPS distance, updating as you move |
| πΊ Women-Friendly Badge | At-a-glance pink/grey pill on every listing β no more guessing |
| π Hijra-Inclusive | Bangladesh's third gender is a first-class category, with its own marker color |
| π Wudu Area | Mosques expose whether ritual ablution facilities are available |
| πΆ Baby Changing & Menstrual Products | Surfaced as filterable amenities, not buried metadata |
| β Freshness Signal | "Verified recently / this month / old / unverified" pill β community trust at a glance |
| β° Open Now | Live parse of opening hours (24/7 or HH:mm-HH:mm) β filter and chip in detail |
| π§Ό Cleanliness Score | Separate sub-rating from overall rating, color-coded |
| π Real Filters | Gender, women-friendly, accessible, free, open-now, amenities β actually applied to map + list |
| β Submit a Washroom | Crowdsource new locations with map preview |
| π Auth | Phone OTP sign-in via Supabase β no email required (backend pending) |
| Welcome | Built for everyone | Permission priming |
|---|---|---|
| First-launch permission prompt | Glass pills + locate-me + nearby sheet |
|---|---|
| Women-friendly Β· Freshness Β· Open-now Β· Cleanliness | Turn-by-turn compass with haptics |
|---|---|
| Gender Β· Inclusivity Β· Accessibility | Amenities Β· Min rating |
|---|---|
| Pin a new washroom | Accessibility Β· Fee Β· Notes | Sign in (demo) | Phone OTP (demo) |
|---|---|---|---|
graph TD
A([App Launch]) --> B[looApp]
B --> C[ContentView]
C --> D[NavigationStack\nAppRouter]
D --> E[πΊ MapView]
D --> F[π DetailView]
D --> G[π§ FinderView]
D --> H[π€ ProfileView]
E --> E1[OSMMapView\nMapLibre]
E --> E2[NearbySheet]
E --> E3[TopBar Search + Filter]
E --> E4[SubmitView]
F --> F1[PhotoGallery]
F --> G
G --> G1[ArrowView\nCompass]
G --> G2[FinderViewModel\nHeading + GPS]
subgraph Core Services
S1[LocationService\nGPS + Compass]
S2[SwiftData\nLocal Cache]
S3[Supabase\nBackend]
end
E1 -.->|washrooms| S2
G2 -.->|location + heading| S1
E4 -.->|submit| S3
style A fill:#00A86B,color:#fff
style Core Services fill:#f0f0f0
sequenceDiagram
participant U as User
participant MV as MapView
participant LS as LocationService
participant SD as SwiftData
participant SB as Supabase
U->>MV: App opens
MV->>LS: requestPermission()
LS-->>MV: location + heading stream
MV->>SD: SeedData.injectIfNeeded()
SD-->>MV: [Washroom] array
MV->>MV: Sort by real GPS distance
MV-->>U: Map + NearbySheet rendered
U->>MV: Tap washroom marker
MV->>U: Navigate β DetailView
U->>U: Tap "Find It"
U->>MV: Navigate β FinderView
MV->>LS: poll heading @ 10Hz
LS-->>MV: arrowRotation updated
MV-->>U: Rotating compass arrow
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SwiftUI Views β
β MapView Β· DetailView Β· FinderView Β· SubmitView β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β ViewModels / State β
β MapViewModel Β· FinderViewModel Β· AppRouter β
ββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ€
β Core Services β Repositories β
β LocationService β WashroomRepository β
β HeadingService β SubmissionRepository β
β Geo Β· Formatting β AuthRepository β
ββββββββββββββββββββββββΌββββββββββββββββββββββββββββββ€
β SwiftData (Local) β Supabase (Remote) β
β Washroom @Model β PostgreSQL + Auth + CDN β
ββββββββββββββββββββββββ΄ββββββββββββββββββββββββββββββ€
β MapLibre GL Native β
β OpenFreeMap OSM Vector Tiles β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββ
The Finder screen gives you a real-time pointing arrow β no map needed, just walk.
arrowRotation = bearing(userLocation β target) [radians, true north]
β deviceHeading [radians, from CLHeading]
graph LR
GPS[GPS Fix\nCLLocation] --> BRG[Haversine\nBearing]
CMP[Compass\nCLHeading.trueHeading] --> LPF[Low-pass Filter\nΞ± = 0.15]
LPF --> HDG[Smoothed\nHeading Β°βrad]
BRG --> SUB[β]
HDG --> SUB
SUB --> ROT[rotationEffect\nArrowView]
ROT --> HAP[Haptic\nevery 10m < 100m]
- Single CLLocationManager handles both GPS and compass β
trueHeadingis accurate because the same manager has location context for magnetic declination - Low-pass filter smooths compass jitter without introducing noticeable lag
- Haptic feedback fires every 10 m bucket when within 100 m of the target
| Google Maps | Apple Maps | MapLibre + OpenFreeMap | |
|---|---|---|---|
| Cost | π° Pay per load | Free (limited) | β Free forever |
| Bangladesh coverage | Moderate | Poor | β Excellent (HOT + OSM) |
| Offline support | No | Limited | β Yes |
| Open source | No | No | β Yes |
| Custom styling | Paid | No | β Yes |
| API key required | Yes | No | β No |
- Xcode 26+
- iOS 26+ device (Liquid Glass UI; map tiles + compass need real hardware)
- Swift 6
# 1. Clone git clone https://github.com/siraajul/Loo.git cd Loo # 2. Open in Xcode β MapLibre SPM resolves automatically open loo.xcodeproj
3. Add location permission in the target's Build Settings:
INFOPLIST_KEY_NSLocationWhenInUseUsageDescription = Used to show washrooms near your location.
4. (Optional) Supabase backend β add your credentials to:
// loo/Core/Network/SupabaseClient.swift let supabaseURL = URL(string: "https://your-project.supabase.co")! let supabaseKey = "your-anon-key"
5. Run on device β the app seeds 10 real Dhaka washrooms on first launch so it works immediately, even without a backend.
The app ships with 12 Dhaka washrooms so it's usable from day one β each with realistic hours, freshness dates, and amenity flags:
| Washroom | Type | Gender | Fee | Highlights |
|---|---|---|---|---|
| Bashundhara City Mall | πͺ Mall | Mixed | Free | πΆ Baby Β· π©Έ Menstrual Β· β¨ 4.3 |
| Gulshan-1 DCC Market | π» Public | Mixed | ΰ§³5 | Verification aging |
| Jamuna Future Park | πͺ Mall | Mixed | Free | πΆ Baby Β· π©Έ Menstrual Β· β¨ 4.6 |
| Baitul Mukarram Mosque | π Mosque | Male | Free | π Wudu area |
| Dhanmondi Lake Park | π» Public | Mixed | ΰ§³2 | Verification stale |
| Panthapath Petrol Pump | β½ Petrol Pump | Mixed | Free | π 24/7 |
| Square Hospital | π₯ Hospital | Mixed | Free | π 24/7 Β· πΆ Baby |
| Star Kabab Restaurant | π΄ Restaurant | Mixed | Free | π Late night |
| Motijheel Shapla Chatter | π» Public | Male | ΰ§³3 | Verification stale |
| Uttara Sector-3 Park | π» Public | Mixed | Free | β |
| Banani DCC Trans-Inclusive Stop | π» Public | π Hijra | Free | Bandhu-affiliated |
| Lalbagh Fort Family Restroom | π» Public | π¨βπ©βπ§ Family | ΰ§³10 | πΆ Baby changing |
| Layer | Technology |
|---|---|
| UI | SwiftUI 5 |
| Map | MapLibre GL Native + OpenFreeMap (OSM vector tiles) |
| Local DB | SwiftData |
| Backend | Supabase (PostgreSQL + Auth + Storage) |
| Location | CoreLocation β GPS + compass heading |
| State | @Observable macro (iOS 17) |
| Navigation | NavigationStack + typed route enum |
| Language | Swift 5.9 |
- OSM map with MapLibre GL Native
- GPS blue dot + auto-center on user
- Locate-me FAB (Liquid Glass) for re-centering
- Custom washroom markers (color-coded by gender/type)
- Nearby sheet sorted by real GPS distance
- Compass Finder with smooth arrow + haptics
- Submit a washroom form with map preview
- 12 seed washrooms (incl. hijra + family) for offline-first experience
- iOS 26 Liquid Glass top bar (search, filter, profile, locate-me)
- Three-page onboarding with permission priming and inclusivity showcase
- Women-friendly badge (pink/grey pill at-a-glance)
- Hijra (third-gender) category with own marker color
- Wudu area attribute on mosques
- Baby changing + menstrual products amenities
- Cleanliness sub-rating (separate from overall)
- Freshness signal ("Verified recently / aging / stale / unverified")
- Open-now status β parses
24/7andHH:mm-HH:mmwindows - Filter sheet wired to map + nearby list (was decorative before)
- Search bar filters by name / Bangla name
- Open in Maps deep-link from detail view
- Live Supabase sync β fetch real washroom data from backend
- Submit washroom β Supabase review queue (currently no-ops)
- Star ratings + written reviews
- Photo upload via Supabase Storage (gallery currently always empty)
- Moderator approval flow
- Real "Open in Maps" deep-link from DetailView (currently dead)
- Phone OTP auth via Supabase (currently stubbed)
- Full-text search across washroom names and areas (search bar currently decorative)
- Search suggestions as you type
- Sort by rating, distance, or price
- Cluster markers at low zoom levels
- Offline tile caching (MapLibre offline packs)
- Push notifications for nearby new submissions
- Expand to Chittagong and Sylhet
- Android version (React Native or Kotlin Multiplatform)
- Import from OpenStreetMap
amenity=toiletstag
- App icon + launch screen
- Bangla language support (বাΰ¦ΰ¦²ΰ¦Ύ UI)
- Accessibility β VoiceOver labels on all map markers
- TestFlight public beta
Dhaka has thousands of washrooms not yet on the map. This is a community project β every contribution matters.
| How | What |
|---|---|
| πΊ Add washrooms | Open an Issue with the name, location, and details of a real Dhaka washroom |
| π Report bugs | Open an Issue with steps to reproduce |
| π‘ Suggest features | Start a Discussion |
| π§ Write code | Pick any open Issue and submit a PR |
| πΈ Add photos | Once photo upload is live, document real washrooms |
| π Translate | Help with Bangla UI strings |
# 1. Fork the repo on GitHub # 2. Clone your fork git clone https://github.com/YOUR_USERNAME/Loo.git # 3. Create a branch git checkout -b feature/your-feature-name # 4. Make your changes, then commit git commit -m "feat: add your feature" # 5. Push and open a PR against siraajul/Loo main git push origin feature/your-feature-name
- SwiftUI views in
Features/<FeatureName>/ - New data fields go on the
WashroomSwiftData model - Follow existing naming:
PascalCasetypes,camelCaseproperties - No force unwraps β use
guard letorif let - No comments explaining what code does β only why if non-obvious
Look for issues tagged good first issue β these are intentionally scoped and well-described.
MIT Β© siraajul
You're free to use, modify, and distribute this code. If you build something with it, a shoutout or star β is always appreciated.
β Star this repo if Loo helped you find a clean washroom in Dhaka
Report Bug Β· Request Feature Β· Submit a Washroom
Built with β€οΈ for Dhaka Β· Powered by OpenStreetMap contributors