AI-powered place discovery and trip planning app for iOS.
Save places from links shared from Instagram, Threads, Xiaohongshu, Maps, or the web — SAV-E extracts place details and pins them on your personal map. Plan trips, optimize routes, and track your adventures.
- Map View — MapKit with custom category-colored pins, clustering, and bottom sheet details
- Place List — Filterable (Want to Go / Visited / All), sortable (Nearest / Recent / Rating), swipe actions
- Trip Planner — City selector, timeline view, drag-to-reorder, AI route optimization
- Share Extension — Accept URLs and text from other apps, parse, and save to map
- Profile — Stats, world map visualization, collections, subscription management
- Onboarding — 3-step carousel
- Place Detail — Photo carousel, info grid, notes, navigate button, source link
- App Clip — Lightweight SAV-E previews for place links (
https://sav-e-app.vercel.app/p/:id) and trip links (https://sav-e-app.vercel.app/trip/:id)
- SwiftUI + MapKit for UI
- Privy iOS SDK for auth (Sign in with Apple / Google / Email + embedded wallet)
- Railway Node API + Railway Postgres for backend persistence
- Gemini API for AI content parsing
- Google Places API for place matching and details
- App Clip target for shareable trip links, including Associated Domains and full-app handoff
- Share Extension target for cross-app saving
-
Clone the repository:
git clone https://github.com/JhiNResH/SAV-E.git cd SAV-E -
Bootstrap local secrets:
cp -n SAV-E/Resources/Secrets.plist.template SAV-E/Resources/Secrets.plist cp -n SAV-EShareExtension/Secrets.plist.template SAV-EShareExtension/Secrets.plist
Xcode also creates these local files from the templates during build if they are missing. It does not overwrite existing local
Secrets.plistfiles; when templates change, compare them manually and add any new keys to your local files. Fill in your local API keys inSAV-E/Resources/Secrets.plistandSAV-EShareExtension/Secrets.plist:GEMINI_API_KEY— from Google AI StudioGOOGLE_PLACES_API_KEY— from Google Cloud ConsoleSAVE_API_URL— Railway backend service URL, currentlyhttps://wanderly-api-production.up.railway.appSAVE_PLACE_SHARE_BASE_URL— production place share route, currentlyhttps://sav-e-app.vercel.app/pSAVE_TRIP_SHARE_BASE_URL— production trip share route, currentlyhttps://sav-e-app.vercel.app/tripSAVE_SHARE_BASE_URL— legacy trip share route fallback, currentlyhttps://sav-e-app.vercel.app/tripSAVE_LIST_SHARE_BASE_URL— production collaborative list share route, currentlyhttps://sav-e-app.vercel.app/listPRIVY_APP_ID— from Privy Dashboard → App Settings → BasicsPRIVY_APP_CLIENT_ID— from Privy Dashboard → App Settings → Clients. The iOS app client must allow bundle idcom.wanderly.appand URL schemewanderly.
New production config should use the
SAVE_*keys above. The app still reads legacyWANDERLY_*keys as a migration fallback when present in an older localSecrets.plist, but the templates and release docs are SAV-E-first.- Keep real values out of commits.
-
Configure the Railway backend:
cd backend npm install npm run buildRailway service variables:
DATABASE_URL=${{Postgres.DATABASE_URL}} PRIVY_APP_ID=... PRIVY_VERIFICATION_KEY='-----BEGIN PUBLIC KEY-----...'
Apply the schema to Railway Postgres:
psql "$DATABASE_URL" -f sql/schema.sql -
Generate the Xcode project:
xcodegen generate
-
Open the project in Xcode:
open SAV-E.xcodeproj
-
Build and run on simulator or device. Simulator builds do not need signing; device builds and archives require either passing
APPLE_TEAM_IDthrough the CLI or selecting your Apple Developer Team in Xcode locally.
Set APPLE_TEAM_ID to the 10-character Apple Developer Team ID for the account that owns the App IDs. XcodeGen passes it into all iOS targets as DEVELOPMENT_TEAM.
export APPLE_TEAM_ID=ABCDE12345
xcodegen generate
xcodebuild \
-project SAV-E.xcodeproj \
-scheme SAV-E \
-configuration Release \
-destination 'generic/platform=iOS' \
-archivePath "$PWD/build/SAV-E.xcarchive" \
-allowProvisioningUpdates \
APPLE_TEAM_ID="$APPLE_TEAM_ID" \
archivePrepare an App Store Connect upload options plist. The generated options are external-TestFlight ready by default. Set TESTFLIGHT_SCOPE=internal only when producing an internal-only review build.
APPLE_TEAM_ID="$APPLE_TEAM_ID" scripts/prepare-testflight-export-options.sh
TESTFLIGHT_SCOPE=internal APPLE_TEAM_ID="$APPLE_TEAM_ID" scripts/prepare-testflight-export-options.sh build/ExportOptions.TestFlight.Internal.plistUpload the archive to App Store Connect for TestFlight processing:
xcodebuild \
-exportArchive \
-archivePath "$PWD/build/SAV-E.xcarchive" \
-exportPath "$PWD/build/TestFlightUpload" \
-exportOptionsPlist "$PWD/build/ExportOptions.TestFlight.plist" \
-allowProvisioningUpdates \
APPLE_TEAM_ID="$APPLE_TEAM_ID"Ship the native iOS app, Share Extension, and App Clip together. App Clip readiness requires Apple Developer and App Store Connect setup in addition to the repo configuration:
Before uploading a build:
- register App IDs for
com.wanderly.app,com.wanderly.app.ShareExtension, andcom.wanderly.app.Clip - enable the App Group
group.com.wanderly.appfor the app and Share Extension - enable Associated Domains on
com.wanderly.appandcom.wanderly.app.Clip - enable the App Clip association capability on
com.wanderly.app
These Apple identifiers are the existing production compatibility layer. The user-facing product, Xcode targets, release config keys, and share URLs should use SAV-E / SAVE naming.
- configure signing team/profiles in Xcode or release xcconfig
- confirm the App Store icon and privacy manifest are included
- keep real API keys out of commits and restrict bundled keys where provider dashboards allow it
- create App Clip Experiences in App Store Connect for
https://sav-e-app.vercel.app/p,https://sav-e-app.vercel.app/trip,https://sav-e-app.vercel.app/list,https://sav-e-app.vercel.app/r, andhttps://sav-e-app.vercel.app/u
SAV-E separates share actions from map actions:
- Share = SAV-E link
- Maps = Apple Maps link
SAV-E place links use this shape:
https://sav-e-app.vercel.app/p/<base64-url-encoded SharedPlaceData JSON>
SAV-E trip links use this shape:
https://sav-e-app.vercel.app/trip/<base64-url-encoded SharedTripData JSON>
SAV-E collaborative list and referral links use these shapes:
https://sav-e-app.vercel.app/list?d=<base64 SharedListPayload JSON>&r=<viewer|editor>
https://sav-e-app.vercel.app/r/<code>
https://sav-e-app.vercel.app/u/<handle>?ref=<code>
The App Clip target can preview place payloads with photo, rating, hours, address, source, and save/open actions. It can preview trip payloads with stops, route summary, copy summary, and import/open actions. The full app handles the same https://sav-e-app.vercel.app/p/... and https://sav-e-app.vercel.app/trip/... universal links when installed. Legacy https://wanderly.app/trip?d=... links remain readable during migration.
Before this works for friends without the full app installed:
- enable Associated Domains on
com.wanderly.appandcom.wanderly.app.Clipin Apple Developer - keep
applinks:sav-e-app.vercel.appandappclips:sav-e-app.vercel.appin the app entitlement - keep
appclips:sav-e-app.vercel.appin the App Clip entitlement - set
APPLE_TEAM_IDin the Vercel build environment sonpm run export:webwrites the real/.well-known/apple-app-site-association - disable bot challenges/WAF rules for
https://sav-e-app.vercel.app/p*,https://sav-e-app.vercel.app/trip*, andhttps://sav-e-app.vercel.app/.well-known/apple-app-site-association; iOS cannot complete App Clip or Universal Link association through an HTML challenge page - create App Clip Experiences in App Store Connect for
https://sav-e-app.vercel.app/p,https://sav-e-app.vercel.app/trip,https://sav-e-app.vercel.app/list,https://sav-e-app.vercel.app/r, andhttps://sav-e-app.vercel.app/u - wait for Apple's associated-domain CDN to pick up the AASA file
Without those Apple/domain steps, the same URL still opens the web app, but iOS will not invoke the App Clip. If APPLE_TEAM_ID is missing, the web build writes a disabled AASA placeholder with no app IDs so Vercel does not serve the SPA shell as Apple association data.
SAV-E referral links use these shapes:
https://sav-e-app.vercel.app/r/<code>
https://sav-e-app.vercel.app/u/<handle>?ref=<code>
The App Clip preview shows the referrer's profile, a starter map pack, and a follow CTA. The handoff opens the full app through wanderly://referral?code=<code>&handle=<handle>&lens=friends, where the full app stores the referrer and intended follow lens before completing follow after install/open.
Before production referral App Clips work, sav-e-app.vercel.app needs the same Apple associated-domain and App Clip Experience setup as wanderly.app.
SAV-E/
├── App/ Main app entry + tab-based root
├── Views/
│ ├── Map/ Map view with annotations
│ ├── List/ Filterable place list with cards
│ ├── Trips/ Trip planner with timeline
│ ├── Profile/ User profile and stats
│ ├── Detail/ Place detail view
│ ├── Onboarding/ 3-step onboarding carousel
│ └── Shared/ Reusable components
├── Models/ Data models (Place, Trip, UserProfile)
├── ViewModels/ MVVM view models
├── Services/ API service protocols + stubs
├── Extensions/ Color theme + utilities
└── Resources/ Assets
SAV-EShareExtension/ Share Extension target
SAV-EClip/ App Clip target
backend/ Railway API + Postgres schema
| Token | Light | Dark |
|---|---|---|
| Background | #FFF8F0 (Cream) | #1C1C1E (Charcoal) |
| Accent | #C75B39 (Terracotta) | #E8A87C (Amber) |
| Secondary | #A8B5A0 (Sage) | #A8B5A0 (Sage) |
| Text | #2C2C2E (Charcoal) | #FFFFFF |
| Corner Radius | 16px | 16px |
| Font | SF Pro (system) | SF Pro (system) |
- privy-io/privy-ios — Authentication
- Railway — Backend hosting + Postgres
- Google Places API — REST via URLSession
- Gemini API — REST via URLSession
Private — All rights reserved.