@sun-asterisk/sunlint 1.3.41 → 1.3.43

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 (120) hide show
  1. package/package.json +1 -1
  2. package/rules/security/S024_xpath_xxe_protection/typescript/regex-based-analyzer.js +4 -4
  3. package/rules/security/S024_xpath_xxe_protection/typescript/symbol-based-analyzer.js +1 -1
  4. package/rules/security/S025_server_side_validation/typescript/regex-based-analyzer.js +5 -5
  5. package/rules/security/S025_server_side_validation/typescript/symbol-based-analyzer.js +6 -6
  6. package/rules/security/S032_httponly_session_cookies/typescript/regex-based-analyzer.js +8 -8
  7. package/rules/security/S033_samesite_session_cookies/typescript/regex-based-analyzer.js +12 -12
  8. package/rules/security/S033_samesite_session_cookies/typescript/symbol-based-analyzer.js +1 -1
  9. package/rules/security/S034_host_prefix_session_cookies/typescript/regex-based-analyzer.js +1 -1
  10. package/rules/security/S041_session_token_invalidation/typescript/regex-based-analyzer.js +4 -4
  11. package/rules/security/S041_session_token_invalidation/typescript/symbol-based-analyzer.js +1 -1
  12. package/rules/security/S044_re_authentication_required/typescript/regex-based-analyzer.js +1 -1
  13. package/rules/security/S044_re_authentication_required/typescript/symbol-based-analyzer.js +1 -1
  14. package/rules/security/S045_brute_force_protection/typescript/analyzer.js +1 -1
  15. package/rules/security/S045_brute_force_protection/typescript/symbol-based-analyzer.js +1 -1
  16. package/skill-assets/sunlint-code-quality/rules/dart/D001-recommended-lints.md +36 -0
  17. package/skill-assets/sunlint-code-quality/rules/dart/D002-dispose-resources.md +44 -0
  18. package/skill-assets/sunlint-code-quality/rules/dart/D003-prefer-widget-classes.md +53 -0
  19. package/skill-assets/sunlint-code-quality/rules/dart/D004-avoid-shrinkwrap.md +31 -0
  20. package/skill-assets/sunlint-code-quality/rules/dart/D005-widget-nesting.md +62 -0
  21. package/skill-assets/sunlint-code-quality/rules/dart/D006-large-callbacks.md +54 -0
  22. package/skill-assets/sunlint-code-quality/rules/dart/D007-lifecycle-order.md +44 -0
  23. package/skill-assets/sunlint-code-quality/rules/dart/D008-long-functions.md +37 -0
  24. package/skill-assets/sunlint-code-quality/rules/dart/D009-function-parameters.md +38 -0
  25. package/skill-assets/sunlint-code-quality/rules/dart/D010-cyclomatic-complexity.md +46 -0
  26. package/skill-assets/sunlint-code-quality/rules/dart/D011-named-parameters.md +31 -0
  27. package/skill-assets/sunlint-code-quality/rules/dart/D012-named-booleans.md +28 -0
  28. package/skill-assets/sunlint-code-quality/rules/dart/D013-single-public-class.md +34 -0
  29. package/skill-assets/sunlint-code-quality/rules/dart/D014-safe-collection-access.md +30 -0
  30. package/skill-assets/sunlint-code-quality/rules/dart/D015-copywith-consistency.md +52 -0
  31. package/skill-assets/sunlint-code-quality/rules/dart/D016-project-tests.md +32 -0
  32. package/skill-assets/sunlint-code-quality/rules/dart/D017-review-dependencies.md +24 -0
  33. package/skill-assets/sunlint-code-quality/rules/dart/D018-no-commented-code.md +34 -0
  34. package/skill-assets/sunlint-code-quality/rules/dart/D019-single-child-wrappers.md +31 -0
  35. package/skill-assets/sunlint-code-quality/rules/dart/D020-if-else-limit.md +41 -0
  36. package/skill-assets/sunlint-code-quality/rules/dart/D021-negated-booleans.md +26 -0
  37. package/skill-assets/sunlint-code-quality/rules/dart/D022-setstate-usage.md +35 -0
  38. package/skill-assets/sunlint-code-quality/rules/dart/D023-unnecessary-overrides.md +32 -0
  39. package/skill-assets/sunlint-code-quality/rules/dart/D024-avoid-unnecessary-statefulwidget.md +45 -0
  40. package/skill-assets/sunlint-code-quality/rules/dart/D025-nested-ternaries.md +35 -0
  41. package/skill-assets/sunlint-code-quality/rules/ruby/RB001-use-snake-case.md +30 -0
  42. package/skill-assets/sunlint-code-quality/rules/ruby/RB002-use-camel-case.md +38 -0
  43. package/skill-assets/sunlint-code-quality/rules/ruby/RB003-use-screaming-snake-case.md +26 -0
  44. package/skill-assets/sunlint-code-quality/rules/ruby/RB004-predicate-methods.md +36 -0
  45. package/skill-assets/sunlint-code-quality/rules/ruby/RB005-dangerous-methods.md +36 -0
  46. package/skill-assets/sunlint-code-quality/rules/ruby/RB006-indentation.md +32 -0
  47. package/skill-assets/sunlint-code-quality/rules/ruby/RB007-line-length.md +25 -0
  48. package/skill-assets/sunlint-code-quality/rules/ruby/RB008-rescue-exception.md +36 -0
  49. package/skill-assets/sunlint-code-quality/rules/ruby/RB009-save-bang.md +41 -0
  50. package/skill-assets/sunlint-code-quality/rules/ruby/RB010-avoid-n-plus-one.md +32 -0
  51. package/skill-assets/sunlint-code-quality/rules/ruby/RB011-use-find-each.md +30 -0
  52. package/skill-assets/sunlint-code-quality/rules/ruby/RB012-sql-injection.md +29 -0
  53. package/skill-assets/sunlint-code-quality/rules/ruby/RB013-prefer-has-many-through.md +35 -0
  54. package/skill-assets/sunlint-code-quality/rules/ruby/RB014-dependent-associations.md +28 -0
  55. package/skill-assets/sunlint-code-quality/rules/ruby/RB015-modern-validations.md +29 -0
  56. package/skill-assets/sunlint-code-quality/rules/ruby/RB016-thin-controllers.md +45 -0
  57. package/skill-assets/sunlint-code-quality/rules/ruby/RB017-avoid-fat-models.md +41 -0
  58. package/skill-assets/sunlint-code-quality/rules/ruby/RB018-service-objects.md +36 -0
  59. package/skill-assets/sunlint-code-quality/rules/ruby/RB019-avoid-metaprogramming.md +40 -0
  60. package/skill-assets/sunlint-code-quality/rules/ruby/RB020-use-pluck.md +29 -0
  61. package/skill-assets/sunlint-code-quality/rules/ruby/RB021-use-size.md +27 -0
  62. package/skill-assets/sunlint-code-quality/rules/ruby/RB022-order-by-timestamps.md +24 -0
  63. package/skill-assets/sunlint-code-quality/rules/ruby/RB023-where-missing.md +24 -0
  64. package/skill-assets/sunlint-code-quality/rules/ruby/RB024-method-length.md +41 -0
  65. package/skill-assets/sunlint-code-quality/rules/ruby/RB025-parameter-limits.md +28 -0
  66. package/skill-assets/sunlint-code-quality/rules/ruby/RB026-avoid-deep-nesting.md +38 -0
  67. package/skill-assets/sunlint-code-quality/rules/ruby/RB027-guard-clauses.md +37 -0
  68. package/skill-assets/sunlint-code-quality/rules/ruby/RB028-class-length.md +32 -0
  69. package/skill-assets/sunlint-code-quality/rules/ruby/RB029-meaningful-names.md +30 -0
  70. package/skill-assets/sunlint-code-quality/rules/ruby/RB030-dry-principle.md +37 -0
  71. package/skill-assets/sunlint-code-quality/rules/ruby/RB031-mvc-architecture.md +37 -0
  72. package/skill-assets/sunlint-code-quality/rules/ruby/RB032-use-concerns.md +31 -0
  73. package/skill-assets/sunlint-code-quality/rules/ruby/RB033-moderate-callbacks.md +31 -0
  74. package/skill-assets/sunlint-code-quality/rules/ruby/RB034-use-decorators.md +33 -0
  75. package/skill-assets/sunlint-code-quality/rules/ruby/RB035-comprehensive-tests.md +32 -0
  76. package/skill-assets/sunlint-code-quality/rules/ruby/RB036-frozen-string-literal.md +29 -0
  77. package/skill-assets/sunlint-code-quality/rules/ruby/RB037-it-parameter.md +25 -0
  78. package/skill-assets/sunlint-code-quality/rules/ruby/RB038-modern-enum-syntax.md +28 -0
  79. package/skill-assets/sunlint-code-quality/rules/ruby/RB039-solid-adapters.md +29 -0
  80. package/skill-assets/sunlint-code-quality/rules/ruby/RB040-rails-authentication.md +26 -0
  81. package/skill-assets/sunlint-code-quality/rules/ruby/RB041-async-query-loading.md +29 -0
  82. package/skill-assets/sunlint-code-quality/rules/ruby/RB042-hotwire-turbo.md +30 -0
  83. package/skill-assets/sunlint-code-quality/rules/ruby/RB043-use-propshaft.md +27 -0
  84. package/skill-assets/sunlint-code-quality/rules/ruby/RB044-structured-logging.md +35 -0
  85. package/skill-assets/sunlint-code-quality/rules/ruby/RB045-prism-parser.md +29 -0
  86. package/skill-assets/sunlint-code-quality/rules/swift/SW001-block-based-kvo.md +40 -0
  87. package/skill-assets/sunlint-code-quality/rules/swift/SW002-class-delegate-protocol.md +36 -0
  88. package/skill-assets/sunlint-code-quality/rules/swift/SW003-compiler-protocol-init.md +28 -0
  89. package/skill-assets/sunlint-code-quality/rules/swift/SW004-contains-over-filter-count.md +28 -0
  90. package/skill-assets/sunlint-code-quality/rules/swift/SW005-convenience-type.md +34 -0
  91. package/skill-assets/sunlint-code-quality/rules/swift/SW006-discarded-notification-center-observer.md +41 -0
  92. package/skill-assets/sunlint-code-quality/rules/swift/SW007-discouraged-direct-init.md +28 -0
  93. package/skill-assets/sunlint-code-quality/rules/swift/SW008-discouraged-optional-boolean.md +32 -0
  94. package/skill-assets/sunlint-code-quality/rules/swift/SW009-empty-count.md +30 -0
  95. package/skill-assets/sunlint-code-quality/rules/swift/SW010-empty-string.md +30 -0
  96. package/skill-assets/sunlint-code-quality/rules/swift/SW011-explicit-init.md +26 -0
  97. package/skill-assets/sunlint-code-quality/rules/swift/SW012-fatal-error-message.md +28 -0
  98. package/skill-assets/sunlint-code-quality/rules/swift/SW013-for-where.md +30 -0
  99. package/skill-assets/sunlint-code-quality/rules/swift/SW014-force-cast.md +26 -0
  100. package/skill-assets/sunlint-code-quality/rules/swift/SW015-force-try.md +30 -0
  101. package/skill-assets/sunlint-code-quality/rules/swift/SW016-force-unwrapping.md +32 -0
  102. package/skill-assets/sunlint-code-quality/rules/swift/SW017-function-parameter-count.md +37 -0
  103. package/skill-assets/sunlint-code-quality/rules/swift/SW018-large-tuple.md +41 -0
  104. package/skill-assets/sunlint-code-quality/rules/swift/SW019-legacy-constructor.md +28 -0
  105. package/skill-assets/sunlint-code-quality/rules/swift/SW020-nesting.md +38 -0
  106. package/skill-assets/sunlint-code-quality/rules/swift/SW021-no-extension-access-modifier.md +28 -0
  107. package/skill-assets/sunlint-code-quality/rules/swift/SW022-overridden-super-call.md +30 -0
  108. package/skill-assets/sunlint-code-quality/rules/swift/SW023-override-in-extension.md +32 -0
  109. package/skill-assets/sunlint-code-quality/rules/swift/SW024-private-over-fileprivate.md +28 -0
  110. package/skill-assets/sunlint-code-quality/rules/swift/SW025-private-unit-test.md +32 -0
  111. package/skill-assets/sunlint-code-quality/rules/swift/SW026-prohibited-super-call.md +29 -0
  112. package/skill-assets/sunlint-code-quality/rules/swift/SW027-sorted-first-last.md +28 -0
  113. package/skill-assets/sunlint-code-quality/rules/swift/SW028-syntactic-sugar.md +28 -0
  114. package/skill-assets/sunlint-code-quality/rules/swift/SW029-unused-closure-parameter.md +28 -0
  115. package/skill-assets/sunlint-code-quality/rules/swift/SW030-unused-enumerated.md +28 -0
  116. package/skill-assets/sunlint-code-quality/rules/swift/SW031-unused-optional-binding.md +26 -0
  117. package/skill-assets/sunlint-code-quality/rules/swift/SW032-valid-ibinspectable.md +26 -0
  118. package/skill-assets/sunlint-code-quality/rules/swift/SW033-vertical-parameter-alignment.md +36 -0
  119. package/skill-assets/sunlint-code-quality/rules/swift/SW034-void-return.md +28 -0
  120. package/skill-assets/sunlint-code-quality/rules/swift/SW035-weak-delegate.md +28 -0
@@ -0,0 +1,29 @@
1
+ ---
2
+ title: Prefer Solid Adapters for Infrastructure (Rails 8+)
3
+ impact: MEDIUM
4
+ impactDescription: Simplify deployment and reduce external dependencies.
5
+ tags: rails, rails8, architecture, infrastructure
6
+ ---
7
+
8
+ ## Prefer Solid Adapters for Infrastructure (Rails 8+)
9
+
10
+ Simplify deployment and reduce external dependencies. For Rails 8 projects, prefer `Solid Queue`, `Solid Cache`, and `Solid Cable` over Redis-based solutions for standard workloads to keep the infrastructure "lean".
11
+
12
+ **Incorrect:**
13
+
14
+ ```ruby
15
+ # config/environments/production.rb
16
+ config.cache_store = :redis_cache_store
17
+ config.active_job.queue_adapter = :sidekiq
18
+ ```
19
+
20
+ **Correct:**
21
+
22
+ ```ruby
23
+ # Rails 8 lean stack
24
+ config.cache_store = :solid_cache_store
25
+ config.active_job.queue_adapter = :solid_queue
26
+ ```
27
+
28
+ **Tools:** Manual Review
29
+ ---
@@ -0,0 +1,26 @@
1
+ ---
2
+ title: Use built-in Rails 8 Authentication for greenfield projects
3
+ impact: MEDIUM
4
+ impactDescription: Use the native, lightweight authentication system to reduce dependency on Devise.
5
+ tags: rails, rails8, authentication, security
6
+ ---
7
+
8
+ ## Use built-in Rails 8 Authentication for greenfield projects
9
+
10
+ Use the native, lightweight authentication system to reduce dependency on Devise. Use `bin/rails generate authentication` for new projects unless complex features mandate a library.
11
+
12
+ **Incorrect:**
13
+
14
+ ```ruby
15
+ # Adding Devise by default without evaluating built-in needs
16
+ bundle add devise
17
+ ```
18
+
19
+ **Correct:**
20
+
21
+ ```bash
22
+ # Using Rails 8 built-in generator
23
+ bin/rails generate authentication
24
+ ```
25
+
26
+ **Tools:** Manual Review
@@ -0,0 +1,29 @@
1
+ ---
2
+ title: Use Async Query Loading for slow interactions
3
+ impact: MEDIUM
4
+ impactDescription: Improve web performance by loading database data concurrently with view rendering.
5
+ tags: rails, performance, active-record, async
6
+ ---
7
+
8
+ ## Use Async Query Loading for slow interactions
9
+
10
+ Improve web performance by loading database data concurrently with view rendering. Use `.load_async` on ActiveRecord relations when multiple independent queries can be executed in the background while the controller or view processes.
11
+
12
+ **Incorrect:**
13
+
14
+ ```ruby
15
+ # Sequential execution (Sync)
16
+ @posts = Post.all.load
17
+ @users = User.all.load
18
+ ```
19
+
20
+ **Correct:**
21
+
22
+ ```ruby
23
+ # Parallel execution (Async)
24
+ @posts = Post.all.load_async
25
+ @users = User.all.load_async
26
+ ```
27
+
28
+ **Tools:** Manual Review
29
+ ---
@@ -0,0 +1,30 @@
1
+ ---
2
+ title: Minimize custom JavaScript with Hotwire/Turbo 2.0
3
+ impact: MEDIUM
4
+ impactDescription: Focus on the Rails "Majestic Monolith" approach for better maintainability.
5
+ tags: rails, hotwire, turbo, stimulus, architecture
6
+ ---
7
+
8
+ ## Minimize custom JavaScript with Hotwire/Turbo 2.0
9
+
10
+ Focus on the Rails "Majestic Monolith" approach for better maintainability. Prefer `Turbo Frames` and `Turbo Streams` for interaction. Use `Stimulus` for small client-side behaviors.
11
+
12
+ **Incorrect:**
13
+
14
+ ```javascript
15
+ // Complex React/Vue component for a simple list update
16
+ ```
17
+
18
+ **Correct:**
19
+
20
+ ```erb
21
+ <%# Rails Turbo Stream response %>
22
+ <turbo-stream action="append" target="messages">
23
+ <template>
24
+ <%= render @message %>
25
+ </template>
26
+ </turbo-stream>
27
+ ```
28
+
29
+ **Tools:** Manual Review
30
+ ---
@@ -0,0 +1,27 @@
1
+ ---
2
+ title: Use Propshaft as the default asset pipeline
3
+ impact: LOW
4
+ impactDescription: Use the modern, simplified asset pipeline instead of Sprockets.
5
+ tags: rails, assets, propshaft, performance
6
+ ---
7
+
8
+ ## Use Propshaft as the default asset pipeline
9
+
10
+ Use the modern, simplified asset pipeline instead of Sprockets. For new Rails 7+ projects, use `Propshaft` to handle asset digests and paths without the complexity of transpilation.
11
+
12
+ **Incorrect:**
13
+
14
+ ```ruby
15
+ # Gemfile
16
+ gem 'sprockets-rails'
17
+ ```
18
+
19
+ **Correct:**
20
+
21
+ ```ruby
22
+ # Gemfile
23
+ gem 'propshaft'
24
+ ```
25
+
26
+ **Tools:** Manual Review
27
+ ---
@@ -0,0 +1,35 @@
1
+ ---
2
+ title: Use Structured Logging for Observability
3
+ impact: MEDIUM
4
+ impactDescription: Improve log parsing and searching in production environments.
5
+ tags: ruby, rails, logs, observability, json
6
+ ---
7
+
8
+ ## Use Structured Logging for Observability
9
+
10
+ Improve log parsing and searching in production environments. Configure structured (JSON) logging in production to facilitate tracing and automated analysis.
11
+
12
+ **Incorrect:**
13
+
14
+ ```text
15
+ Processing by UsersController#show as HTML
16
+ Parameters: {"id"=>"1"}
17
+ Completed 200 OK in 10ms
18
+ ```
19
+
20
+ **Correct:**
21
+
22
+ ```json
23
+ {
24
+ "timestamp": "2025-02-05T12:00:00Z",
25
+ "level": "INFO",
26
+ "controller": "UsersController",
27
+ "action": "show",
28
+ "params": {"id": "1"},
29
+ "status": 200,
30
+ "duration_ms": 10
31
+ }
32
+ ```
33
+
34
+ **Tools:** Lograge gem
35
+ ---
@@ -0,0 +1,29 @@
1
+ ---
2
+ title: Use Prism as the default parser for tooling
3
+ impact: LOW
4
+ impactDescription: Leverage the faster, more accurate Ruby parser.
5
+ tags: ruby, tooling, prism, performance
6
+ ---
7
+
8
+ ## Use Prism as the default parser for tooling
9
+
10
+ Leverage the faster, more accurate Ruby parser. Configure tools like `RuboCop` or `SyntaxTree` to use `Prism` for parsing where supported in Ruby 3.4+.
11
+
12
+ **Incorrect:**
13
+
14
+ ```yaml
15
+ # .rubocop.yml
16
+ AllCops:
17
+ ParserEngine: parser # Default
18
+ ```
19
+
20
+ **Correct:**
21
+
22
+ ```yaml
23
+ # .rubocop.yml
24
+ AllCops:
25
+ ParserEngine: prism
26
+ ```
27
+
28
+ **Tools:** RuboCop (`ParserEngine: prism`)
29
+ ---
@@ -0,0 +1,40 @@
1
+ ---
2
+ title: Sử dụng observe property của Swift thay vì KVO cũ
3
+ impact: MEDIUM
4
+ impactDescription: Tránh sử dụng cơ chế KVO lỗi thời, giảm thiểu độ phức tạp và lỗi khi override observeValue.
5
+ tags: swift, ios, kvo, observation, code-quality
6
+ ---
7
+
8
+ ## Sử dụng observe property của Swift thay vì KVO cũ
9
+
10
+ Ưu tiên dùng block-based KVO API với `keypaths` khi dùng Swift 3.2 trở lên. Loại bỏ việc override `observeValue` truyền thống vốn phức tạp và dễ sai.
11
+
12
+ **Incorrect (KVO cũ):**
13
+
14
+ ```swift
15
+ class MyObserver: NSObject {
16
+ @objc var objectToObserve: MyObject
17
+
18
+ override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
19
+ if keyPath == "someProperty" {
20
+ // Xử lý thay đổi
21
+ }
22
+ }
23
+ }
24
+ ```
25
+
26
+ **Correct (Block-based KVO):**
27
+
28
+ ```swift
29
+ class MyObserver {
30
+ var observation: NSKeyValueObservation?
31
+
32
+ init(object: MyObject) {
33
+ observation = object.observe(\.someProperty, options: [.new]) { object, change in
34
+ // Xử lý thay đổi qua block
35
+ }
36
+ }
37
+ }
38
+ ```
39
+
40
+ **Tools:** SwiftLint (block_based_kvo)
@@ -0,0 +1,36 @@
1
+ ---
2
+ title: Delegate Protocol phải là class-only
3
+ impact: HIGH
4
+ impactDescription: Cho phép dùng weak để tránh retain cycles (memory leak) giữa các đối tượng.
5
+ tags: swift, ios, delegate, memory-leak, AnyObject
6
+ ---
7
+
8
+ ## Delegate Protocol phải là class-only
9
+
10
+ `weak` chỉ hỗ trợ class types, do đó các delegate protocol phải được khai báo là `class`-based (`AnyObject`). Điều này giúp ngăn chặn retain cycles khi delegate trỏ ngược lại đối tượng sở hữu nó.
11
+
12
+ **Incorrect (không giới hạn class):**
13
+
14
+ ```swift
15
+ protocol MyDelegate {
16
+ func didUpdateData()
17
+ }
18
+
19
+ class MyView {
20
+ var delegate: MyDelegate? // Lỗi: Không thể dùng weak ở đây nếu protocol không là AnyObject
21
+ }
22
+ ```
23
+
24
+ **Correct (class-only protocol):**
25
+
26
+ ```swift
27
+ protocol MyDelegate: AnyObject {
28
+ func didUpdateData()
29
+ }
30
+
31
+ class MyView {
32
+ weak var delegate: MyDelegate? // Đúng: Có thể dùng weak để tránh memory leak
33
+ }
34
+ ```
35
+
36
+ **Tools:** SwiftLint (class_delegate_protocol)
@@ -0,0 +1,28 @@
1
+ ---
2
+ title: Không khởi tạo trực tiếp các protocol hệ thống
3
+ impact: MEDIUM
4
+ impactDescription: Tránh misuse các compiler protocol như ExpressibleByArrayLiteral, khuyến khích sử dụng literal syntax.
5
+ tags: swift, ios, compiler, literals, code-quality
6
+ ---
7
+
8
+ ## Không khởi tạo trực tiếp các protocol hệ thống
9
+
10
+ Các protocol của compiler (như `ExpressibleByArrayLiteral`) không nên được gọi `init` trực tiếp. Hãy sử dụng cú pháp gọn gàng (literals) để khởi tạo giá trị.
11
+
12
+ **Incorrect (Gọi init trực tiếp):**
13
+
14
+ ```swift
15
+ let array = Array(arrayLiteral: 1, 2, 3)
16
+ let dict = Dictionary(dictionaryLiteral: ("key", "value"))
17
+ let string = String(stringLiteral: "Hello")
18
+ ```
19
+
20
+ **Correct (Sử dụng literal syntax):**
21
+
22
+ ```swift
23
+ let array = [1, 2, 3]
24
+ let dict = ["key": "value"]
25
+ let string = "Hello"
26
+ ```
27
+
28
+ **Tools:** SwiftLint (compiler_protocol_init)
@@ -0,0 +1,28 @@
1
+ ---
2
+ title: Ưu tiên sử dụng .contains trong một số phép lọc
3
+ impact: LOW
4
+ impactDescription: Cải thiện hiệu năng và độ rõ ràng của code bằng cách dùng phương thức chuyên dụng thay vì lọc rồi đếm.
5
+ tags: swift, ios, performance, collections, readability
6
+ ---
7
+
8
+ ## Ưu tiên sử dụng .contains trong một số phép lọc
9
+
10
+ Thay vì `.filter { ... }.count > 0` hoặc `.first != nil`, hãy dùng `.contains`. `.contains` thường ngắn hơn, dễ hiểu hơn và hiệu quả hơn vì nó dừng ngay khi tìm thấy phần tử khớp.
11
+
12
+ **Incorrect (Lọc rồi đếm):**
13
+
14
+ ```swift
15
+ let numbers = [1, 2, 3, 4, 5]
16
+ let hasEven = numbers.filter { $0 % 2 == 0 }.count > 0
17
+ let hasFive = numbers.first { $0 == 5 } != nil
18
+ ```
19
+
20
+ **Correct (Sử dụng .contains):**
21
+
22
+ ```swift
23
+ let numbers = [1, 2, 3, 4, 5]
24
+ let hasEven = numbers.contains { $0 % 2 == 0 }
25
+ let hasFive = numbers.contains(5)
26
+ ```
27
+
28
+ **Tools:** SwiftLint (contains_over_*)
@@ -0,0 +1,34 @@
1
+ ---
2
+ title: Dùng enum cho type chỉ gồm static members
3
+ impact: LOW
4
+ impactDescription: Ngăn chặn việc khởi tạo các type không cần thiết khi chúng chỉ đóng vai trò là container cho hằng số hoặc tiện ích.
5
+ tags: swift, ios, architecture, enum, static-members
6
+ ---
7
+
8
+ ## Dùng enum cho type chỉ gồm static members
9
+
10
+ Nếu một type chỉ chứa static members (như hằng số, helper methods), nên dùng `enum` không có case để không cho phép khởi tạo. `enum` không thể được khởi tạo trực tiếp, giúp tránh việc lạm dụng hoặc tạo ra các instance vô nghĩa.
11
+
12
+ **Incorrect (Sử dụng struct/class):**
13
+
14
+ ```swift
15
+ struct Config {
16
+ static let apiKey = "SECRET"
17
+ static let baseUrl = "https://api.example.com"
18
+ }
19
+
20
+ let config = Config() // Vẫn có thể tạo instance
21
+ ```
22
+
23
+ **Correct (Sử dụng enum):**
24
+
25
+ ```swift
26
+ enum Config {
27
+ static let apiKey = "SECRET"
28
+ static let baseUrl = "https://api.example.com"
29
+ }
30
+
31
+ // let config = Config() // Lỗi: Enum không có case không thể khởi tạo
32
+ ```
33
+
34
+ **Tools:** SwiftLint (convenience_type)
@@ -0,0 +1,41 @@
1
+ ---
2
+ title: Luôn dispose observer của NotificationCenter
3
+ impact: HIGH
4
+ impactDescription: Tránh memory leak và các hành vi không mong muốn khi observer vẫn tồn tại dù đối tượng đã bị giải phóng.
5
+ tags: swift, ios, notification-center, memory-leak, observer
6
+ ---
7
+
8
+ ## Luôn dispose observer của NotificationCenter
9
+
10
+ Luôn lưu lại token khi `addObserver` với NotificationCenter bằng block-based API. Gọi `removeObserver()` khi observer không còn cần thiết hoặc trong `deinit` để giải phóng bộ nhớ.
11
+
12
+ **Incorrect (không lưu token hoặc không remove):**
13
+
14
+ ```swift
15
+ NotificationCenter.default.addObserver(forName: .myNotification, object: nil, queue: .main) { _ in
16
+ print("Notification received")
17
+ }
18
+ // Không có cách nào để remove observer này sau đó
19
+ ```
20
+
21
+ **Correct (lưu token và remove trong deinit):**
22
+
23
+ ```swift
24
+ class MyManager {
25
+ private var observer: NSObjectProtocol?
26
+
27
+ init() {
28
+ observer = NotificationCenter.default.addObserver(forName: .myNotification, object: nil, queue: .main) { _ in
29
+ print("Notification received")
30
+ }
31
+ }
32
+
33
+ deinit {
34
+ if let observer = observer {
35
+ NotificationCenter.default.removeObserver(observer)
36
+ }
37
+ }
38
+ }
39
+ ```
40
+
41
+ **Tools:** SwiftLint (discarded_notification_center_observer)
@@ -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)