Signed-off-by: Matt Bruce <matt.bruce1@toyota.com>

This commit is contained in:
Matt Bruce 2026-02-11 13:22:51 -06:00
parent 88e4402d38
commit f3dd8d92bd
4 changed files with 26 additions and 46 deletions

View File

@ -8,13 +8,9 @@ globs: ["**/*.swift", "**/*.xcstrings"]
Use **String Catalogs** (`.xcstrings` files) for localization in modern Swift projects.
## Required Language Support
## Language Support
At minimum, support these languages:
- **English (en)** - Base language
- **Spanish - Mexico (es-MX)**
- **French - Canada (fr-CA)**
At minimum, support the base development language (typically English). Add additional locales based on your app's target markets. Common additions include Spanish and French variants.
## How String Catalogs Work

View File

@ -261,7 +261,9 @@ let result = largeArray
## Error Handling
### Prefer Typed Throws (Swift 6)
### Prefer Typed Throws (Swift 6 / Xcode 16+)
Typed throws requires the **Swift 6 compiler** (shipped with Xcode 16). The feature works at any deployment target, but your project must compile with Swift 6. If your team hasn't migrated yet, continue using untyped `throws`.
```swift
// Swift 6 - Typed throws

View File

@ -45,10 +45,7 @@ Each protocol should represent one capability (Interface Segregation Principle):
```swift
// GOOD - Focused protocols
protocol Identifiable {
var id: UUID { get }
}
// Note: Swift provides Identifiable already — adopt it, don't redefine it.
protocol Nameable {
var displayName: String { get }
}
@ -58,8 +55,13 @@ protocol Timestamped {
var updatedAt: Date { get }
}
// Compose as needed
struct User: Identifiable, Nameable, Timestamped { ... }
// Compose as needed (Identifiable comes from Swift standard library)
struct User: Identifiable, Nameable, Timestamped {
let id: UUID
var displayName: String
var createdAt: Date
var updatedAt: Date
}
```
```swift

View File

@ -99,34 +99,6 @@ final class NavigationStore {
}
```
## Tab View
### Use Tab API Not tabItem()
```swift
// BAD - Old tabItem pattern
TabView {
HomeView()
.tabItem {
Label("Home", systemImage: "house")
}
SettingsView()
.tabItem {
Label("Settings", systemImage: "gear")
}
}
// GOOD - Tab API (iOS 18+)
TabView {
Tab("Home", systemImage: "house") {
HomeView()
}
Tab("Settings", systemImage: "gear") {
SettingsView()
}
}
```
## Observable Pattern
### Use @Observable Not ObservableObject
@ -294,24 +266,32 @@ func makeView(for type: ViewType) -> some View {
## Lists and ForEach
### Don't Convert to Array for Enumeration
### Use Identifiable Conformance for ForEach
Let `ForEach` use `Identifiable` conformance directly — don't bypass it with manual key paths or offset-based identity.
```swift
// BAD - Unnecessary Array conversion
// BAD - offset-based id breaks animations when items change
ForEach(Array(items.enumerated()), id: \.offset) { index, item in
// ...
}
// GOOD - Use indices or zip
// BAD - index-based id has the same animation problem
ForEach(items.indices, id: \.self) { index in
let item = items[index]
// ...
}
// GOOD - If you need both
ForEach(Array(zip(items.indices, items)), id: \.0) { index, item in
// GOOD - Identifiable items work directly (protocol-based)
ForEach(items) { item in
// ...
}
// GOOD - When index is also needed, use enumerated keyed on the item's identity
ForEach(Array(items.enumerated()), id: \.element.id) { index, item in
// Stable identity comes from item.id (Identifiable conformance),
// not from the offset — so animations and diffing work correctly.
}
```
### Hide Scroll Indicators