Sweep the whole blog, not just the front page
153 posts under the SwiftUI tag, plus wwdc25 and wwdc26. Filtering them against what our code actually contains turned up one real defect and a short list worth keeping. The defect is plurals. A checkout screen renders "\(count) coupons", whose Czech translation is the single form "%lld kupónů" — so it says "1 kupónů" and "3 kupónů", wrong for every count below five. The article that surfaced it recommends inflect:, which covers English, Spanish and six others but not Czech, the language that needs three forms rather than two. Plural variations in the String Catalog are the fix for us; the entry says so rather than repeating the article's advice. Two entries are filed against a future rather than now: swipe action buttons draw their titles from iOS 26, which will surprise us the day we add a custom one, and a Form inside a sheet covers the glass behind it, which starts to matter if the payment sheet ever gets a medium detent. The rest is recorded as swept and skipped, with reasons — including that Core Spotlight and the search tab are things we already do correctly, and that three posts are covered better by the skills shipping inside Xcode. A sweep that leaves no trace of what it rejected gets repeated. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
463f45cacb
commit
3ad73df76a
6 files changed
+146
-2
No files matched your search
@@ -12,7 +12,7 @@
|
|||||||
"name": "pdat-swift-practice",
|
"name": "pdat-swift-practice",
|
||||||
"source": "./pdat-swift-practice",
|
"source": "./pdat-swift-practice",
|
||||||
"description": "Curated Apple platform findings with sources, plus practices learned from production incidents.",
|
"description": "Curated Apple platform findings with sources, plus practices learned from production incidents.",
|
||||||
"version": "0.4.0",
|
"version": "0.5.0",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "PDAT s.r.o."
|
"name": "PDAT s.r.o."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "pdat-swift-practice",
|
"name": "pdat-swift-practice",
|
||||||
"version": "0.4.0",
|
"version": "0.5.0",
|
||||||
"description": "Curated Apple platform findings with sources, plus practices learned from production incidents.",
|
"description": "Curated Apple platform findings with sources, plus practices learned from production incidents.",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "PDAT s.r.o.",
|
"name": "PDAT s.r.o.",
|
||||||
|
|||||||
@@ -16,7 +16,9 @@ Read `references/` for the individual entries.
|
|||||||
|---|---|
|
|---|---|
|
||||||
| `references/ios26-ui.md` | `Button(role: .close)`, concentric corners |
|
| `references/ios26-ui.md` | `Button(role: .close)`, concentric corners |
|
||||||
| `references/scrolling.md` | `ScrollPosition` and friends |
|
| `references/scrolling.md` | `ScrollPosition` and friends |
|
||||||
|
| `references/localization.md` | Plurals, and why `inflect:` does not cover Czech |
|
||||||
| `references/waiting-on-target.md` | Known, not yet usable at our deployment target |
|
| `references/waiting-on-target.md` | Known, not yet usable at our deployment target |
|
||||||
|
| `references/checked-and-skipped.md` | Swept and deliberately not adopted, with reasons |
|
||||||
|
|
||||||
## How to add an entry
|
## How to add an entry
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
# Checked, not adopted
|
||||||
|
|
||||||
|
The sweep through nilcoalescing.com covered 153 posts tagged SwiftUI plus
|
||||||
|
the wwdc25 and wwdc26 tags. Most of it does not apply to us. This file
|
||||||
|
records the ones worth explaining, so the next sweep does not re-open
|
||||||
|
them.
|
||||||
|
|
||||||
|
## Already doing it
|
||||||
|
|
||||||
|
- **Core Spotlight indexing** — we index cards and handle
|
||||||
|
`onContinueUserActivity(CSSearchableItemActionType)` already.
|
||||||
|
- **The iOS 26 search tab** — we use `Tab(role: .search)` with
|
||||||
|
`searchable()`, which is what the article recommends. Its other
|
||||||
|
suggestions (`searchToolbarBehavior()`, `DefaultToolbarItem(kind: .search)`)
|
||||||
|
are for apps that keep search in a toolbar rather than a tab.
|
||||||
|
|
||||||
|
Re-reading these was not wasted: confirming that a decision still matches
|
||||||
|
current guidance is worth something, and takes a minute.
|
||||||
|
|
||||||
|
## Apple's own skills cover it better
|
||||||
|
|
||||||
|
Three posts overlap directly with the skills that ship inside Xcode, which
|
||||||
|
go deeper and are maintained with the SDK. Prefer those.
|
||||||
|
|
||||||
|
| Post | Covered by |
|
||||||
|
|---|---|
|
||||||
|
| Equatable properties in @Observable classes | `swiftui-specialist/references/dataflow.md` |
|
||||||
|
| The hidden cost of unstable environment defaults | `swiftui-specialist/references/environment.md` |
|
||||||
|
| Custom bindings: closures vs subscripts | `swiftui-specialist/references/dataflow.md` |
|
||||||
|
|
||||||
|
The binding one is a good example of why the Apple skills earn their
|
||||||
|
place: it describes the exact anti-pattern we removed from four sheets —
|
||||||
|
`Binding(get:set:)` built inside `body` — and we found it through the
|
||||||
|
skill during review, before the article ever came up.
|
||||||
|
|
||||||
|
## Not our shape
|
||||||
|
|
||||||
|
Blend modes, mesh gradients, `TimelineView`, macOS window and menu-bar
|
||||||
|
work, `Canvas` frame reprojection, MapKit polyline gradients, UIKit
|
||||||
|
interop. Good writing, nothing to act on here.
|
||||||
|
|
||||||
|
## Filed elsewhere
|
||||||
|
|
||||||
|
- Dynamic Type truncation — `fixedSize(horizontal: false, vertical: true)`
|
||||||
|
is the fix when text in a scrollable container gets cut off at
|
||||||
|
accessibility sizes. Our card tiles deliberately use
|
||||||
|
`minimumScaleFactor` instead, which is right for a fixed-height tile.
|
||||||
|
Worth remembering for descriptive text, not for the tiles.
|
||||||
@@ -59,3 +59,45 @@ means re-tuning every time padding changes. Costs nothing at runtime.
|
|||||||
**Source.** Natalia Panferova, "Corner concentricity in SwiftUI on
|
**Source.** Natalia Panferova, "Corner concentricity in SwiftUI on
|
||||||
iOS 26", nilcoalescing.com, 21 Aug 2025. Read 16 Aug 2026. Apple's own
|
iOS 26", nilcoalescing.com, 21 Aug 2025. Read 16 Aug 2026. Apple's own
|
||||||
skills shipped with Xcode 27 do not cover this.
|
skills shipped with Xcode 27 do not cover this.
|
||||||
|
|
||||||
|
## Swipe action buttons now show their title
|
||||||
|
|
||||||
|
**What it is.** Before iOS 26, a `Button("Delete", systemImage: "trash")`
|
||||||
|
in a swipe action rendered as the symbol alone. From iOS 26 it draws the
|
||||||
|
title too, stacked or side by side depending on row height. To get the
|
||||||
|
old look back, say so explicitly:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
Button("Delete", systemImage: "trash", role: .destructive) { … }
|
||||||
|
.labelStyle(.iconOnly)
|
||||||
|
```
|
||||||
|
|
||||||
|
**What it replaces.** Nothing of ours yet — our rows use `.onDelete`, so
|
||||||
|
the system supplies the button and this does not apply. Recorded because
|
||||||
|
the moment we add a custom swipe action, the rows will look wider than
|
||||||
|
intended and the cause will not be obvious.
|
||||||
|
|
||||||
|
**Source.** Natalia Panferova, "Show icons only in SwiftUI swipe actions
|
||||||
|
on iOS 26", nilcoalescing.com, 15 Oct 2025. Read 18 Aug 2026.
|
||||||
|
|
||||||
|
## A Form inside a sheet hides the glass behind it
|
||||||
|
|
||||||
|
**What it is.** `Form` and `List` paint their own opaque background, which
|
||||||
|
covers the Liquid Glass surface of the sheet they sit in. Clearing it
|
||||||
|
takes two modifiers, because a pushed navigation destination adds a second
|
||||||
|
opaque layer of its own:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
.scrollContentBackground(.hidden)
|
||||||
|
.containerBackground(.clear, for: .navigation)
|
||||||
|
```
|
||||||
|
|
||||||
|
**What it replaces.** Nothing yet. Our sheets that pair `NavigationStack`
|
||||||
|
with `Form` — the payment code sheet, the card editor — are presented at
|
||||||
|
full height, where the opaque background is what you want anyway. It
|
||||||
|
starts to matter the day one of them gets a medium detent, which is
|
||||||
|
plausible for the payment sheet: the amount is three fields, and a
|
||||||
|
half-height sheet would keep the card visible behind it.
|
||||||
|
|
||||||
|
**Source.** Natalia Panferova, "SwiftUI Liquid Glass sheets with
|
||||||
|
NavigationStack and Form", nilcoalescing.com, 9 Sep 2025. Read 18 Aug 2026.
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# Localization
|
||||||
|
|
||||||
|
## Plurals: inflection is not enough for Czech
|
||||||
|
|
||||||
|
**What it is.** SwiftUI parses an inflection attribute inside a `Text`
|
||||||
|
literal and applies Foundation's grammar agreement, so the noun follows
|
||||||
|
the number without any manual branching:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
Text("You read ^[\(bookCount) book](inflect: true) this year!")
|
||||||
|
```
|
||||||
|
|
||||||
|
**Language support is the catch.** It shipped for English and Spanish in
|
||||||
|
iOS 15, and iOS 18 added German, French, Italian, Portuguese, Hindi and
|
||||||
|
Korean. **Czech is not on that list**, and Czech is exactly the language
|
||||||
|
that needs it: 1 kupón, 2–4 kupóny, 5+ kupónů — three forms where English
|
||||||
|
has two.
|
||||||
|
|
||||||
|
**What it found in our code.** A real bug, not a nicety:
|
||||||
|
|
||||||
|
```swift
|
||||||
|
Text("\(activeCoupons.count) coupons") // CardDetailView, checkout screen
|
||||||
|
```
|
||||||
|
|
||||||
|
The Czech translation in the catalogue is a single string, `%lld kupónů`,
|
||||||
|
so the app renders "1 kupónů" and "3 kupónů" — wrong for every count
|
||||||
|
except five and up. It sits on the checkout screen, which is the screen
|
||||||
|
people actually look at in a shop.
|
||||||
|
|
||||||
|
**The fix for us** is plural variations in the String Catalog rather than
|
||||||
|
`inflect:`. Xcode's "Vary by Plural" writes a `variations.plural` block
|
||||||
|
with `one` / `few` / `many` / `other`, and Czech uses all of them:
|
||||||
|
|
||||||
|
| category | counts | Czech |
|
||||||
|
|---|---|---|
|
||||||
|
| one | 1 | 1 kupón |
|
||||||
|
| few | 2–4 | 3 kupóny |
|
||||||
|
| many | fractions | 1,5 kupónu |
|
||||||
|
| other | 0, 5+ | 5 kupónů |
|
||||||
|
|
||||||
|
Worth auditing every interpolated count in the UI at the same time; a
|
||||||
|
count wrapped in parentheses — "Send unsent receipts (3)" — dodges the
|
||||||
|
problem entirely and is a reasonable fallback where a plural form would
|
||||||
|
be awkward.
|
||||||
|
|
||||||
|
**Step forward?** Yes, and overdue. This is the kind of defect that never
|
||||||
|
shows up in a test and that a Czech speaker notices immediately.
|
||||||
|
|
||||||
|
**Source.** Natalia Panferova, "Handle plurals in SwiftUI Text views with
|
||||||
|
inflection", nilcoalescing.com, 13 Jan 2025. Read 18 Aug 2026. The article
|
||||||
|
is about `inflect:`; the Czech conclusion is ours, from checking which
|
||||||
|
languages that API actually covers.
|
||||||
Reference in new issue
Block a user