Releases: slavins-co/keep-in-touch
Release list
v0.5.0 - Siri Shortcuts, Insights, Accessory Widgets & a Big Internals Cleanup
🎉 v0.5.0 is our biggest release yet. Keeping in touch just got effortless - ask Siri to log a call, glance at a Lock Screen widget to see who's overdue, and check the new Insights dashboard to see how your habits stack up against your intentions. Add a guided tutorial, group-hangout logging, and a major under-the-hood cleanup, and this is the most capable, polished build to date.
✨ Features
- App Intents v1 - Log Touch, Get Overdue / Due-Soon, Open, Shortcuts provider (#304)
- Stats & Insights v1 - Performance vs Intent + Method Breakdown (#138)
- Lock Screen + StandBy accessory widgets (#279)
- Upcoming birthdays in the widget family (#329)
- Self-guided tutorial - anchored walkthroughs + TipKit tips (#10)
- Bulk log touch for group hangouts (#293)
- Snoozed Contacts management view (#334)
- Flexible messenger routing + FaceTime on Call button (#233)
- Streamlined Log Connection picker - medium-only TouchMethod (#299)
- Screenshot blur protection on app background - privacy (#105)
🔧 Engineering / performance (the #302 tech-debt audit)
- Repository CRUD base, shared helpers, DI/layering cleanup (#308, #307, #314)
- Cold-launch wins: formatter caching, dedupe fetch (#309)
- Batch saves + list-render efficiency (#316, #317)
- Status-drift fix - unify Home buckets through PersonStatusService (#310)
- PersonDetailVM in-place edits + notification debounce (#311)
- View component extraction, ViewModelErrorHandling helper, stringly/magic-number cleanup (#313, #315, #312)
- Person initializer to nested config structs (#318)
- Swift 6 strict-concurrency Sendable hotfix (#326)
Build: 0.5.0 (14) · TestFlight: https://testflight.apple.com/join/UY7Pgusg
Full changelog: v0.4.0...v0.5.0
v0.4.0 — iOS Widget Family + Home Polish
10 PRs closing 12 issues. The flagship is a new iOS Home Screen widget family. Supporting work tightens the home screen and contact display, fixes a search-bar bug, and consolidates multi-target infra.
New Features
- #60 iOS Home Screen widgets — small (overdue count + most-overdue avatar) and medium (3-row list with interactive Log Touch, no app launch) (#282)
keepintouch://deep-link scheme routes taps to the overdue list or a specific person- Empty and all-caught-up states mirror the in-app affordances
- #283 Add "all caught up" celebratory banner on Home when no one is overdue or due soon — matches the widget's copy (#289)
- #278 Display nickname in contact detail and list views, with case-insensitive dedupe against
displayName(#287) - #275 Nickname search from contact info — search matches nickname in addition to name (#277)
- #274 Auto-expand collapsed Home sections while a search is active; restore state when cleared (#277)
Bug Fixes
- #272 Search bar no longer disappears when search returns no results on Contacts tab — users can clear/edit without force-quitting the view (#273)
- #271 Remove tab content
.accessibilityLabelthat was overriding all child labels and breaking UI-test element discovery
Internal Improvements
- #60 Move Core Data store to an App Group container so the widget extension can read it (#280)
- #284 Share
FrequencyCalculatorbetween app and widget via narrow protocols inShared/— widget and app now agree oncustomDueDate+ grace-period rules; 3 documented divergences removed (#288) - #286 Add widget orchestration tests —
WidgetDataProvidertestable core moved toShared/; 12 new tests covering snapshot ordering, filtering, andFrequencyCalculatorparity (#288) - #285 Extract brand color tokens into
Shared/BrandColors.swift— single source of truth for the hero accent green previously duplicated across the widget and DesignSystem (#290) - #281 Consolidate
CLAUDE.mdandlessons.mdper the 250-line policy
Metrics
| Metric | v0.3.6 | v0.4.0 | Change |
|---|---|---|---|
| Swift source lines | 13,131 | 14,489 | +1,358 |
| Test lines | 6,827 | 8,026 | +1,199 |
| Tests | 333 | 397 | +19% |
| Source:test ratio | 1.9:1 | 1.8:1 | ✓ |
Issues Closed
#60, #271, #272, #274, #275, #278, #281, #283, #284, #285, #286
v0.3.6 — Accessibility, CSV Export & Terminology Cleanup
5 PRs closing 5 issues. Accessibility improvements, CSV export, terminology cleanup, and a status filtering fix.
Accessibility
- #197 Add VoiceOver accessibility labels to all picker and editor sheets (#266)
- #202 Replace hardcoded font sizes with Dynamic Type text styles throughout the app (#264)
New Features
Internal Improvements
- #241 Rename "Groups" → "Cadences" and "Tags" → "Groups" throughout the codebase — aligns terminology with how users think about contact organization (#267)
- Export format updated to v3 with full backward compatibility for v2 imports
Bug Fixes
- #268 Fix Due Soon contacts missing from Home View — removed redundant
dueSoonWindowDaysfilter from PersonStatusService (#269)
Metrics
| Metric | v0.3.5 | v0.3.6 | Change |
|---|---|---|---|
| Swift source lines | 12,911 | 13,126 | +215 |
| Test lines | 6,368 | 6,743 | +375 |
| Tests | 309 | 330 | +7% |
| Source:test ratio | 2.0:1 | 1.9:1 | ✓ |
Issues Closed
v0.3.5
Patch release — fixes stale status indicator colors after bulk updates (#263).
No other changes from v0.3.4.
v0.3.4 — Notifications, Polish & Architecture
8 PRs closing 13 issues. Notification UX improvements, birthday polish, a new DI container, and Swift 6 concurrency cleanup.
New Features
- #253 Birthday today indicator on contact cards (#254)
- #247 Combine multiple birthday notifications into a single grouped alert (#252)
- #232 Pull-to-refresh re-syncs full contact info from Contacts app (#255)
- #248 Extract notification settings into a dedicated sub-screen (#256)
Notification Improvements
- #246 Deliver notifications when the app is in the foreground (#257)
- #230 Suppress weekly digest when only 1 person is overdue — avoids a redundant notification alongside the daily breach alert (#259)
Bug Fixes
Architecture & Code Quality
- #215
AppDependenciesDI container — eliminates directCoreDataStack.sharedreferences in views; all 8 ViewModels gainconvenience init(dependencies:)(#258) - #216 Domain error types —
RepositoryErrorandImportErrorreplace untyped throws across all 4 repositories and 5 ViewModels (#258) - #214 Logged assertions in entity mappings — silent
?? UUID()/?? ""fallbacks replaced withAppLogger.logWarningcalls that surface data corruption at runtime (#258) - #168 Consolidate contact import logic —
OnboardingViewModeldelegates toContactImportService, removing ~60 lines of duplicated Person/TouchEvent construction (#259) - #250 Fix Swift 6 actor-isolation warnings in
OnboardingViewModel(#259) - #260 Thread
coreDataStackthroughContactImportService— completes DI wiring so injected repos and background context use the same store (#259)
Metrics
| Metric | v0.3.3 | v0.3.4 | Change |
|---|---|---|---|
| Swift source lines | 12,506 | 12,911 | +405 |
| Test lines | 5,929 | 6,368 | +439 |
| Tests | 288 | 309 | +7% |
| Source:test ratio | 2.1:1 | 2.0:1 | — |
Issues Closed
#168, #214, #215, #216, #230, #232, #246, #247, #248, #249, #250, #253, #260
v0.3.3 — Professional Grade Upgrade
Systematic codebase upgrade from B/B+ to A- through 9 professional-grade issues, 5 new features, and 364% test coverage growth (62 → 288 tests).
New Features
- #231 Birthday push notifications with per-person toggle and customizable settings (#245)
- #106 Notification privacy mode — hide contact names on lock screen (#244)
- #236 Display multiple groups on contact cards (#242)
- #235 "Link to Contact" recovery for unavailable contacts (#243)
- #137 Fresh Start auto-prompt for inactive or overwhelmed users (#227)
Code Quality — Tier 0 (Safety)
Code Quality — Tier 1 (Structure)
- #209 Add loading states to ViewModels with guard+defer cancellation (#218)
- #206 Decompose PersonDetailView from 1,168 → 433 lines into 7 focused files (#219)
- #207 Extract SettingsViewModel into 3 dedicated services (DataImport/Export/ContactImport) (#220)
- #208 Replace onChange modal chains with navigation state enum (#221)
Code Quality — Tier 2 (Testing)
- #210 NotificationScheduler test coverage with protocol injection (#223)
- #211 Import integration tests with context-injectable DataImportService (#224)
- #212 ViewModel edge-case tests (#225)
- #213 Repository error path tests (#225)
Accessibility & Polish
- Fix accessibility labels, WCAG AA contrast compliance, and double-tap guard (#222)
- #204 Replace UITabBar.appearance() with SwiftUI .toolbarBackground modifier (#226)
- #237 Fix concurrency and redundant modifier warnings — Swift 6 prep (#239)
- #229 Fix incorrect app version displayed in Settings (#238)
- #228 Audit and update TelemetryDeck events after refactors (#240)
Metrics
| Metric | v0.3.2 | v0.3.3 | Change |
|---|---|---|---|
| Swift source lines | 11,609 | 12,506 | +897 |
| Test lines | 3,393 | 5,929 | +2,536 |
| Tests | 62 | 288 | +364% |
| Source:test ratio | 3.4:1 | 2.1:1 | — |
| Overall grade | B/B+ | A- | ⬆ |
Issues Closed
#106, #137, #204, #205, #206, #207, #208, #209, #210, #211, #212, #213, #228, #229, #231, #235, #236, #237
v0.3.2 — UX Redesign
v0.3.2 — UX Redesign
Complete visual refresh with new navigation, redesigned screens, and polished interactions.
Navigation
- #172 Tab bar navigation — Home, Contacts, and Settings tabs replace the previous single-screen layout
- #183 Contact detail opens as full-screen cover instead of push navigation
Home Screen
- #174 Redesigned header with status summary cards (Overdue, Due Soon, All Good counts)
- #175 Sticky section headers, floating search bar, and redesigned contact cards
Contact Detail
- #177 Redesigned notes card, history timeline with
TimelineEntryView, and contact settings section - #186 New hero section with action buttons and fixed bottom CTA
- #192 Delete option in Edit Connection modal with confirmation alert
- #193 Half-height picker sheets with titles for birthday, reminder time, snooze, due date, frequency, groups, and digest day
Contacts Tab
- New alphabetical A-Z contact list with section index sidebar and search
- Uniform row dividers throughout the list
Design System
- #171 Centralized design tokens — colors, typography, spacing, and radius constants in
DesignSystem.swift - #195 Restored random avatar colors (stored per contact) replacing the fixed accent green
Visual Polish
- #178 Dark mode audit — structural fixes for adaptive colors across all screens
- #179 Design token propagation through Settings and Onboarding screens
- #194 iPhone locked to portrait orientation
- Weekly Digest settings row color matched to Daily Reminders
- Groups row icon updated to
person.2to match Settings
Accessibility
- #180 VoiceOver labels, hints, and interaction polish across all screens
Bug Fixes
- Paused contacts no longer leak into Home "All Good" section or inflate summary card count
- Fixed double
.padding(.leading)stacking to 32pt on contact list dividers
Issues Closed
#150, #171, #172, #174, #175, #177, #178, #179, #180, #183, #186, #191, #192, #193, #194, #195
v0.3.1 — Rename, Custom Due Dates & Import Fixes
v0.3.1 — Rename, Custom Due Dates & Import Fixes
App Rename
- #139 Renamed from "Stay in Touch" to Keep In Touch — new bundle ID, background task ID, display name, all user-facing strings, and documentation updated. Internal directory/target/module names preserved for stability.
New Features
- #9 Custom due dates per contact — set a specific "reach out by" date that overrides the group frequency; auto-clears when a touch is logged
- #146 Frequency member management — tap a frequency in Settings to view/add/remove members, with swipe-to-remove reassigning to default
- #132 Last-connected date seeding — new onboarding/import step lets users pick "when did you last connect?" so home screen shows meaningful status from day one
- #141 Birthday display — cake icon + M/DD in contact header, auto-pulled from address book with manual override via editor sheet
Import/Export Improvements
- #114 Remove
cnIdentifierfrom JSON export (privacy) - #145 Export now includes frequencies and groups for complete backups, with case-insensitive merge on re-import
- #149 Post-import auto-matching to device address book by display name, with manual linking for unmatched contacts
- #162 Fix
.limitedcontacts auth excluded from CN dedup,Dictionary(uniqueKeysWithValues:)crash on duplicate identifiers, and synchronousenumerateContactsblocking main thread - Import deduplicates contacts (4-tier matching: UUID → CN → name → new) and touch events (content-based dedup)
- Imported touch events now correctly populate
lastTouchAt/lastTouchMethod/lastTouchNotes
Bug Fixes
- #152 Use calendar days instead of 24-hour periods in FrequencyCalculator — fixes "yesterday late evening" showing as "Today"
- #148 Default theme to System instead of Light for new installs
- #133 Fix notes truncation in touch history — dynamic row height replaces fixed 52pt
- #111 Add character limits: 500 chars for notes, 100 chars for search
- #140 Remove "Last connected Nd ago" badge that caused layout issues in detail header
Performance
- #112 Replace N+1 loop saves with
batchSavein tag/group deletion — deleting a tag used by 50 people now triggers 1 Core Data save instead of 50
Code Quality & Accessibility
- #113 Standardize
@MainActorusage, remove allDispatchQueue.main.asynccalls - #144 Add VoiceOver accessibility label to TagPill
- #147 Reorganize Settings view into 6 logical sections: Appearance, People, Notifications, Data, About, Danger Zone
15 PRs merged · 16 commits · All tests passing
v0.3.0 — Features, Security & Accessibility
What's New in v0.3.0
New Features
- #72 Contact photos — display iOS Contacts thumbnails with initials fallback and NSCache for scroll performance
- #73 JSON import — file picker, preview screen, conflict resolution by UUID matching
- #74 Enriched data export — includes
groupName,tagNames, and fulltouchEventshistory - #59 "Reset All Frequencies" — emergency button in Settings resets all contacts to today
- #51 Multi-value contact picker — choose between multiple phone numbers/emails with labeled options
- #52 Badge settings — configure badge count to show overdue only, or overdue + due soon
- #67 TelemetryDeck analytics — privacy-first, anonymous, opt-out toggle in Settings
Accessibility & UX
- #39 VoiceOver labels on home screen — ContactCard, StatusIndicator, section headers
- #43 "Last connected Xd ago" timestamp on PersonDetailView hero
- #71 Notification tap navigates via push (not modal sheet)
- #58 Onboarding back navigation with progress indicator
- #42 Animated section count transitions on home screen
- #44 Touch method icon grid replaces Picker dropdown
Security & Privacy (12 fixes)
- #126 Sanitize JSON import — regenerate UUIDs, clear cnIdentifier, remove cnIdentifier matching
- #125 AnalyticsService thread safety — NSLock, cached enabled state
- #109 Sanitize notification userInfo before deep link forwarding
- #104 Clean up temp export files after share sheet dismissal
- #99 Remove PII from log statements
- #100 Fix demo mode data isolation with isDemoData flag
- #101 Cascade deletes for Person→TouchEvent and Group/Tag cleanup
- #102 Fix notification handler thread safety (background context)
- #103 Verify and reorder PrivacyInfo.xcprivacy declarations
- #108 Replace silent
try?with proper error handling (14 sites, 5 files) - #98 Add NSContactsUsageDescription to Info.plist
- #107 Fix background contact sync race condition
Performance
- #81 Predicate-based contact fetching with
fetchBatchSize = 50
27 PRs merged · 12 security fixes · All tests passing
v0.2.2 — Performance, UX & Polish
What's New in v0.2.2
⚡ Performance
- #62 FrequencyCalculator optimized — single instance per render instead of multiple per card
- #63 Batch CoreData saves during contact import — single
context.save()instead of per-contact, reducing I/O for large imports
🤝 Auto-Log & Undo UX
- #54 Auto-log touch after quick action — tap Call/Message/Email, the app logs the connection automatically. On return, an undo banner appears for 5 seconds: "Logged call. Didn't connect?"
- #36 Undo for person deletion — 5-second red banner with "Undo" button before deletion commits, following the same pattern as auto-log undo
🔔 Notifications
- #61 Improved notification reliability — repeating triggers + foreground rescheduling ensure reminders fire even when background refresh is throttled
- #75 Quick-log from notification — "Log Connection" action button on person reminders logs a touch without opening the app
✨ Accessibility & Polish
- #53 Limited contact access handling — shows "grant full access" alert instead of misleading "up to date" when all selected contacts are imported
- #33 Design system typography token —
DS.Typography.heroTitlereplaces hardcoded font in PersonDetailView - #38 Proper FlowLayout — filter chips and tag pills now wrap correctly using the Layout protocol, replacing the WrapLayout stub
- #41 Haptic feedback — tactile responses on touch logging (success), deletions (medium), quick action auto-log and undo (light)
🧹 Code Quality
- #76 48 new ViewModel unit tests — PersonDetailViewModel, SettingsViewModel, OnboardingViewModel with shared mock infrastructure
- #82 Dead code removal — 3 unused methods deleted (ExportPerson helpers, digitsOnly)
- #83 Sort switch cleanup — removed misleading identical branches in HomeViewModel
- Bug fix: undo-wrong-touch and double-delete bugs in PersonDetailView
24 files changed · 1,415 additions · 152 deletions · Build 8 · All tests passing
Closes #62, #54, #53, #61, #76, #33, #63, #36, #75, #38, #82, #83, #41