opencodekit 0.16.15 → 0.16.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -242
- package/dist/index.js +19 -6
- package/dist/template/.opencode/AGENTS.md +72 -236
- package/dist/template/.opencode/README.md +49 -482
- package/dist/template/.opencode/agent/build.md +71 -345
- package/dist/template/.opencode/agent/explore.md +47 -139
- package/dist/template/.opencode/agent/general.md +61 -172
- package/dist/template/.opencode/agent/looker.md +65 -161
- package/dist/template/.opencode/agent/painter.md +46 -200
- package/dist/template/.opencode/agent/plan.md +37 -220
- package/dist/template/.opencode/agent/review.md +72 -153
- package/dist/template/.opencode/agent/scout.md +44 -486
- package/dist/template/.opencode/agent/vision.md +63 -178
- package/dist/template/.opencode/command/create.md +75 -307
- package/dist/template/.opencode/command/design.md +53 -589
- package/dist/template/.opencode/command/handoff.md +76 -180
- package/dist/template/.opencode/command/init.md +45 -211
- package/dist/template/.opencode/command/plan.md +62 -514
- package/dist/template/.opencode/command/pr.md +56 -226
- package/dist/template/.opencode/command/research.md +55 -266
- package/dist/template/.opencode/command/resume.md +33 -138
- package/dist/template/.opencode/command/review-codebase.md +54 -202
- package/dist/template/.opencode/command/ship.md +78 -127
- package/dist/template/.opencode/command/start.md +47 -577
- package/dist/template/.opencode/command/status.md +60 -353
- package/dist/template/.opencode/command/ui-review.md +52 -298
- package/dist/template/.opencode/command/verify.md +36 -250
- package/dist/template/.opencode/memory.db-shm +0 -0
- package/dist/template/.opencode/memory.db-wal +0 -0
- package/dist/template/.opencode/opencode.json +133 -35
- package/dist/template/.opencode/plugin/README.md +40 -166
- package/dist/template/.opencode/plugin/compaction.ts +162 -131
- package/dist/template/.opencode/plugin/lib/memory-db.ts +112 -0
- package/dist/template/.opencode/plugin/swarm-enforcer.ts +182 -27
- package/dist/template/.opencode/skill/augment-context-engine/SKILL.md +112 -0
- package/dist/template/.opencode/skill/augment-context-engine/mcp.json +6 -0
- package/dist/template/.opencode/skill/core-data-expert/SKILL.md +82 -0
- package/dist/template/.opencode/skill/core-data-expert/references/batch-operations.md +543 -0
- package/dist/template/.opencode/skill/core-data-expert/references/cloudkit-integration.md +259 -0
- package/dist/template/.opencode/skill/core-data-expert/references/concurrency.md +522 -0
- package/dist/template/.opencode/skill/core-data-expert/references/fetch-requests.md +643 -0
- package/dist/template/.opencode/skill/core-data-expert/references/glossary.md +233 -0
- package/dist/template/.opencode/skill/core-data-expert/references/migration.md +393 -0
- package/dist/template/.opencode/skill/core-data-expert/references/model-configuration.md +597 -0
- package/dist/template/.opencode/skill/core-data-expert/references/performance.md +300 -0
- package/dist/template/.opencode/skill/core-data-expert/references/persistent-history.md +553 -0
- package/dist/template/.opencode/skill/core-data-expert/references/project-audit.md +60 -0
- package/dist/template/.opencode/skill/core-data-expert/references/saving.md +574 -0
- package/dist/template/.opencode/skill/core-data-expert/references/stack-setup.md +625 -0
- package/dist/template/.opencode/skill/core-data-expert/references/testing.md +300 -0
- package/dist/template/.opencode/skill/core-data-expert/references/threading.md +589 -0
- package/dist/template/.opencode/skill/swift-concurrency/SKILL.md +246 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/actors.md +640 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/async-algorithms.md +822 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/async-await-basics.md +249 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/async-sequences.md +670 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/core-data.md +533 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/glossary.md +128 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/linting.md +142 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/memory-management.md +542 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/migration.md +1076 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/performance.md +574 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/sendable.md +578 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/tasks.md +604 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/testing.md +565 -0
- package/dist/template/.opencode/skill/swift-concurrency/references/threading.md +452 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/SKILL.md +290 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/animation-advanced.md +351 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/animation-basics.md +284 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/animation-transitions.md +326 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/image-optimization.md +286 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/layout-best-practices.md +312 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/liquid-glass.md +377 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/list-patterns.md +153 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/modern-apis.md +400 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/performance-patterns.md +377 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/scroll-patterns.md +305 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/sheet-navigation-patterns.md +292 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/state-management.md +447 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/text-formatting.md +285 -0
- package/dist/template/.opencode/skill/swiftui-expert-skill/references/view-structure.md +276 -0
- package/dist/template/.opencode/tool/action-queue.ts +308 -0
- package/dist/template/.opencode/tool/swarm.ts +65 -40
- package/package.json +16 -3
- package/dist/template/.opencode/.agents/skills/context7/SKILL.md +0 -88
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
# Modern SwiftUI APIs Reference
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
This reference covers modern SwiftUI API usage patterns and deprecated API replacements. Always use the latest APIs to ensure forward compatibility and access to new features.
|
|
6
|
+
|
|
7
|
+
## Styling and Appearance
|
|
8
|
+
|
|
9
|
+
### foregroundStyle() vs foregroundColor()
|
|
10
|
+
|
|
11
|
+
**Always use `foregroundStyle()` instead of `foregroundColor()`.**
|
|
12
|
+
|
|
13
|
+
```swift
|
|
14
|
+
// Modern (Correct)
|
|
15
|
+
Text("Hello")
|
|
16
|
+
.foregroundStyle(.primary)
|
|
17
|
+
|
|
18
|
+
Image(systemName: "star")
|
|
19
|
+
.foregroundStyle(.blue)
|
|
20
|
+
|
|
21
|
+
// Legacy (Avoid)
|
|
22
|
+
Text("Hello")
|
|
23
|
+
.foregroundColor(.primary)
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Why**: `foregroundStyle()` supports hierarchical styles, gradients, and materials, making it more flexible and future-proof.
|
|
27
|
+
|
|
28
|
+
### clipShape() vs cornerRadius()
|
|
29
|
+
|
|
30
|
+
**Always use `clipShape(.rect(cornerRadius:))` instead of `cornerRadius()`.**
|
|
31
|
+
|
|
32
|
+
```swift
|
|
33
|
+
// Modern (Correct)
|
|
34
|
+
Image("photo")
|
|
35
|
+
.clipShape(.rect(cornerRadius: 12))
|
|
36
|
+
|
|
37
|
+
VStack {
|
|
38
|
+
// content
|
|
39
|
+
}
|
|
40
|
+
.clipShape(.rect(cornerRadius: 16))
|
|
41
|
+
|
|
42
|
+
// Legacy (Avoid)
|
|
43
|
+
Image("photo")
|
|
44
|
+
.cornerRadius(12)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**Why**: `cornerRadius()` is deprecated. `clipShape()` is more explicit and supports all shape types.
|
|
48
|
+
|
|
49
|
+
### fontWeight() vs bold()
|
|
50
|
+
|
|
51
|
+
**Don't apply `fontWeight()` unless there's a good reason. Always use `bold()` for bold text.**
|
|
52
|
+
|
|
53
|
+
```swift
|
|
54
|
+
// Correct
|
|
55
|
+
Text("Important")
|
|
56
|
+
.bold()
|
|
57
|
+
|
|
58
|
+
// Avoid (unless you need a specific weight)
|
|
59
|
+
Text("Important")
|
|
60
|
+
.fontWeight(.bold)
|
|
61
|
+
|
|
62
|
+
// Acceptable (specific weight needed)
|
|
63
|
+
Text("Semibold")
|
|
64
|
+
.fontWeight(.semibold)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Navigation
|
|
68
|
+
|
|
69
|
+
### NavigationStack vs NavigationView
|
|
70
|
+
|
|
71
|
+
**Always use `NavigationStack` instead of `NavigationView`.**
|
|
72
|
+
|
|
73
|
+
```swift
|
|
74
|
+
// Modern (Correct)
|
|
75
|
+
NavigationStack {
|
|
76
|
+
List(items) { item in
|
|
77
|
+
NavigationLink(value: item) {
|
|
78
|
+
Text(item.name)
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
.navigationDestination(for: Item.self) { item in
|
|
82
|
+
DetailView(item: item)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Legacy (Avoid)
|
|
87
|
+
NavigationView {
|
|
88
|
+
List(items) { item in
|
|
89
|
+
NavigationLink(destination: DetailView(item: item)) {
|
|
90
|
+
Text(item.name)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### navigationDestination(for:)
|
|
97
|
+
|
|
98
|
+
**Use `navigationDestination(for:)` for type-safe navigation.**
|
|
99
|
+
|
|
100
|
+
```swift
|
|
101
|
+
struct ContentView: View {
|
|
102
|
+
var body: some View {
|
|
103
|
+
NavigationStack {
|
|
104
|
+
List {
|
|
105
|
+
NavigationLink("Profile", value: Route.profile)
|
|
106
|
+
NavigationLink("Settings", value: Route.settings)
|
|
107
|
+
}
|
|
108
|
+
.navigationDestination(for: Route.self) { route in
|
|
109
|
+
switch route {
|
|
110
|
+
case .profile:
|
|
111
|
+
ProfileView()
|
|
112
|
+
case .settings:
|
|
113
|
+
SettingsView()
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
enum Route: Hashable {
|
|
121
|
+
case profile
|
|
122
|
+
case settings
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Tabs
|
|
127
|
+
|
|
128
|
+
### Tab API vs tabItem()
|
|
129
|
+
|
|
130
|
+
**For iOS 18 and later, prefer the `Tab` API over `tabItem()` to access modern tab features, and use availability checks or `tabItem()` for earlier OS versions.**
|
|
131
|
+
|
|
132
|
+
```swift
|
|
133
|
+
// Modern (Correct) - iOS 18+
|
|
134
|
+
TabView {
|
|
135
|
+
Tab("Home", systemImage: "house") {
|
|
136
|
+
HomeView()
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
Tab("Search", systemImage: "magnifyingglass") {
|
|
140
|
+
SearchView()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
Tab("Profile", systemImage: "person") {
|
|
144
|
+
ProfileView()
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Legacy (Avoid)
|
|
149
|
+
TabView {
|
|
150
|
+
HomeView()
|
|
151
|
+
.tabItem {
|
|
152
|
+
Label("Home", systemImage: "house")
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Important**: When using `Tab(role:)` with roles, you must use the new `Tab { } label: { }` syntax for all tabs. Mixing with `.tabItem()` causes compilation errors.
|
|
158
|
+
|
|
159
|
+
```swift
|
|
160
|
+
// Correct - all tabs use Tab syntax
|
|
161
|
+
TabView {
|
|
162
|
+
Tab(role: .search) {
|
|
163
|
+
SearchView()
|
|
164
|
+
} label: {
|
|
165
|
+
Label("Search", systemImage: "magnifyingglass")
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
Tab {
|
|
169
|
+
HomeView()
|
|
170
|
+
} label: {
|
|
171
|
+
Label("Home", systemImage: "house")
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Wrong - mixing Tab and tabItem causes errors
|
|
176
|
+
TabView {
|
|
177
|
+
Tab(role: .search) {
|
|
178
|
+
SearchView()
|
|
179
|
+
} label: {
|
|
180
|
+
Label("Search", systemImage: "magnifyingglass")
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
HomeView() // Error: can't mix with Tab(role:)
|
|
184
|
+
.tabItem {
|
|
185
|
+
Label("Home", systemImage: "house")
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Interactions
|
|
191
|
+
|
|
192
|
+
### Button vs onTapGesture()
|
|
193
|
+
|
|
194
|
+
**Never use `onTapGesture()` unless you specifically need tap location or tap count. Always use `Button` otherwise.**
|
|
195
|
+
|
|
196
|
+
```swift
|
|
197
|
+
// Correct - standard tap action
|
|
198
|
+
Button("Tap me") {
|
|
199
|
+
performAction()
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Correct - need tap location
|
|
203
|
+
Text("Tap anywhere")
|
|
204
|
+
.onTapGesture { location in
|
|
205
|
+
handleTap(at: location)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Correct - need tap count
|
|
209
|
+
Image("photo")
|
|
210
|
+
.onTapGesture(count: 2) {
|
|
211
|
+
handleDoubleTap()
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Wrong - use Button instead
|
|
215
|
+
Text("Tap me")
|
|
216
|
+
.onTapGesture {
|
|
217
|
+
performAction()
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
**Why**: `Button` provides proper accessibility, visual feedback, and semantic meaning. Use `onTapGesture()` only when you need its specific features.
|
|
222
|
+
|
|
223
|
+
### Button with Images
|
|
224
|
+
|
|
225
|
+
**Always specify text alongside images in buttons for accessibility.**
|
|
226
|
+
|
|
227
|
+
```swift
|
|
228
|
+
// Correct - includes text label
|
|
229
|
+
Button("Add Item", systemImage: "plus") {
|
|
230
|
+
addItem()
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Also correct - custom label
|
|
234
|
+
Button {
|
|
235
|
+
addItem()
|
|
236
|
+
} label: {
|
|
237
|
+
Label("Add Item", systemImage: "plus")
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Wrong - image only, no text
|
|
241
|
+
Button {
|
|
242
|
+
addItem()
|
|
243
|
+
} label: {
|
|
244
|
+
Image(systemName: "plus")
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Layout and Sizing
|
|
249
|
+
|
|
250
|
+
### Avoid UIScreen.main.bounds
|
|
251
|
+
|
|
252
|
+
**Never use `UIScreen.main.bounds` to read available space.**
|
|
253
|
+
|
|
254
|
+
```swift
|
|
255
|
+
// Wrong - uses UIKit, doesn't respect safe areas
|
|
256
|
+
let screenWidth = UIScreen.main.bounds.width
|
|
257
|
+
|
|
258
|
+
// Correct - use GeometryReader
|
|
259
|
+
GeometryReader { geometry in
|
|
260
|
+
Text("Width: \(geometry.size.width)")
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Better - use containerRelativeFrame (iOS 17+)
|
|
264
|
+
Text("Full width")
|
|
265
|
+
.containerRelativeFrame(.horizontal)
|
|
266
|
+
|
|
267
|
+
// Best - let SwiftUI handle sizing
|
|
268
|
+
Text("Auto-sized")
|
|
269
|
+
.frame(maxWidth: .infinity)
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### GeometryReader Alternatives
|
|
273
|
+
|
|
274
|
+
> **iOS 17+**: `containerRelativeFrame` and `visualEffect` require iOS 17 or later.
|
|
275
|
+
|
|
276
|
+
**Don't use `GeometryReader` if a newer alternative works.**
|
|
277
|
+
|
|
278
|
+
```swift
|
|
279
|
+
// Modern - containerRelativeFrame
|
|
280
|
+
Image("hero")
|
|
281
|
+
.resizable()
|
|
282
|
+
.containerRelativeFrame(.horizontal) { length, axis in
|
|
283
|
+
length * 0.8
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Modern - visualEffect for position-based effects
|
|
287
|
+
Text("Parallax")
|
|
288
|
+
.visualEffect { content, geometry in
|
|
289
|
+
content.offset(y: geometry.frame(in: .global).minY * 0.5)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Legacy - only use if necessary
|
|
293
|
+
GeometryReader { geometry in
|
|
294
|
+
Image("hero")
|
|
295
|
+
.frame(width: geometry.size.width * 0.8)
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Type Erasure
|
|
300
|
+
|
|
301
|
+
### Avoid AnyView
|
|
302
|
+
|
|
303
|
+
**Avoid `AnyView` unless absolutely required.**
|
|
304
|
+
|
|
305
|
+
```swift
|
|
306
|
+
// Prefer - use @ViewBuilder
|
|
307
|
+
@ViewBuilder
|
|
308
|
+
func content() -> some View {
|
|
309
|
+
if condition {
|
|
310
|
+
Text("Option A")
|
|
311
|
+
} else {
|
|
312
|
+
Image(systemName: "photo")
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Avoid - type erasure has performance cost
|
|
317
|
+
func content() -> AnyView {
|
|
318
|
+
if condition {
|
|
319
|
+
return AnyView(Text("Option A"))
|
|
320
|
+
} else {
|
|
321
|
+
return AnyView(Image(systemName: "photo"))
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Acceptable - when protocol conformance requires it
|
|
326
|
+
var body: some View {
|
|
327
|
+
// Complex conditional logic that requires type erasure
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
## Styling Best Practices
|
|
332
|
+
|
|
333
|
+
### Dynamic Type
|
|
334
|
+
|
|
335
|
+
**Don't force specific font sizes. Prefer Dynamic Type.**
|
|
336
|
+
|
|
337
|
+
```swift
|
|
338
|
+
// Correct - respects user's text size preferences
|
|
339
|
+
Text("Title")
|
|
340
|
+
.font(.title)
|
|
341
|
+
|
|
342
|
+
Text("Body")
|
|
343
|
+
.font(.body)
|
|
344
|
+
|
|
345
|
+
// Avoid - fixed size doesn't scale
|
|
346
|
+
Text("Title")
|
|
347
|
+
.font(.system(size: 24))
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### UIKit Colors
|
|
351
|
+
|
|
352
|
+
**Avoid using UIKit colors in SwiftUI code.**
|
|
353
|
+
|
|
354
|
+
```swift
|
|
355
|
+
// Correct - SwiftUI colors
|
|
356
|
+
Text("Hello")
|
|
357
|
+
.foregroundStyle(.blue)
|
|
358
|
+
.background(.gray.opacity(0.2))
|
|
359
|
+
|
|
360
|
+
// Wrong - UIKit colors
|
|
361
|
+
Text("Hello")
|
|
362
|
+
.foregroundColor(Color(UIColor.systemBlue))
|
|
363
|
+
.background(Color(UIColor.systemGray))
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## Static Member Lookup
|
|
367
|
+
|
|
368
|
+
**Prefer static member lookup to struct instances.**
|
|
369
|
+
|
|
370
|
+
```swift
|
|
371
|
+
// Correct - static member lookup
|
|
372
|
+
Circle()
|
|
373
|
+
.fill(.blue)
|
|
374
|
+
Button("Action") { }
|
|
375
|
+
.buttonStyle(.borderedProminent)
|
|
376
|
+
|
|
377
|
+
// Verbose - unnecessary struct instantiation
|
|
378
|
+
Circle()
|
|
379
|
+
.fill(Color.blue)
|
|
380
|
+
Button("Action") { }
|
|
381
|
+
.buttonStyle(BorderedProminentButtonStyle())
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
## Summary Checklist
|
|
385
|
+
|
|
386
|
+
- [ ] Use `foregroundStyle()` instead of `foregroundColor()`
|
|
387
|
+
- [ ] Use `clipShape(.rect(cornerRadius:))` instead of `cornerRadius()`
|
|
388
|
+
- [ ] Use `Tab` API instead of `tabItem()`
|
|
389
|
+
- [ ] Use `Button` instead of `onTapGesture()` (unless need location/count)
|
|
390
|
+
- [ ] Use `NavigationStack` instead of `NavigationView`
|
|
391
|
+
- [ ] Use `navigationDestination(for:)` for type-safe navigation
|
|
392
|
+
- [ ] Avoid `AnyView` unless required
|
|
393
|
+
- [ ] Avoid `UIScreen.main.bounds`
|
|
394
|
+
- [ ] Avoid `GeometryReader` when alternatives exist
|
|
395
|
+
- [ ] Use Dynamic Type instead of fixed font sizes
|
|
396
|
+
- [ ] Avoid hard-coded padding/spacing unless requested
|
|
397
|
+
- [ ] Avoid UIKit colors in SwiftUI
|
|
398
|
+
- [ ] Use static member lookup (`.blue` vs `Color.blue`)
|
|
399
|
+
- [ ] Include text labels with button images
|
|
400
|
+
- [ ] Use `bold()` instead of `fontWeight(.bold)`
|