På tur & Tintin: hele appen, fra klokke til kart
En SwiftUI-app i tre mål — iPhone, Apple Watch og en Live Activity — bygget rundt én idé: klokka eier turen, telefonen forteller historien om den. Under er arkitekturen, funksjonaliteten, designet og brukerscenarioene, slik de faktisk står i koden i versjon 0.3 (build 17).
- Språk
- Swift · SwiftUI
- Omfang
- 34 filer · 10 360 linjer
- Mål
- 3 (iOS, watchOS, widget)
- Versjon
- 0.3 (17)
- Min. OS
- iOS 26 · watchOS
- Region
- nb — norsk først
Hva prosjektet er
«På tur» er en personlig treningsapp for turer med hunden Tintin. Den er ikke en generisk løpeapp med hundetema — hele datamodellen, statistikken og til og med kjøringsdeteksjonen er formet av hva en hundetur faktisk er: uforutsigbar, oppdelt i etapper, full av snusepauser, og av og til avbrutt av en biltur til turområdet.
Prosjektet startet 22. august 2026 som en 22-minutters MVP generert i en kodeagent-økt (skjermbildene i Tidslinje_og_Logg/ dokumenterer kjøringen), og har siden vokst betydelig: GPX-import, live navigasjon, flerarts-etapper, kjøringsdeteksjon, utforskningskart, Live Activity og et eget diagnoseverktøy for HealthKit-ruter er alt kommet etterpå.
Appen distribueres via TestFlight (fire ExportOptions-varianter ligger i repoet), og AGENTS.md fastsetter arbeidsflyten: brukervendt navn er alltid «På tur», tekniske identifikatorer er PaTur, og CURRENT_PROJECT_VERSION skal bumpes for hver TestFlight-build mens MARKETING_VERSION bare endres på faktiske releaser.
Den bærende arkitekturbeslutningen
Apple Watch er eneste eier av en pågående tur. Telefonen muterer aldri treningstilstand — den sender kommandoer med UUID og venter på at klokka bekrefter dem tilbake i neste snapshot. Det gir én skriver, ingen flettekonflikter, og en telefonapp som kan miste kontakten uten at turen påvirkes.
Arkitektur
Xcode-prosjektet (objectVersion 71, Xcode 27) bruker PBXFileSystemSynchronizedRootGroup — mappene er byggekonfigurasjonen, det finnes ingen manuelle fillister. Tre mål deler kode gjennom to bevisst atskilte «shared»-mapper.
| Mål | Bundle ID | Kildemapper | Rolle |
|---|---|---|---|
| På tur | com.ding.patur | iOS + Shared + LiveActivityShared | Arkiv, statistikk, rutebibliotek, speiling av klokka |
| På tur Watch App | com.ding.patur.watchkitapp | Watch + Shared | Selve opptaket: HealthKit, GPS, etapper |
| PaTurLiveActivity | com.ding.patur.liveactivity | LiveActivity + LiveActivityShared | Låseskjerm og Dynamic Island |
Shared/ går til telefon og klokke, men ikke til widgeten. Det er derfor WalkActivityAttributes bruker rå String for aktivitet og fase i stedet for enum-typene: widget-utvidelsen skal ikke trenge domenemodellen. Prisen er at koblingen bare håndheves av strengliteralen phase == "paused".
TintinWalk over WatchConnectivity. Telefonen har i tillegg en fjerde vei inn — den kan rekonstruere tapte turer direkte fra Helse.Tre uavhengige overføringskanaler
Redundansen er ikke tilfeldig. Hver kanal dekker en annen feilmodus:
- HealthKit workout mirroring (
sendToRemoteWorkoutSession) bærer live-trafikken: snapshot hvert 2. sekund, rutepunkter i biter på 120, kommandoer med UUID-kvittering og de-duplisering av de siste 32 ID-ene. Speilingen forsøkes gjenopprettet maks hvert 20. sekund, og en død speiling påvirker aldri selve turen. - WatchConnectivity bærer det holdbare: ferdig tur som
transferUserInfo(["walk": Data])fra klokka, og rutebiblioteket motsatt vei somapplicationContext+sendMessage. Rutevalg er bevisst utelatt fra nyttelasten — klokka velger rute selv, uten telefonen. - HealthKit som sannhetskilde:
HealthRecoveryCoordinatorkjører ved oppstart og hver gang appen blir aktiv, spør etter alt merketcom.ding.patur.tintinpluss alt fra egen bundle-ID de siste 90 dagene, og rekonstruerer turer som aldri kom over.
Lagring
Ingen Core Data, ingen SwiftData, ingen iCloud. Alt er ObservableObject + @Published + @EnvironmentObject, og persistens er atomiske JSON-filer.
| Data | Sted | Format |
|---|---|---|
| Turer | Application Support/PaTur/walks.json | JSON, atomisk skriving |
| Planlagte ruter | …/planned-routes.json | JSON, atomisk skriving |
| Hundeprofil | UserDefaults["dogProfile"] | JSON |
| Aktiv økt på klokka | UserDefaults activeTintin* | 7 nøkler for krasjgjenoppretting |
| Kartlag, turnumre | @AppStorage | String / Bool |
| Turnumre («Tur 12») | — | Aldri lagret, alltid utledet |
Det siste punktet er en fin detalj: det synlige turnummeret regnes ut fra kronologi hver gang, med id.uuidString som deterministisk tie-break. Sletter du tur 4, lukkes hullet — men ingen permanent ID i HealthKit eller statistikken flytter seg.
Datamodellen: turen som etapper
Kjernen er TintinWalk — en hel utflukt med permanent UUID — som inneholder én eller flere kronologiske TintinTripSegment-etapper, hver med sin egen permanente UUID. En etappe er gåtur, løpetur, biltur eller pause. En vanlig énaktivitetstur lagres som én tur med én etappe, og gamle poster migreres automatisk til samme form.
TintinWalk holder samtidig en denormalisert oppsummering på toppnivå (total distanse, sluttid, hele ruten) for det eldre enkeltaktivitets-brukergrensesnittet. segments er private(set) — tidslinjen er autoritativ, oppsummeringen er avledet.
Avledningene som er verdt å merke seg
- Varighet er summen av etappene, ikke
endedAt − startedAt. Dødtid mens HealthKit lagrer mellom to etapper telles bevisst ikke — ellers ville hver aktivitetsbytte blåst opp turtiden. - Snittpuls er et varighetsvektet gjennomsnitt over etappene som faktisk har puls, med 1-sekunds gulv per vekt.
- Distanse for hundesteg bruker
pedestrianDistanceMeters— bare gå- og løpeetapper. En biltur skal ikke gi Tintin-steg. - Aktivitetstittel følger norsk listegrammatikk: «Gåtur», «Gåtur og løpetur», «Gåtur, Løpetur og biltur».
Innstillingene fryses ved opptak: steglengde, snusefaktor og hundenavn kopieres inn i hver tur, så en senere justering i profilen endrer ikke historiske estimat. Codable er håndskrevet med skjemaversjon, og alle valgfrie nøkler har defaults ("Tintin", 0,32 m, 1,12) slik at gamle filer alltid leses.
Tintin-steg
estimatedDogSteps = pedestrianDistanceMeters × wanderingFactor ÷ dogStepLengthMeters
Standard: 0,32 m steglengde (justerbar 18–60 cm) og 1,12 i «snusefaktor» (justerbar 1,00–1,50), altså +12 % for at Tintin beveger seg mer enn GPS-sporet til mennesket. En 3 km gåtur blir ≈ 10 500 hundesteg. Appen kaller det selv «et lekent estimat, ikke en helsemåling».
Apple Watch: der turen skjer
WatchWorkoutManager er prosjektets tyngste fil (1 730 linjer) og inneholder hele opptaksmotoren. Tilstandsmaskinen er ready → requesting → active ⇄ paused → finishing → finished | failed.
Én HealthKit-økt per etappe
Dette er det mest uvanlige designvalget: hver pause, hvert aktivitetsbytte og hver avslutning avslutter HealthKit-økten og starter en ny. Det gir separate, korrekt typede treninger i Helse (gåturen som gåtur, løpeturen som løpetur) mens «På tur» holder dem sammen gjennom felles tripID i metadataene. Pauser lagres som egne .pause-etapper. Bilturer er rene GPS-etapper uten HealthKit i det hele tatt.
Metadata skrevet på hver trening
HKMetadataKeyWorkoutBrandName = "På tur", IndoorWorkout = false, og et eget navnerom: com.ding.patur.tintin, .routeSchema = 3, .tripID, .walkID (legacy-alias), .segmentID, .segmentKind, .activity, .recoveredSession. Rutesamplet får i tillegg punkttelling, avviste punkter, største råluke og maks nøyaktighet — telemetri som diagnoseverktøyet senere leser.
GPS-håndtering
kCLLocationAccuracyBest, distanceFilter = 2, headingFilter = 2, activityType = .fitness (byttes til .automotiveNavigation for bilturer). Punkter forkastes utenfor 0–50 m nøyaktighet eller utenfor etappens tidsvindu.
GPS-lås krever minst 3 av de 5 siste kandidatene, beste nøyaktighet ≤ 12 m, og at de tre siste ligger innenfor 15 m av den beste. Før det får du «Venter på GPS» i stedet for en tilfeldig strek over kartet.
Skjermene
I tur vises tre sider i en TabView: Kontroller → Måltall → Kart.
Klar start
«Gå med Tintin», «Løpe med Tintin», «Kjøre med Tintin» — og «Velg rute» med navn og distanse på valgt GPX-rute.
Måltall
Varighet i 43 pt, distanse, og puls — eller bilikon og «BILTUR» i turkis når etappen er en biltur.
Kontroller
Pause/Fortsett og Fullfør øverst; deretter «Bytt til …», «Marker», «Rute» og «Mål» (destinasjonssøk).
Kart
Planlagt rute, navigasjonsrute i cyan, eget spor i oransje, potemarkør med retningskjegle. Tre kameramoduser, 420 m kamerahøyde.
Ferdig
«Gåturen er lagret», «≈ N Tintin-steg 🐾», og en advarsel hvis ruten ikke nådde Helse.
Feilet
«Noe gikk galt» med konkret melding og «Prøv igjen» — ingen tapt tilstand, økten kan gjenopprettes.
Haptikk brukes gjennomgående og med mening: .click ved valg, .success ved markering, .failure når GPS mangler, .notification når kjøring oppdages.
Krasjgjenoppretting
recoverActiveWorkoutSession kalles fra init, ved applicationDidBecomeActive og i egen håndterer. Bygger, datakilde og rutebygger kobles på igjen, og tur-ID, etappe-ID, starttider og ferdige etapper leses tilbake fra UserDefaults. Den gjenopprettede økten merkes med recoveredSession: true.
Live Activity
Låseskjermen viser potemerke, «Gåtur med Tintin», «Live fra Apple Watch» og tid + distanse. I Dynamic Island: potepote og kort distanse kompakt, tid og puls utvidet. Tidsvisningen bruker Text(timerInterval:) så den tikker selv uten oppdateringer. Oppdateringer sendes bare ved faseendring, ≥ 10 m distanseendring, ≥ 4 slag pulsendring, eller etter 8 sekunder — med staleDate på 15 s og avvisning 30 s etter slutt.
iPhone: arkivet og analysen
Fire faner, hver med egen NavigationStack.
| Fane | Tittel | Innhold |
|---|---|---|
| På tur | På tur | Live-kort fra klokka med fjernkontroll, siste tur med kart, hundeprofil i verktøylinjen |
| Turer | Turene våre | Kronologisk liste, sveip for å slette, «Fjern eksempel» |
| Ruter | Planlagte ruter | GPX-import, høydeprofil, «Kjør til start», offline-status for klokka |
| Oversikt | Våre eventyr | Utforskningskart, filtre, søylediagram, siste etapper |
Live-speilet
Når klokka starter en tur dukker den opp av seg selv på telefonen. Kortet ekstrapolerer forløpt tid mellom snapshotene, viser statusprikk (oransje = live, gul = kobler til igjen), og gir fjernkontroll: pause, marker sted, bytt gå/løp, fullfør — hver med sin egen «Setter turen på pause …»-tilstand til klokka kvitterer. Kartet kan følge kompassretningen, med kurs beregnet som storsirkelretning fra det siste punktet minst 10 m bak.
Turdetaljer
Kart med fargekodede etapper (gåtur oransje, løpetur blå, biltur turkis, pause grå), åtte måltall i rutenett, en etappe-tidslinje med klokkeslett, varighet, distanse og tempo per etappe, «Lagre som fast rute», hundesteg-kort og «Turminne» — et fritekstnotat som skrives til disk for hvert tastetrykk.
Utforskningskartet
Den mest ambisiøse iOS-skjermen (1 230 linjer) og direkte svar på ønsket om å «se hvor vi går oftest». Alle turer tegnes samtidig, fargelagt etter ukedag — mandag blå, lørdag rosa, søndag lilla — med filtre for ukedag/hverdag/helg, periode (30 dager, i år, alle år), aktivitetstype og tre kartlag (Minimal, Standard, Satellitt).
Tre detaljer gjør det brukbart i praksis: turnummer-etikettene plasseres 55 % inn i ruten (ikke ved start, der alle turer overlapper), de klustres i tre zoomnivåer (4×4-rutenett med maks 8 grupper langt ute, opptil 18×18 med 40 grupper nært), og trykk treffer nærmeste polylinje med toleranse skalert etter zoom — ligger flere turer oppå hverandre får du dialogen «Velg tur».
Rutebiblioteket
GPX importeres med XMLParser (med shouldResolveExternalEntities = false mot XXE), tynnes til 2 m mellom punkter og maks 5 000 punkter, og parses på en detached task. Rutedetaljen viser høydeprofil som Swift Charts-areal, «Kjør til start» via Apple Kart med kjøreanvisning og trafikk, og et offline-kort. Eksempelfilen i repoet — Ramstad Terrasse 15 til Langhus stasjon, 134 punkter — er en planlagt rute uten tidsstempler.
Budsjettet mot klokka
Hele GPX-en blir på telefonen. Til klokka sendes maks 560 punkter totalt, fordelt likt (2–180 per rute), og hvis JSON-en likevel passerer 48 000 byte krympes den i runder på 0,72 til punkttallet treffer gulvet på 12.
Statistikk
Kategori (Alle / Gå / Løp / Bil) × periode (Måned / I år / Alle) med månedstepper. I «Alle» aggregeres hele turer; ellers aggregeres etapper, så «utflukter med løping» og «separate etapper» rapporteres hver for seg. Snittempo krever minst 10 m distanse; for bil vises snittfart i km/t i stedet.
Fire signaturmekanismer
Dette er delene som ikke finnes i andre treningsapper, og som forklarer hvorfor kodebasen er så stor for en «MVP».
1 · Kjøringsdeteksjon med tilbakedatert kutt
Problemet: du går tur, setter deg i bilen, og turen fortsetter å registrere 60 km/t som «gåing». DrivingDetectionEngine er en liten, plattformuavhengig verdi-type med tre terskler: 8,5 m/s (≈ 31 km/t, trygt over løpshastighet), 12 sekunder vedvarende på GPS-fart alene, eller 8 sekunder hvis CoreMotion samtidig melder automotive. Ett raskt punkt utløser aldri noe, og et enkelt saktepunkt nullstiller hele timeren.
Det elegante er cutoff: den peker på siste tidspunkt du plausibelt gikk, ikke da mistanken startet. Når kjøring bekreftes trunkeres gåetappen dit, så akselerasjonen fra ganghastighet til bilfart aldri havner i gådataene. Punktene i mellomtiden holdes i karantene — ikke forkastet — så de kan spilles tilbake hvis mistanken var feil.
Deretter får du tre valg, både på klokka og telefonen: Avslutt, Biltur (karantenen blir en .driving-etappe) eller Fortsett. Merk at klokka alltid sender automotive: false — bare iPhone har bevegelsesklassifisering, og bare iPhone kan derfor bekrefte på 8 sekunder.
2 · Rutefortetting for Apple Mosjon
Apple Mosjon avviser ruter med tidsluker. Derfor genererer klokka syntetiske punkter med 2 sekunders mellomrom gjennom hele økten — interpolert mellom faktiske GPS-fikser, og gjentatt der du står stille — pluss «pre-roll» fra etappestart og «post-roll» til etappeslutt. Punktene skrives i batcher på 150 gjennom insertRouteData, med flush hvert 12. sekund eller ved 10 køede jobber. Innkommende punkter avvises hvis nøyaktigheten er over 50 m eller distansen overgår max(60, tid × maksfart + nøyaktighet), med maksfart 12 m/s løping og 7 m/s gåing.
routeSchema = 3 markerer at ruten er skrevet av denne fortettende versjonen — informasjon RouteDiagnostics bruker for å vite om tidsluker er en feil eller bare gammel modell.
3 · Kartdiagnostikk
Et helt verktøy bygget for å feilsøke Apples egne apper. Det leser siste «På tur»-økt fra Helse og rapporterer punkter utenfor økten, nøyaktighet over 50 m, ugyldige koordinater, største tidsluke, luker over 3,25 s, duplikate tidsstempler og hopp over 15 m/s — og gir en konkret konklusjon, for eksempel: «Den nye rutemodellen skulle ha fylt pausene, men ruten har fortsatt tidsluker.»
Det kan også lage en syntetisk testtur: to minutter, 61 punkter med 2 sekunders mellomrom på en sirkel med 25 m radius, der punkt 16–45 står helt stille et helt minutt — nettopp for å teste om Mosjon aksepterer gjentatte stillestående punkter. Testturer merkes routeTest: true og kan slettes samlet.
4 · Idempotent kommandoprotokoll
Seks kommandoer: pause, resume, finish, markPlace, switchToWalking, switchToRunning. Alle er absolutte, ingen er en toggle — en dobbeltlevert pause på en pauset tur er en no-op, mens en dobbeltlevert toggle ville ødelagt tilstanden. Hver kommando har en UUID, klokka ekker den tilbake i acknowledgedCommandID sammen med en norsk melding, og telefonen slutter først da å vise spinneren. Ingen egen svarmeldingstype er nødvendig.
Tallene, samlet
| Mekanisme | Terskel | Hvorfor |
|---|---|---|
| Kjøring bekreftes | 8,5 m/s i 12 s (8 s m/CoreMotion) | Over all løpsfart; krever varighet, ikke enkeltpunkt |
| GPS-punkt godtas | ≤ 50 m nøyaktighet | Filtrerer bytunnel-fikser |
| GPS-lås | 3 av 5 · ≤ 12 m · innen 15 m | Unngår villstrek ved turstart |
| Rutefortetting | 2 s mellomrom, batch 150 | Mosjon avviser tidsluker |
| Snapshot til telefon | hvert 2. s, 120 punkter per bit | Nok til live-UI, lite nok for kanalen |
| Live Activity | 10 m / 4 bpm / 8 s | Sparer budsjett for oppdateringer |
| Rutebibliotek til klokka | 560 punkter / 48 kB | WatchConnectivity-nyttelast |
| Duplikat-tur | ±15 s start, ±90 s slutt | Samme tur via WC og Helse skal flettes |
| Rutegjenbruk | 2,5 m tynning, maks 5 000 punkter | Fjerner stillestående jitter |
| Tempo beregnes | ≥ 10 m distanse | Ellers absurde min/km-verdier |
Design og merkevare
Systemet er strengt: én aksentfarge, og et semantisk par som snus i mørk modus. TintinPrimary og TintinCanvas er eksakte inverser av hverandre, så en visning kan skrives én gang og snu seg selv. Oransjen holdes konstant i begge utseender for gjenkjennelse, med TintinOnOrange som garantert lesbar forgrunn.
I tillegg finnes et konsekvent fargespråk for etapper, gjentatt i tre visninger: gåtur oransje, løpetur blå, biltur turkis, pause grå. Utforskningskartet har sitt eget sett med syv ukedagsfarger.
Potemerket
PawBadge er hele appens visuelle signatur: pawprint.fill i en sirkel med automatisk gradient, der glyfen alltid er 0,46 × diameteren. Samme komponent brukes fra 34 pt i kartannotasjoner til 58 pt i hjemmeskjermens hero. Den er accessibilityHidden — rent dekorativ.
Appikonet
Ikonet bygges av et Swift-skript (Design/build_app_icons.swift) som komponerer en gradient-PNG med en vektor-SVG av en person som går med hund, skalert 1,20 så motivet blør 20 % utenfor kanten. To render: 1024 px for iPhone, 1088 px for Watch.
Design/AppIcon.svg er det opprinnelige ikonet fra første MVP — grønn gradient (#426F4B → #123C2B), kremhvit pote, terrakotta-smil — synlig i skjermbildet fra 22. august. Det er nå foreldet i forhold til byggeskriptet.
Språk som designmateriale
Alt er norsk, og tonen er gjennomført varm og konkret: «Ut på eventyr?», «Turene våre», «Våre eventyr», «Turminne», «Fint sted», «Husk dette», «Rolig runde med en lang snusepause ved vannet». Feilmeldinger forklarer: «Turen har ikke nok brukbare GPS-punkter.» Strengene ligger direkte i koden, også i modell-laget — developmentRegion = nb, men ingen strengkatalog.
Brukerscenarioer
Slik flytene faktisk henger sammen i koden.
A · Den vanlige turen
- Trykk «Gå med Tintin» på klokkawatch
HealthKit-autorisasjon, økt startes med
.walkingutendørs, GPS og retning slås på. «Gjør klar …» til låsen er god. - Turen dukker opp på telefonen av seg selviphone
Speilingen kobler seg på, live-kortet vises på hjemmeskjermen, og Live Activity tar plass på låseskjermen.
- Snusepausewatch
«Pause» avslutter HealthKit-økten og starter en
.pause-etappe. Kan også utløses fra telefonen eller fra sidetast-kommandoen. - «Marker» ved vannetbegge
Et
WalkMarkermed siste GPS-punkt, hapisk kvittering, og toast i 1,6 s. - «Fullfør»watch
Økt avsluttes, skritt hentes med en
HKStatisticsQuery, ruten fortettes og skrives,TintinWalkbygges av alle etappene og sendes til telefonen. - «Gåturen er lagret · ≈ 9 800 Tintin-steg 🐾»watch
Hvis ruten ikke nådde Helse, sier klokka det eksplisitt her.
- Turen ligger i «Turene våre»iphone
Kart, etappe-tidslinje, åtte måltall, notatfelt — og en ny linje i utforskningskartet.
B · Planlagt rute fra GPX
- Importer GPX i «Ruter»iphone
Fra Filer eller en annen turapp. Navn foreslås fra filen, høydeprofil og distanse vises før lagring.
- Ruten synkroniseres automatiskiphone → watch
Punktbudsjettet krymper den til klokkestørrelse. «N ruter synkroniseres automatisk».
- «Kjør til start»iphone
Apple Kart åpnes med kjøreanvisning og trafikk til rutens startpunkt.
- «Velg rute» før eller under turenwatch
Offline. Under tur sorteres rutene etter avstand fra deg, med «X unna» som undertekst.
- Ruten tegnes under sporet dittwatch
Svart/hvit stiplet planlagt linje under din egen oransje strek, så avvik er synlig med ett blikk.
C · Bilturen midt i turen
- Du setter deg i bilen — turen går fortsattwatch
Farten passerer 8,5 m/s. Punktene går i karantene, men ingenting skjer utad ennå.
- 12 sekunder senere: «Kjøring oppdaget»begge
Haptisk varsel, gåetappen kuttes ved siste plausible gåpunkt, turen tvangspauses. «Registrert fart: opptil 47 km/t».
- Du velgerbegge
Biltur gjør karantenen til en
.driving-etappe og fortsetter opptaket i bilmodus. Fortsett spiller punktene tilbake. Avslutt lagrer det som finnes. - Resultatet i Helseiphone
Gåturen står som gåtur uten bilfart. Bilturen er registrert i «På tur», men gir aldri Tintin-steg og er alltid utelatt fra kjøredistansen i statistikken.
D · Navigasjon hjem
- «Mål» / «Finn destinasjon»begge
Søk med
MKLocalSearchCompleter, begrenset til 0,22° rundt deg, maks 12 forslag. - Gårute beregnesbegge
MKDirectionsmed.walking, polylinjen komprimeres til 220 punkter og sendes over speilingen — begge veier, så telefonen kan sette et mål på klokka. - Cyan linje på klokkekartetwatch
Med «Mål»-markør. Telefonen viser gjenstående distanse og tid, regnet fra nærmeste navigasjonspunkt til der du faktisk er.
- «Avslutt navigasjon»begge
En
LiveNavigationUpdatemedroute: nil. Turen fortsetter uendret.
E · Når noe går galt
- Klokkeappen krasjer midt i turenwatch
recoverActiveWorkoutSessionkobler på igjen ved neste oppstart; tur-ID, etappe-ID og ferdige etapper leses fraUserDefaults. Turen fortsetter. - Turen kom aldri over til telefoneniphone
Ved hver aktivering spør telefonen Helse etter alt merket med appens metadata (og alt fra egen bundle siste 90 dager) og rekonstruerer turen med etapper og rute.
- Samme tur kommer to veieriphone
Duplikatvinduet ±15 s / ±90 s fletter dem: lengste rute vinner, tomme felt fylles, ingenting overskrives destruktivt.
- Mosjon viser ikke kartetiphone
«Kartdiagnostikk» under hundeprofilen leser siste økt, peker på hva som er galt, og kan lage en 61-punkts testtur for å isolere om feilen er appens eller Apples.
Krav vs. levert
Kravene ligger som et designark i Tidslinje_og_Logg/KRAV.png, under tittelen «Tur med Tintin». Status målt mot koden:
| Krav (Apple Watch) | Status | Hvor |
|---|---|---|
| Velg rute — importert GPX eller tidligere tur | Levert | Rutevelger + «Lagre som fast rute» |
| Starte turen nå | Levert | Gå / løp / kjør fra klar-skjermen |
| Spore tid, puls, kart, skritt, avstand | Levert | Live builder + HKStatisticsQuery for skritt |
| Vise vær og temperatur | Mangler | Ingen WeatherKit i kodebasen |
| Navigere med eller uten planlagt rute | Levert | Destinasjonssøk + MKDirections |
| Pause og fortsett | Levert | Egne .pause-etapper |
| Lagre et sted underveis | Levert | WalkMarker, 4 typer |
| Synkronisere til Helse og Mosjon | Delvis | Skriving er på plass; Mosjon-visning er nettopp derfor diagnoseverktøyet finnes |
| Krav (iPhone) | Status | Hvor |
|---|---|---|
| Se turdata og kart i appen | Levert | Turdetalj med 8 måltall og etappe-tidslinje |
| Tydelig hundeaktig UI | Levert | Potemerke, potespor på kart, «Tintin-steg», hundeprofil |
| Administrere turer: importere, planlegge, historikk | Levert | Rutebibliotek + historikk + rutegjenbruk |
| God statistikk: skritt, puls, kart over hvor vi går oftest | Delvis | Utforskningskart med ukedagsfarger og filtre; puls og skritt i turdetaljen |
| … inkludert vær og musikk/medier | Mangler | Ingen WeatherKit, ingen MediaPlayer/MusicKit |
Verdt å merke: i den opprinnelige MVP-en var «vær, GPX-import og navigasjon» eksplisitt utenfor omfanget. To av de tre er nå levert.
Løse tråder
Observasjoner fra gjennomgangen, ikke feilrapporter — flere er bevisste valg.
Konfigurasjon
WATCHOS_DEPLOYMENT_TARGET = 10.0mens iOS-målet er 26.0. Klokkekoden bruker API-er som er nyere enn watchOS 10, så tallet er sannsynligvis bare aldri oppdatert.- iOS-målet erklærer ingen
UIBackgroundModes. Det stemmer med at telefonopptak er merket «Kun for simulator og testing», menPhoneWalkRecorderber likevel omrequestAlwaysAuthorization. - Ingen app-gruppe, tross innebygd widget-utvidelse. Live Activity oppdateres i stedet direkte fra appprosessen via ActivityKit, så det fungerer — men utvidelsen kan ikke lese delt data.
- De to
ExportOptions-filene med manuell signering mangler profil forcom.ding.patur.liveactivity. SWIFT_VERSION = 5.0, altså ingen Swift 6-språkmodus, selv om koden er gjennomførtSendable- og@MainActor-annotert.
Kode
- Tre ulike desimeringsformler for samme problem:
ceil(n/maks)iwatchOptimized(240),ceil((n−1)/(maks−1))i navigasjonspolylinjen (220), ogceil((n−1)/4999)i rutegjenbruk. Alle tre legger endepunktet tilbake til slutt. - Nesten all disk- og HealthKit-feilhåndtering er stille (
try?, tomme callbacks). Unntaket er den syntetiske testturen, som rapporterer hvert steg presist — nyttig mønster som kunne vært brukt bredere. LiveWorkoutMirrorManagerfanger hundenavnet ved oppstart, så navneendring i profilen slår ikke inn i Live Activity før appen restartes.- Ingen tester i prosjektet.
DrivingDetectionEngineer skrevet som en ren, plattformuavhengig verdi-type nettopp for å være enhetstestbar — den er det åpenbare stedet å begynne. Design/AppIcon.svger foreldet i forhold tilbuild_app_icons.swift.
Naturlige neste steg
- Vær er det eneste helt urørte kravet: WeatherKit ved turstart, lagret på turen, vist i turdetaljen og som filter i statistikken.
- Medier — hva du hørte på turen — krever
MPMusicPlayerControllereller ShazamKit-lignende avlesning, og passer som et felt på etappen. - Frekvenskart: utforskningskartet viser hvor du har gått, men ikke hvor ofte. En rutenettbasert heatmap over de samme dataene ville svart direkte på kravet «hvor er der vi går oftest».