首页 > 解决方案 > 定时器和依赖注入

问题描述

我刚刚开始使用 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 功能。

有没有很好的方法来实现这一点?

非常感谢,代码

标签: iosswift

解决方案


从概念上讲,您希望将实现解耦。因此,您不必传递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您可以手动控制的实现(或根据您的需要使用不同的“延迟”操作)

这只是一个概念示例,您的实现/需求可能会有所不同,并导致您的设计略有不同,但想法保持不变,以某种方式解耦物理实现。

另一种方法是要求您重新考虑您的设计,而不是视图模型执行它自己的刷新,而是视图/控制器将接管该责任。既然这是一个重大的设计决策,你真的只是可以做出这个决定的人,但这是另一个想法


推荐阅读