code-abyss 1.6.16 → 1.7.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.
Files changed (92) hide show
  1. package/package.json +2 -2
  2. package/skills/SKILL.md +24 -16
  3. package/skills/domains/ai/SKILL.md +2 -2
  4. package/skills/domains/ai/prompt-and-eval.md +279 -0
  5. package/skills/domains/architecture/SKILL.md +2 -3
  6. package/skills/domains/architecture/security-arch.md +87 -0
  7. package/skills/domains/data-engineering/SKILL.md +188 -26
  8. package/skills/domains/development/SKILL.md +1 -4
  9. package/skills/domains/devops/SKILL.md +3 -5
  10. package/skills/domains/devops/performance.md +63 -0
  11. package/skills/domains/devops/testing.md +97 -0
  12. package/skills/domains/frontend-design/SKILL.md +12 -3
  13. package/skills/domains/frontend-design/claymorphism/SKILL.md +117 -0
  14. package/skills/domains/frontend-design/claymorphism/references/tokens.css +52 -0
  15. package/skills/domains/frontend-design/engineering.md +287 -0
  16. package/skills/domains/frontend-design/glassmorphism/SKILL.md +138 -0
  17. package/skills/domains/frontend-design/glassmorphism/references/tokens.css +32 -0
  18. package/skills/domains/frontend-design/liquid-glass/SKILL.md +135 -0
  19. package/skills/domains/frontend-design/liquid-glass/references/tokens.css +81 -0
  20. package/skills/domains/frontend-design/neubrutalism/SKILL.md +141 -0
  21. package/skills/domains/frontend-design/neubrutalism/references/tokens.css +44 -0
  22. package/skills/domains/infrastructure/SKILL.md +174 -34
  23. package/skills/domains/mobile/SKILL.md +211 -21
  24. package/skills/domains/orchestration/SKILL.md +1 -0
  25. package/skills/domains/security/SKILL.md +4 -6
  26. package/skills/domains/security/blue-team.md +57 -0
  27. package/skills/domains/security/red-team.md +54 -0
  28. package/skills/domains/security/threat-intel.md +50 -0
  29. package/skills/orchestration/multi-agent/SKILL.md +195 -46
  30. package/skills/run_skill.js +134 -0
  31. package/skills/tools/gen-docs/SKILL.md +6 -4
  32. package/skills/tools/gen-docs/scripts/doc_generator.js +349 -0
  33. package/skills/tools/verify-change/SKILL.md +8 -6
  34. package/skills/tools/verify-change/scripts/change_analyzer.js +270 -0
  35. package/skills/tools/verify-module/SKILL.md +6 -4
  36. package/skills/tools/verify-module/scripts/module_scanner.js +145 -0
  37. package/skills/tools/verify-quality/SKILL.md +5 -3
  38. package/skills/tools/verify-quality/scripts/quality_checker.js +276 -0
  39. package/skills/tools/verify-security/SKILL.md +7 -5
  40. package/skills/tools/verify-security/scripts/security_scanner.js +133 -0
  41. package/skills/__pycache__/run_skill.cpython-312.pyc +0 -0
  42. package/skills/domains/COVERAGE_PLAN.md +0 -232
  43. package/skills/domains/ai/model-evaluation.md +0 -790
  44. package/skills/domains/ai/prompt-engineering.md +0 -703
  45. package/skills/domains/architecture/compliance.md +0 -299
  46. package/skills/domains/architecture/data-security.md +0 -184
  47. package/skills/domains/data-engineering/data-pipeline.md +0 -762
  48. package/skills/domains/data-engineering/data-quality.md +0 -894
  49. package/skills/domains/data-engineering/stream-processing.md +0 -791
  50. package/skills/domains/development/dart.md +0 -963
  51. package/skills/domains/development/kotlin.md +0 -834
  52. package/skills/domains/development/php.md +0 -659
  53. package/skills/domains/development/swift.md +0 -755
  54. package/skills/domains/devops/e2e-testing.md +0 -914
  55. package/skills/domains/devops/performance-testing.md +0 -734
  56. package/skills/domains/devops/testing-strategy.md +0 -667
  57. package/skills/domains/frontend-design/build-tools.md +0 -743
  58. package/skills/domains/frontend-design/performance.md +0 -734
  59. package/skills/domains/frontend-design/testing.md +0 -699
  60. package/skills/domains/infrastructure/gitops.md +0 -735
  61. package/skills/domains/infrastructure/iac.md +0 -855
  62. package/skills/domains/infrastructure/kubernetes.md +0 -1018
  63. package/skills/domains/mobile/android-dev.md +0 -979
  64. package/skills/domains/mobile/cross-platform.md +0 -795
  65. package/skills/domains/mobile/ios-dev.md +0 -931
  66. package/skills/domains/security/secrets-management.md +0 -834
  67. package/skills/domains/security/supply-chain.md +0 -931
  68. package/skills/domains/security/threat-modeling.md +0 -828
  69. package/skills/run_skill.py +0 -153
  70. package/skills/tests/README.md +0 -225
  71. package/skills/tests/SUMMARY.md +0 -362
  72. package/skills/tests/__init__.py +0 -3
  73. package/skills/tests/__pycache__/test_change_analyzer.cpython-312.pyc +0 -0
  74. package/skills/tests/__pycache__/test_doc_generator.cpython-312.pyc +0 -0
  75. package/skills/tests/__pycache__/test_module_scanner.cpython-312.pyc +0 -0
  76. package/skills/tests/__pycache__/test_quality_checker.cpython-312.pyc +0 -0
  77. package/skills/tests/__pycache__/test_security_scanner.cpython-312.pyc +0 -0
  78. package/skills/tests/test_change_analyzer.py +0 -558
  79. package/skills/tests/test_doc_generator.py +0 -538
  80. package/skills/tests/test_module_scanner.py +0 -376
  81. package/skills/tests/test_quality_checker.py +0 -516
  82. package/skills/tests/test_security_scanner.py +0 -426
  83. package/skills/tools/gen-docs/scripts/__pycache__/doc_generator.cpython-312.pyc +0 -0
  84. package/skills/tools/gen-docs/scripts/doc_generator.py +0 -520
  85. package/skills/tools/verify-change/scripts/__pycache__/change_analyzer.cpython-312.pyc +0 -0
  86. package/skills/tools/verify-change/scripts/change_analyzer.py +0 -529
  87. package/skills/tools/verify-module/scripts/__pycache__/module_scanner.cpython-312.pyc +0 -0
  88. package/skills/tools/verify-module/scripts/module_scanner.py +0 -321
  89. package/skills/tools/verify-quality/scripts/__pycache__/quality_checker.cpython-312.pyc +0 -0
  90. package/skills/tools/verify-quality/scripts/quality_checker.py +0 -481
  91. package/skills/tools/verify-security/scripts/__pycache__/security_scanner.cpython-312.pyc +0 -0
  92. package/skills/tools/verify-security/scripts/security_scanner.py +0 -374
@@ -1,755 +0,0 @@
1
- ---
2
- name: swift
3
- description: Swift 开发技术。SwiftUI、UIKit、Combine、Swift Concurrency、ARC 内存管理。当用户提到 Swift、SwiftUI、UIKit、Combine、iOS 开发、async/await 时使用。
4
- ---
5
-
6
- # 🍎 Swift 开发 · Swift Development
7
-
8
- ## 生态架构
9
-
10
- ```
11
- Swift Concurrency
12
-
13
- ┌─────────┼─────────┐
14
- │ │ │
15
- SwiftUI UIKit Combine
16
- │ │ │
17
- └─────────┼─────────┘
18
-
19
- Foundation
20
-
21
- ┌─────────┼─────────┐
22
- CoreData Network ARC
23
- ```
24
-
25
- ## SwiftUI 基础
26
-
27
- ### 视图声明
28
- ```swift
29
- import SwiftUI
30
-
31
- struct ContentView: View {
32
- @State private var count = 0
33
- @State private var isPresented = false
34
-
35
- var body: some View {
36
- VStack(spacing: 20) {
37
- Text("Count: \(count)")
38
- .font(.largeTitle)
39
- .foregroundColor(.blue)
40
-
41
- Button("Increment") {
42
- count += 1
43
- }
44
- .buttonStyle(.borderedProminent)
45
-
46
- Button("Show Sheet") {
47
- isPresented = true
48
- }
49
- .sheet(isPresented: $isPresented) {
50
- DetailView()
51
- }
52
- }
53
- .padding()
54
- }
55
- }
56
- ```
57
-
58
- ### 状态管理
59
- ```swift
60
- // @State - 视图内部状态
61
- struct CounterView: View {
62
- @State private var count = 0
63
-
64
- var body: some View {
65
- Button("Count: \(count)") {
66
- count += 1
67
- }
68
- }
69
- }
70
-
71
- // @Binding - 双向绑定
72
- struct ChildView: View {
73
- @Binding var text: String
74
-
75
- var body: some View {
76
- TextField("Enter text", text: $text)
77
- }
78
- }
79
-
80
- // @ObservedObject - 外部可观察对象
81
- class ViewModel: ObservableObject {
82
- @Published var items: [Item] = []
83
- @Published var isLoading = false
84
-
85
- func fetchItems() async {
86
- isLoading = true
87
- defer { isLoading = false }
88
-
89
- items = await APIClient.shared.fetchItems()
90
- }
91
- }
92
-
93
- struct ListView: View {
94
- @StateObject private var viewModel = ViewModel()
95
-
96
- var body: some View {
97
- List(viewModel.items) { item in
98
- Text(item.name)
99
- }
100
- .task {
101
- await viewModel.fetchItems()
102
- }
103
- }
104
- }
105
-
106
- // @EnvironmentObject - 环境对象
107
- struct ParentView: View {
108
- @StateObject private var settings = AppSettings()
109
-
110
- var body: some View {
111
- ChildView()
112
- .environmentObject(settings)
113
- }
114
- }
115
- ```
116
-
117
- ### 列表与导航
118
- ```swift
119
- struct ItemListView: View {
120
- let items: [Item]
121
-
122
- var body: some View {
123
- NavigationStack {
124
- List(items) { item in
125
- NavigationLink(value: item) {
126
- ItemRow(item: item)
127
- }
128
- }
129
- .navigationTitle("Items")
130
- .navigationDestination(for: Item.self) { item in
131
- ItemDetailView(item: item)
132
- }
133
- }
134
- }
135
- }
136
-
137
- // 自定义行视图
138
- struct ItemRow: View {
139
- let item: Item
140
-
141
- var body: some View {
142
- HStack {
143
- AsyncImage(url: item.imageURL) { image in
144
- image.resizable()
145
- } placeholder: {
146
- ProgressView()
147
- }
148
- .frame(width: 50, height: 50)
149
- .clipShape(RoundedRectangle(cornerRadius: 8))
150
-
151
- VStack(alignment: .leading) {
152
- Text(item.name)
153
- .font(.headline)
154
- Text(item.description)
155
- .font(.caption)
156
- .foregroundColor(.secondary)
157
- }
158
- }
159
- }
160
- }
161
- ```
162
-
163
- ### 动画与过渡
164
- ```swift
165
- struct AnimatedView: View {
166
- @State private var isExpanded = false
167
- @State private var rotation = 0.0
168
-
169
- var body: some View {
170
- VStack {
171
- Rectangle()
172
- .fill(.blue)
173
- .frame(width: isExpanded ? 200 : 100,
174
- height: isExpanded ? 200 : 100)
175
- .rotationEffect(.degrees(rotation))
176
- .animation(.spring(response: 0.5, dampingFraction: 0.6), value: isExpanded)
177
-
178
- Button("Toggle") {
179
- withAnimation {
180
- isExpanded.toggle()
181
- rotation += 180
182
- }
183
- }
184
- }
185
- }
186
- }
187
-
188
- // 自定义过渡
189
- extension AnyTransition {
190
- static var slideAndFade: AnyTransition {
191
- .asymmetric(
192
- insertion: .move(edge: .trailing).combined(with: .opacity),
193
- removal: .move(edge: .leading).combined(with: .opacity)
194
- )
195
- }
196
- }
197
- ```
198
-
199
- ## UIKit 核心
200
-
201
- ### 视图控制器
202
- ```swift
203
- import UIKit
204
-
205
- class UserViewController: UIViewController {
206
- private let tableView = UITableView()
207
- private var users: [User] = []
208
-
209
- override func viewDidLoad() {
210
- super.viewDidLoad()
211
- setupUI()
212
- fetchUsers()
213
- }
214
-
215
- private func setupUI() {
216
- view.backgroundColor = .systemBackground
217
-
218
- tableView.delegate = self
219
- tableView.dataSource = self
220
- tableView.register(UserCell.self, forCellReuseIdentifier: "UserCell")
221
-
222
- view.addSubview(tableView)
223
- tableView.translatesAutoresizingMaskIntoConstraints = false
224
- NSLayoutConstraint.activate([
225
- tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
226
- tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
227
- tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
228
- tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
229
- ])
230
- }
231
-
232
- private func fetchUsers() {
233
- Task {
234
- users = await APIClient.shared.fetchUsers()
235
- tableView.reloadData()
236
- }
237
- }
238
- }
239
-
240
- extension UserViewController: UITableViewDataSource {
241
- func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
242
- users.count
243
- }
244
-
245
- func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
246
- let cell = tableView.dequeueReusableCell(withIdentifier: "UserCell", for: indexPath) as! UserCell
247
- cell.configure(with: users[indexPath.row])
248
- return cell
249
- }
250
- }
251
- ```
252
-
253
- ### Auto Layout
254
- ```swift
255
- class CustomView: UIView {
256
- private let titleLabel = UILabel()
257
- private let imageView = UIImageView()
258
-
259
- override init(frame: CGRect) {
260
- super.init(frame: frame)
261
- setupViews()
262
- }
263
-
264
- required init?(coder: NSCoder) {
265
- fatalError("init(coder:) has not been implemented")
266
- }
267
-
268
- private func setupViews() {
269
- addSubview(imageView)
270
- addSubview(titleLabel)
271
-
272
- imageView.translatesAutoresizingMaskIntoConstraints = false
273
- titleLabel.translatesAutoresizingMaskIntoConstraints = false
274
-
275
- NSLayoutConstraint.activate([
276
- imageView.topAnchor.constraint(equalTo: topAnchor, constant: 16),
277
- imageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
278
- imageView.widthAnchor.constraint(equalToConstant: 60),
279
- imageView.heightAnchor.constraint(equalToConstant: 60),
280
-
281
- titleLabel.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 12),
282
- titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
283
- titleLabel.centerYAnchor.constraint(equalTo: imageView.centerYAnchor)
284
- ])
285
- }
286
- }
287
- ```
288
-
289
- ### 导航与生命周期
290
- ```swift
291
- class MainViewController: UIViewController {
292
- override func viewDidLoad() {
293
- super.viewDidLoad()
294
- title = "Main"
295
- navigationItem.rightBarButtonItem = UIBarButtonItem(
296
- barButtonSystemItem: .add,
297
- target: self,
298
- action: #selector(addTapped)
299
- )
300
- }
301
-
302
- override func viewWillAppear(_ animated: Bool) {
303
- super.viewWillAppear(animated)
304
- // 视图即将显示
305
- }
306
-
307
- override func viewDidAppear(_ animated: Bool) {
308
- super.viewDidAppear(animated)
309
- // 视图已显示
310
- }
311
-
312
- @objc private func addTapped() {
313
- let detailVC = DetailViewController()
314
- navigationController?.pushViewController(detailVC, animated: true)
315
- }
316
- }
317
- ```
318
-
319
- ## Combine 响应式编程
320
-
321
- ### Publisher 与 Subscriber
322
- ```swift
323
- import Combine
324
-
325
- class DataService {
326
- private var cancellables = Set<AnyCancellable>()
327
-
328
- func fetchData() {
329
- URLSession.shared.dataTaskPublisher(for: url)
330
- .map(\.data)
331
- .decode(type: [Item].self, decoder: JSONDecoder())
332
- .receive(on: DispatchQueue.main)
333
- .sink(
334
- receiveCompletion: { completion in
335
- switch completion {
336
- case .finished:
337
- print("Completed")
338
- case .failure(let error):
339
- print("Error: \(error)")
340
- }
341
- },
342
- receiveValue: { items in
343
- print("Received \(items.count) items")
344
- }
345
- )
346
- .store(in: &cancellables)
347
- }
348
- }
349
- ```
350
-
351
- ### 操作符链
352
- ```swift
353
- class SearchViewModel: ObservableObject {
354
- @Published var searchText = ""
355
- @Published var results: [Result] = []
356
-
357
- private var cancellables = Set<AnyCancellable>()
358
-
359
- init() {
360
- $searchText
361
- .debounce(for: .milliseconds(300), scheduler: DispatchQueue.main)
362
- .removeDuplicates()
363
- .filter { !$0.isEmpty }
364
- .flatMap { query in
365
- self.search(query: query)
366
- .catch { _ in Just([]) }
367
- }
368
- .assign(to: &$results)
369
- }
370
-
371
- private func search(query: String) -> AnyPublisher<[Result], Error> {
372
- URLSession.shared.dataTaskPublisher(for: searchURL(query))
373
- .map(\.data)
374
- .decode(type: [Result].self, decoder: JSONDecoder())
375
- .eraseToAnyPublisher()
376
- }
377
- }
378
- ```
379
-
380
- ### Subject 类型
381
- ```swift
382
- import Combine
383
-
384
- class EventBus {
385
- static let shared = EventBus()
386
-
387
- let userLoggedIn = PassthroughSubject<User, Never>()
388
- let dataUpdated = CurrentValueSubject<[Item], Never>([])
389
-
390
- private init() {}
391
- }
392
-
393
- // 使用
394
- EventBus.shared.userLoggedIn
395
- .sink { user in
396
- print("User logged in: \(user.name)")
397
- }
398
- .store(in: &cancellables)
399
-
400
- EventBus.shared.userLoggedIn.send(currentUser)
401
- ```
402
-
403
- ## Swift Concurrency
404
-
405
- ### async/await
406
- ```swift
407
- // 异步函数
408
- func fetchUser(id: String) async throws -> User {
409
- let url = URL(string: "https://api.example.com/users/\(id)")!
410
- let (data, _) = try await URLSession.shared.data(from: url)
411
- return try JSONDecoder().decode(User.self, from: data)
412
- }
413
-
414
- // 并发调用
415
- func fetchMultipleUsers(ids: [String]) async throws -> [User] {
416
- try await withThrowingTaskGroup(of: User.self) { group in
417
- for id in ids {
418
- group.addTask {
419
- try await fetchUser(id: id)
420
- }
421
- }
422
-
423
- var users: [User] = []
424
- for try await user in group {
425
- users.append(user)
426
- }
427
- return users
428
- }
429
- }
430
-
431
- // 在视图中使用
432
- struct UserView: View {
433
- @State private var user: User?
434
-
435
- var body: some View {
436
- Group {
437
- if let user = user {
438
- Text(user.name)
439
- } else {
440
- ProgressView()
441
- }
442
- }
443
- .task {
444
- user = try? await fetchUser(id: "123")
445
- }
446
- }
447
- }
448
- ```
449
-
450
- ### Actor 并发安全
451
- ```swift
452
- actor DatabaseManager {
453
- private var cache: [String: Data] = [:]
454
-
455
- func getData(key: String) async -> Data? {
456
- if let cached = cache[key] {
457
- return cached
458
- }
459
-
460
- let data = await fetchFromNetwork(key: key)
461
- cache[key] = data
462
- return data
463
- }
464
-
465
- func clearCache() {
466
- cache.removeAll()
467
- }
468
- }
469
-
470
- // 使用
471
- let db = DatabaseManager()
472
- let data = await db.getData(key: "user_123")
473
- ```
474
-
475
- ### AsyncSequence
476
- ```swift
477
- struct AsyncLineReader: AsyncSequence {
478
- typealias Element = String
479
-
480
- let url: URL
481
-
482
- func makeAsyncIterator() -> AsyncIterator {
483
- AsyncIterator(url: url)
484
- }
485
-
486
- struct AsyncIterator: AsyncIteratorProtocol {
487
- let url: URL
488
- private var lines: [String]?
489
- private var index = 0
490
-
491
- mutating func next() async throws -> String? {
492
- if lines == nil {
493
- let content = try String(contentsOf: url)
494
- lines = content.components(separatedBy: .newlines)
495
- }
496
-
497
- guard let lines = lines, index < lines.count else {
498
- return nil
499
- }
500
-
501
- defer { index += 1 }
502
- return lines[index]
503
- }
504
- }
505
- }
506
-
507
- // 使用
508
- for try await line in AsyncLineReader(url: fileURL) {
509
- print(line)
510
- }
511
- ```
512
-
513
- ## 内存管理 (ARC)
514
-
515
- ### 强引用循环
516
- ```swift
517
- class Person {
518
- let name: String
519
- var apartment: Apartment?
520
-
521
- init(name: String) {
522
- self.name = name
523
- }
524
-
525
- deinit {
526
- print("\(name) is being deinitialized")
527
- }
528
- }
529
-
530
- class Apartment {
531
- let unit: String
532
- weak var tenant: Person? // weak 避免循环引用
533
-
534
- init(unit: String) {
535
- self.unit = unit
536
- }
537
-
538
- deinit {
539
- print("Apartment \(unit) is being deinitialized")
540
- }
541
- }
542
- ```
543
-
544
- ### 闭包捕获列表
545
- ```swift
546
- class ViewController: UIViewController {
547
- var name = "View Controller"
548
-
549
- func setupHandler() {
550
- // ❌ 强引用循环
551
- someAsyncOperation {
552
- print(self.name)
553
- }
554
-
555
- // ✅ 使用 weak
556
- someAsyncOperation { [weak self] in
557
- guard let self = self else { return }
558
- print(self.name)
559
- }
560
-
561
- // ✅ 使用 unowned (确定不会为 nil)
562
- someAsyncOperation { [unowned self] in
563
- print(self.name)
564
- }
565
- }
566
- }
567
- ```
568
-
569
- ### 值类型 vs 引用类型
570
- ```swift
571
- // 值类型 (struct, enum) - 复制语义
572
- struct Point {
573
- var x: Int
574
- var y: Int
575
- }
576
-
577
- var p1 = Point(x: 0, y: 0)
578
- var p2 = p1
579
- p2.x = 10
580
- print(p1.x) // 0 (未改变)
581
-
582
- // 引用类型 (class) - 共享语义
583
- class Rectangle {
584
- var width: Int
585
- var height: Int
586
-
587
- init(width: Int, height: Int) {
588
- self.width = width
589
- self.height = height
590
- }
591
- }
592
-
593
- let r1 = Rectangle(width: 10, height: 20)
594
- let r2 = r1
595
- r2.width = 30
596
- print(r1.width) // 30 (已改变)
597
- ```
598
-
599
- ## 网络请求
600
-
601
- ### URLSession
602
- ```swift
603
- class APIClient {
604
- static let shared = APIClient()
605
-
606
- func fetch<T: Decodable>(_ type: T.Type, from url: URL) async throws -> T {
607
- let (data, response) = try await URLSession.shared.data(from: url)
608
-
609
- guard let httpResponse = response as? HTTPURLResponse,
610
- (200...299).contains(httpResponse.statusCode) else {
611
- throw APIError.invalidResponse
612
- }
613
-
614
- return try JSONDecoder().decode(T.self, from: data)
615
- }
616
-
617
- func post<T: Encodable, R: Decodable>(
618
- _ endpoint: String,
619
- body: T
620
- ) async throws -> R {
621
- var request = URLRequest(url: URL(string: endpoint)!)
622
- request.httpMethod = "POST"
623
- request.setValue("application/json", forHTTPHeaderField: "Content-Type")
624
- request.httpBody = try JSONEncoder().encode(body)
625
-
626
- let (data, _) = try await URLSession.shared.data(for: request)
627
- return try JSONDecoder().decode(R.self, from: data)
628
- }
629
- }
630
- ```
631
-
632
- ## CoreData 持久化
633
-
634
- ### 数据模型
635
- ```swift
636
- import CoreData
637
-
638
- @objc(Task)
639
- class Task: NSManagedObject {
640
- @NSManaged var id: UUID
641
- @NSManaged var title: String
642
- @NSManaged var isCompleted: Bool
643
- @NSManaged var createdAt: Date
644
- }
645
-
646
- class PersistenceController {
647
- static let shared = PersistenceController()
648
-
649
- let container: NSPersistentContainer
650
-
651
- init() {
652
- container = NSPersistentContainer(name: "Model")
653
- container.loadPersistentStores { _, error in
654
- if let error = error {
655
- fatalError("Core Data failed: \(error)")
656
- }
657
- }
658
- }
659
-
660
- func save() {
661
- let context = container.viewContext
662
- if context.hasChanges {
663
- try? context.save()
664
- }
665
- }
666
- }
667
-
668
- // 使用
669
- let context = PersistenceController.shared.container.viewContext
670
- let task = Task(context: context)
671
- task.id = UUID()
672
- task.title = "New Task"
673
- task.isCompleted = false
674
- task.createdAt = Date()
675
- PersistenceController.shared.save()
676
- ```
677
-
678
- ## 测试
679
-
680
- ### XCTest 单元测试
681
- ```swift
682
- import XCTest
683
- @testable import MyApp
684
-
685
- class CalculatorTests: XCTestCase {
686
- var calculator: Calculator!
687
-
688
- override func setUp() {
689
- super.setUp()
690
- calculator = Calculator()
691
- }
692
-
693
- override func tearDown() {
694
- calculator = nil
695
- super.tearDown()
696
- }
697
-
698
- func testAddition() {
699
- let result = calculator.add(2, 3)
700
- XCTAssertEqual(result, 5)
701
- }
702
-
703
- func testAsyncOperation() async throws {
704
- let result = try await calculator.fetchResult()
705
- XCTAssertGreaterThan(result, 0)
706
- }
707
- }
708
- ```
709
-
710
- ### UI 测试
711
- ```swift
712
- class UITests: XCTestCase {
713
- func testLoginFlow() {
714
- let app = XCUIApplication()
715
- app.launch()
716
-
717
- let emailField = app.textFields["Email"]
718
- emailField.tap()
719
- emailField.typeText("test@example.com")
720
-
721
- let passwordField = app.secureTextFields["Password"]
722
- passwordField.tap()
723
- passwordField.typeText("password123")
724
-
725
- app.buttons["Login"].tap()
726
-
727
- XCTAssertTrue(app.staticTexts["Welcome"].exists)
728
- }
729
- }
730
- ```
731
-
732
- ## 框架对比
733
-
734
- | 特性 | SwiftUI | UIKit |
735
- |------|---------|-------|
736
- | 声明式 | ✅ | ❌ |
737
- | 学习曲线 | 平缓 | 陡峭 |
738
- | 性能 | 优秀 | 优秀 |
739
- | 兼容性 | iOS 13+ | iOS 2+ |
740
- | 自定义能力 | 中等 | 强大 |
741
- | 预览功能 | ✅ | ❌ |
742
-
743
- ## 工具清单
744
-
745
- | 工具 | 用途 |
746
- |------|------|
747
- | Xcode | 官方 IDE |
748
- | Swift Package Manager | 依赖管理 |
749
- | CocoaPods | 依赖管理 |
750
- | Carthage | 依赖管理 |
751
- | Instruments | 性能分析 |
752
- | SwiftLint | 代码规范 |
753
- | Fastlane | 自动化部署 |
754
-
755
- ---