ios - 定时器和依赖注入
问题描述
我刚刚开始使用 Swift 并使用 MVVM 和依赖注入。
在我的 ViewModel 中,我有处理刷新数据的计时器。为了清楚起见,我稍微简化了代码。
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let viewModel = ViewModel()
}
}
class ViewModel: NSObject {
private var timer: Timer?
override init() {
super.init()
setUpTimer()
}
func setUpTimer() {
timer = Timer.scheduledTimer(withTimeInterval: 30, repeats: true){_ in
self.refreshData()
}
}
func refreshData() {
//refresh data
print("refresh data")
}
}
我想使用依赖注入将 Timer 传递到 ViewModel 中,以便在进行单元测试时可以控制计时器并使其立即调用。
所以传递 Timer 非常简单。如何将计时器传递给能够调用属于 ViewModel 的 refreshData() 的 ViewModel。Swift 中是否有允许这样做的技巧?
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let timer = Timer.scheduledTimer(withTimeInterval: 30, repeats: true){_ in
// call refreshData() from the class ViewModel
}
var viewModel = ViewModel(myTimer:timer)
}
}
class ViewModel: NSObject {
private var timer: Timer?
init(myTimer:Timer) {
super.init()
//setUpTimer()
timer = myTimer
}
func refreshData() {
//refresh data
print("refresh data")
}
}
我认为可以使用带有选择器而不是块的 scheduelTimer ,但这需要在 func refreshData() 之前使用 @objc ,这似乎很笨重,因为我在 Swift 中使用了 Objective C 功能。
有没有很好的方法来实现这一点?
非常感谢,代码
解决方案
从概念上讲,您希望将实现解耦。因此,您不必传递Timer
给视图模型,而是传递一些其他“控制”对象,它保证执行操作(延迟后回调)
如果那不叫喊protocol
,我不知道是什么...
typealias Ticker = () -> Void
protocol Refresher {
var isRunning: Bool { get }
func register(_ ticker: @escaping Ticker)
func start();
func stop();
}
所以,非常基本的概念。它可以启动、停止,观察者可以向它注册,并在“滴答”发生时得到通知。观察者并不关心它“如何”工作,只要它保证执行指定的操作。
然后一个Timer
基于实现可能看起来像......
class TimerRefresher: Refresher {
private var timer: Timer? = nil
private var ticker: Ticker? = nil
var isRunning: Bool = false
func register(_ ticker: @escaping Ticker) {
self.ticker = ticker
guard timer == nil else {
return
}
}
func start() {
guard ticker != nil else {
return
}
stop()
isRunning = true
timer = Timer.scheduledTimer(withTimeInterval: 30, repeats: true, block: { (timer) in
self.tick()
})
}
func stop() {
guard let timer = timer else {
return
}
isRunning = false
timer.invalidate()
self.timer = nil
}
private func tick() {
guard let ticker = ticker else {
stop()
return
}
ticker()
}
}
这为您提供了模拟依赖注入的入口点,通过将实现替换为Refresher
您可以手动控制的实现(或根据您的需要使用不同的“延迟”操作)
这只是一个概念示例,您的实现/需求可能会有所不同,并导致您的设计略有不同,但想法保持不变,以某种方式解耦物理实现。
另一种方法是要求您重新考虑您的设计,而不是视图模型执行它自己的刷新,而是视图/控制器将接管该责任。既然这是一个重大的设计决策,你真的只是可以做出这个决定的人,但这是另一个想法
推荐阅读
- windows - 我们可以在一台机器上从 cmake 制作解决方案文件并在另一台机器上运行吗?
- c# - 重新定义一个类或函数
- vba - 您如何转到从列表框中选择并显示在导航表单上的特定记录?
- python - 为功能性 api 模型创建 keras 序列
- javascript - CSS - 如何将我的小模板帖子彼此相邻显示
- java - Eclipse Neon 不会运行
- javascript - 过滤数组并匹配至少一个条件
- celery - uwsgi 错误:“芹菜:未找到”,芹菜在带有 attach-daemon2 的 venv 中运行
- .net - 使用 MSAL.NET 针对 Azure Magment API SDK 进行身份验证
- python - 如何在 odoo 中创建多步骤表单