Selected Work — 2025

Dishcover iOS App

A restaurant discovery app for Australian food lovers. From first screen to App Store approval — built fast, built right, built to last.

Platform
iOS
Outcome
Live on App Store
Authentication · Apple Sign In · Google OAuth · Firebase · Google Places API · React Native · Expo · AsyncStorage · Bayesian Ranking · EAS Builds · TestFlight · App Store Connect · Authentication · Apple Sign In · Google OAuth · Firebase · Google Places API · React Native · Expo · AsyncStorage · Bayesian Ranking · EAS Builds · TestFlight · App Store Connect ·
01

Auth & Sign In

Apple Sign In is mandatory for App Store submission when social login is offered. A dedicated Firebase iOS app registration was required — the web app's audience token doesn't satisfy Apple's native auth check. Google OAuth sits alongside it. Session persistence keeps users logged in across restarts.

Firebase Auth Apple Sign In Google OAuth Session Persistence
Welcome screen
Splash screen
02

User Journey

The home feed surfaces curated collections by time of day. Tapping into any collection shows real restaurants with photos, ratings, and price — in list or map view. A restaurant detail page shows the full picture: reviews, photos, hours, and a direct share button to send it to friends.

Home Feed Collections Restaurant Detail Sharing Saved Places
Collections Time of Day
Home feed restaurants
Toko Restaurant detail
Restaurant photos
03

Tech Aspects

Built on Google Places API, Firebase Firestore, and a Bayesian ranking engine. Two-layer caching cuts API cost by 90%. Geofenced to Sydney, Melbourne and Brisbane. Native iOS sharing built in.

Google Places API Bayesian Ranking Firestore Two-layer Cache Native Share
≡ List
⊕ Map
Aria
★ 4.5
Toko
★ 4.4
Quay
★ 4.7
China Doll
★ 4.5
Rockpool
★ 4.4
Quay Restaurant
Fine Dining · $$$$
4.7 Open now 0.4 km
🔍 Explore
⊞ Collections
👤 Profile
01
Map View
Every collection can be viewed in list or map mode. Pins show the restaurant name and live rating. Tapping a pin surfaces a card — photo, price, distance, and open status — matching exactly what the app shows.
50+ restaurants pinned per search
02
No Ads.
Pure Algorithm.
Most apps surface paid placements first. Dishcover doesn't. A Bayesian ranking algorithm orders every result purely on merit — review volume weighted against score. A new spot with 5★ from 3 friends never outranks a trusted venue with 4.7★ from 2,000 genuine reviews. You always see what's actually best, not who paid most.
0 paid placements — ever
Other apps
AD
Sponsored Restaurant
#1
AD
Promoted Listing
#2
Dishcover — Bayesian ranking
Score = (C×m + R×v) ÷ (v+m)
New spot · 5.0★ · 3 reviews
3.8
Toko · 4.4★ · 1,217 reviews
4.39
Quay · 4.7★ · 2,100 reviews
4.69
C = global mean · m = min threshold · R = restaurant avg · v = review count
Toko Restaurant
🍴
Toko Restaurant
dishcover.app
J
Jack's iPhone
S
Sarah's Mac
Messages
WhatsApp
Instagram
Facebook
Copy Link
Copy
More...
03
Native Sharing
The share button on any restaurant page triggers the iOS native share sheet — exactly as shown. Users can send a restaurant directly to friends via Messages, WhatsApp, Instagram, or any installed app. No custom share flow needed — it uses the OS.
1 tap to share any restaurant
04
Google Places API & Data Storage
Every restaurant card is powered by Google Places — real photos, live ratings, opening hours, price range, and phone numbers. Data is layered: Firestore syncs user profiles and saved places in real time, while AsyncStorage caches feeds for 6 hours and restaurant details for 7 days. The result: 90% fewer API calls on return visits and an app that loads instantly even on slow networks.
90% fewer API calls via two-layer cache
placeService.ts
1import { getFromCache, setCache } from './cache';
2import { firestore } from './firebase';
3 
4export async function getPlaceDetails(placeId: string) {
5  // 1. Check AsyncStorage cache (7-day TTL)
6  const cached = await getFromCache(`place:${placeId}`);
7  if (cached) return cached; // 0 API calls
8 
9  // 2. Fetch Google Places API
10  const res = await fetch(`/place/details?place_id=${placeId}&fields=name,rating,photos,opening_hours,price_level`);
11  const data = await res.json();
12 
13  // 3. Cache result + sync user saves to Firestore
14  await setCache(`place:${placeId}`, data, 604800); // 7 days
15  await firestore.collection('recentlyViewed').add({ placeId, ts: Date.now() });
16  return data;
17}
Google Places response CACHED
{
  name: "Toko Restaurant", rating: 4.4, price_level: 3,
  photos: [10 images], open_now: true, user_ratings_total: 1217
}
04

Key Details

Native iOS binary compiled in the cloud, distributed via TestFlight, and shipped to the App Store — all in a single sprint. Zero rejections from Apple review.

EAS Build TestFlight App Store Connect Expo Vercel
01
EAS Cloud Build
Expo Application Services compiles a native iOS binary entirely in the cloud — no Xcode, no Mac build server required. The project is pushed to Expo's infrastructure, dependencies are installed, and a signed .ipa is produced ready for TestFlight distribution. Every build is reproducible from the same commit.
1
Configureeas.json defines the build profile (simulator, internal, production). Bundle ID and Apple Team ID are set once.
2
Pusheas build --platform ios uploads the project archive to Expo's build queue.
3
Compile — Expo installs CocoaPods, compiles Swift/ObjC, signs with the provisioning profile, and produces a .ipa.
4
Download — A signed build artefact is available to download or submit directly to TestFlight.
0 local Xcode builds required
Terminal — eas build
$ eas build --platform ios --profile production
✔ Expo account logged in
✔ Project configuration validated
✔ Credentials verified (Apple Developer)
Build queued — iOS · production
  Build ID: a4f2c8e1-…
  Queue position: 1 of 1
Installing pods (CocoaPods 1.14.3)…
  Running pod install in /ios…
Compiling native modules…
Code signing with provisioning profile…
  Team: RUVO STUDIO PTY LTD (ABC123XYZ)
✔ Build successful — dishcovermobile.ipa (47 MB)
✔ Uploaded to TestFlight automatically
 
Build time: 6m 42s
02
App Store Submission
Apple's review process requires a complete submission package before any build goes public. Screenshots, metadata, a privacy policy URL, entitlement declarations, and correct capability configuration all need to be in order. Getting it right the first time — no rejections — requires attention to every detail.
1
Privacy Policy — Deployed to Vercel (dishcover.app/privacy). Required for any app that uses Sign In with Apple or collects location data.
2
Screenshots — 6.7" screenshots at 1320×2868px capturing every key screen. Required in App Store Connect before submission.
3
Entitlements — Sign In with Apple, location services (NSLocationWhenInUseUsageDescription), and photo library access declared in app.json.
4
Review — Submitted via App Store Connect. Apple review completed within 24 hours. Approved on the first submission — no rejections.
1st submission — approved immediately
App Store Connect checklist
App information & metadata
Name, subtitle, keywords, description
Screenshots — 6.7" (1320×2868)
Home, Collections, Restaurant, Auth
Privacy Policy URL
dishcover.app/privacy — hosted on Vercel
Sign In with Apple entitlement
Required when any social login is offered
Location usage description
NSLocationWhenInUseUsageDescription set
Age rating declared
4+ — no restricted content
Binary uploaded via EAS Submit
eas submit --platform ios --latest
Apple Review — Approved
0 rejections · reviewed in <24 hours
03
Shipped to Production
Production release followed a staged rollout — internal TestFlight testing, then a phased App Store release. The app is live on the Australian App Store, geofenced to Sydney, Melbourne and Brisbane, with real-time Firestore sync and a CDN-cached backend that handles load without additional infrastructure.
1
TestFlight internal — Build distributed to internal testers. UI, auth flow, and data accuracy verified across devices.
2
Production EAS buildautoIncrement: true in eas.json bumps the build number automatically. Signed with distribution certificate.
3
App Store release — Promoted from TestFlight to public release via App Store Connect. Phased rollout enabled.
4
Live — Available on the Australian App Store. Firestore, Google Places, and Vercel-hosted privacy policy all running in production.
Live on Australian App Store
💻
Local Dev
Expo Go · Hot reload
☁️
EAS Build
Cloud compile · Signed .ipa
🧪
TestFlight
Internal testing · QA
🍎
App Store
Live · AU · v1.0
dishcover
Restaurant Discovery
● Live
★★★★★
App Store · AU

Shipped — 2025

4
Stages from concept to App Store — auth, journey, tech, launch
90%
Reduction in Places API calls through two-layer caching
10k+
Restaurants ranked using Bayesian scoring across 3 cities
0
App Store rejections — approved and live on first submission
dish
cover

From first screen
to App Store
in one sprint.

Work with Ruvo Studio →
Ruvo Studio · 2025
dishcover.app