Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,28 +30,30 @@ jobs:
brew install swiftlint
fi
swiftlint version
# 不加 --strict:warning 不阻断 CI,仅 .swiftlint.yml 中明确为 error 级别的规则会失败构建。
# 待存量 warning 清零后可改为 --strict 提升门槛。
swiftlint --reporter github-actions-logging
# --strict:所有 warning 提升为 error。已清零规则(force_unwrapping、modifier_order、
# convenience_type、computed_accessors_order 等)一旦回归即阻断构建。
# missing_docs 存量未清零,已从 opt_in_rules 移入 disabled_rules(见 .swiftlint.yml),
# 补齐文档后恢复并继续由 --strict 守护。
swiftlint --strict --reporter github-actions-logging

- name: Resolve SPM dependencies
run: xcodebuild -resolvePackageDependencies -scheme STBaseProject-Package
run: xcodebuild -resolvePackageDependencies -scheme STBaseProject

- name: Build (iOS Simulator) with compile log
run: |
# 完整编译日志供 swiftlint analyze 使用(analyzer_rules 依赖 type-checked AST)。
# 不使用增量构建缓存,确保日志完整。
rm -rf ~/Library/Developer/Xcode/DerivedData
xcodebuild \
-scheme STBaseProject-Package \
-scheme STBaseProject \
-destination 'generic/platform=iOS Simulator' \
clean build | tee xcodebuild.log

- name: SwiftLint Analyze (unused_import / unused_declaration)
run: |
# analyzer_rules 仅在 `swiftlint analyze` 下执行;普通 `swiftlint lint` 不会跑。
# 需完整编译日志(上方 Build 步骤产出 xcodebuild.log)。
swiftlint analyze \
swiftlint analyze --strict \
--compiler-log-path xcodebuild.log \
--reporter github-actions-logging

54 changes: 29 additions & 25 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ disabled_rules:
# 注意:line_length 是默认开启规则,不在 disabled_rules 中关闭,
# 直接在下方 line_length: 配置块生效(置于 disabled_rules 会导致同名配置块被忽略)
- force_try # 由 st_no_force_try_outside_markdown 精准限制,允许 STMarkdown 静态正则
- blanket_disable_command # 基础库大量历史 disable 注释,不卡"禁用过多/需 re-enable"风格
- superfluous_disable_command # 同上:保留历史 disable 豁免,不因区域内未触发而告警
- notification_center_detachment # 生命周期中主动解绑是计时器/HUD的既有行为,不能限定在 deinit
- missing_docs # 存量 882 个公开 API 缺文档,暂不纳入 --strict 门槛;文档补齐专项后再恢复 opt_in_rules

opt_in_rules:
- empty_count
Expand All @@ -44,13 +44,10 @@ opt_in_rules:
- contains_over_filter_count
- contains_over_first_not_nil
- convenience_type
- discouraged_optional_boolean # ★ 标记 Optional<Bool>;若确需三态语义,局部说明并豁免
- redundant_objc_attribute # ★ 检查不必要的 @objc,与顶部注释“@objc 滥用”策略一致
- fallthrough
- fatal_error_message
- flatmap_over_map_reduce
- force_unwrapping # ★ 强解包警告(不直接 error,避免一次性失血)
- implicitly_unwrapped_optional # ★ 隐式解包变量
- joined_default_parameter
- literal_expression_end_indentation
- lower_acl_than_parent # ★ 子声明可见性高于父类型时报警
Expand All @@ -59,11 +56,10 @@ opt_in_rules:
- redundant_string_enum_value
- sorted_imports
- toggle_bool
- missing_docs # ★ 公开 API 缺文档报警(先 warning 观察存量,后续可收紧)
- computed_accessors_order
# === 公共基础库 Bug Prevention(第一批,低风险高价值)===
- weak_delegate # ★ delegate 必须为 weak,否则循环引用
- discarded_notification_center_observer # ★ block observer token 应被持有,便于按生命周期移除
- private_subject # ★ Combine Subject 不应作为公开属性暴露
- unhandled_throwing_task # ★ 显式处理 throwing Task 的错误或结果
# 注:SwiftLint 无“公开类强制 final”规则(final_class 不存在);如需约束可改用
# 架构评审/PR 模板,或 static_over_final_class(语义不同,未启用)。
Expand All @@ -74,16 +70,16 @@ analyzer_rules:
- unused_import
- unused_declaration

# 公开 API 文档缺失检查:先 warning 观察存量,待清零后可改为 error 收紧。
# 仅检查 public 级别(open/internal/private 豁免),避免内部实现噪音
# CI 不使用 --strict,因此当前 warning 不阻断;使用 --strict 时会提升为 error。
missing_docs:
warning: public
# 公开 API 文档缺失检查:存量 882 个未清零,暂移出 opt_in_rules(见 disabled_rules),
# 待文档补齐专项后再恢复并重新纳入 --strict 门槛。仅检查 public 级别。
# 恢复方式:从 disabled_rules 移回 opt_in_rules,并重新启用下方配置块:
# missing_docs:
# warning: public

# === 规则参数化 ===
line_length:
warning: 220
error: 300
warning: 300
error: 400
ignores_urls: true
ignores_function_declarations: true
ignores_comments: true
Expand All @@ -93,7 +89,7 @@ function_body_length:
error: 250

type_body_length:
warning: 600
warning: 750
error: 1000

file_length:
Expand All @@ -102,9 +98,18 @@ file_length:
ignore_comment_only_lines: true

cyclomatic_complexity:
warning: 15
warning: 22
error: 30

function_parameter_count:
warning: 8
error: 9
ignores_default_parameters: true

large_tuple:
warning: 3
error: 4

nesting:
type_level:
warning: 3
Expand All @@ -113,6 +118,14 @@ nesting:

# === 自定义规则 ===
custom_rules:
st_no_swiftlint_disable:
name: "SwiftLint disable comments are forbidden"
regex: 'swiftlint\s*:\s*disable(?:\s|:|$)'
match_kinds:
- comment
message: "禁止使用 swiftlint:disable 规避规则;请修复真实问题或调整项目级规则配置。"
severity: error

st_no_force_try_outside_markdown:
name: "try! outside STMarkdown is forbidden"
regex: '\\btry!'
Expand All @@ -133,13 +146,4 @@ custom_rules:
- "Sources/STMarkdown/Resources/.*"
- "Sources/STNetwork/STWebSocket.swift" # DEBUG-only 日志封装层,等同 STLog 豁免

# Apple 并未禁止 Bool 参数;这里只提示检查调用点是否清晰。
# 若 true/false 语义由参数标签即可明确,或遵循系统 API 形态,可局部豁免;
# 只有参数代表可能扩展的模式选择时,才优先改为 enum。
st_avoid_bool_flag_param:
name: "Review Bool flag parameter clarity"
regex: '\b(?<!override\s)func\s+\w+\([^)]*(?:\b(?:enabled|disabled|on|off|flag|animated|immediately|prettyPrinted|fromEnd|ellipsis|uppercase|sync|force|reset|notify|selected|highlighted|displayMode|imageReady|iconOnLeft|animatingIn)|\b(?:is|has|can|should|allows?|enable|disable|use|uses|include|exclude|show|hide|needs|wants|requires)[A-Z]\w*)\s*:\s*Bool(?:\s*=\s*[^,\)\n]+)?(?=\s*[,)\n])'
message: "请检查 Bool 参数在调用点是否清晰;仅当它表达可扩展模式时考虑改为 enum。"
severity: warning

reporter: "xcode"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ STBaseProject 是整包发布的库(SPM 单一 product),引入即包含全

```swift
dependencies: [
.package(url: "https://github.com/i-stack/STBaseProject.git", from: "1.5.0")
.package(url: "https://github.com/i-stack/STBaseProject.git", from: "1.3.0")
],
targets: [
.target(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public final class STShimmerCursorView: UIView {

private var blinkAnimation: CABasicAnimation?

public override init(frame: CGRect) {
override public init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .label
self.layer.cornerRadius = 1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class STShimmerRendererView: UIView {

private var lastHeight: CGFloat = 0
private let cursor = STShimmerCursorView()
private let renderer: STShimmerTextView = STShimmerTextView(usingTextLayoutManager: false)
private let renderer = STShimmerTextView(usingTextLayoutManager: false)
private let controller = STShimmerController()
weak var delegate: STShimmerRendererViewDelegate?

Expand Down Expand Up @@ -53,12 +53,12 @@ public class STShimmerRendererView: UIView {
self.setup()
}

public override func layoutSubviews() {
override public func layoutSubviews() {
super.layoutSubviews()
self.updateCursor()
}

public override var intrinsicContentSize: CGSize {
override public var intrinsicContentSize: CGSize {
let size = self.renderer.sizeThatFits(
CGSize(width: self.bounds.width, height: .greatestFiniteMagnitude)
)
Expand Down
30 changes: 14 additions & 16 deletions Sources/STBaseModel/STBaseModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ open class STBaseModel: NSObject {
STLog("dealloc: \(String(describing: type(of: self)))", level: .debug)
}

public required override init() {
override public required init() {
super.init()
}

Expand Down Expand Up @@ -72,16 +72,16 @@ open class STBaseModel: NSObject {
self.st_update(from: dictionary)
}

open override func value(forUndefinedKey key: String) -> Any? {
override open func value(forUndefinedKey key: String) -> Any? {
STLog("Key = \(key) isValueForUndefinedKey", level: .warning)
return nil
}

open override class func setValue(_ value: Any?, forUndefinedKey key: String) {
override open class func setValue(_ value: Any?, forUndefinedKey key: String) {
STLog("Key = \(key) isUndefinedKey", level: .warning)
}

open override func setValue(_ value: Any?, forUndefinedKey key: String) {
override open func setValue(_ value: Any?, forUndefinedKey key: String) {
STLog("Key = \(key) isUndefinedKey", level: .warning)
}

Expand Down Expand Up @@ -492,7 +492,7 @@ open class STBaseModel: NSObject {
}
}

open override var description: String {
override open var description: String {
if self.st_isFlexibleMode {
let className = String(describing: type(of: self))
let keys = self.st_getAllKeys()
Expand All @@ -518,7 +518,7 @@ open class STBaseModel: NSObject {
}

/// 模型调试描述
open override var debugDescription: String {
override open var debugDescription: String {
return description
}

Expand Down Expand Up @@ -586,13 +586,13 @@ open class STBaseModel: NSObject {
}
}

open override func isEqual(_ object: Any?) -> Bool {
override open func isEqual(_ object: Any?) -> Bool {
guard let other = object as? STBaseModel else { return false }
guard type(of: self) == type(of: other) else { return false }
return self.normalizedDictionary().isEqual(other.normalizedDictionary())
}

open override var hash: Int {
override open var hash: Int {
return self.normalizedDictionary().hash
}

Expand Down Expand Up @@ -691,7 +691,7 @@ public struct STCodingKeys: CodingKey {

// MARK: - 属性类型解析(用于 KVC 类型安全写入)
/// 由 `property_getAttributes` 解析出的、与 KVC 写入兼容性相关的属性类型描述。
fileprivate struct STPropertyType {
private struct STPropertyType {
enum Kind {
case object(className: String?) // @"NSString" / @"NSArray<...>" / @ (id)
case block // @?
Expand All @@ -707,11 +707,9 @@ fileprivate struct STPropertyType {
// attributes 形如:T@"NSString",&,N,V_name
// 第一段以 'T' 开头,描述类型编码。
var typeEncoding = ""
for component in attributes.split(separator: ",") {
if component.first == "T" {
typeEncoding = String(component.dropFirst())
break
}
for component in attributes.split(separator: ",") where component.first == "T" {
typeEncoding = String(component.dropFirst())
break
}
self.kind = STPropertyType.parseKind(typeEncoding)
switch self.kind {
Expand Down Expand Up @@ -766,7 +764,7 @@ fileprivate struct STPropertyType {
}
}

/// 把传入值尝试转换为属性接受的形式;不兼容时返回 nil。
// 把传入值尝试转换为属性接受的形式;不兼容时返回 nil。
func coerce(_ value: Any) -> Any? {
switch kind {
case .object(let className):
Expand Down Expand Up @@ -799,7 +797,7 @@ fileprivate struct STPropertyType {
return value
}
}

private static func coerceObject(_ value: Any, expectedClassName: String?) -> Any? {
guard let className = expectedClassName else { return value }
let resolvedClass: AnyClass? = NSClassFromString(className)
Expand Down
Loading
Loading