A modern SwiftUI app for iOS/iPadOS that helps users find places, view them on a map, and get public transit directions. Built with MapKit, MVVM, and dependency injection for clean separation of concerns and testability.
Scope
• Core focus
• Show a map centered around the user with smooth camera control.
• Search for places with region bias to the user’s location when available.
• Calculate and display public transit routes, including steps and ETA.
• Manage and navigate to saved locations (e.g., Home, Work, Custom).
• Out of scope (for now)
• Turn-by-turn voice navigation.
• Offline maps and routing.
• Full persistence for saved locations (placeholder TODOs exist for SwiftData).
• Map and Location
• Request and manage location permissions.
• Start/stop location updates via a LocationServiceProtocol.
• Center and bias the map camera with MapCameraPosition.
• Smart initial positioning with a slight vertical offset to keep the user in the upper portion of the screen.
• Search
• Location search using MapKit (MKLocalSearch) through SearchServiceProtocol.
• Region-aware search when the user’s location is available.
• Clean address formatting for placemarks.
• Transit Routing
• Transit routes via DirectionsServiceProtocol (MKDirections under the hood).
• RouteInfo exposes distance, expected travel time, and categorized steps (walking, transit, general instructions) with SF Symbols.
• Clear the current route and handle errors gracefully (e.g., user location unavailable).
• Saved Locations
• LocationType enum (home, work, custom) with display name and system icon.
• Add, remove, and navigate to saved locations.
• Hooks in place to persist later (e.g., SwiftData).
• MVVM with a coordinating ViewModel
• DirectionViewModel (Main coordinator)
• Lazily initializes MapViewModel, SearchViewModel, and RouteViewModel to keep startup fast.
• Forwards child state to the UI via @Published properties:
• region: MapCameraPosition
• searchResults: [MKMapItem]
• isSearching: Bool
• currentRoute: RouteInfo?
• isCalculatingRoute: Bool
• Delegates actions:
• requestLocationPermission()
• startLocationUpdates()
• centerOnUserLocation()
• searchLocation(query:)
• getDirections(to:destinationName:)
• navigateToSavedLocation(type:)
• clearRoute()
• MapViewModel
• Wraps LocationServiceProtocol publishers for location and authorization.
• Maintains userLocation and region (MapCameraPosition).
• Provides centerOnUserLocation() and setRegion(center:span:).
• SearchViewModel
• Wraps SearchServiceProtocol for async search.
• Emits searchResults, isSearching, and searchError.
• Formats placemark addresses.
• RouteViewModel
• Wraps DirectionsServiceProtocol for async transit route calculation.
• Emits currentRoute, isCalculatingRoute, and routeError.
• Ensures a user coordinate is available with a short timeout before routing.
• Manages saved locations and navigation by LocationType.
• Models
• RouteInfo: wraps MKRoute and exposes distance, expectedTravelTime, and parsed TransitStep list.
• TransitStep: step details with formatted distance and type.
• TransitStepType: walking/transit/instruction with SF Symbols.
• LocationType: home/work/custom with display metadata.
• SwiftUI for UI and state binding
• MapKit for maps, search, and directions
• Combine for reactive bindings between services and view models
• Swift Concurrency (async/await) for networked/async operations
• Core Location (via LocationServiceProtocol) for permissions and updates
• Dependency Injection for services (Search, Directions, Location)
(overview)
• DirectionViewModel.swift — main coordinator VM
• ViewModels/
• MapViewModel.swift — location and camera
• SearchViewModel.swift — place search
• RouteViewModel.swift — routing and saved locations
• Models/
• RouteInfo.swift — route metadata and steps
• LocationType.swift — saved location types
• Services/ (not shown here)
• LocationServiceProtocol / LocationService.shared
• SearchServiceProtocol / SearchService.shared
• DirectionsServiceProtocol / DirectionsService.shared
• ServiceContainer.shared (for DI resolution)
Prerequisites
• Xcode 15 or later
• iOS 17 / iPadOS 17 or later (adjust to your deployment target)
• Swift 5.9+
1. Clone
git clone https://github.com/your-org/marty.git
cd marty
2. Open Open Marty.xcodeproj or Marty.xcworkspace in Xcode. 3. Configure Info.plist Add keys with meaningful descriptions: • NSLocationWhenInUseUsageDescription • NSLocationAlwaysAndWhenInUseUsageDescription (if you support always-on) 4. Build & Run Use a device or simulator with location enabled. You can simulate location in Xcode (a metro Atlanta location is recommended, as that is the project's focus area).
Usage in the App
• Permissions
• Call DirectionViewModel.requestLocationPermission() on first launch or when the map appears.
• Location updates
• Call startLocationUpdates() to begin receiving user location.
• Center map
• Call centerOnUserLocation() to move the camera to the user’s coordinate.
• Search
• Call searchLocation(query:) to search; it will bias to the user’s region when available.
• Directions
• Call getDirections(to:destinationName:) with a destination coordinate to compute a transit route.
• Saved locations
• Call navigateToSavedLocation(type:) to route to a saved place (home/work/custom).
• Use addSavedLocation/removeSavedLocation on RouteViewModel to manage entries.
• Clear route
• Call clearRoute() to remove the current route and errors.
• Lazy Initialization
• Child view models are created only when needed to keep app launch responsive.
• Binding Strategy
• DirectionViewModel forwards child @Published properties via Combine pipelines.
• Location Availability
• Route calculation waits for a user coordinate (with timeout) to avoid hanging.
• DI and Testability
• View models depend on protocols; swap in mock services for unit tests.
Roadmap
• Persist saved locations with SwiftData and sync across devices.
• Add route options (avoid tolls/highways, transport modes).
• Enhanced overlays and annotations for steps and stations.
• Offline caching for recent searches and routes.
• Swift Testing test suites for services and view models.
Troubleshooting
• No location permission
• Ensure Info.plist contains required keys and the user has granted permission.
• Location not updating
• Verify location services are enabled in device settings; startLocationUpdates() must be called.
• Empty search results
• Check network connectivity; try a broader query; confirm region bias is appropriate.
• Route fails
• Ensure a valid destination coordinate; confirm network connectivity and that transit is available in the selected region.
• Issues and pull requests are welcome. Please discuss major changes in an issue first.
• Follow MVVM and DI patterns consistent with the current codebase.
License TBD
Acknowledgments
• Built with SwiftUI, MapKit, and Combine.
• ❤️Thanks to Apple Developer Documentation and community examples for MapKit and Core Location❤️