You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

macOS开发中,Manager类等业务逻辑对象是否应使用ObservableObject?

纯业务逻辑对象是否该用SwiftUI专属属性包装器?

结论很明确:纯业务逻辑的Manager、服务类,应该尽量避免使用ObservableObject、Published、AppStorage这类SwiftUI专属的属性包装器,原因和替代方案如下:

  • 核心逻辑:业务层要和UI层解耦
    这些属性包装器是SwiftUI框架的一部分,一旦在纯业务对象中使用,就把业务逻辑和SwiftUI绑定死了。后续如果要切换UI框架(比如部分模块改用UIKit,或者做跨平台开发),业务层的代码就得大面积修改。而且业务逻辑本身的职责是处理业务规则、数据流转,不该依赖UI框架的特性。

  • 替代方案:用通用的响应式工具做分层衔接
    想要让业务层的变化能通知到ViewModel,完全可以用Foundation自带的Combine框架(不是SwiftUI专属),比如用PassthroughSubject或者CurrentValueSubject发布变化,然后在ViewModel层订阅这些事件,转换成SwiftUI需要的@Published属性。

    举个简单的例子:
    业务层的UserManager:

    import Combine
    
    class UserManager {
        private(set) var userName: String?
        private let _userNameUpdated = PassthroughSubject<String?, Never>()
        // 对外暴露只读的Publisher
        var userNameUpdated: AnyPublisher<String?, Never> {
            _userNameUpdated.eraseToAnyPublisher()
        }
    
        func updateUserName(_ newName: String) {
            // 这里可以加业务校验逻辑,比如长度限制
            guard newName.count >= 2 else { return }
            userName = newName
            _userNameUpdated.send(newName)
        }
    }
    

    ViewModel层做转换:

    import SwiftUI
    import Combine
    
    class UserViewModel: ObservableObject {
        @Published var displayName: String?
        private var cancellables = Set<AnyCancellable>()
    
        init(userManager: UserManager) {
            userManager.userNameUpdated
                .assign(to: \.displayName, on: self)
                .store(in: &cancellables)
        }
    }
    
  • 关于AppStorage的替代
    AppStorage是SwiftUI封装的UserDefaults快捷访问方式,纯业务层应该直接操作UserDefaults(或者封装自己的存储抽象),而不是依赖AppStorage。比如:

    class SettingsService {
        private let defaults = UserDefaults.standard
        private let _themeChanged = PassthroughSubject<String, Never>()
        var themeChanged: AnyPublisher<String, Never> {
            _themeChanged.eraseToAnyPublisher()
        }
    
        var activeTheme: String {
            get { defaults.string(forKey: "activeTheme") ?? "system" }
            set {
                defaults.set(newValue, forKey: "activeTheme")
                _themeChanged.send(newValue)
            }
        }
    }
    
  • 例外情况(不推荐)
    如果你的项目100%绑定SwiftUI,且确定未来不会有框架切换、跨平台的需求,偶尔用这些包装器也能跑通代码,但这会牺牲代码的可维护性和可测试性——测试业务逻辑时,你得依赖SwiftUI的运行环境,而不是直接测试纯逻辑。

内容的提问来源于stack exchange,提问作者Damian Dudycz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 18:01:18