我只是想运行一些东西
- 每说20秒
- 仅当应用程序处于前台时(明确不在后台运行时运行(
- 显然,如果您不必费心跟随应用程序进出前台,它会更优雅。
- 这个问题,我很惊讶地
scheduleRepeating
了解到,在模拟器中,当应用程序在后台运行时,它会继续运行:这并不能让我相信它明确不会在后台运行(某些? - 这个问题,我发现在设备上,使用尽可能多的代/操作系统进行测试,
scheduleRepeating
烦人地(在某些情况下?所有?情况下(仅在应用程序进入后台时才运行一次。 也就是说:一旦应用程序进入后台,它就会"再运行一次"。吸。
所以重复一遍:在iOS中,如何拥有每20秒运行一次的简单服务,并且仅在应用程序处于前台时才运行,并且无需大惊小怪..理想情况下,您不必重新启动它,检查它或在应用程序的生命周期内进行任何事情...
做这么简单的事情,真的有什么最好的方法呢?
这是我(看似(有效的解决方案,这是不优雅的。肯定有一种内置的方法或其他东西可以在iOS中做如此明显的事情??
open class SomeDaemon {
static let shared = SomeDaemon()
fileprivate init() { print("SomeDaemon is running") }
var timer: DispatchSourceTimer? = nil
let gap = 10 // seconds between runs
func launch() {
// you must call this from application#didLaunch
// harmless if you accidentally call more than once
// you DO NOT do ANYTHING on app pause/unpause/etc
if timer != nil {
print("SomeDaemon, timer running already")
return
}
timer = DispatchSource.makeTimerSource(flags: [], queue: DispatchQueue.main)
timer?.scheduleRepeating(deadline: .now(), interval: .seconds(gap))
timer?.setEventHandler{ self. doSomeThing() }
timer?.resume()
}
private func doSomeThing() {
if UIApplication.shared.applicationState != .active {
print("avoided infuriating 'runs once more in bg' problem.")
return
}
// actually do your thing here
}
}
幕后花絮:
它并不华丽,但它工作得很好。
struct ForegroundTimer {
static var sharedTimerDelegate: ForegroundTimerDelegate?
static var sharedTimer = Timer()
private var timeInterval: Double = 20.0
static func startTimer() {
ForegroundTimer.waitingTimer.invalidate()
var wait = 0.0
if let time = ForegroundTimer.startedTime {
wait = timeInterval - (Date().timeIntervalSince1970 - time).truncatingRemainder(dividingBy: Int(timeInterval))
} else {
ForegroundTimer.startedTime = Date().timeIntervalSince1970
}
waitingTimer = Timer.scheduledTimer(withTimeInterval: wait, repeats: false) { (timer) in
ForegroundTimer.sharedTimerDelegate?.timeDidPass()
ForegroundTimer.sharedTimer = Timer.scheduledTimer(withTimeInterval: timeInterval, repeats: true, block: { (timer) in
ForegroundTimer.sharedTimerDelegate?.timeDidPass()
})
}
}
static func pauseTimer() {
ForegroundTimer.sharedTimer.invalidate()
}
private static var startedTime: TimeInterval?
private static var waitingTimer = Timer()
}
protocol ForegroundTimerDelegate {
func timeDidPass()
}
上面的这段代码可以按原样使用。当然,您可以timeInterval
到您想要的任何时间间隔。
用法很简单:
在 AppDelegate.swift 中,具有以下两种方法:
func applicationDidBecomeActive(_ application: UIApplication) {
ForegroundTimer.startTimer()
}
func applicationDidEnterBackground(_ application: UIApplication) {
ForegroundTimer.pauseTimer()
}
然后,让任何你想要"侦听"的类来实现计时器ForegroundTimerDelegate
,这是必需的方法,timeDidPass()
.最后,将该类/self
分配给ForegroundTimer.sharedTimerDelegate
。例如:
class ViewController: UIViewController, ForegroundTimerDelegate {
func viewDidLoad() {
ForegroundTimer.sharedTimerDelegate = self
}
func timeDidPass() {
print("20 in-foreground seconds passed")
}
}
希望这有帮助!
@Dopapp的答案非常可靠,我会接受它,但如果你想要它更简单,你可以尝试使用RunLoop:
let timer = Timer.scheduledTimer(withTimeInterval: wait, repeats: true) { _ in print("time passed") }
RunLoop.current.add(timer, forMode: RunLoopMode.commonModes)
这甚至会为您提供有关后台执行的一些功能。
警告:代码未测试!