Commit e65811e
Substrate v2: Generic CRUD app foundation (#47)
* Phase 2A-1: Port ItemTag schema update from paid iOS
Ports nativeapptemplate/NativeAppTemplate-iOS#49
- ItemTag model: queueNumber → name; add description/position;
remove scanState/customerReadAt/alreadyCompleted
- ItemTagAdapter: parse name/description/position; drop legacy attrs
- PermissionsRequest: maximum_queue_number_length meta now optional
with default 256 (full removal deferred to 2A-3)
- SessionController / App.swift null stub: maximumQueueNumberLength
default 0 → 256
- ViewModels: queueNumber → name; validateQueueNumberLength →
validateNameLength; hasInvalidDataQueueNumber → hasInvalidDataName
- Drop 5 production call sites of the deleted fields
(ShowTagInfoScanResultView, ScanViewModel, ShopDetailViewModel,
ShopDetailCardView, MainViewModel) — each flagged
// TODO: removed in Phase 2A-2
- Shop model: drop displayShopServerPath / displayShopServerUrl; drop
"Open Server Number Tags Webpage" link from ShopDetailView; delete
NumberTagsWebpageListView/ViewModel and the Shop Settings section
that linked to it
- Tests: update helpers and adapter fixtures for the new schema;
delete ScanViewModelTest (queue/NFC-specific, goes away in 2A-2),
completeTagWhenAlreadyCompleted test (asserted the removed branch),
and NumberTagsWebpageListViewModelTest
Main target and test target both compile. Some tests may fail at
runtime (deferred to Phase 2A-4 per plan).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Phase 2A-2: Remove NFC / QR / Scan
Ports nativeapptemplate/NativeAppTemplate-iOS#50
to the Free iOS app. Drops all NFC, QR-code, and Scan-tab functionality;
ItemTagDetailView becomes a placeholder until Phase 2A-3 lands the full
generic CRUD UI.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* remove NativeAppTemplate.xcsheme
* Restore item-tag swipe actions on ShopDetailView; rename reset → idle
Reverts the upstream PR's removal of the complete/idle swipe actions on
ShopDetailView so users can still mark items completed or send them back
to idled without going through Settings.
Renames the user-facing "reset" terminology to "idle" since the action
just transitions a completed tag back to the idled state:
- ItemTag UI/ViewModel/strings: resetTag → idleTag, isResetting → isIdling,
itemTagReset[Error] → itemTagIdled[Error], button label "Reset" → "Idle".
- ItemTag repository layer: ItemTagRepositoryProtocol.reset → idle,
ItemTagRepository / DemoItemTagRepository / TestItemTagRepository updated.
- ItemTag network layer: ResetItemTagRequest → IdleItemTagRequest,
ItemTagsService.resetItemTag → idleItemTag, HTTP path
/shopkeeper/item_tags/:id/reset → /shopkeeper/item_tags/:id/idle.
Backend must expose PATCH /shopkeeper/item_tags/:id/idle.
Shop repository / network layer left as `reset` (no UI surfaces it).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Silence success toasts on item-tag complete/idle in ShopDetailView
Ports nativeapptemplate/NativeAppTemplate-iOS#52.
The swipe action already reflects the state change via ShopDetailCardView's
re-render after reload(); the extra success toast was redundant noise.
Errors still post to the message bus so users still see feedback when
something actually fails.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Phase 2A-3: Generic CRUD UI for ItemTag
Ports nativeapptemplate/NativeAppTemplate-iOS#53
to the Free iOS app. Replaces the Phase 2A-2 ItemTagDetailView placeholder
with a full generic CRUD UI; relaxes Create/Edit validation; adds a
description field; makes ItemTag.position non-optional.
Schema:
- ItemTag.position: Int? -> Int = 0; toJson() excludes position (server
auto-assigns).
- ItemTagAdapter strict-guards position; throws invalidOrMissingAttributes
when missing.
- Mock ItemTag(position: nil) -> position: 1 across tests.
Rename:
- maximumQueueNumberLength -> maximumNameLength across SessionController,
protocol, App.swift NullSessionController, TestSessionController,
PermissionsRequest. Default fallback 256 -> 100.
- PermissionsRequest reads meta["maximum_name_length"].
Create + Edit:
- Validation: name 1-100 chars (any string), description 0-1000 chars.
Drops alphanumeric and count >= 2 checks; standard keyboard.
- New description field with multi-line TextEditor.
- Edit's hasInvalidData also requires name OR description changed.
Detail view full rewrite:
- State badge (IdlingTag / CompletedTag), description display,
completedAt timestamp.
- Toggle button (Mark as completed / Mark as idled) calling
ItemTagRepository.complete(id:) / idle(id:).
- New isToggling, completeItemTag(), idleItemTag(); failure posts error
toast, success silently re-renders (matching PR #52).
ItemTagListCardView: name + description preview (2 lines) + state badge +
completedAt.
Constants: removed tagNumber/tagNumberIsInvalid; added nameLabel,
descriptionLabel, itemTagNamePlaceholder, itemTagNameIsInvalid,
itemTagDescriptionIsInvalid, completedAtLabel, markAsCompleted,
markAsIdled, itemTagNameHelp/itemTagDescriptionHelp helpers, and
NativeAppTemplateConstants.maximumItemTagDescriptionLength = 1_000.
Deviations from upstream PR #53:
- Skipped permission gates (canUpdateShops/canDeleteShops/canCreateShops)
- Free repo has no Permission system. Edit/delete/toggle/create are
unconditionally available. Skipped the upstream permissionChecking
parameterized tests for the same reason.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Bump TestSessionController.maximumNameLength default 4 → 100
The new hasInvalidDataChangeDetection test in ItemTagEditViewModelTest
sets name = "Updated" (7 chars), which tripped the old default of 4
(carried over from the queue-number-length era). Match upstream Paid PR's
default of 100.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* remove scan folder
* Display completedAt as yyyy/MM/dd HH:mm regardless of locale
Ports nativeapptemplate/NativeAppTemplate-iOS#54.
ItemTagDetailView's completed-timestamp row was using
Text(completedAt.formatted()), which is locale-dependent — same instant
rendered as 4/26/2026, 10:30 AM (en_US), 26/04/2026, 10:30 (en_GB), or
2026/04/26 10:30 (ja_JP). Non-Gregorian calendars (Buddhist, Japanese
imperial) could shift the year value entirely.
Introduces Date.cardDateTimeString that stitches existing cardDateString
+ cardTimeString format constants, and pins Locale(identifier:
"en_US_POSIX") on the shared DateFormatter.formatter(for:) factory so
every fixed-format formatter is calendar/locale-stable.
- DateFormatter+Extensions.swift:
- cardDateString: "MMM dd yyyy" → "yyyy/MM/dd" (the MMM token was
locale-aware; the constant had zero callers).
- formatter(for:) factory: pins Locale(identifier: "en_US_POSIX").
- Date+Extensions.swift: new cardDateTimeString computed property.
- ItemTagDetailView, ShopDetailCardView, ItemTagListCardView: switched
to cardDateTimeString.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Remove "How To Use" link from Support section in SettingsView
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Remove NativeAppTemplate.entitlements
The associated-domains entitlement is no longer needed; the entitlements
file is now empty and dropped. CODE_SIGN_ENTITLEMENTS unset for both
Debug and Release configs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Drop maximum_name_length from permissions; move to Constants
Ports nativeapptemplate/NativeAppTemplate-iOS#59.
Two-step cleanup of maximumNameLength:
1. Stop reading maximum_name_length from /shopkeeper/permissions. The
client already tolerated its absence via a "?? 100" fallback, so this
is dead plumbing. Drops the field from PermissionsResponse, the meta
read in PermissionsRequest.handle, and the assignment in
SessionController.fetchPermissions.
2. Move maximumNameLength from SessionController to Constants. Now that
it's a fixed value, it's just a constant — like
maximumItemTagDescriptionLength. Adds
NativeAppTemplateConstants.maximumItemTagNameLength = 100, drops
maximumNameLength from SessionControllerProtocol / SessionController /
NullSessionController / TestSessionController. ItemTagCreateViewModel
and ItemTagEditViewModel now read the constant directly and no longer
take sessionController. Updates the call sites in ItemTagListView /
ItemTagDetailView. Tests rewritten to drop sessionController field
and update truncation tests to use 100+ char strings.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add client-side length caps + truncation for Shop name/description
Ports nativeapptemplate/NativeAppTemplate-iOS#60.
Mirror the ItemTag pattern from PR #49 for Shop. Server has no caps on
Shop name/description; this is a client-only UX guard.
- Constants: maximumShopNameLength = 100, maximumShopDescriptionLength
= 1_000. New strings: shopNameIsInvalid, shopDescriptionIsInvalid,
plus shopNameHelp(maximumLength:) / shopDescriptionHelp(maximumLength:)
parametric helpers.
- ShopCreateViewModel + ShopBasicSettingsViewModel: split hasInvalidData
into hasInvalidDataName + hasInvalidDataDescription, expose
maximumNameLength / maximumDescriptionLength, add validateNameLength()
/ validateDescriptionLength().
- ShopCreateView + ShopBasicSettingsView: wire .onChange truncation on
Name and Description; switch to two-line footer (always-visible help
+ conditional red "is invalid" text), matching ItemTagCreateView.
- Tests: added maximumNameLength, maximumDescriptionLength, parametric
nameValidation / descriptionValidation, and the two truncation tests
on both viewmodel test suites; replaced the old simple
hasInvalidData(name:) parametric test in ShopCreateViewModelTest.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Drop unused tags-scanned count and howToUse; tighten ItemTag labels
- Drop scannedItemTagsCount from Shop model, adapter, demo repo, and tests; remove the "tags scanned by customers" stat from ShopListCardView.
- Replace ShopDetailView "Learn More" link with a left-aligned shopDetailInstruction ("Swipe an item tag to change its status.").
- Swap the OnboardingView "How to use" link for the Support Website link; trim onboarding from 13 to 8 descriptions and drop unused String(localized:) wrap.
- Normalize ItemTag string keys: editTag/addTag/deleteTag/etc. -> editItemTag/addItemTag/deleteItemTag/etc.
- Drop unused constants: howToUseUrl, learnMore, createShops, createTags.
* Wrap String constants in enum Strings namespace
Convert the large `extension String` block in `Constants.swift` to a
caseless `enum Strings` namespace, matching the existing
`NativeAppTemplateConstants` idiom in the same file. Inline the
two `cardDateString`/`cardTimeString` literals in
`DateFormatter+Extensions.swift` and remove the small extension
they lived in. Update call sites across the app and tests from
`String.foo` to `Strings.foo`, including dot-shorthand uses for
`message:`, `text:`, `buttonTitle:`, and `?.message ==` parameters.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Replace onboarding image assets with new set; add hero asset
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Slim onboarding to 4 slides; ImageOrientation enum; hero asset
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Drop onboarding reload ceremony
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add unit tests for untested Models and Date extension
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Ignore .claude/scheduled_tasks.lock
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Remove vestigial code from NFC/Scan removal
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* README: drop stale NFC/QR feature lines
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Remove dead code: ImageSaver, composited(), unused colors and fonts
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Cleanup: dead Date helper, swiftier String predicates, README env vars
- Drop unused cardTimeAgoInWordsDateString and its timeAgoInWordsDateFormatter
- Move isBlank and validateEmail from Utility static helpers to String
computed-property extensions (isBlank, isValidEmail) and update all call
sites in production and tests
- Fold UtilityTest cases into StringExtensionsTest and delete UtilityTest
- README: clarify that Xcode is in practice the only NATEMPLATE_API_* env
var injector, so the hardcoded fallbacks are what keep Debug builds
working when launched from SpringBoard or after Xcode disconnects
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Remove Beta build configuration
The Beta config has no shared scheme and isn't referenced by CI, README, or
any xcconfig — drop it from the pbxproj so the project only ships Debug and
Release.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Remove unused String helpers: image(withAttributes:size:), isAlphanumeric
The String.image(...) renderer and both isAlphanumeric() overloads have no
production callers — only their own tests reference them. Drop the helpers
and their tests; UIKit/Foundation imports collapse to Foundation since the
remaining isBlank / isValidEmail need only that.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* add docs-private to gitignore
* phase2-prestep-number-tag-rename.md removed
* Normalize substrate naming: drop NATI-/NATEMPLATE_API_ abbreviations
- Env vars: NATEMPLATE_API_{SCHEME,DOMAIN,PORT} -> NATIVEAPPTEMPLATE_API_*
in Constants.swift and README.md
- Error codes: NATI-XXXX -> NATIVEAPPTEMPLATE-XXXX across production and
test Swift files, plus CHANGELOG.md
- CLAUDE.md: error-code section reframed as a shared iOS+Android prefix;
dropped the stale NFCError.swift / NATIVEAPPTEMPLATE-3xxx row (file no
longer present in this fork)
Mirrors upstream PR nativeapptemplate/NativeAppTemplate-iOS#71.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Add CHANGELOG entry for substrate naming normalization
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Update CHANGELOG for 3.2.0 release
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Bump version to 3.2.0 (build 10)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent d67e78e commit e65811e
224 files changed
Lines changed: 1911 additions & 5054 deletions
File tree
- NativeAppTemplate.xcodeproj
- NativeAppTemplateTests
- Data/ViewModels
- Demo/Data/Repositories
- Extensions
- Models
- Networking/Adapters
- Testing
- Repositories
- UI
- App Root
- Scan
- Settings
- Shop Detail
- Shop List
- Shop Settings
- ItemTag Detail
- ItemTag List
- Utilities
- NativeAppTemplate
- Common/Errors
- Data/Repositories
- Extensions
- Login
- Models
- Networking
- Adapters/EntityAdapters
- Network
- Requests
- Services
- Persistence/KeychainStore
- Resources/Assets.xcassets
- Onboarding
- onboarding1.imageset
- onboarding10.imageset
- onboarding11.imageset
- onboarding12.imageset
- onboarding13.imageset
- onboarding1Slim.imageset
- onboarding2.imageset
- onboarding3.imageset
- onboarding4.imageset
- onboarding5.imageset
- onboarding6.imageset
- onboarding7.imageset
- onboarding8.imageset
- onboarding9.imageset
- hero.imageset
- Sessions
- Styleguide
- UI
- App Root
- Empty States
- Scan
- Settings
- Shop Detail
- Shop List
- Shop Settings
- ItemTag Detail
- ItemTag List
- Utilities
- docs
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
89 | 89 | | |
90 | 90 | | |
91 | 91 | | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7 | 7 | | |
8 | 8 | | |
9 | 9 | | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
10 | 39 | | |
11 | 40 | | |
12 | 41 | | |
| |||
17 | 46 | | |
18 | 47 | | |
19 | 48 | | |
20 | | - | |
| 49 | + | |
21 | 50 | | |
22 | 51 | | |
23 | 52 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
107 | 107 | | |
108 | 108 | | |
109 | 109 | | |
110 | | - | |
| 110 | + | |
111 | 111 | | |
112 | 112 | | |
113 | 113 | | |
114 | | - | |
115 | | - | |
116 | | - | |
| 114 | + | |
| 115 | + | |
117 | 116 | | |
118 | 117 | | |
119 | 118 | | |
120 | 119 | | |
121 | | - | |
| 120 | + | |
122 | 121 | | |
123 | 122 | | |
124 | 123 | | |
| |||
Large diffs are not rendered by default.
Lines changed: 0 additions & 95 deletions
This file was deleted.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
36 | | - | |
37 | | - | |
38 | | - | |
39 | 36 | | |
40 | 37 | | |
41 | 38 | | |
42 | | - | |
43 | 39 | | |
44 | 40 | | |
45 | 41 | | |
| |||
This file was deleted.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
| 19 | + | |
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
6 | | - | |
7 | | - | |
8 | | - | |
| 6 | + | |
| 7 | + | |
9 | 8 | | |
10 | 9 | | |
11 | 10 | | |
| |||
This file was deleted.
0 commit comments