Skip to content

Read the App Store page from a build setting and fix rename leaks - #113

Merged
dadachi merged 1 commit into
mainfrom
fix/app-store-id-and-rename-safety
Oct 18, 2026
Merged

dadachi merged 1 commit into
mainfrom
fix/app-store-id-and-rename-safety

Conversation

@dadachi

@dadachi dadachi commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

Problem

Fix

#109: App Store page from a build setting

  • New AppStore.xcconfig with APP_STORE_ID = (empty), overridable in the gitignored AppStore.local.xcconfig, and a new Project.xcconfig that includes it, set as the project-level base configuration of Debug and Release, as in NativeAppTemplate-iOS (where it also includes RevenueCat.xcconfig / Sentry.xcconfig). The target's Debug base stays API.xcconfig. Built into Info.plist as AppStoreID.
  • Strings.appStoreId reads it; Strings.appStoreUrl is now URL?: https://apps.apple.com/app/id<APP_STORE_ID>, nil when the ID is empty or not all digits (Strings.appStorePage(appStoreId:)).
  • Design choice: the setting is the Apple ID, not a URL, and the button is hidden (not disabled) while it is empty.
    • An Apple ID is just digits, so it cannot hit the xcconfig // comment trap (a URL would need https:/$()/…), and it is easy to validate.
    • Hiding: a disabled button an end user cannot use explains nothing. Without it the screen still shows "Update App" / "Please install new version app.", so users update from the App Store app. The copy is unchanged (no new strings to translate).
  • MainView passes { NeedAppUpdatesView() } instead of NeedAppUpdatesView.init, since the view now has an init(appStoreUrl:) with a default.
  • README "App Store page", CHANGELOG.

#110: no domain words as unknown types

otherTypeNamesHaveNoEntityType now checks account (not in EntityType), shopkeeperSignIn (camelCase) and Shop (capitalized), keeping the "only those exact names" intent.

  • account is fine here: the agent does not rename it, and Free's EntityType has no account.
  • Free's EntityType has only domain types, so a camelCase / capitalized near miss has to use a domain word. These two stay different from the real names after any rename: shopkeeper_sign_in → <x>_sign_in vs <x>SignIn (sign_in is not renamed), and the rename keeps case, so Shop → Clinic vs shop → clinic. itemTag was the only one that collapsed (item_tag and itemTag both become the same single word).
  • NativeAppTemplate-iOS (same test name and shape) uses non-domain unknownThing, accountsInvitation, Account, since its EntityType has non-domain types.

#111: Local Network prompt

"Connects to the app's API server running on your local network during development." in Info.plist and InfoPlist.xcstrings (en). The Japanese is unchanged. New LocalizationTest.noArticleBeforeARenamedWord fails on "a"/"an" right before shop(s), shopkeeper(s), item tag(s) or the product name in the English strings (Localizable keys and en values, InfoPlist en values, the built Info.plist's usage descriptions). The pattern itself is renamed with the rest, so it keeps working in generated apps.

#112: comments (and other occurrences found by grep)

\b[Aa]n? (item ?tag|shop|shopkeeper|NativeAppTemplate) (case-insensitive, also across line breaks):

File Was Now
ItemTagRepositoryTest.swift:90 An item tag resource … Item tag resource …
LocalizationTest.swift:77 An ItemTag is アイテムタグ in full: nativeapptemplate-agent renames … ItemTag is アイテムタグ in full: the agent renames …
LocalizationTest.swift:66 … nativeapptemplate-agent renames 店舗 … … The agent renames 店舗 …
APIEndpointTest.swift:27 nativeapptemplate-agent passes … The agent passes …
ShopRepositoryTest.swift:74 appends a shop it does not have, replaces one it has appends what it does not have, replaces what it has
RootPathServicesTest.swift:32 A shopkeeper resource … Shopkeeper resource …
DataStateListContentTest.swift:12 while a shop was open while shop detail was open
CHANGELOG.md [Unreleased] or an ItemTag not written … or ItemTag not written …

Left as is: CONTRIBUTING.md's quoted bad example ("Swipe an item tag …"), and the mentions of / links to nativeapptemplate-agent in AGENTS.md / CONTRIBUTING.md (docs about the agent, not code or test comments).

Tests

  • New NeedAppUpdatesViewTest: no page for "", " ", id…, a whole URL, 12a4; https://apps.apple.com/app/id1234567890 for 1234567890; the button is there with a page and gone without one (via AccessibilityElements), while the instruction text stays.
  • InfoPlistTest.declaresTheAppStoreID and theAppStoreIDIsEmptyOrAnAppleID.
  • Seen red in NativeAppTemplate-iOS, whose code and tests are the same: with the old prompt text noArticleBeforeARenamedWord failed; with the button always shown noAppStorePageHidesTheUpdateAppButton failed; without the digits check noAppleIdHasNoAppStorePage failed (4 cases); APP_STORE_ID = abc in AppStore.local.xcconfig failed theAppStoreIDIsEmptyOrAnAppleID. Here, APP_STORE_ID = 1234567890 in AppStore.local.xcconfig reached the built Info.plist (AppStoreID) in both Debug and Release, so the new project-level base configuration is wired.

Verification

  • make lint: 0 SwiftLint violations, 0 files need SwiftFormat
  • Full suite on the iPhone 17 Pro Max simulator (iOS 27.0, Xcode 27.0): all passed, 0 failures
  • Not exercised in the running app: the update screen needs an AppVersion with forced_update in the API. The view is rendered by NeedAppUpdatesViewTest instead.

🤖 Generated with Claude Code

https://claude.ai/code/session_01UZJxUoubntzu7Ec7DPVfAr

Strings.appStoreUrl was hardcoded to MyTurnTag Creator's App Store page,
so the forced-update screen's Update App button sent users of every app
built from the template there. It now comes from APP_STORE_ID, the app's
Apple ID, in the new AppStore.xcconfig (included by the new
Project.xcconfig, now the project-level base configuration; overridable
in the gitignored AppStore.local.xcconfig), built into
Info.plist as AppStoreID: https://apps.apple.com/app/id<APP_STORE_ID>.
Empty, as committed, or not all digits hides the button; the screen
still asks users to install the new version. An ID avoids the xcconfig
"//" comment trap a whole URL would hit.

Rename safety (SUBSTRATE-CONTRACT rules 1, 5 and 6):
- JSONAPIDocumentTest used "itemTag" as an unknown type; the agent
  renames item_tag and itemTag to the same word, so it became a known
  type in generated apps. It now checks account (not in EntityType),
  shopkeeperSignIn (camelCase; sign_in is not renamed, so it stays
  different from shopkeeper_sign_in) and Shop (capitalized).
- NSLocalNetworkUsageDescription had an article before the product name;
  it now reads "Connects to the app's API server running on your local
  network during development." LocalizationTest fails on "a"/"an" right
  before a renamed word in the English strings.
- Comments drop the article before domain words and say "the agent"
  instead of the agent's name, which the rename turns into <slug>-agent.

Closes #109
Closes #110
Closes #111
Closes #112

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UZJxUoubntzu7Ec7DPVfAr
@dadachi
dadachi merged commit e3aa0e2 into main Oct 18, 2026
2 checks passed
@dadachi
dadachi deleted the fix/app-store-id-and-rename-safety branch October 18, 2026 00:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant