iOS app for tracking people and viewing their current age broken down into time components — years, months, days, hours, minutes and seconds — updating live.
- Keep a list of people, each with a name, birth date and photo.
- Add or edit a person, including picking and cropping a photo.
- Person overview with the age counting up in real time.
- A Home Screen widget (WidgetKit) in three sizes shows up to six pinned people as photo tiles with a big milestone age and birthday countdowns, refreshing only when that text changes.
- Lock Screen widgets (ring, rectangular and inline) with the first pinned person's age and next birthday, also shown in StandBy.
- Data is shared between the app and the widget through a shared App Group.
- Localized in English and Russian.
The project follows MVP + Clean Architecture.
Core— a framework holding the business logic, free of UIKit scene code:Entities—Person,PersonImage,AddPersonParameters.UseCases—Add/Edit/Remove/FetchPersonsuse cases.Gateways/EntityGateway— persistence and caching behind protocols.AgeCalculator— converts a birth date into time components.
GrowingUp— the SwiftUI app target.Presentation/contains the people, overview, empty and editor scenes. Observable main-actor presenters own scene state, andSceneConfiguratorinjects Core use cases.Scenes/EditPerson/ImageCapture/contains the shared photo, crop and camera views and platform adapters.Widget— the WidgetKit extension (SwiftUI), reusingCorefor its data.
- Core Data (
GrowingUp.xcdatamodeld) is the source of truth, stored in a shared App Group container (group.pro.ziganshin.aging) so the app and the widget read and write the same data. - Disk caches person images in the shared container.
Managed with Swift Package Manager and resolved automatically by Xcode — no Carthage or CocoaPods step is required:
- Disk — file/image persistence.
- SnapshotTesting — UI-test image comparisons only.
- Xcode 26 or newer (CI pins Xcode 26.6 and 26.3).
- iOS 18.1+ deployment target.
- swift-format uses the
repository
.swift-formatpolicy. Runscripts/format-swift.shto format all first-party Swift sources orscripts/check-formatting.shto check without changing files. The scripts use the formatter from the selected Xcode toolchain on macOS and a standaloneswift-formaton Linux. On Linux, install the pinned toolchain withswiftly install. CI uses Xcode's formatter. VS Code formats on save through Xcode's SourceKit-LSP via.vscode/settings.json; install the recommended Swift and SweetPad extensions. For full code completion, install the SweetPad CLI (brew install sweetpad-dev/tap/sweetpad) and runsweetpad bsp initonce per clone. The generatedbuildServer.jsonis machine-specific; keep it out of Git. - SwiftLint runs as an optional build
phase (skipped with a warning if not installed); run
scripts/lint-swift.shfor the same strict check used in CI. - Git hooks are installed once per clone with
scripts/install-git-hooks.sh. They check formatting and SwiftLint before every commit. Fix reported violations instead of bypassing the hook. - fastlane lanes under
fastlane/handle TestFlight distribution andmatch-based signing for release builds. - CI — GitHub Actions (
.github/workflows/ci.yml) checks formatting, runs SwiftLint, and builds + tests on an iOS Simulator on every pull request and on pushes tomaster.
One-time Mac setup (an existing App Store Connect API key JSON, kept outside Git):
scripts/testflight setup local --api-key /path/to/asc_api_key.jsonGenerate English/Russian notes with your signed-in local Codex CLI and review the printed text. No filename is needed:
scripts/testflight notesThen choose one deployment location:
scripts/testflight deploy local --version 2.2.0 # build and upload on your Mac
# or
scripts/testflight deploy cloud --version 2.2.0 # build and upload in GitHub ActionsBoth paths select the next free build number, apply matching app/widget versions,
sign, upload, publish EN/RU notes and distribute to the tester group. Omit --version
to choose the next patch above the recorded Apple/beta versions. New explicit
versions must exceed the approved App Store version. Notes are regenerated if the
source changes. Credentials stay in the Mac Keychain/private API key file locally,
or the existing protected GitHub testflight environment in the cloud.
scripts/testflight check local (or cloud) checks without uploading;
archive local (or cloud) creates an archive without uploading.
Use status local / status cloud to check the latest release and resume local /
resume cloud to continue it after an interruption, preserving its version/build.
An optional release ID selects an older release. Apple beta review may still be
required before the build appears on a tester's device.
See release setup for prerequisites and compatibility commands.
- Danis Ziganshin — Initial work — zigdanis
GrowingUp is available under the MIT License.