@sun-asterisk/sunlint 1.3.40 → 1.3.42

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 (172) hide show
  1. package/core/rule-selection-service.js +11 -0
  2. package/package.json +1 -1
  3. package/skill-assets/sunlint-code-quality/rules/dart/D001-recommended-lints.md +36 -0
  4. package/skill-assets/sunlint-code-quality/rules/dart/D002-dispose-resources.md +44 -0
  5. package/skill-assets/sunlint-code-quality/rules/dart/D003-prefer-widget-classes.md +53 -0
  6. package/skill-assets/sunlint-code-quality/rules/dart/D004-avoid-shrinkwrap.md +31 -0
  7. package/skill-assets/sunlint-code-quality/rules/dart/D005-widget-nesting.md +62 -0
  8. package/skill-assets/sunlint-code-quality/rules/dart/D006-large-callbacks.md +54 -0
  9. package/skill-assets/sunlint-code-quality/rules/dart/D007-lifecycle-order.md +44 -0
  10. package/skill-assets/sunlint-code-quality/rules/dart/D008-long-functions.md +37 -0
  11. package/skill-assets/sunlint-code-quality/rules/dart/D009-function-parameters.md +38 -0
  12. package/skill-assets/sunlint-code-quality/rules/dart/D010-cyclomatic-complexity.md +46 -0
  13. package/skill-assets/sunlint-code-quality/rules/dart/D011-named-parameters.md +31 -0
  14. package/skill-assets/sunlint-code-quality/rules/dart/D012-named-booleans.md +28 -0
  15. package/skill-assets/sunlint-code-quality/rules/dart/D013-single-public-class.md +34 -0
  16. package/skill-assets/sunlint-code-quality/rules/dart/D014-safe-collection-access.md +30 -0
  17. package/skill-assets/sunlint-code-quality/rules/dart/D015-copywith-consistency.md +52 -0
  18. package/skill-assets/sunlint-code-quality/rules/dart/D016-project-tests.md +32 -0
  19. package/skill-assets/sunlint-code-quality/rules/dart/D017-review-dependencies.md +24 -0
  20. package/skill-assets/sunlint-code-quality/rules/dart/D018-no-commented-code.md +34 -0
  21. package/skill-assets/sunlint-code-quality/rules/dart/D019-single-child-wrappers.md +31 -0
  22. package/skill-assets/sunlint-code-quality/rules/dart/D020-if-else-limit.md +41 -0
  23. package/skill-assets/sunlint-code-quality/rules/dart/D021-negated-booleans.md +26 -0
  24. package/skill-assets/sunlint-code-quality/rules/dart/D022-setstate-usage.md +35 -0
  25. package/skill-assets/sunlint-code-quality/rules/dart/D023-unnecessary-overrides.md +32 -0
  26. package/skill-assets/sunlint-code-quality/rules/dart/D024-avoid-unnecessary-statefulwidget.md +45 -0
  27. package/skill-assets/sunlint-code-quality/rules/dart/D025-nested-ternaries.md +35 -0
  28. package/skill-assets/sunlint-code-quality/rules/go/C006-verb-noun-functions.md +45 -0
  29. package/skill-assets/sunlint-code-quality/rules/go/C013-no-dead-code.md +48 -0
  30. package/skill-assets/sunlint-code-quality/rules/go/C014-dependency-injection.md +85 -0
  31. package/skill-assets/sunlint-code-quality/rules/go/C017-no-constructor-logic.md +67 -0
  32. package/skill-assets/sunlint-code-quality/rules/go/C018-generic-errors.md +63 -0
  33. package/skill-assets/sunlint-code-quality/rules/go/C019-error-log-level.md +50 -0
  34. package/skill-assets/sunlint-code-quality/rules/go/C020-no-unused-imports.md +45 -0
  35. package/skill-assets/sunlint-code-quality/rules/go/C022-no-unused-variables.md +34 -0
  36. package/skill-assets/sunlint-code-quality/rules/go/C023-no-duplicate-names.md +41 -0
  37. package/skill-assets/sunlint-code-quality/rules/go/C024-centralize-constants.md +55 -0
  38. package/skill-assets/sunlint-code-quality/rules/go/C029-catch-log-root-cause.md +56 -0
  39. package/skill-assets/sunlint-code-quality/rules/go/C030-custom-error-classes.md +69 -0
  40. package/skill-assets/sunlint-code-quality/rules/go/C033-separate-data-access.md +68 -0
  41. package/skill-assets/sunlint-code-quality/rules/go/C035-error-context-logging.md +48 -0
  42. package/skill-assets/sunlint-code-quality/rules/go/C041-no-hardcoded-secrets.md +45 -0
  43. package/skill-assets/sunlint-code-quality/rules/go/C042-boolean-naming.md +42 -0
  44. package/skill-assets/sunlint-code-quality/rules/go/C052-controller-parsing.md +62 -0
  45. package/skill-assets/sunlint-code-quality/rules/go/C060-superclass-logic.md +60 -0
  46. package/skill-assets/sunlint-code-quality/rules/go/C067-no-hardcoded-config.md +51 -0
  47. package/skill-assets/sunlint-code-quality/rules/go/S003-open-redirect.md +80 -0
  48. package/skill-assets/sunlint-code-quality/rules/go/S004-no-log-credentials.md +66 -0
  49. package/skill-assets/sunlint-code-quality/rules/go/S005-server-authorization.md +55 -0
  50. package/skill-assets/sunlint-code-quality/rules/go/S006-default-credentials.md +47 -0
  51. package/skill-assets/sunlint-code-quality/rules/go/S007-output-encoding.md +50 -0
  52. package/skill-assets/sunlint-code-quality/rules/go/S009-approved-crypto.md +63 -0
  53. package/skill-assets/sunlint-code-quality/rules/go/S010-csprng.md +53 -0
  54. package/skill-assets/sunlint-code-quality/rules/go/S011-encrypted-client-hello.md +34 -0
  55. package/skill-assets/sunlint-code-quality/rules/go/S012-secrets-management.md +49 -0
  56. package/skill-assets/sunlint-code-quality/rules/go/S013-tls-connections.md +61 -0
  57. package/skill-assets/sunlint-code-quality/rules/go/S016-no-sensitive-query-string.md +42 -0
  58. package/skill-assets/sunlint-code-quality/rules/go/S017-parameterized-queries.md +36 -0
  59. package/skill-assets/sunlint-code-quality/rules/go/S019-email-input-sanitization.md +44 -0
  60. package/skill-assets/sunlint-code-quality/rules/go/S020-eval-code-execution.md +47 -0
  61. package/skill-assets/sunlint-code-quality/rules/go/S022-context-escaping.md +49 -0
  62. package/skill-assets/sunlint-code-quality/rules/go/S023-dynamic-js-encoding.md +51 -0
  63. package/skill-assets/sunlint-code-quality/rules/go/S025-server-validation.md +57 -0
  64. package/skill-assets/sunlint-code-quality/rules/go/S026-tls-encryption.md +46 -0
  65. package/skill-assets/sunlint-code-quality/rules/go/S027-mtls-validation.md +52 -0
  66. package/skill-assets/sunlint-code-quality/rules/go/S028-upload-limits.md +58 -0
  67. package/skill-assets/sunlint-code-quality/rules/go/S029-csrf-protection.md +53 -0
  68. package/skill-assets/sunlint-code-quality/rules/go/S030-directory-browsing.md +53 -0
  69. package/skill-assets/sunlint-code-quality/rules/go/S031-secure-cookie-flag.md +48 -0
  70. package/skill-assets/sunlint-code-quality/rules/go/S032-httponly-cookie.md +42 -0
  71. package/skill-assets/sunlint-code-quality/rules/go/S033-samesite-cookie.md +49 -0
  72. package/skill-assets/sunlint-code-quality/rules/go/S034-host-prefix-cookie.md +44 -0
  73. package/skill-assets/sunlint-code-quality/rules/go/S035-app-hostnames.md +50 -0
  74. package/skill-assets/sunlint-code-quality/rules/go/S036-internal-file-paths.md +56 -0
  75. package/skill-assets/sunlint-code-quality/rules/go/S037-anti-cache-headers.md +43 -0
  76. package/skill-assets/sunlint-code-quality/rules/go/S039-tls-certificate-validation.md +41 -0
  77. package/skill-assets/sunlint-code-quality/rules/go/S041-logout-invalidation.md +46 -0
  78. package/skill-assets/sunlint-code-quality/rules/go/S042-long-lived-sessions.md +58 -0
  79. package/skill-assets/sunlint-code-quality/rules/go/S044-critical-changes-reauth.md +53 -0
  80. package/skill-assets/sunlint-code-quality/rules/go/S045-brute-force-protection.md +55 -0
  81. package/skill-assets/sunlint-code-quality/rules/go/S047-oauth-csrf-protection.md +51 -0
  82. package/skill-assets/sunlint-code-quality/rules/go/S048-oauth-redirect-validation.md +58 -0
  83. package/skill-assets/sunlint-code-quality/rules/go/S049-auth-code-expiry.md +52 -0
  84. package/skill-assets/sunlint-code-quality/rules/go/S050-token-entropy.md +53 -0
  85. package/skill-assets/sunlint-code-quality/rules/go/S051-password-length.md +49 -0
  86. package/skill-assets/sunlint-code-quality/rules/go/S052-otp-entropy.md +48 -0
  87. package/skill-assets/sunlint-code-quality/rules/go/S053-generic-error-messages.md +51 -0
  88. package/skill-assets/sunlint-code-quality/rules/go/S054-no-default-admin.md +43 -0
  89. package/skill-assets/sunlint-code-quality/rules/go/S055-content-type-validation.md +52 -0
  90. package/skill-assets/sunlint-code-quality/rules/go/S056-log-injection.md +40 -0
  91. package/skill-assets/sunlint-code-quality/rules/go/S057-synchronized-time.md +40 -0
  92. package/skill-assets/sunlint-code-quality/rules/go/S058-ssrf-protection.md +70 -0
  93. package/skill-assets/sunlint-code-quality/rules/ruby/RB001-use-snake-case.md +30 -0
  94. package/skill-assets/sunlint-code-quality/rules/ruby/RB002-use-camel-case.md +38 -0
  95. package/skill-assets/sunlint-code-quality/rules/ruby/RB003-use-screaming-snake-case.md +26 -0
  96. package/skill-assets/sunlint-code-quality/rules/ruby/RB004-predicate-methods.md +36 -0
  97. package/skill-assets/sunlint-code-quality/rules/ruby/RB005-dangerous-methods.md +36 -0
  98. package/skill-assets/sunlint-code-quality/rules/ruby/RB006-indentation.md +32 -0
  99. package/skill-assets/sunlint-code-quality/rules/ruby/RB007-line-length.md +25 -0
  100. package/skill-assets/sunlint-code-quality/rules/ruby/RB008-rescue-exception.md +36 -0
  101. package/skill-assets/sunlint-code-quality/rules/ruby/RB009-save-bang.md +41 -0
  102. package/skill-assets/sunlint-code-quality/rules/ruby/RB010-avoid-n-plus-one.md +32 -0
  103. package/skill-assets/sunlint-code-quality/rules/ruby/RB011-use-find-each.md +30 -0
  104. package/skill-assets/sunlint-code-quality/rules/ruby/RB012-sql-injection.md +29 -0
  105. package/skill-assets/sunlint-code-quality/rules/ruby/RB013-prefer-has-many-through.md +35 -0
  106. package/skill-assets/sunlint-code-quality/rules/ruby/RB014-dependent-associations.md +28 -0
  107. package/skill-assets/sunlint-code-quality/rules/ruby/RB015-modern-validations.md +29 -0
  108. package/skill-assets/sunlint-code-quality/rules/ruby/RB016-thin-controllers.md +45 -0
  109. package/skill-assets/sunlint-code-quality/rules/ruby/RB017-avoid-fat-models.md +41 -0
  110. package/skill-assets/sunlint-code-quality/rules/ruby/RB018-service-objects.md +36 -0
  111. package/skill-assets/sunlint-code-quality/rules/ruby/RB019-avoid-metaprogramming.md +40 -0
  112. package/skill-assets/sunlint-code-quality/rules/ruby/RB020-use-pluck.md +29 -0
  113. package/skill-assets/sunlint-code-quality/rules/ruby/RB021-use-size.md +27 -0
  114. package/skill-assets/sunlint-code-quality/rules/ruby/RB022-order-by-timestamps.md +24 -0
  115. package/skill-assets/sunlint-code-quality/rules/ruby/RB023-where-missing.md +24 -0
  116. package/skill-assets/sunlint-code-quality/rules/ruby/RB024-method-length.md +41 -0
  117. package/skill-assets/sunlint-code-quality/rules/ruby/RB025-parameter-limits.md +28 -0
  118. package/skill-assets/sunlint-code-quality/rules/ruby/RB026-avoid-deep-nesting.md +38 -0
  119. package/skill-assets/sunlint-code-quality/rules/ruby/RB027-guard-clauses.md +37 -0
  120. package/skill-assets/sunlint-code-quality/rules/ruby/RB028-class-length.md +32 -0
  121. package/skill-assets/sunlint-code-quality/rules/ruby/RB029-meaningful-names.md +30 -0
  122. package/skill-assets/sunlint-code-quality/rules/ruby/RB030-dry-principle.md +37 -0
  123. package/skill-assets/sunlint-code-quality/rules/ruby/RB031-mvc-architecture.md +37 -0
  124. package/skill-assets/sunlint-code-quality/rules/ruby/RB032-use-concerns.md +31 -0
  125. package/skill-assets/sunlint-code-quality/rules/ruby/RB033-moderate-callbacks.md +31 -0
  126. package/skill-assets/sunlint-code-quality/rules/ruby/RB034-use-decorators.md +33 -0
  127. package/skill-assets/sunlint-code-quality/rules/ruby/RB035-comprehensive-tests.md +32 -0
  128. package/skill-assets/sunlint-code-quality/rules/ruby/RB036-frozen-string-literal.md +29 -0
  129. package/skill-assets/sunlint-code-quality/rules/ruby/RB037-it-parameter.md +25 -0
  130. package/skill-assets/sunlint-code-quality/rules/ruby/RB038-modern-enum-syntax.md +28 -0
  131. package/skill-assets/sunlint-code-quality/rules/ruby/RB039-solid-adapters.md +29 -0
  132. package/skill-assets/sunlint-code-quality/rules/ruby/RB040-rails-authentication.md +26 -0
  133. package/skill-assets/sunlint-code-quality/rules/ruby/RB041-async-query-loading.md +29 -0
  134. package/skill-assets/sunlint-code-quality/rules/ruby/RB042-hotwire-turbo.md +30 -0
  135. package/skill-assets/sunlint-code-quality/rules/ruby/RB043-use-propshaft.md +27 -0
  136. package/skill-assets/sunlint-code-quality/rules/ruby/RB044-structured-logging.md +35 -0
  137. package/skill-assets/sunlint-code-quality/rules/ruby/RB045-prism-parser.md +29 -0
  138. package/skill-assets/sunlint-code-quality/rules/swift/SW001-block-based-kvo.md +40 -0
  139. package/skill-assets/sunlint-code-quality/rules/swift/SW002-class-delegate-protocol.md +36 -0
  140. package/skill-assets/sunlint-code-quality/rules/swift/SW003-compiler-protocol-init.md +28 -0
  141. package/skill-assets/sunlint-code-quality/rules/swift/SW004-contains-over-filter-count.md +28 -0
  142. package/skill-assets/sunlint-code-quality/rules/swift/SW005-convenience-type.md +34 -0
  143. package/skill-assets/sunlint-code-quality/rules/swift/SW006-discarded-notification-center-observer.md +41 -0
  144. package/skill-assets/sunlint-code-quality/rules/swift/SW007-discouraged-direct-init.md +28 -0
  145. package/skill-assets/sunlint-code-quality/rules/swift/SW008-discouraged-optional-boolean.md +32 -0
  146. package/skill-assets/sunlint-code-quality/rules/swift/SW009-empty-count.md +30 -0
  147. package/skill-assets/sunlint-code-quality/rules/swift/SW010-empty-string.md +30 -0
  148. package/skill-assets/sunlint-code-quality/rules/swift/SW011-explicit-init.md +26 -0
  149. package/skill-assets/sunlint-code-quality/rules/swift/SW012-fatal-error-message.md +28 -0
  150. package/skill-assets/sunlint-code-quality/rules/swift/SW013-for-where.md +30 -0
  151. package/skill-assets/sunlint-code-quality/rules/swift/SW014-force-cast.md +26 -0
  152. package/skill-assets/sunlint-code-quality/rules/swift/SW015-force-try.md +30 -0
  153. package/skill-assets/sunlint-code-quality/rules/swift/SW016-force-unwrapping.md +32 -0
  154. package/skill-assets/sunlint-code-quality/rules/swift/SW017-function-parameter-count.md +37 -0
  155. package/skill-assets/sunlint-code-quality/rules/swift/SW018-large-tuple.md +41 -0
  156. package/skill-assets/sunlint-code-quality/rules/swift/SW019-legacy-constructor.md +28 -0
  157. package/skill-assets/sunlint-code-quality/rules/swift/SW020-nesting.md +38 -0
  158. package/skill-assets/sunlint-code-quality/rules/swift/SW021-no-extension-access-modifier.md +28 -0
  159. package/skill-assets/sunlint-code-quality/rules/swift/SW022-overridden-super-call.md +30 -0
  160. package/skill-assets/sunlint-code-quality/rules/swift/SW023-override-in-extension.md +32 -0
  161. package/skill-assets/sunlint-code-quality/rules/swift/SW024-private-over-fileprivate.md +28 -0
  162. package/skill-assets/sunlint-code-quality/rules/swift/SW025-private-unit-test.md +32 -0
  163. package/skill-assets/sunlint-code-quality/rules/swift/SW026-prohibited-super-call.md +29 -0
  164. package/skill-assets/sunlint-code-quality/rules/swift/SW027-sorted-first-last.md +28 -0
  165. package/skill-assets/sunlint-code-quality/rules/swift/SW028-syntactic-sugar.md +28 -0
  166. package/skill-assets/sunlint-code-quality/rules/swift/SW029-unused-closure-parameter.md +28 -0
  167. package/skill-assets/sunlint-code-quality/rules/swift/SW030-unused-enumerated.md +28 -0
  168. package/skill-assets/sunlint-code-quality/rules/swift/SW031-unused-optional-binding.md +26 -0
  169. package/skill-assets/sunlint-code-quality/rules/swift/SW032-valid-ibinspectable.md +26 -0
  170. package/skill-assets/sunlint-code-quality/rules/swift/SW033-vertical-parameter-alignment.md +36 -0
  171. package/skill-assets/sunlint-code-quality/rules/swift/SW034-void-return.md +28 -0
  172. package/skill-assets/sunlint-code-quality/rules/swift/SW035-weak-delegate.md +28 -0
@@ -0,0 +1,28 @@
1
+ ---
2
+ title: Tránh khởi tạo trực tiếp các system types
3
+ impact: MEDIUM
4
+ impactDescription: Tránh việc tạo các instance không hợp lệ hoặc không cần thiết của các system types có sẵn cơ chế factory/singleton.
5
+ tags: swift, ios, bundle, uidevice, initialization
6
+ ---
7
+
8
+ ## Tránh khởi tạo trực tiếp các system types
9
+
10
+ Tránh khởi tạo trực tiếp các kiểu như: `Bundle`, `NSError`, `UIDevice`. Sử dụng các factory method hoặc properties sẵn có (như `.main`, `.current`) thay vì gọi `init()` trực tiếp để đảm bảo tính nhất quán và hiệu năng.
11
+
12
+ **Incorrect (khởi tạo trực tiếp):**
13
+
14
+ ```swift
15
+ let bundle = Bundle()
16
+ let device = UIDevice()
17
+ let error = NSError()
18
+ ```
19
+
20
+ **Correct (sử dụng instance có sẵn):**
21
+
22
+ ```swift
23
+ let mainBundle = Bundle.main
24
+ let currentDevice = UIDevice.current
25
+ let designError = NSError(domain: "com.example", code: 404, userInfo: nil) // Cần truyền tham số nếu tạo mới hợp lệ
26
+ ```
27
+
28
+ **Tools:** SwiftLint (discouraged_direct_init)
@@ -0,0 +1,32 @@
1
+ ---
2
+ title: Không dùng optional cho Boolean
3
+ impact: HIGH
4
+ impactDescription: Tránh ambiguity logic (3 trạng thái: true, false, nil) khi chỉ cần 2 trạng thái logic nhị phân.
5
+ tags: swift, ios, boolean, optional, logic
6
+ ---
7
+
8
+ ## Không dùng optional cho Boolean
9
+
10
+ Không khai báo kiểu `Bool?` nếu có thể tránh được. Việc có 3 trạng thái (`true`, `false`, `nil`) cho một biến logic thường gây khó khăn trong việc kiểm soát luồng điều kiện và dễ dẫn đến lỗi logic. Hãy sử dụng giá trị mặc định rõ ràng.
11
+
12
+ **Incorrect (dùng optional bool):**
13
+
14
+ ```swift
15
+ var isExpanded: Bool?
16
+
17
+ if isExpanded == true {
18
+ // ...
19
+ }
20
+ ```
21
+
22
+ **Correct (dùng bool với giá trị mặc định):**
23
+
24
+ ```swift
25
+ var isExpanded: Bool = false
26
+
27
+ if isExpanded {
28
+ // ...
29
+ }
30
+ ```
31
+
32
+ **Tools:** SwiftLint (discouraged_optional_boolean)
@@ -0,0 +1,30 @@
1
+ ---
2
+ title: Ưu tiên .isEmpty thay vì .count == 0
3
+ impact: LOW
4
+ impactDescription: Code rõ ràng hơn và hiệu năng tốt hơn (một số collection cần duyệt để đếm nhưng chỉ cần check phần tử đầu để biết có trống không).
5
+ tags: swift, ios, performance, collection, isEmpty
6
+ ---
7
+
8
+ ## Ưu tiên .isEmpty thay vì .count == 0
9
+
10
+ Dùng `.isEmpty` giúp code dễ đọc hơn và mang ý nghĩa rõ ràng hơn về mục đích kiểm tra tập hợp trống. Ngoài ra, `.count == 0` có thể chậm hơn trên một số loại collection so với `.isEmpty`.
11
+
12
+ **Incorrect (kiểm tra count):**
13
+
14
+ ```swift
15
+ let items = [String]()
16
+ if items.count == 0 {
17
+ print("Danh sách trống")
18
+ }
19
+ ```
20
+
21
+ **Correct (dùng isEmpty):**
22
+
23
+ ```swift
24
+ let items = [String]()
25
+ if items.isEmpty {
26
+ print("Danh sách trống")
27
+ }
28
+ ```
29
+
30
+ **Tools:** SwiftLint (empty_count)
@@ -0,0 +1,30 @@
1
+ ---
2
+ title: Ưu tiên isEmpty thay vì so sánh == ""
3
+ impact: LOW
4
+ impactDescription: Tăng độ rõ ràng và tránh lỗi tiềm ẩn khi xử lý chuỗi rỗng.
5
+ tags: swift, ios, string, isEmpty, readability
6
+ ---
7
+
8
+ ## Ưu tiên isEmpty thay vì so sánh == ""
9
+
10
+ Dùng `.isEmpty` thay cho so sánh bằng chuỗi rỗng `""`. Điều này giúp code đồng bộ, chuyên nghiệp và rõ ràng hơn về mặt ngữ nghĩa trong Swift.
11
+
12
+ **Incorrect (so sánh chuỗi rỗng):**
13
+
14
+ ```swift
15
+ let name = ""
16
+ if name == "" {
17
+ print("Tên không được để trống")
18
+ }
19
+ ```
20
+
21
+ **Correct (dùng isEmpty):**
22
+
23
+ ```swift
24
+ let name = ""
25
+ if name.isEmpty {
26
+ print("Tên không được để trống")
27
+ }
28
+ ```
29
+
30
+ **Tools:** SwiftLint (empty_string)
@@ -0,0 +1,26 @@
1
+ ---
2
+ title: Không sử dụng .init() khi không cần thiết
3
+ impact: LOW
4
+ impactDescription: Giảm bớt sự rườm rà trong code, tuân thủ phong cách viết Swift ngắn gọn và hiện đại.
5
+ tags: swift, ios, initialization, init, readability
6
+ ---
7
+
8
+ ## Không sử dụng .init() khi không cần thiết
9
+
10
+ Tránh gọi `.init()` một cách tường minh nếu không bắt buộc. Trong Swift, việc khởi tạo đối tượng bằng cách gọi trực tiếp tên kiểu dữ liệu kèm tham số được ưu tiên hơn vì tính ngắn gọn và dễ đọc.
11
+
12
+ **Incorrect (gọi .init tường minh):**
13
+
14
+ ```swift
15
+ let point = CGPoint.init(x: 10, y: 20)
16
+ let user = User.init(name: "John")
17
+ ```
18
+
19
+ **Correct (khởi tạo trực tiếp):**
20
+
21
+ ```swift
22
+ let point = CGPoint(x: 10, y: 20)
23
+ let user = User(name: "John")
24
+ ```
25
+
26
+ **Tools:** SwiftLint (explicit_init)
@@ -0,0 +1,28 @@
1
+ ---
2
+ title: Luôn phải có message rõ ràng khi sử dụng fatalError
3
+ impact: MEDIUM
4
+ impactDescription: Giúp xác định nguyên nhân lỗi nhanh chóng khi ứng dụng bị crash trong quá trình phát triển hoặc test.
5
+ tags: swift, ios, error-handling, fatalError, debugging
6
+ ---
7
+
8
+ ## Luôn phải có message rõ ràng khi sử dụng fatalError
9
+
10
+ Bắt buộc thêm mô tả khi gọi `fatalError(...)`. Message này rất quan trọng để cung cấp ngữ cảnh về lý do tại sao code rơi vào trạng thái không thể hồi phục, giúp trace lỗi dễ dàng hơn trong log hoặc khi debug.
11
+
12
+ **Incorrect (không có message):**
13
+
14
+ ```swift
15
+ func test() {
16
+ fatalError()
17
+ }
18
+ ```
19
+
20
+ **Correct (có message mô tả):**
21
+
22
+ ```swift
23
+ func test() {
24
+ fatalError("Hàm này chưa được triển khai hoặc trạng thái không hợp lệ")
25
+ }
26
+ ```
27
+
28
+ **Tools:** SwiftLint (fatal_error_message)
@@ -0,0 +1,30 @@
1
+ ---
2
+ title: Ưu tiên for-where thay vì if trong loop
3
+ impact: LOW
4
+ impactDescription: Code gọn gàng hơn, giảm bớt một tầng lồng nhau (nesting) và thể hiện rõ ý định lọc phần tử.
5
+ tags: swift, ios, loops, for-where, readability
6
+ ---
7
+
8
+ ## Ưu tiên for-where thay vì if trong loop
9
+
10
+ Nếu chỉ có một điều kiện lọc duy nhất bên trong toàn bộ nội dung của vòng lặp, nên sử dụng cú pháp `for ... where` thay vì dùng câu lệnh `if` lồng bên trong. Điều này giúp mã nguồn phẳng hơn và làm rõ mục đích duyệt các phần tử thỏa mãn điều kiện.
11
+
12
+ **Incorrect (dùng if lồng):**
13
+
14
+ ```swift
15
+ for user in users {
16
+ if user.isActive {
17
+ print(user.name)
18
+ }
19
+ }
20
+ ```
21
+
22
+ **Correct (dùng for-where):**
23
+
24
+ ```swift
25
+ for user in users where user.isActive {
26
+ print(user.name)
27
+ }
28
+ ```
29
+
30
+ **Tools:** SwiftLint (for_where)
@@ -0,0 +1,26 @@
1
+ ---
2
+ title: Tránh as! (force cast)
3
+ impact: CRITICAL
4
+ impactDescription: Ngăn chặn crash ứng dụng do ép kiểu sai thực tế. Force-cast là nguyên nhân phổ biến gây lỗi runtime.
5
+ tags: swift, ios, casting, force-cast, safety
6
+ ---
7
+
8
+ ## Tránh as! (force cast)
9
+
10
+ Không sử dụng ép kiểu bắt buộc `as!`, thay vào đó nên dùng `as?` kết hợp với kiểm tra null (optional binding). Force-cast sẽ khiến ứng dụng bị crash ngay lập tức nếu đối tượng không thuộc kiểu dữ liệu mong đợi.
11
+
12
+ **Incorrect (force cast):**
13
+
14
+ ```swift
15
+ let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as! MyCustomCell
16
+ ```
17
+
18
+ **Correct (safe cast):**
19
+
20
+ ```swift
21
+ guard let cell = tableView.dequeueReusableCell(withIdentifier: "Cell") as? MyCustomCell else {
22
+ fatalError("Không thể cast cell sang MyCustomCell")
23
+ }
24
+ ```
25
+
26
+ **Tools:** SwiftLint (force_cast)
@@ -0,0 +1,30 @@
1
+ ---
2
+ title: Tránh try! (force try)
3
+ impact: CRITICAL
4
+ impactDescription: Ngăn chặn crash ứng dụng khi có lỗi phát sinh từ các phương thức ném exception.
5
+ tags: swift, ios, error-handling, force-try, safety
6
+ ---
7
+
8
+ ## Tránh try! (force try)
9
+
10
+ Tránh sử dụng `try!`, thay vào đó hãy dùng `try?` hoặc cấu trúc `do-catch`. `try!` bỏ qua việc xử lý lỗi và sẽ gây crash ứng dụng nếu có bất kỳ exception nào xảy ra trong quá trình thực thi.
11
+
12
+ **Incorrect (force try):**
13
+
14
+ ```swift
15
+ let content = try! String(contentsOfFile: filePath)
16
+ ```
17
+
18
+ **Correct (xử lý lỗi an toàn):**
19
+
20
+ ```swift
21
+ do {
22
+ let content = try String(contentsOfFile: filePath)
23
+ } catch {
24
+ print("Lỗi khi đọc file: \(error)")
25
+ }
26
+ // Hoặc
27
+ let content = try? String(contentsOfFile: filePath)
28
+ ```
29
+
30
+ **Tools:** SwiftLint (force_try)
@@ -0,0 +1,32 @@
1
+ ---
2
+ title: Tránh ! (force unwrap)
3
+ impact: CRITICAL
4
+ impactDescription: Loại bỏ nguy cơ crash ứng dụng do truy cập trực tiếp vào giá trị nil của Optional.
5
+ tags: swift, ios, optional, force-unwrap, safety
6
+ ---
7
+
8
+ ## Tránh ! (force unwrap)
9
+
10
+ Không sử dụng `value!` để lấy giá trị từ một Optional. Force unwrap là một "code smell" nguy hiểm vì nó giả định giá trị luôn tồn tại. Ưu tiên dùng optional binding (`if let`, `guard let`) hoặc nil-coalescing operator (`??`) để xử lý an toàn.
11
+
12
+ **Incorrect (force unwrap):**
13
+
14
+ ```swift
15
+ let url = URL(string: "https://invalid url")!
16
+ print(url.absoluteString)
17
+ ```
18
+
19
+ **Correct (optional binding):**
20
+
21
+ ```swift
22
+ if let url = URL(string: "https://example.com") {
23
+ print(url.absoluteString)
24
+ }
25
+
26
+ // Hoặc dùng guard
27
+ guard let url = URL(string: "https://example.com") else {
28
+ return
29
+ }
30
+ ```
31
+
32
+ **Tools:** SwiftLint (force_unwrapping)
@@ -0,0 +1,37 @@
1
+ ---
2
+ title: Giới hạn số lượng tham số của hàm dưới 6
3
+ impact: MEDIUM
4
+ impactDescription: Giúp hàm dễ đọc, dễ kiểm thử và giảm độ phức tạp khi gọi hàm.
5
+ tags: swift, ios, clean-code, functions, architecture
6
+ ---
7
+
8
+ ## Giới hạn số lượng tham số của hàm dưới 6
9
+
10
+ Tránh khai báo function có quá nhiều tham số (nên giữ dưới 6). Một hàm có quá nhiều tham số thường vi phạm nguyên tắc Single Responsibility và gây khó khăn cho việc bảo trì. Nếu cần truyền nhiều dữ liệu, hãy cân nhắc gộp chúng vào một `struct` hoặc `class` chuyên biệt.
11
+
12
+ **Incorrect (quá nhiều tham số):**
13
+
14
+ ```swift
15
+ func registerUser(name: String, age: Int, email: String, phone: String, city: String, country: String) {
16
+ // ...
17
+ }
18
+ ```
19
+
20
+ **Correct (gộp vào struct):**
21
+
22
+ ```swift
23
+ struct UserRegistrationInfo {
24
+ let name: String
25
+ let age: Int
26
+ let email: String
27
+ let phone: String
28
+ let city: String
29
+ let country: String
30
+ }
31
+
32
+ func registerUser(info: UserRegistrationInfo) {
33
+ // ...
34
+ }
35
+ ```
36
+
37
+ **Tools:** SwiftLint (function_parameter_count)
@@ -0,0 +1,41 @@
1
+ ---
2
+ title: Không sử dụng tuple có quá nhiều phần tử
3
+ impact: LOW
4
+ impactDescription: Giảm độ phức tạp, tăng khả năng đọc hiểu (readability) và bảo trì (maintainability).
5
+ tags: swift, ios, tuple, struct, code-quality
6
+ ---
7
+
8
+ ## Không sử dụng tuple có quá nhiều phần tử
9
+
10
+ Tuple chỉ nên được sử dụng cho các nhóm dữ liệu nhỏ, tạm thời (tối đa 2-3 phần tử). Nếu một tuple dài hơn, người đọc sẽ khó biết được ý nghĩa của từng phần tử qua index. Trong trường hợp này, hãy thay thế bằng một `struct` có tên thuộc tính rõ ràng.
11
+
12
+ **Incorrect (tuple lớn):**
13
+
14
+ ```swift
15
+ func getInfo() -> (String, Int, Double, String) {
16
+ return ("John", 30, 180.5, "New York")
17
+ }
18
+
19
+ let info = getInfo()
20
+ print(info.0) // Khó hiểu index 0 là gì
21
+ ```
22
+
23
+ **Correct (dùng struct):**
24
+
25
+ ```swift
26
+ struct PersonInfo {
27
+ let name: String
28
+ let age: Int
29
+ let height: Double
30
+ let city: String
31
+ }
32
+
33
+ func getInfo() -> PersonInfo {
34
+ return PersonInfo(name: "John", age: 30, height: 180.5, city: "New York")
35
+ }
36
+
37
+ let info = getInfo()
38
+ print(info.name) // Rõ ràng và dễ hiểu
39
+ ```
40
+
41
+ **Tools:** SwiftLint (large_tuple)
@@ -0,0 +1,28 @@
1
+ ---
2
+ title: Sử dụng khởi tạo Swift thay vì Objective-C
3
+ impact: LOW
4
+ impactDescription: Giữ cho mã nguồn Swift thuần túy (idiomatic), hiện đại và dễ bảo trì.
5
+ tags: swift, ios, legacy, objective-c, initialization
6
+ ---
7
+
8
+ ## Sử dụng khởi tạo Swift thay vì Objective-C
9
+
10
+ Không sử dụng các hàm khởi tạo kiểu cũ (legacy constructors) kế thừa từ Objective-C như `CGPointMake`, `CGRectMake`, `CGSizeMake`, v.v. Hãy sử dụng các hàm khởi tạo chính thức của Swift để tận dụng các tính năng của ngôn ngữ như tham số có tên (named parameters).
11
+
12
+ **Incorrect (Legacy constructor):**
13
+
14
+ ```swift
15
+ let point = CGPointMake(10, 20)
16
+ let rect = CGRectMake(0, 0, 100, 100)
17
+ let size = CGSizeMake(50, 50)
18
+ ```
19
+
20
+ **Correct (Swift constructor):**
21
+
22
+ ```swift
23
+ let point = CGPoint(x: 10, y: 20)
24
+ let rect = CGRect(x: 0, y: 0, width: 100, height: 100)
25
+ let size = CGSize(width: 50, height: 50)
26
+ ```
27
+
28
+ **Tools:** SwiftLint (legacy_constructor)
@@ -0,0 +1,38 @@
1
+ ---
2
+ title: Data type chỉ lồng nhau tối đa 1 level
3
+ impact: LOW
4
+ impactDescription: Tránh mã nguồn khó đọc và khó debug do có quá nhiều tầng lồng nhau.
5
+ tags: swift, ios, nesting, architecture, clean-code
6
+ ---
7
+
8
+ ## Data type chỉ lồng nhau tối đa 1 level
9
+
10
+ Không nên lồng quá một cấp trong các định nghĩa kiểu dữ liệu như `class`, `struct`, `enum`, `actor`. Việc lồng nhau quá sâu làm tăng độ phức tạp của tên kiểu dữ liệu (fully qualified name) và thường vi phạm nguyên tắc Phân tách mối quan tâm (Separation of Concerns).
11
+
12
+ **Incorrect (lồng quá sâu):**
13
+
14
+ ```swift
15
+ class Parent {
16
+ class Child {
17
+ class GrandChild {
18
+ // ...
19
+ }
20
+ }
21
+ }
22
+ ```
23
+
24
+ **Correct (phẳng hóa hoặc chỉ lồng 1 cấp):**
25
+
26
+ ```swift
27
+ class Parent {
28
+ class Child {
29
+ // ...
30
+ }
31
+ }
32
+
33
+ class GrandChild {
34
+ // ...
35
+ }
36
+ ```
37
+
38
+ **Tools:** SwiftLint (nesting)
@@ -0,0 +1,28 @@
1
+ ---
2
+ title: Không sử dụng access modifier với extension
3
+ impact: MEDIUM
4
+ impactDescription: Giữ cho extension rõ ràng và nhất quán theo chuẩn Swift style guide.
5
+ tags: swift, ios, extension, access-modifier, style-guide
6
+ ---
7
+
8
+ ## Không sử dụng access modifier với extension
9
+
10
+ Không nên khai báo `public`, `private`, `internal`, hoặc `fileprivate` trực tiếp cho phần `extension`. Quyền truy cập nên được định nghĩa cụ thể cho từng thành phần (biến, hàm) bên trong extension. Điều này giúp kiểm soát truy cập linh hoạt hơn và tránh các hành vi không mong muốn khi gom nhóm code.
11
+
12
+ **Incorrect (khai báo modifier cho extension):**
13
+
14
+ ```swift
15
+ public extension MyClass {
16
+ func doSomething() {}
17
+ }
18
+ ```
19
+
20
+ **Correct (khai báo modifier cho thành phần bên trong):**
21
+
22
+ ```swift
23
+ extension MyClass {
24
+ public func doSomething() {}
25
+ }
26
+ ```
27
+
28
+ **Tools:** SwiftLint (no_extension_access_modifier)
@@ -0,0 +1,30 @@
1
+ ---
2
+ title: Gọi super trong các phương thức lifecycle
3
+ impact: HIGH
4
+ impactDescription: Đảm bảo các hành vi mặc định của hệ thống được thực thi đúng cách, tránh lỗi UI hoặc logic không đồng bộ.
5
+ tags: swift, ios, uikit, lifecycle, super-call
6
+ ---
7
+
8
+ ## Gọi super trong các phương thức lifecycle
9
+
10
+ Khi override các phương thức lifecycle của hệ thống (như `viewDidLoad`, `viewWillAppear`, `viewDidAppear`, `viewWillDisappear`, v.v. trong UIKit), bạn bắt buộc phải gọi `super.methodName()`. Việc thiếu lời gọi tới lớp cha có thể khiến các khởi tạo mặc định hoặc logic quan trọng của hệ thống không được thực hiện, dẫn đến các lỗi khó xác định.
11
+
12
+ **Incorrect (không gọi super):**
13
+
14
+ ```swift
15
+ override func viewWillAppear(_ animated: Bool) {
16
+ // Thiếu super.viewWillAppear(animated)
17
+ setupTheme()
18
+ }
19
+ ```
20
+
21
+ **Correct (gọi super đầy đủ):**
22
+
23
+ ```swift
24
+ override func viewWillAppear(_ animated: Bool) {
25
+ super.viewWillAppear(animated)
26
+ setupTheme()
27
+ }
28
+ ```
29
+
30
+ **Tools:** SwiftLint (overridden_super_call)
@@ -0,0 +1,32 @@
1
+ ---
2
+ title: Không sử dụng override trong extension
3
+ impact: CRITICAL
4
+ impactDescription: Tránh thay đổi hành vi định nghĩa ban đầu của lớp cha qua extension, giữ code ổn định và dễ dự đoán.
5
+ tags: swift, ios, extension, override, architecture
6
+ ---
7
+
8
+ ## Không sử dụng override trong extension
9
+
10
+ Không được `override` thuộc tính hoặc phương thức của lớp cha bên trong một `extension`. Extension được thiết kế để mở rộng tính năng chứ không phải để thay đổi hành vi thừa kế. Nếu cần override, hãy thực hiện việc này trong định nghĩa của chính class đó hoặc class con.
11
+
12
+ **Incorrect (override trong extension):**
13
+
14
+ ```swift
15
+ extension MyViewController {
16
+ override func viewDidLoad() { // Nguy hiểm: Không nên override ở đây
17
+ super.viewDidLoad()
18
+ }
19
+ }
20
+ ```
21
+
22
+ **Correct (override trong class chính):**
23
+
24
+ ```swift
25
+ class MyViewController: UIViewController {
26
+ override func viewDidLoad() {
27
+ super.viewDidLoad()
28
+ }
29
+ }
30
+ ```
31
+
32
+ **Tools:** SwiftLint (override_in_extension)
@@ -0,0 +1,28 @@
1
+ ---
2
+ title: Ưu tiên private thay vì fileprivate
3
+ impact: MEDIUM
4
+ impactDescription: Giới hạn phạm vi truy cập chặt chẽ nhất có thể, giúp tăng tính đóng gói (encapsulation).
5
+ tags: swift, ios, access-control, private, fileprivate
6
+ ---
7
+
8
+ ## Ưu tiên private thay vì fileprivate
9
+
10
+ Sử dụng `private` để giới hạn phạm vi truy cập trong cùng một khối khai báo `class`/`struct`. Chỉ sử dụng `fileprivate` nếu bạn thực sự cần chia sẻ dữ liệu giữa các class/struct khác nhau cùng nằm trong một file vật lý Duy nhất. Việc lạm dụng `fileprivate` làm loãng tính đóng gói của code.
11
+
12
+ **Incorrect (dùng fileprivate khi không cần thiết):**
13
+
14
+ ```swift
15
+ class User {
16
+ fileprivate var name: String = ""
17
+ }
18
+ ```
19
+
20
+ **Correct (dùng private):**
21
+
22
+ ```swift
23
+ class User {
24
+ private var name: String = ""
25
+ }
26
+ ```
27
+
28
+ **Tools:** SwiftLint (private_over_fileprivate)
@@ -0,0 +1,32 @@
1
+ ---
2
+ title: Không khai báo private cho Unit Test function
3
+ impact: CRITICAL
4
+ impactDescription: Đảm bảo các hàm test có thể được nhận diện và thực thi bởi trình chạy test (test runner).
5
+ tags: swift, ios, unit-test, xctest, access-control
6
+ ---
7
+
8
+ ## Không khai báo private cho Unit Test function
9
+
10
+ Không sử dụng `private` cho các class hoặc function bên trong `XCTestCase`. Các hàm test cần có quyền truy cập `internal` (mặc định) hoặc `public` để Swift test runner có thể gọi và thực thi chúng. Nếu đánh dấu là `private`, các bài test này sẽ bị bỏ qua một cách lặng lẽ.
11
+
12
+ **Incorrect (test function bị private):**
13
+
14
+ ```swift
15
+ class MyTests: XCTestCase {
16
+ private func testLoginSuccess() { // Lỗi: Test runner không tìm thấy
17
+ // ...
18
+ }
19
+ }
20
+ ```
21
+
22
+ **Correct (hàm test công khai):**
23
+
24
+ ```swift
25
+ class MyTests: XCTestCase {
26
+ func testLoginSuccess() { // Đúng: Mặc định là internal
27
+ // ...
28
+ }
29
+ }
30
+ ```
31
+
32
+ **Tools:** SwiftLint (private_unit_test)
@@ -0,0 +1,29 @@
1
+ ---
2
+ title: Không gọi super trong các specific method
3
+ impact: MEDIUM
4
+ impactDescription: Tránh các hành vi không mong muốn hoặc crash khi gọi super trong các hàm mà Apple khuyến nghị không nên gọi.
5
+ tags: swift, ios, uikit, super-call, prohibited-call
6
+ ---
7
+
8
+ ## Không gọi super trong các specific method
9
+
10
+ Không nên gọi `super` nếu phương thức không định nghĩa gì trong lớp cha hoặc bị Apple khuyến nghị không gọi trong các phương thức cụ thể (ví dụ: `loadView()`). Việc gọi `super.loadView()` có thể gây ra các vòng lặp khởi tạo vô hạn hoặc chiếm dụng tài nguyên không cần thiết.
11
+
12
+ **Incorrect (gọi super trong loadView):**
13
+
14
+ ```swift
15
+ override func loadView() {
16
+ super.loadView() // Không cần thiết và không được khuyến khích
17
+ view = MyCustomView()
18
+ }
19
+ ```
20
+
21
+ **Correct (không gọi super):**
22
+
23
+ ```swift
24
+ override func loadView() {
25
+ view = MyCustomView()
26
+ }
27
+ ```
28
+
29
+ **Tools:** SwiftLint (prohibited_super_call)
@@ -0,0 +1,28 @@
1
+ ---
2
+ title: Ưu tiên dùng .min() hoặc .max() thay vì sorted().first/last
3
+ impact: CRITICAL
4
+ impactDescription: Tăng hiệu năng đáng kể (O(n) so với O(n log n)) khi tìm phần tử lớn nhất/nhỏ nhất trong collection.
5
+ tags: swift, ios, performance, algorithms, collection
6
+ ---
7
+
8
+ ## Ưu tiên dùng .min() hoặc .max() thay vì sorted().first/last
9
+
10
+ Sử dụng `.min()` hoặc `.max()` thay vì `.sorted().first` hoặc `.sorted().last`. Việc gọi `sorted()` sẽ phải sắp xếp toàn bộ tập hợp (độ phức tạp O(n log n)), trong khi `.min()`/`.max()` chỉ duyệt qua tập hợp đúng 1 lần (độ phức tạp O(n)). Điều này giúp tăng hiệu năng rõ rệt trên các tập dữ liệu lớn.
11
+
12
+ **Incorrect (sort rồi lấy phần tử đầu/cuối):**
13
+
14
+ ```swift
15
+ let numbers = [5, 3, 8, 1, 9]
16
+ let minNumber = numbers.sorted().first
17
+ let maxNumber = numbers.sorted().last
18
+ ```
19
+
20
+ **Correct (dùng hàm chuyên dụng):**
21
+
22
+ ```swift
23
+ let numbers = [5, 3, 8, 1, 9]
24
+ let minNumber = numbers.min()
25
+ let maxNumber = numbers.max()
26
+ ```
27
+
28
+ **Tools:** SwiftLint (sorted_first_last)