Stamped! — A City Passport

A SwiftUI travel discovery app that gamifies architecture exploration with passport stamps, city mastery, and AI-curated itineraries.

Demo Video

Demo video: Stamped iPad walkthrough.

Problem

Most travel apps optimize for logistics but do little to help users understand the places they visit. I wanted to solve this by turning architecture discovery into a clear learning loop instead of a passive checklist.

Project Snapshot

  • Platform: iOS (SwiftUI)
  • Type: Educational travel companion
  • Focus: Gamified learning, accessibility, offline-first content
  • Team: Solo
  • Role: Product designer and iOS engineer
  • Timeline: November 2025 - February 2026

Role

Solo product designer and iOS engineer responsible for concept definition, interaction design, architecture, implementation, and release preparation.

My Contributions

  • Designed and implemented the end-to-end product flow from onboarding through mastery progression
  • Built modular SwiftUI feature surfaces across city exploration, passport tracking, quiz interactions, and settings
  • Implemented shared state/persistence managers for progress, media capture, and user preferences
  • Integrated Apple Intelligence itinerary generation with robust fallback behavior for unsupported environments
  • Shipped accessibility-focused UX including high contrast, reduced motion, audio/haptic controls, and dark mode support

Constraints

Deliver a feature-rich app in a 14-week timeline while keeping the experience intuitive for first-time users and robust across iPhone and iPad form factors.

Key Decisions

  • Designed a modular SwiftUI architecture with feature-based folders (City List, Detail, Passport, Quiz, Itinerary, Settings)
  • Used shared observable managers for progress, haptics, sound, speech, and navigation state
  • Implemented a progression loop: visit landmarks → complete city → trigger celebration → persist stamp history
  • Built with local data registries and persistence so core flows remain usable without network dependency

Core Features

  • Global city explorer with hierarchical grouping (continent → country → city) and mixed search (cities, landmarks, architects)
  • Passport gallery with mastery tiers, completion tracking, and celebratory stamp animations
  • City detail module with landmark check-ins, custom photo capture/upload, and local travel information
  • Interactive quiz mode with multiple question types, hints, high-score persistence, streak logic, voice input, haptics, and audio feedback
  • AI-curated itinerary cards with Apple Intelligence integration and graceful fallback content when unavailable
  • Offline currency conversion support using embedded rate mappings for travel context

Accessibility Decisions

  • System + manual support for high contrast and reduced motion, applied across major flows
  • Full dark mode compatibility across app surfaces, interactive controls, and readability states
  • Voice and screen-reader feedback in quiz interactions (including spoken correctness announcements)
  • Configurable sensory settings (sound, haptics, motion) in-app rather than hard-coded defaults
  • Adaptive UI behavior between iPhone and iPad, including orientation and split-view patterns

Technical Highlights

  • State persistence with AppStorage/UserDefaults and disk-backed image storage for user-captured landmark photos
  • Single source of truth for visited landmarks through a shared global progress manager
  • Conditional AI path: FoundationModels session on supported OS versions with deterministic fallback generation
  • Custom app icon system built in Icon Composer with Light, Dark, Clear, and Tinted variants
  • Feature toggles and reset controls for testability and content maintenance

Code Highlights

Selected snippets from my Stamped Swift source showing AI fallback handling, persistence, and user-configurable accessibility settings.

ITINERARY / AI + FALLBACK

Swift function for generating travel content using AI and fallback logic.

func generateAIContent(previousZip: String? = nil) async {
	self.isGenerating = true

	#if canImport(FoundationModels)
	if #available(iOS 18.0, *) {
		if let aiResponse = await tryRunAppleIntelligence(time: self.timeSlot, hint: distanceHint) {
			self.curatedActivity = aiResponse.activity
			self.icon = aiResponse.icon
			self.foodSuggestion = aiResponse.food
			self.isGenerating = false
			return
		}
	}
	#endif

	try? await Task.sleep(nanoseconds: 600_000_000)
	self.curatedActivity = "\(distanceHint) Admire the \(building.buildingStyle) details at \(building.address)."
	self.icon = "mappin.and.ellipse"
	self.foodSuggestion = building.foodSpots.randomElement() ?? "Local Favorite"
	self.isGenerating = false
}

MANAGERS / GLOBAL PROGRESS PERSISTENCE

Swift property and methods for tracking visited locations and saving progress with UserDefaults.

@Published var visitedIDs: Set = [] {
	didSet { save() }
}

private let saveKey = "GlobalVisitedBuildingsKey"

private init() {
	if let savedData = UserDefaults.standard.array(forKey: saveKey) as? [String] {
		self.visitedIDs = Set(savedData)
	}
	loadImagesFromDisk()
}

private func save() {
	UserDefaults.standard.set(Array(visitedIDs), forKey: saveKey)
}

SETTINGS / ACCESSIBILITY + PREFERENCES

Swift code for managing accessibility and user preferences in the app.

@AppStorage("reduce_motion") var reduceMotion = false
@AppStorage("high_contrast_mode") var highContrast = false
@AppStorage("haptics_enabled") var hapticsEnabled = true
@AppStorage("is_sound_enabled") var isSoundEnabled = true

func resetAllContent() {
	if let domain = Bundle.main.bundleIdentifier {
		UserDefaults.standard.removePersistentDomain(forName: domain)
	}

	GlobalProgressManager.shared.resetAllProgress()

	reduceMotion = false
	highContrast = false
	hapticsEnabled = true
	isSoundEnabled = true
}

Key Screens

Key screens: iPhone and iPad UI states from Stamped.

iPhone Screens

Stamped light mode city list view

Light mode city list flow in the Stamped travel discovery experience.

Stamped dark mode city list view

Dark mode interface preserving hierarchy and readability across key interactions.

Stamped high contrast city list view

High contrast city list view supporting accessibility-first visual clarity.

iPad Screens

Stamped quiz light mode iPad view

Quiz flow in light mode optimized for iPad screen space and readability.

Stamped travel dossier high contrast iPad view

Travel dossier in high contrast mode for improved visual accessibility on iPad.

Stamped AI curated dark mode iPad view

AI-curated itinerary experience in dark mode for low-light planning workflows.

App Store Update

Version 1.1.0 — Published!

  • Faster, More Accurate Rates: Integrated a new high-frequency Exchange Rate API to provide mid-market rates with even higher precision.
  • Live Updates: Rates now refresh automatically so you never miss a market move.
  • Expanded Compatibility: Now supports more devices! Optimized for iOS 18.6 and higher.
  • Enhanced Accessibility: Improved text contrast across the app to meet the 4.5:1 ratio, making rates easier to read for everyone.
  • Performance Fixes: Optimized API logic to reduce data usage and improve loading speeds on slower connections.

I published my first update to the App Store with these improvements. Stamped! is currently on version 1.1.0.

Outcome

Stamped! shipped as a cohesive, production-style SwiftUI app with a complete progression loop, accessibility-first settings, and TestFlight distribution. The project demonstrates full product ownership from concept framing through implementation and release readiness.

Next Iteration

Next steps include integrating a full in-app translator for international travel and expanding the content library with more cities and landmark datasets.