Appendix I: Claudes Outdoor Utility (Shop Manual)
Status: Live. GPL v3. Public repo at github.com/fluhartyml/Claudes-Outdoor-Utility. Build-Along chapter: Part VII Build-Along 05. Claudes Outdoor Utility is a weather-stamped photo journal for iPhone and iPad. One tap captures a photo, the current weather, the user's location, a title, and a free-text note — one entry, frozen at that moment. Past entries browse chronologically or pinned on a map. Apple Photos remembers GPS but not weather; this app fills that gap.
What it is
A weather + location journal. Each entry is one moment in time: a photo, a title, the weather at the time of capture, the lat/lng coordinates, an optional free-text note, and a timestamp. The Home tab shows a map with a pin for every entry. The Log tab shows a reverse-chronological list. The New Entry tab is the capture flow. The Under the Hood tab surfaces the source for curious readers.
v1.0 ships the journal + map. v2.0 adds a RainViewer radar overlay on top of the existing map — same data model, new visualization layer.
Architecture overview
SwiftUI + SwiftData app with five Apple framework dependencies (no third-party). 20 Swift files.
- App entry:
Claudes_Outdoor_UtilityApp.swift—WindowGroupwith the SwiftDataModelContainerwired forOutsideLog. - Shell:
ContentView.swift— four-tabTabView(Home / New Entry / Log / Under the Hood).AppShell.swift— wrapping container withNavigationStack, an (i) info button, and an About sheet. - Data model:
OutsideLog.swift— SwiftData@Modelwithid,timestamp,title,lat/lng, weather,photoData,note. - Tab views:
HomeView.swift—Mapfills the tab; one pin per past entry.NewEntryView.swift— capture flow: title, photo (camera or library), weather (auto), location (auto), note.LogView.swift— reverse-chronological list with date + title display.UnderTheHoodView.swift— source-tour tab with file list, per-file callout, embedded source, Open-on-GitHub.
- Per-entry detail:
EntryDetailView.swift— full entry view (photo hero, weather card, note, timestamp + coords, map last). - System bridges:
CameraPicker.swift—UIViewControllerRepresentablewrappingUIImagePickerControllerfor camera capture.LocationManager.swift— CoreLocation wrapper with when-in-use authorization.WeatherFetcher.swift— WeatherKit wrapper that fetches current conditions at log time and freezes them onto the entry.WeatherAttributionRow.swift— the Apple-required WeatherKit attribution UI.MapSnapshotter.swift— rendered map snapshot for the share artifact.ActivityShareSheet.swift—UIActivityViewControllerwrapper.EntryShareBuilder.swift— assembles the share artifact (photo + weather card + map snapshot) for sharing.
- Auxiliary surfaces:
AboutView.swift,FeedbackView.swift(mailto form with auto-included device info),PlaceholderTab.swift(shared placeholder used during stubbing). - Notes:
DeveloperNotes.swift— canonical reference; mirrors to the wiki's Developer-Notes.md.
The data model
@Model
final class OutsideLog {
var id: UUID
var timestamp: Date
var title: String
var lat: Double
var lng: Double
var weatherSummary: String
var weatherTemp: Double
var weatherSymbol: String
var photoData: Data
var note: String
init(/* ... */) { /* ... */ }
}
Weather is stored as a flat triple (summary + temp + SF Symbol name) rather than a nested struct because the frozen reading doesn't need to be re-queryable — the entry is a moment, not a live data source.
The photo + location privacy rule (locked)
Per project memory feedback_photo_location_privacy.md, when an app handles photos plus location, it stores the location in its own data model and never modifies the photo's EXIF. The iOS camera-location setting is the user's single authority over whether their photos carry location data; an app that writes its own location into a photo's EXIF overrides that authority without the user's awareness.
Outdoor Utility follows this rule strictly: OutsideLog.photoData holds the raw bytes the user picked / captured, and OutsideLog.lat + OutsideLog.lng hold the location the app reads from CoreLocation. The photo's own EXIF is not touched. If the user has iOS camera location turned off, the photo's EXIF stays clean; the app's location data is its own.
WeatherKit fetch + attribution
WeatherFetcher calls WeatherKit's current-conditions API at log time, extracts the summary string, temperature, and SF Symbol name, and writes them into the new OutsideLog entry. The reading freezes — the entry stores what the weather was at capture time, not what it is right now.
Apple requires attribution for WeatherKit data. WeatherAttributionRow renders that attribution as part of any view that displays weather data — the Entry Detail view, the share-builder map snapshot, the New Entry preview. Per project memory feedback_weatherkit_attribution_varies.md, App Review's verdict on attribution placement is reviewer-variable; an attribution that passes one reviewer may get flagged by another. The app's defensive posture is to show attribution prominently everywhere weather appears, not just once.
Capture flow
- User opens the New Entry tab.
- The user types a title (required — the rule below).
- The user picks a photo source: camera (
CameraPicker) or library (PhotosPicker). - The app reads the user's current location via
LocationManager. Permission prompt fires on first use only. - The app calls
WeatherFetcherfor current conditions at those coordinates. - The user adds an optional free-text note.
- On Save, a new
OutsideLogentry is inserted into the SwiftData store.
Locked design rules
- iOS only. iPhone and iPad. No macOS, no visionOS.
- SwiftData persists. The diary survives restarts. iCloud sync is a v2.x candidate, not v1.0 scope.
- Photos untouched. The app never reads or modifies photo EXIF.
- Title required at New Entry time. Keeps the Log tab scannable long-term — a list of "Untitled" entries defeats the purpose.
- WeatherKit attribution everywhere. Not just on the entry detail; on any surface that shows weather data, including share artifacts.
Sharing
EntryShareBuilder assembles a composite share artifact: the photo on top, the weather card below, a map snapshot from MapSnapshotter at the bottom. The result is a single rendered image the user can share through any normal share target (Messages, Mail, AirDrop, Photos). ActivityShareSheet wraps UIActivityViewController for the presentation.
Build + deployment
- App name: Claudes Outdoor Utility ("Outside" is internal shorthand; never the public name).
- Platforms: iPhone + iPad universal.
- Frameworks: SwiftUI, SwiftData, CoreLocation, WeatherKit, MapKit, PhotosUI, UIKit (for the
UIImagePickerControllerbridge andUIActivityViewController). - Required Info.plist keys:
NSLocationWhenInUseUsageDescription(CoreLocation),NSCameraUsageDescription(camera capture). PhotosPicker remains permission-free. - Capabilities: WeatherKit must be enabled via the Apple Developer portal (App Services), not Xcode — per project memory
reference_musickit_portal.md.
v2.0 roadmap
v2.0 adds the RainViewer radar overlay to the Home tab map. Same data model, new visualization. Other v2.x candidates:
- iCloud sync via CloudKit (so the journal mirrors across the user's devices).
- Multiple photos per entry.
- Export the journal to PDF or Markdown.
- Per-entry tags + tag-based filtering.
Known gotchas
- WeatherKit attribution is reviewer-variable. Same attribution UI that passes one App Review pass can get flagged on a future update. Show attribution prominently on every weather-bearing view rather than relying on a single instance.
- WeatherKit capability lives in App Services, not Xcode. First time enabling WeatherKit, the developer portal is where the capability gets switched on; Xcode's signing & capabilities tab doesn't surface it as an option until the portal change propagates.
- Photo EXIF is untouchable. Don't add a "save location to photo" toggle to settings. The iOS camera-location setting is the single authority for that decision.
- Title is required for a reason. Don't make it optional in a v1.x update; the Log tab's value depends on every entry being identifiable at a glance.
Sources
- Project source: github.com/fluhartyml/Claudes-Outdoor-Utility (verified 2026-05-10).
- Canonical roadmap:
Claudes Outdoor Utility/DeveloperNotes.swiftin the project repo (mirrors to wiki Developer-Notes.md). - Build-Along chapter: Part VII Build-Along 05.
- Apple Developer Documentation: WeatherKit reference; CoreLocation reference; SwiftData + MapKit references.
- Related project memory:
feedback_photo_location_privacy.md,feedback_weatherkit_attribution_varies.md,reference_musickit_portal.md,feedback_app_naming_strategy.md.