Cookle is a SwiftUI recipe manager that lets you collect dishes, log your cooking history, and surface what to make next. The app ships on the App Store and this repository contains the full iOS project together with its shared Swift package.
- Personal recipe database backed by SwiftData models for names, photos, ingredients, steps, categories, servings, timings, and notes.
- Cooking diary that organizes breakfasts, lunches, and dinners per day so you can reflect on what you prepared.
- Dedicated photo gallery that separates imported images from Image Playground creations while preserving stored photo assets even after they are unlinked from recipes.
- Ingredient and category tag management with search and filtering to keep large collections tidy.
- Full-text search across recipe names, ingredients, and categories with smart handling of short and long queries.
- Optional iCloud sync and data deletion controls that stay behind a subscription paywall managed in Settings.
- App Shortcuts and App Intents for opening the app, running searches, showing the last or a random recipe, and creating diary entries.
- Image Playground integration (iOS 18.1+) to generate dish photos from recipe content.
- Google Mobile Ads monetization and StoreKit-based subscriptions configured through MHPlatform runtime defaults.
- Remote configuration fetch that can require users to update before continuing, keeping deployed binaries aligned with server rules.
- Developer utilities including a regular-width debug tab and preview helpers that seed SwiftData models for SwiftUI previews.
Cookle/– main SwiftUI application target with feature-based sources, platform wiring, remote-configuration adapters, and target configuration. App entry code lives inSources/App, product surfaces live inSources/Features, Apple-framework glue lives inSources/Platform, and reusable target-local UI lives inSources/SharedUI.CookleLibrary/– shared Swift package that exposes SwiftData models, Operations facades, predicates, migrations, and utilities used by the app and intents. Source and tests are organized by capability.Widgets/– home-screen widget extension target built on top ofCookleLibrary.Watch/– watch companion target for the active cooking session.MHPlatform– shared app-runtime package family. TheCookleapp intentionally adopts the defaultMHPlatformumbrella surface, whileCookleLibrarystays onMHPlatformCorefor core-safe shared logic.MHUI/MHDesign– shared presentation package family.Cookleadopts full MHUI for its root theme and selected presentation primitives, with existing metrics accessed through the MHDesign re-export. Product-specific screen composition stays in the app target.Designs/Architecture/– current architecture rules and placement guidance.Designs/Decisions/– architecture decision records that capture why major design choices were made.Designs/Overviews/– current project snapshot and product overview.CookleLibrary/Tests/Default/– package tests for public Operations contracts, reusable utilities, preferences, photo sources, and shared sub-object logic.ci_scripts/– automation helpers used by Xcode Cloud and CI pipelines to inject secrets and configure the build environment.
- Swift 6 toolchain with Xcode 26.3 project settings and a minimum deployment target of iOS 18.0.
- SwiftUI for all user interfaces, including adaptive tab navigation and preview infrastructure.
- SwiftData for persistence, schema migrations, and model container previews shared between the app and App Intents.
- AppIntents for Shortcuts support and automation workflows built on top of SwiftData entities.
- MHPlatform 1.x using the current consumer boundaries: the
Cookleapp target stays on the defaultMHPlatformumbrella,CookleLibrarystays onMHPlatformCore, and the repository keeps MHPlatform on the1.13.0..<2.0.0range to preserve verified subscription entitlement handling. - MHUI
2.3.0..<3.0.0through the fullMHUIproduct. The main app applies the neutral standard root theme and its native appearance once, keeping Cookle's orange accent app-owned as the native control tint. Product collections and editors use MHUI content List/Form presentation, recipe/cooking/diary/photo screens use stack composition, and settings and diagnostics keep native grouping and row geometry with MHUI's themed canvas and row surfaces. Content actions keep MHUI's non-glass default; any floating action opts into Liquid Glass only at its bounded control layer. See the screen rollout record for presentation exceptions and verification coverage. - Cookle does not keep a generic utility package dependency. Small app-owned helper behavior stays local, while generic utilities and thin host-app presentation shortcuts remain outside MHUI.
- MHPlatform-managed StoreKit, Google Mobile Ads, and license presentation delivered through Swift Package Manager.
Cookle follows Apple's app architecture closely, but keeps reusable logic in the shared package so multiple targets can call the same workflows.
Primary records:
-
CookleLibraryowns shared SwiftData models, public*Operationsfacades, predicates, queries, validation, mutations, migrations, and route helpers. -
MHPlatform consumer boundaries are explicit in this repo:
Cookleis the umbrella-app adopter,CookleLibrarystays onMHPlatformCore, andWidgets/Watchstay off the umbrella. -
MHUI consumer boundaries are explicit: only
Cookleadopts full MHUI.CookleLibrarystays presentation-free and Widgets retains native WidgetKit composition. Watch adoption is deferred after a failed MHUI 1.18 readability comparison. Companion targets and App Intent implementations currently stay off MHUI/MHDesign dependencies; future adoption is evaluated per surface. -
MHAppRuntimeremains available as an advanced app-root surface, but this repo does not use it as the default adoption path. -
Repository-owned unit tests stay concentrated in
CookleLibrary/Tests/Default. -
The repository does not maintain a separate
CookleTestsunit test target; app adapters are verified throughCooklebuilds plus shared-library tests. -
The
Cookleapp target owns workflow services such asRecipeActionService,DiaryActionService,PhotoActionService,TagActionService, andSettingsActionServicethat add app-only side effects after shared mutations. -
The root
CookleAppAssemblycentralizes model-container wiring, app service graph assembly, runtime bootstrap, and environment injection for the live app and SwiftUI previews. -
App startup and foreground refreshes are driven through
MHAppRuntimeBootstrapandMHAppRuntimeLifecyclePlaninstead of ad-hocscenePhasehandlers. -
SwiftUI views and App Intents call workflow services for commands instead of mutating models directly, and delivery surfaces call
*Operationsfacades instead of service collaborators for shared business use cases. -
RecipeOperations.searchis the canonical recipe search API used by views, intents, and widgets. -
Route parsing and execution stay shared so deep links, widgets, and intents speak the same navigation language through a single
MHAppRoutePipeline. -
Mutation follow-up uses
MHMutationWorkflow.runThrowing(..., adapterValue:),MHMutationProjectionStrategy, andMHReviewFlowinstead of app-local wrapper layers around successful mutations. -
App-owned preference persistence extends
MHPreferenceDescriptors, and the repository keepsCookleUserDefaultsKeysas the central key catalog plus a single known-descriptor catalog for lifecycle cleanup and storage audits. -
Route meaning, notification copy and delivery meaning, review eligibility, and mutation-effect interpretation remain app-owned even when orchestration uses MHPlatform shells.
CookleLibrary defines all persisted entities so the app, intents, and previews share the same schema.
Recipestores the core cooking data, maintains relationships to photos, ingredients, categories, diaries, and tracks timestamps for sync logic.Diarycaptures a specific day along with orderedDiaryObjectchildren that tag meals as breakfast, lunch, or dinner.Photostores binary image data, the capture source, and reverse links to the recipes using it, enabling shared galleries.CategoryandIngredientmodels act as tags for recipes, and provide predicates for filtering and App Intent queries.DiaryObject,PhotoObject, andIngredientObjectare parent-owned structural rows, whileRecipe,Diary,Photo,Category, andIngredientare the main persisted root or shared records.- The schema is versioned through
CookleMigrationPlan, making room for future migrations without data loss.
Cookle exposes several intents so users can automate their workflows.
CookleShortcutsregisters the app shortcuts and updates model containers based on the current iCloud setting.- Recipe intents cover open, create, update, delete, search, last-opened, and
random suggestions, with mutations routed through
RecipeActionService. - Diary intents cover create, update, delete, add-to-today, and show flows, and
use
DiaryActionServicefor shared command handling. - Tag rename intents wrap
TagActionService. Category delete now uses the same shared action flow as the product UI, and ingredient delete now succeeds only when the ingredient is unused. - Photo delete is exposed only from photo-centric UI, and it discloses how many linked recipe photo rows will be removed before deleting the asset.
- Settings and navigation intents open the same route-based destinations used by deep links and widgets.
- On iOS 26.0 and later, recipe inference uses Foundation Models when available and falls back to deterministic extraction when the model is unavailable or cannot produce a meaningful result.
Cookle uses a route-based deep-link system shared by the app and widgets.
- Custom scheme:
cookle://... - Universal Links:
https://muhiro12.github.io/Cookle/...
Supported routes:
homediarydiary/YYYY-MM-DDreciperecipe?id=<base64PersistentIdentifier>photophoto?id=<base64PersistentIdentifier>tag/categorytag/category?id=<base64PersistentIdentifier>tag/ingredienttag/ingredient?id=<base64PersistentIdentifier>searchsearch?q=<query>settingssettings/subscriptionsettings/license
Legacy widget URLs such as cookle://widget/diary are no longer supported.
Universal Links require Apple App Site Association (AASA) deployment for
muhiro12.github.io.
- A StoreKit subscription unlocks premium features such as iCloud sync, and the app observes the runtime's verified entitlement state independently of product catalog metadata. Unknown state preserves existing preferences; active state preserves the user's iCloud choice, and inactive state disables iCloud sync.
- Settings surfaces the subscription paywall, iCloud toggle, and bulk delete controls while guarding destructive actions behind confirmation dialogs.
- Remote configuration is loaded from GitHub to determine whether the current build must force an update before the main UI is shown.
- Root lifecycle tasks refresh remote configuration, notification schedules, and pending routes during launch and foreground re-entry. Subscription changes update local preferences as soon as the runtime publishes a resolved state.
- Google Mobile Ads native placements are embedded through the shared runtime so
ad units can be refreshed from a single place. Placements use
MHNativeAdSizeand omit the whole section when the cached subscription or runtime availability suppresses ads.
- Clone the repository and open the workspace directory.
- Open
Cookle.xcodeprojin Xcode 26.3 or later and select the Cookle scheme. - Build and run on an iOS 18 simulator or device.
The monetization identifiers live in
Cookle/Sources/Platform/CookleMonetizationConfiguration.swift. They
are source-controlled production identifiers, not local-only credentials.
-
Repository-owned unit tests stay in
CookleLibrary/Tests/Default. -
Cookle,Widgets, and App Intents are verified through app builds plus shared-library tests instead of a separate app unit test target. -
Use the Xcode-native integration available in the agent environment for Apple build, test, run, Simulator, runtime-log, Preview, live UI, and screenshot evidence.
-
For app compile checks, use its build capability with project
Cookle.xcodeproj, schemeCookle, and a discovered iOS Simulator destination. -
For shared-library tests, use its test capability with the
CookleLibraryscheme and a compatible discovered destination. -
For Watch builds and previews, use the shared
Watchscheme and a discovered watchOS Simulator destination. Verify paired delivery separately. -
For runtime or UI-sensitive checks, add a targeted run, runtime-log review, Preview rendering when appropriate, and live UI or screenshot evidence.
-
Run retained repository rule checks after Xcode-native build/test evidence:
bash ci_scripts/tasks/check_repository_rules.sh
-
Run the manual unused code audit only after an Xcode-native build has refreshed a supported DerivedData index:
bash ci_scripts/tasks/check_unused_code.sh
ci_scripts/ci_post_clone.shadjusts Xcode defaults for plugin validation inside automated builds.
The repository contract is Xcode-native-first:
Direct entrypoints live in ci_scripts/tasks/, shared shell helpers live in
ci_scripts/lib/, and ci_scripts/ci_post_clone.sh is reserved for external
post-clone CI setup.
- The available Xcode-native integration owns Apple build, test, run, Simulator, runtime-log, Preview, live UI, and screenshot evidence.
bash ci_scripts/tasks/check_environment.sh --profile <swiftlint|rules>diagnoses missing local prerequisites before retained shell checks.bash ci_scripts/tasks/format_swift.shis the explicit SwiftLint autofix step to run after Swift edits.bash ci_scripts/tasks/check_repository_rules.shruns retained SwiftLint and static architecture checks that are not naturally covered by the available Xcode-native integration.- Xcode Cloud owns formal CI builds, tests, and archives.
- Release UI smoke auditing is intentionally separate from the normal verify
gate. Use the global
$xcode-ui-smoke-auditorskill and the release UI smoke audit guide when a release or UI-sensitive change needs live Simulator evidence. bash ci_scripts/tasks/check_unused_code.shruns the opt-in Periphery audit after an Xcode-native build has refreshed a supported index store.
SwiftLint is resolved from the SimplyDanny/SwiftLintPlugins package declared
in Cookle.xcodeproj. The repository scripts do not require a separately
installed swiftlint binary on your PATH.
Before running retained repository rules, diagnose the local prerequisites:
bash ci_scripts/tasks/check_environment.sh --profile rulesAfter Swift edits, run the explicit autofix step:
bash ci_scripts/tasks/format_swift.shThen run retained repository rules:
bash ci_scripts/tasks/check_repository_rules.shIf you prefer to run the SwiftLint steps directly:
bash ci_scripts/tasks/format_swift.sh
bash ci_scripts/tasks/lint_swift.shPeriphery is an opt-in manual audit tool in this repository. It is not part of the standard Xcode-native build/test and retained-rule flow.
Install periphery manually before using the audit task. For example:
brew install peripheryThen build the Cookle scheme through the available Xcode-native integration.
The audit task uses PERIPHERY_INDEX_STORE_PATH when set. Otherwise, it
selects the index with the most recently modified versioned unit artifacts
across the repository shared DerivedData location and matching directories
under Xcode's default DerivedData root.
Run the audit after the Xcode-native build completes:
bash ci_scripts/tasks/check_unused_code.shThe repository keeps stable scan options, including skip_build, in
.periphery.yml; the task passes the selected index store explicitly. Set
PERIPHERY_INDEX_STORE_PATH to an existing DataStore directory when Xcode
uses another DerivedData location. Relative override paths are resolved from
the repository root. This repository does not maintain a Periphery baseline
file. Keep intentional framework entry points with // periphery:ignore when
needed.
Cookle helper scripts may write disposable cache data under .build/ci/shared/.
Xcode-native build and test evidence is owned by the active integration. The
repository keeps .build/ci/shared/DerivedData as a compatibility DerivedData
location, while opt-in follow-up tools such as Periphery can also use Xcode's
default DerivedData index.
| iPhone | iPhone | iPhone |
|---|---|---|
![]() |
![]() |
![]() |
| iPad |
|---|
![]() |
![]() |
![]() |





