Skip to content

Commit 3e9fe13

Browse files
committed
Add SwiftLint to CI workflow and enhance error handling in STBaseModel and STHUD
1 parent 9d2ccc2 commit 3e9fe13

6 files changed

Lines changed: 173 additions & 3 deletions

File tree

.github/workflows/swift.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@ jobs:
1818
- uses: actions/checkout@v4
1919
- name: Check try? policy
2020
run: bash .github/check_try_question_mark.sh
21+
- name: SwiftLint
22+
run: |
23+
if ! command -v swiftlint >/dev/null 2>&1; then
24+
brew install swiftlint
25+
fi
26+
swiftlint version
27+
# 不加 --strict:warning 不阻断 CI,仅 .swiftlint.yml 中明确为 error 级别的规则会失败构建。
28+
# 待存量 warning 清零后可改为 --strict 提升门槛。
29+
swiftlint --reporter github-actions-logging
2130
- name: Build
2231
run: xcodebuild -scheme STBaseProject-Package -destination 'generic/platform=iOS Simulator' build
2332
- name: Build For Testing

.swiftlint.yml

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
# SwiftLint 配置 —— STBaseProject
2+
# 与 .github/check_try_question_mark.sh 互补:try? 政策由专门脚本兜底,本文件聚焦
3+
# 其他反模式:强解包、as!、过长文件、Bool 旗参痕迹、`@objc` 滥用等。
4+
#
5+
# 渐进式落地策略:
6+
# - 严重项标 error(as!、Markdown 外 try! 在源码区)
7+
# - 风格项标 warning,先看清存量数量再考虑收紧
8+
# - 大量历史代码可以通过 `disabled_rules` 精确豁免
9+
10+
included:
11+
- Sources
12+
13+
excluded:
14+
- Sources/STMarkdown/Resources
15+
- .build
16+
- DerivedData
17+
18+
# 默认规则中需要关闭或软化的
19+
disabled_rules:
20+
- todo # TODO/FIXME 不视为告警
21+
- identifier_name # 历史命名以 st_ / ST 前缀为主,先不卡
22+
- type_name # 同上
23+
- trailing_whitespace # 历史空白多,独立 PR 清理
24+
- opening_brace # 与历史风格不一致
25+
- line_length # 由下方自定义长度覆盖
26+
- force_try # 由 st_no_force_try_outside_markdown 精准限制,允许 STMarkdown 静态正则
27+
28+
opt_in_rules:
29+
- empty_count
30+
- empty_string
31+
- explicit_init
32+
- first_where
33+
- last_where
34+
- redundant_nil_coalescing
35+
- redundant_type_annotation
36+
- sorted_first_last
37+
- prohibited_super_call
38+
- overridden_super_call
39+
- closure_end_indentation
40+
- closure_spacing
41+
- contains_over_filter_count
42+
- contains_over_first_not_nil
43+
- convenience_type
44+
- discouraged_optional_boolean # ★ 标记 Optional<Bool>,常是 Bool 旗参的延伸坏味
45+
- fallthrough
46+
- fatal_error_message
47+
- flatmap_over_map_reduce
48+
- force_unwrapping # ★ 强解包警告(不直接 error,避免一次性失血)
49+
- implicitly_unwrapped_optional # ★ 隐式解包变量
50+
- joined_default_parameter
51+
- literal_expression_end_indentation
52+
- lower_acl_than_parent # ★ 子声明可见性高于父类型时报警
53+
- modifier_order
54+
- operator_usage_whitespace
55+
- redundant_string_enum_value
56+
- sorted_imports
57+
- toggle_bool
58+
59+
force_cast: error # as! 直接 error
60+
61+
analyzer_rules:
62+
- unused_import
63+
- unused_declaration
64+
65+
# === 规则参数化 ===
66+
line_length:
67+
warning: 220
68+
error: 300
69+
ignores_urls: true
70+
ignores_function_declarations: true
71+
ignores_comments: true
72+
73+
function_body_length:
74+
warning: 120
75+
error: 250
76+
77+
type_body_length:
78+
warning: 600
79+
error: 1000
80+
81+
file_length:
82+
warning: 1200
83+
error: 2500
84+
ignore_comment_only_lines: true
85+
86+
cyclomatic_complexity:
87+
warning: 15
88+
error: 30
89+
90+
nesting:
91+
type_level: 3
92+
93+
# === 自定义规则 ===
94+
custom_rules:
95+
st_no_force_try_outside_markdown:
96+
name: "try! outside STMarkdown is forbidden"
97+
regex: '\\btry!'
98+
match_kinds:
99+
- keyword
100+
message: "try! 仅在 STMarkdown 静态正则编译路径允许,其它位置请使用 do/catch。"
101+
severity: error
102+
excluded:
103+
- "Sources/STMarkdown/.*"
104+
105+
st_no_print:
106+
name: "Use STLog instead of print"
107+
regex: '(?<![\w.])print\('
108+
message: "请使用 STLog(...) 输出日志,避免 print 在生产环境泄露。"
109+
severity: warning
110+
excluded:
111+
- "Sources/STUIKit/STLog/.*" # 日志模块自身
112+
- "Sources/STMarkdown/Resources/.*"
113+
114+
st_avoid_bool_flag_param:
115+
name: "Avoid trailing Bool parameters (use enum)"
116+
regex: '\bfunc\s+\w+\([^)]*\b(enabled|disabled|isEnabled|on|off|flag)\s*:\s*Bool\s*\)'
117+
message: "Bool 旗参可读性差,建议改为枚举(如 case enabled / disabled)。"
118+
severity: warning
119+
120+
st_chinese_only_doc:
121+
name: "Public doc should include English"
122+
regex: '^\s*///\s*[一-鿿]+[^/]*$\n^(?!\s*///)'
123+
message: "公共 API 文档建议提供英文版本(中文可保留作为辅助说明)。"
124+
severity: warning
125+
excluded:
126+
- "Sources/STLocalizable/.*"
127+
128+
reporter: "xcode"

Sources/STBaseModel/STBaseModel.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -594,7 +594,11 @@ open class STBaseModel: NSObject {
594594
// 同上,必须早于 [String: Any] 分支。
595595
let copy = NSMutableDictionary(capacity: mutableDict.count)
596596
for (k, v) in mutableDict {
597-
copy.setObject(st_deepCopy(value: v), forKey: k as! NSCopying)
597+
guard let key = k as? NSCopying else {
598+
assertionFailure("NSMutableDictionary key does not conform to NSCopying.")
599+
continue
600+
}
601+
copy.setObject(st_deepCopy(value: v), forKey: key)
598602
}
599603
return copy
600604
case let array as [Any]:

Sources/STHUD/STHUD.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,13 @@ public enum STHUDType {
3232
case text // 纯文本 Toast(无图标,自动隐藏)
3333
}
3434

35+
// MARK: - HUD 阴影开关
36+
/// Shadow toggle for HUD theme. Prefer this over the legacy `Bool` setter for clearer call-sites.
37+
public enum STHUDShadow: Equatable {
38+
case enabled
39+
case disabled
40+
}
41+
3542
// MARK: - HUD 主题配置
3643
public struct STHUDTheme {
3744
public var backgroundColor: UIColor
@@ -51,6 +58,12 @@ public struct STHUDTheme {
5158
public var labelFont: UIFont?
5259
public var detailLabelFont: UIFont?
5360

61+
/// 类型化阴影开关。底层仍用 Bool 存储,便于现有持久化/编码兼容。
62+
public var shadow: STHUDShadow {
63+
get { self.shadowEnabled ? .enabled : .disabled }
64+
set { self.shadowEnabled = (newValue == .enabled) }
65+
}
66+
5467
public init(backgroundColor: UIColor = UIColor.black.withAlphaComponent(0.8),
5568
textColor: UIColor = .white,
5669
detailTextColor: UIColor = .lightGray,
@@ -191,10 +204,18 @@ public class STHUD: NSObject {
191204

192205
/// 设置阴影
193206
/// - Parameter enabled: 是否启用阴影
207+
@available(*, deprecated, message: "Use setShadow(_:) with STHUDShadow for clearer call-sites.")
208+
// swiftlint:disable:next st_avoid_bool_flag_param
194209
public func setShadowEnabled(_ enabled: Bool) {
195210
self.theme.shadowEnabled = enabled
196211
}
197212

213+
/// 设置阴影(类型化 API)。
214+
/// - Parameter shadow: `.enabled` 启用阴影;`.disabled` 关闭。
215+
public func setShadow(_ shadow: STHUDShadow) {
216+
self.theme.shadow = shadow
217+
}
218+
198219
/// 设置成功图标
199220
/// - Parameter iconName: 图标名称
200221
public func setSuccessIcon(_ iconName: String?) {

Sources/STMarkdown/Table/STMarkdownTableView.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,10 @@ extension STMarkdownTableView: UICollectionViewDataSource {
174174
let cell = collectionView.dequeueReusableCell(
175175
withReuseIdentifier: STMarkdownTableCell.reuseIdentifier,
176176
for: indexPath
177-
) as! STMarkdownTableCell
177+
)
178+
guard let cell = cell as? STMarkdownTableCell else {
179+
return cell
180+
}
178181

179182
if let tableData,
180183
indexPath.section < tableData.cells.count,

Sources/STUIKit/STLog/STLogView.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,12 @@ extension STLogView: UITableViewDataSource, UITableViewDelegate {
425425
}
426426

427427
public func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
428-
let cell = tableView.dequeueReusableCell(withIdentifier: "STLogTableViewCell", for: indexPath) as! STLogTableViewCell
428+
guard let cell = tableView.dequeueReusableCell(
429+
withIdentifier: "STLogTableViewCell",
430+
for: indexPath
431+
) as? STLogTableViewCell else {
432+
return UITableViewCell(style: .subtitle, reuseIdentifier: nil)
433+
}
429434
guard indexPath.row < self.displayedLogEntries.count else { return cell }
430435
cell.configure(with: self.displayedLogEntries[indexPath.row])
431436
return cell

0 commit comments

Comments
 (0)