agentic-team-templates 0.13.2 → 0.14.0
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 +6 -1
- package/package.json +1 -1
- package/src/index.js +22 -2
- package/src/index.test.js +5 -0
- package/templates/cpp-expert/.cursorrules/concurrency.md +211 -0
- package/templates/cpp-expert/.cursorrules/error-handling.md +170 -0
- package/templates/cpp-expert/.cursorrules/memory-and-ownership.md +220 -0
- package/templates/cpp-expert/.cursorrules/modern-cpp.md +211 -0
- package/templates/cpp-expert/.cursorrules/overview.md +87 -0
- package/templates/cpp-expert/.cursorrules/performance.md +223 -0
- package/templates/cpp-expert/.cursorrules/testing.md +230 -0
- package/templates/cpp-expert/.cursorrules/tooling.md +312 -0
- package/templates/cpp-expert/CLAUDE.md +242 -0
- package/templates/csharp-expert/.cursorrules/aspnet-core.md +311 -0
- package/templates/csharp-expert/.cursorrules/async-patterns.md +206 -0
- package/templates/csharp-expert/.cursorrules/dependency-injection.md +206 -0
- package/templates/csharp-expert/.cursorrules/error-handling.md +235 -0
- package/templates/csharp-expert/.cursorrules/language-features.md +204 -0
- package/templates/csharp-expert/.cursorrules/overview.md +92 -0
- package/templates/csharp-expert/.cursorrules/performance.md +251 -0
- package/templates/csharp-expert/.cursorrules/testing.md +282 -0
- package/templates/csharp-expert/.cursorrules/tooling.md +254 -0
- package/templates/csharp-expert/CLAUDE.md +360 -0
- package/templates/java-expert/.cursorrules/concurrency.md +209 -0
- package/templates/java-expert/.cursorrules/error-handling.md +205 -0
- package/templates/java-expert/.cursorrules/modern-java.md +216 -0
- package/templates/java-expert/.cursorrules/overview.md +81 -0
- package/templates/java-expert/.cursorrules/performance.md +239 -0
- package/templates/java-expert/.cursorrules/persistence.md +262 -0
- package/templates/java-expert/.cursorrules/spring-boot.md +262 -0
- package/templates/java-expert/.cursorrules/testing.md +272 -0
- package/templates/java-expert/.cursorrules/tooling.md +301 -0
- package/templates/java-expert/CLAUDE.md +325 -0
- package/templates/javascript-expert/.cursorrules/overview.md +5 -3
- package/templates/javascript-expert/.cursorrules/typescript-deep-dive.md +348 -0
- package/templates/javascript-expert/CLAUDE.md +34 -3
- package/templates/kotlin-expert/.cursorrules/coroutines.md +237 -0
- package/templates/kotlin-expert/.cursorrules/error-handling.md +149 -0
- package/templates/kotlin-expert/.cursorrules/frameworks.md +227 -0
- package/templates/kotlin-expert/.cursorrules/language-features.md +231 -0
- package/templates/kotlin-expert/.cursorrules/overview.md +77 -0
- package/templates/kotlin-expert/.cursorrules/performance.md +185 -0
- package/templates/kotlin-expert/.cursorrules/testing.md +213 -0
- package/templates/kotlin-expert/.cursorrules/tooling.md +258 -0
- package/templates/kotlin-expert/CLAUDE.md +276 -0
- package/templates/swift-expert/.cursorrules/concurrency.md +230 -0
- package/templates/swift-expert/.cursorrules/error-handling.md +213 -0
- package/templates/swift-expert/.cursorrules/language-features.md +246 -0
- package/templates/swift-expert/.cursorrules/overview.md +88 -0
- package/templates/swift-expert/.cursorrules/performance.md +260 -0
- package/templates/swift-expert/.cursorrules/swiftui.md +260 -0
- package/templates/swift-expert/.cursorrules/testing.md +286 -0
- package/templates/swift-expert/.cursorrules/tooling.md +285 -0
- package/templates/swift-expert/CLAUDE.md +275 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# Swift Tooling and Build System
|
|
2
|
+
|
|
3
|
+
Swift Package Manager, Xcode, static analysis, and CI/CD pipelines.
|
|
4
|
+
|
|
5
|
+
## Swift Package Manager
|
|
6
|
+
|
|
7
|
+
```swift
|
|
8
|
+
// Package.swift
|
|
9
|
+
// swift-tools-version: 6.0
|
|
10
|
+
import PackageDescription
|
|
11
|
+
|
|
12
|
+
let package = Package(
|
|
13
|
+
name: "MyApp",
|
|
14
|
+
platforms: [
|
|
15
|
+
.iOS(.v17),
|
|
16
|
+
.macOS(.v14)
|
|
17
|
+
],
|
|
18
|
+
products: [
|
|
19
|
+
.library(name: "MyAppCore", targets: ["MyAppCore"]),
|
|
20
|
+
.executable(name: "MyAppCLI", targets: ["MyAppCLI"])
|
|
21
|
+
],
|
|
22
|
+
dependencies: [
|
|
23
|
+
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.3.0"),
|
|
24
|
+
.package(url: "https://github.com/apple/swift-log", from: "1.5.0"),
|
|
25
|
+
.package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.15.0"),
|
|
26
|
+
],
|
|
27
|
+
targets: [
|
|
28
|
+
.target(
|
|
29
|
+
name: "MyAppCore",
|
|
30
|
+
dependencies: [
|
|
31
|
+
.product(name: "Logging", package: "swift-log"),
|
|
32
|
+
]
|
|
33
|
+
),
|
|
34
|
+
.executableTarget(
|
|
35
|
+
name: "MyAppCLI",
|
|
36
|
+
dependencies: [
|
|
37
|
+
"MyAppCore",
|
|
38
|
+
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
|
39
|
+
]
|
|
40
|
+
),
|
|
41
|
+
.testTarget(
|
|
42
|
+
name: "MyAppCoreTests",
|
|
43
|
+
dependencies: [
|
|
44
|
+
"MyAppCore",
|
|
45
|
+
.product(name: "SnapshotTesting", package: "swift-snapshot-testing"),
|
|
46
|
+
]
|
|
47
|
+
),
|
|
48
|
+
]
|
|
49
|
+
)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Essential Commands
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Build
|
|
56
|
+
swift build # Debug build
|
|
57
|
+
swift build -c release # Release build
|
|
58
|
+
xcodebuild -scheme MyApp build # Xcode build
|
|
59
|
+
|
|
60
|
+
# Test
|
|
61
|
+
swift test # Run all tests
|
|
62
|
+
swift test --filter UserServiceTests # Run specific suite
|
|
63
|
+
xcodebuild test -scheme MyApp \
|
|
64
|
+
-destination 'platform=iOS Simulator,name=iPhone 16'
|
|
65
|
+
|
|
66
|
+
# Run
|
|
67
|
+
swift run MyAppCLI # Run executable target
|
|
68
|
+
swift run MyAppCLI -- --help # Pass arguments
|
|
69
|
+
|
|
70
|
+
# Dependencies
|
|
71
|
+
swift package resolve # Resolve dependencies
|
|
72
|
+
swift package update # Update to latest compatible
|
|
73
|
+
swift package show-dependencies # Dependency tree
|
|
74
|
+
|
|
75
|
+
# Format and lint
|
|
76
|
+
swift format . --recursive # swift-format
|
|
77
|
+
swiftlint # SwiftLint analysis
|
|
78
|
+
swiftlint --fix # Auto-fix violations
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## SwiftLint
|
|
82
|
+
|
|
83
|
+
```yaml
|
|
84
|
+
# .swiftlint.yml
|
|
85
|
+
opt_in_rules:
|
|
86
|
+
- empty_count
|
|
87
|
+
- closure_spacing
|
|
88
|
+
- contains_over_filter_count
|
|
89
|
+
- discouraged_optional_boolean
|
|
90
|
+
- explicit_init
|
|
91
|
+
- fatal_error_message
|
|
92
|
+
- first_where
|
|
93
|
+
- force_unwrapping
|
|
94
|
+
- implicit_return
|
|
95
|
+
- joined_default_parameter
|
|
96
|
+
- last_where
|
|
97
|
+
- overridden_super_call
|
|
98
|
+
- private_action
|
|
99
|
+
- private_outlet
|
|
100
|
+
- redundant_nil_coalescing
|
|
101
|
+
- sorted_first_last
|
|
102
|
+
- unowned_variable_capture
|
|
103
|
+
- vertical_whitespace_closing_braces
|
|
104
|
+
|
|
105
|
+
disabled_rules:
|
|
106
|
+
- trailing_comma
|
|
107
|
+
|
|
108
|
+
force_cast: error
|
|
109
|
+
force_try: error
|
|
110
|
+
force_unwrapping: warning
|
|
111
|
+
|
|
112
|
+
line_length:
|
|
113
|
+
warning: 120
|
|
114
|
+
error: 200
|
|
115
|
+
|
|
116
|
+
type_body_length:
|
|
117
|
+
warning: 300
|
|
118
|
+
error: 500
|
|
119
|
+
|
|
120
|
+
file_length:
|
|
121
|
+
warning: 500
|
|
122
|
+
error: 1000
|
|
123
|
+
|
|
124
|
+
function_body_length:
|
|
125
|
+
warning: 40
|
|
126
|
+
error: 80
|
|
127
|
+
|
|
128
|
+
excluded:
|
|
129
|
+
- Pods
|
|
130
|
+
- .build
|
|
131
|
+
- DerivedData
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Xcode Project Configuration
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
Build Settings (recommended):
|
|
138
|
+
- SWIFT_STRICT_CONCURRENCY = complete # Full Sendable checking
|
|
139
|
+
- SWIFT_VERSION = 6.0
|
|
140
|
+
- ENABLE_STRICT_OBJC_MSGSEND = YES
|
|
141
|
+
- GCC_TREAT_WARNINGS_AS_ERRORS = YES # Zero-warning policy
|
|
142
|
+
- SWIFT_TREAT_WARNINGS_AS_ERRORS = YES
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## CI/CD (GitHub Actions)
|
|
146
|
+
|
|
147
|
+
```yaml
|
|
148
|
+
name: CI
|
|
149
|
+
|
|
150
|
+
on:
|
|
151
|
+
push:
|
|
152
|
+
branches: [main]
|
|
153
|
+
pull_request:
|
|
154
|
+
branches: [main]
|
|
155
|
+
|
|
156
|
+
jobs:
|
|
157
|
+
build-and-test:
|
|
158
|
+
runs-on: macos-14
|
|
159
|
+
|
|
160
|
+
steps:
|
|
161
|
+
- uses: actions/checkout@v4
|
|
162
|
+
|
|
163
|
+
- name: Select Xcode
|
|
164
|
+
run: sudo xcode-select -s /Applications/Xcode_16.0.app
|
|
165
|
+
|
|
166
|
+
- name: Build
|
|
167
|
+
run: |
|
|
168
|
+
xcodebuild build \
|
|
169
|
+
-scheme MyApp \
|
|
170
|
+
-destination 'platform=iOS Simulator,name=iPhone 16' \
|
|
171
|
+
-skipPackagePluginValidation
|
|
172
|
+
|
|
173
|
+
- name: Test
|
|
174
|
+
run: |
|
|
175
|
+
xcodebuild test \
|
|
176
|
+
-scheme MyApp \
|
|
177
|
+
-destination 'platform=iOS Simulator,name=iPhone 16' \
|
|
178
|
+
-resultBundlePath TestResults.xcresult \
|
|
179
|
+
-skipPackagePluginValidation
|
|
180
|
+
|
|
181
|
+
- name: SwiftLint
|
|
182
|
+
run: |
|
|
183
|
+
brew install swiftlint
|
|
184
|
+
swiftlint --strict
|
|
185
|
+
|
|
186
|
+
- name: Upload test results
|
|
187
|
+
if: always()
|
|
188
|
+
uses: actions/upload-artifact@v4
|
|
189
|
+
with:
|
|
190
|
+
name: test-results
|
|
191
|
+
path: TestResults.xcresult
|
|
192
|
+
|
|
193
|
+
# SPM-only projects
|
|
194
|
+
spm-test:
|
|
195
|
+
runs-on: macos-14
|
|
196
|
+
steps:
|
|
197
|
+
- uses: actions/checkout@v4
|
|
198
|
+
- name: Select Xcode
|
|
199
|
+
run: sudo xcode-select -s /Applications/Xcode_16.0.app
|
|
200
|
+
- name: Build and test
|
|
201
|
+
run: swift test --parallel
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Logging
|
|
205
|
+
|
|
206
|
+
```swift
|
|
207
|
+
import os
|
|
208
|
+
|
|
209
|
+
// Unified logging (Apple platforms)
|
|
210
|
+
private let logger = Logger(subsystem: "com.example.myapp", category: "networking")
|
|
211
|
+
|
|
212
|
+
logger.info("Fetching user \(userId, privacy: .public)")
|
|
213
|
+
logger.debug("Response: \(data.count) bytes")
|
|
214
|
+
logger.error("Request failed: \(error.localizedDescription, privacy: .public)")
|
|
215
|
+
|
|
216
|
+
// Log levels: debug, info, notice, error, fault
|
|
217
|
+
// debug/info are not persisted by default — minimal performance cost
|
|
218
|
+
|
|
219
|
+
// swift-log (cross-platform / server)
|
|
220
|
+
import Logging
|
|
221
|
+
|
|
222
|
+
var logger = Logger(label: "com.example.myapp")
|
|
223
|
+
logger[metadataKey: "requestId"] = "\(requestId)"
|
|
224
|
+
logger.info("Processing request", metadata: ["userId": "\(userId)"])
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
## Networking (URLSession)
|
|
228
|
+
|
|
229
|
+
```swift
|
|
230
|
+
// Modern URLSession with async/await
|
|
231
|
+
actor APIClient {
|
|
232
|
+
private let session: URLSession
|
|
233
|
+
private let decoder: JSONDecoder
|
|
234
|
+
|
|
235
|
+
init(session: URLSession = .shared) {
|
|
236
|
+
self.session = session
|
|
237
|
+
self.decoder = JSONDecoder()
|
|
238
|
+
self.decoder.dateDecodingStrategy = .iso8601
|
|
239
|
+
self.decoder.keyDecodingStrategy = .convertFromSnakeCase
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
func request<T: Decodable>(_ endpoint: Endpoint) async throws -> T {
|
|
243
|
+
var request = URLRequest(url: endpoint.url)
|
|
244
|
+
request.httpMethod = endpoint.method.rawValue
|
|
245
|
+
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
|
246
|
+
|
|
247
|
+
if let body = endpoint.body {
|
|
248
|
+
request.httpBody = try JSONEncoder().encode(body)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
let (data, response) = try await session.data(for: request)
|
|
252
|
+
|
|
253
|
+
guard let httpResponse = response as? HTTPURLResponse else {
|
|
254
|
+
throw APIError.invalidResponse
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
guard (200..<300).contains(httpResponse.statusCode) else {
|
|
258
|
+
throw APIError.serverError(
|
|
259
|
+
statusCode: httpResponse.statusCode,
|
|
260
|
+
message: String(data: data, encoding: .utf8) ?? ""
|
|
261
|
+
)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return try decoder.decode(T.self, from: data)
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Anti-Patterns
|
|
270
|
+
|
|
271
|
+
```swift
|
|
272
|
+
// Never: print() for logging in production
|
|
273
|
+
print("User logged in: \(user.id)") // No levels, no filtering, stdout only
|
|
274
|
+
// Use: os.Logger or swift-log
|
|
275
|
+
|
|
276
|
+
// Never: ignoring SwiftLint in CI
|
|
277
|
+
// Static analysis catches real bugs and code smells
|
|
278
|
+
|
|
279
|
+
// Never: hardcoded strings for UI
|
|
280
|
+
Text("Welcome back!") // Not localizable
|
|
281
|
+
// Use: String(localized: "welcome_back")
|
|
282
|
+
|
|
283
|
+
// Never: Cocoapods for new projects (when SPM works)
|
|
284
|
+
// SPM is the standard. Use Cocoapods only for libraries without SPM support
|
|
285
|
+
```
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# Swift Expert Development Guide
|
|
2
|
+
|
|
3
|
+
Principal-level guidelines for Swift engineering. Deep language mastery, structured concurrency, SwiftUI, protocol-oriented design, and Apple platform expertise.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
This guide applies to:
|
|
10
|
+
- iOS and iPadOS applications (UIKit, SwiftUI)
|
|
11
|
+
- macOS applications (AppKit, SwiftUI)
|
|
12
|
+
- watchOS and tvOS applications
|
|
13
|
+
- Server-side Swift (Vapor, Hummingbird)
|
|
14
|
+
- Swift packages and frameworks
|
|
15
|
+
- Command-line tools
|
|
16
|
+
|
|
17
|
+
### Core Philosophy
|
|
18
|
+
|
|
19
|
+
Swift is a safe, fast, and expressive language. Use its type system to make invalid states unrepresentable.
|
|
20
|
+
|
|
21
|
+
- **Safety is the foundation.** Optionals, value types, and the compiler are your first line of defense
|
|
22
|
+
- **Protocol-oriented design.** Prefer protocols and composition over class inheritance
|
|
23
|
+
- **Value semantics by default.** Structs over classes unless you need reference semantics
|
|
24
|
+
- **Concurrency is structured.** async/await, actors, and task groups — not GCD callbacks
|
|
25
|
+
- **Expressiveness without obscurity.** Clear is better than clever
|
|
26
|
+
- **If you don't know, say so.** Admitting uncertainty is professional
|
|
27
|
+
|
|
28
|
+
### Key Principles
|
|
29
|
+
|
|
30
|
+
1. **Optionals Are Not Enemies** — Embrace `Optional`. Never force-unwrap without proof
|
|
31
|
+
2. **Value Types by Default** — Structs, enums, tuples. Classes only when identity or reference semantics are required
|
|
32
|
+
3. **Protocol-Oriented Design** — Small, focused protocols. Composition over inheritance
|
|
33
|
+
4. **Structured Concurrency** — `async`/`await`, `TaskGroup`, actors. No completion handler callbacks in new code
|
|
34
|
+
5. **Exhaustive Pattern Matching** — `switch` on enums is exhaustive. The compiler enforces completeness
|
|
35
|
+
|
|
36
|
+
### Project Structure
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
MyApp/
|
|
40
|
+
├── Sources/
|
|
41
|
+
│ ├── App/
|
|
42
|
+
│ ├── Features/
|
|
43
|
+
│ │ ├── Auth/
|
|
44
|
+
│ │ └── Home/
|
|
45
|
+
│ ├── Core/
|
|
46
|
+
│ │ ├── Networking/
|
|
47
|
+
│ │ ├── Persistence/
|
|
48
|
+
│ │ └── Extensions/
|
|
49
|
+
│ └── Shared/
|
|
50
|
+
├── Tests/
|
|
51
|
+
├── Package.swift
|
|
52
|
+
└── README.md
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Language Features
|
|
58
|
+
|
|
59
|
+
### Optionals
|
|
60
|
+
|
|
61
|
+
```swift
|
|
62
|
+
guard let user = fetchUser(id: userId) else { throw AppError.userNotFound(userId) }
|
|
63
|
+
let displayName = user.nickname ?? user.fullName ?? "Anonymous"
|
|
64
|
+
let uppercasedEmail = user.email.map { $0.uppercased() }
|
|
65
|
+
// Never: user.email! — use guard/if let instead
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Value Types and Enums
|
|
69
|
+
|
|
70
|
+
```swift
|
|
71
|
+
struct User: Identifiable, Sendable {
|
|
72
|
+
let id: UUID
|
|
73
|
+
var name: String
|
|
74
|
+
var email: String
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
enum LoadingState<T> {
|
|
78
|
+
case idle, loading, loaded(T), failed(Error)
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Protocols and Extensions
|
|
83
|
+
|
|
84
|
+
```swift
|
|
85
|
+
protocol Displayable {
|
|
86
|
+
var displayName: String { get }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
extension Collection where Element: Identifiable {
|
|
90
|
+
func element(withId id: Element.ID) -> Element? {
|
|
91
|
+
first { $0.id == id }
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Concurrency
|
|
99
|
+
|
|
100
|
+
### async/await
|
|
101
|
+
|
|
102
|
+
```swift
|
|
103
|
+
func fetchDashboard(userId: UUID) async throws -> Dashboard {
|
|
104
|
+
async let user = fetchUser(id: userId)
|
|
105
|
+
async let orders = fetchOrders(userId: userId)
|
|
106
|
+
return try await Dashboard(user: user, orders: orders)
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Actors
|
|
111
|
+
|
|
112
|
+
```swift
|
|
113
|
+
actor ImageCache {
|
|
114
|
+
private var cache: [URL: UIImage] = [:]
|
|
115
|
+
func image(for url: URL) -> UIImage? { cache[url] }
|
|
116
|
+
func store(_ image: UIImage, for url: URL) { cache[url] = image }
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Rules
|
|
121
|
+
|
|
122
|
+
- No `DispatchQueue.main.async` in new code — use `@MainActor`
|
|
123
|
+
- No completion handlers in new code — use `async`/`await`
|
|
124
|
+
- Always check `Task.isCancelled` in loops
|
|
125
|
+
- Use actors for shared mutable state
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## Error Handling
|
|
130
|
+
|
|
131
|
+
### Error Types
|
|
132
|
+
|
|
133
|
+
```swift
|
|
134
|
+
enum APIError: Error, LocalizedError {
|
|
135
|
+
case networkUnavailable
|
|
136
|
+
case notFound(resource: String, id: String)
|
|
137
|
+
case serverError(statusCode: Int, message: String)
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Guard Pattern
|
|
142
|
+
|
|
143
|
+
```swift
|
|
144
|
+
func processOrder(_ order: Order) throws {
|
|
145
|
+
guard !order.items.isEmpty else { throw OrderError.emptyOrder }
|
|
146
|
+
guard order.status == .confirmed else { throw OrderError.invalidStatus(order.status) }
|
|
147
|
+
guard let payment = order.paymentMethod else { throw OrderError.noPaymentMethod }
|
|
148
|
+
charge(payment, for: order)
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## SwiftUI
|
|
155
|
+
|
|
156
|
+
### State Management
|
|
157
|
+
|
|
158
|
+
```swift
|
|
159
|
+
@Observable
|
|
160
|
+
class UserViewModel {
|
|
161
|
+
var users: [User] = []
|
|
162
|
+
var isLoading = false
|
|
163
|
+
|
|
164
|
+
func loadUsers() async {
|
|
165
|
+
isLoading = true
|
|
166
|
+
defer { isLoading = false }
|
|
167
|
+
users = (try? await userService.fetchAll()) ?? []
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Navigation
|
|
173
|
+
|
|
174
|
+
```swift
|
|
175
|
+
enum Route: Hashable {
|
|
176
|
+
case userDetail(User)
|
|
177
|
+
case settings
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
NavigationStack(path: $path) {
|
|
181
|
+
HomeView()
|
|
182
|
+
.navigationDestination(for: Route.self) { route in
|
|
183
|
+
switch route {
|
|
184
|
+
case .userDetail(let user): UserDetailView(user: user)
|
|
185
|
+
case .settings: SettingsView()
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## Testing
|
|
194
|
+
|
|
195
|
+
### Framework Stack
|
|
196
|
+
|
|
197
|
+
| Tool | Purpose |
|
|
198
|
+
|------|---------|
|
|
199
|
+
| Swift Testing | Modern test framework |
|
|
200
|
+
| XCTest | Traditional test framework |
|
|
201
|
+
| XCUITest | UI automation |
|
|
202
|
+
| swift-snapshot-testing | Snapshot testing |
|
|
203
|
+
|
|
204
|
+
### Test Structure
|
|
205
|
+
|
|
206
|
+
```swift
|
|
207
|
+
@Suite("UserService")
|
|
208
|
+
struct UserServiceTests {
|
|
209
|
+
@Test("creates user with valid input")
|
|
210
|
+
func createUserValid() async throws {
|
|
211
|
+
let user = try await sut.create(request)
|
|
212
|
+
#expect(user.name == "Alice")
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
@Test("validates email format", arguments: ["invalid", "", "@missing"])
|
|
216
|
+
func invalidEmails(email: String) async {
|
|
217
|
+
await #expect(throws: ValidationError.invalidEmail) {
|
|
218
|
+
try await sut.create(CreateUserRequest(name: "Test", email: email))
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Performance
|
|
227
|
+
|
|
228
|
+
### Key Patterns
|
|
229
|
+
|
|
230
|
+
- Profile with Instruments before optimizing
|
|
231
|
+
- Value types for stack allocation and no ARC overhead
|
|
232
|
+
- `lazy` collections for chained operations on large datasets
|
|
233
|
+
- `Set` for membership tests, `Dictionary` for keyed access
|
|
234
|
+
- `reserveCapacity` for collections with known sizes
|
|
235
|
+
- `[weak self]` in closures to prevent retain cycles
|
|
236
|
+
- `LazyVStack`/`LazyHStack` in SwiftUI for large lists
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## Tooling
|
|
241
|
+
|
|
242
|
+
### Essential Stack
|
|
243
|
+
|
|
244
|
+
| Tool | Purpose |
|
|
245
|
+
|------|---------|
|
|
246
|
+
| Swift Package Manager | Dependency management and build |
|
|
247
|
+
| SwiftLint | Static analysis and style |
|
|
248
|
+
| swift-format | Code formatting |
|
|
249
|
+
| Instruments | Profiling and performance |
|
|
250
|
+
| os.Logger | Unified logging |
|
|
251
|
+
|
|
252
|
+
### CI Essentials
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
swift build # Build
|
|
256
|
+
swift test --parallel # Run tests
|
|
257
|
+
swiftlint --strict # Static analysis
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
## Definition of Done
|
|
263
|
+
|
|
264
|
+
A Swift feature is complete when:
|
|
265
|
+
|
|
266
|
+
- [ ] Compiles with zero warnings
|
|
267
|
+
- [ ] All tests pass (unit + UI)
|
|
268
|
+
- [ ] No force-unwraps without documented justification
|
|
269
|
+
- [ ] No `var` where `let` suffices
|
|
270
|
+
- [ ] Proper access control applied
|
|
271
|
+
- [ ] Structured concurrency used (no raw GCD in new code)
|
|
272
|
+
- [ ] SwiftLint reports zero violations
|
|
273
|
+
- [ ] No retain cycles verified
|
|
274
|
+
- [ ] Accessibility labels on interactive UI
|
|
275
|
+
- [ ] Code reviewed and approved
|