ios - 定时器和依赖注入(inject)

标签 ios swift

我刚刚开始使用 Swift 并将 MVVM 与依赖注入(inject)结合使用。

在我的 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")
    }
}

我想使用依赖注入(inject)将定时器传递给 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")
    }
}

我认为使用带有选择器而不是 block 的 scheduelTimer 可能是可能的,但这需要在 func refreshData() 之前使用 @objc,这看起来很笨重,因为我在 Swift 中使用 Objective C 功能。

有什么好的方法可以做到这一点吗?

非常感谢, 代码

最佳答案

从概念上讲,您想要分离实现。因此,不必将 Timer 传递给 View 模型,而是传递一些其他“控制”对象,它保证执行操作(延迟后回调)

如果那不叫 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()
    }
}

这为您提供了模拟依赖项注入(inject)的入口点,通过将 Refresher 的实现替换为您可以手动控制的实现(或使用不同的“延迟”操作,具体取决于您的需要)

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

替代方案需要您重新考虑您的设计,而不是 View 模型执行它自己的刷新, View / Controller 将接管该责任。由于这是一个重要的设计决定,您实际上只是可以做出该决定的人,但这是另一个想法

关于ios - 定时器和依赖注入(inject),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51956833/

相关文章:

javascript - jQuery Flexslider 无法在 iPhone 和 iPad 上工作

ios - 我如何从嵌套的 NSMutableDictionary 中获取 numberOfRowsInSection?

ios - 对于可编码结构,由于 'private' 保护级别,“CodingKeys”无法访问

php - iOS 的自动续订订阅 : How to get Server-side receipt validation?

swift - DGElasticPullToRefresh - 由于信号 : Segmentation fault: 11,命令失败

iPhone:UISegmentedControl,如何让导航返回后记住索引?

ios - 直接从 Parse.com 下载图像,而不是使用他们的 API

swift - 从自定义相机获取深度数据

Swift Firebase Storage 如何检索名称未知的图像(NSUUID)

swift - SKSpriteNode Overlay 未显示