mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2025-06-29 04:39:53 +00:00
* ios: delay suspendChat in NSE * different background refresh interval depending on the settings * simplify * comment * reduce NSE suspend interval * space
144 lines
5 KiB
Swift
144 lines
5 KiB
Swift
//
|
|
// BGManager.swift
|
|
// SimpleX
|
|
//
|
|
// Created by Evgeny Poberezkin on 08/02/2022.
|
|
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
import BackgroundTasks
|
|
import SimpleXChat
|
|
|
|
private let receiveTaskId = "chat.simplex.app.receive"
|
|
|
|
// TCP timeout + 2 sec
|
|
private let waitForMessages: TimeInterval = 6
|
|
|
|
// This is the smallest interval between refreshes, and also target interval in "off" mode
|
|
private let bgRefreshInterval: TimeInterval = 600 // 10 minutes
|
|
|
|
// This intervals are used for background refresh in instant and periodic modes
|
|
private let periodicBgRefreshInterval: TimeInterval = 1200 // 20 minutes
|
|
|
|
private let maxBgRefreshInterval: TimeInterval = 2400 // 40 minutes
|
|
|
|
private let maxTimerCount = 9
|
|
|
|
class BGManager {
|
|
static let shared = BGManager()
|
|
var chatReceiver: ChatReceiver?
|
|
var bgTimer: Timer?
|
|
var completed = true
|
|
var timerCount = 0
|
|
|
|
func register() {
|
|
logger.debug("BGManager.register")
|
|
BGTaskScheduler.shared.register(forTaskWithIdentifier: receiveTaskId, using: nil) { task in
|
|
self.handleRefresh(task as! BGAppRefreshTask)
|
|
}
|
|
}
|
|
|
|
func schedule(interval: TimeInterval? = nil) {
|
|
if !ChatModel.shared.ntfEnableLocal {
|
|
logger.debug("BGManager.schedule: disabled")
|
|
return
|
|
}
|
|
logger.debug("BGManager.schedule")
|
|
let request = BGAppRefreshTaskRequest(identifier: receiveTaskId)
|
|
request.earliestBeginDate = Date(timeIntervalSinceNow: interval ?? runInterval)
|
|
do {
|
|
try BGTaskScheduler.shared.submit(request)
|
|
} catch {
|
|
logger.error("BGManager.schedule error: \(error.localizedDescription)")
|
|
}
|
|
}
|
|
|
|
var runInterval: TimeInterval {
|
|
switch ChatModel.shared.notificationMode {
|
|
case .instant: maxBgRefreshInterval
|
|
case .periodic: periodicBgRefreshInterval
|
|
case .off: bgRefreshInterval
|
|
}
|
|
}
|
|
|
|
var lastRanLongAgo: Bool {
|
|
Date.now.timeIntervalSince(chatLastBackgroundRunGroupDefault.get()) > runInterval
|
|
}
|
|
|
|
private func handleRefresh(_ task: BGAppRefreshTask) {
|
|
if !ChatModel.shared.ntfEnableLocal {
|
|
logger.debug("BGManager.handleRefresh: disabled")
|
|
return
|
|
}
|
|
logger.debug("BGManager.handleRefresh")
|
|
let shouldRun_ = lastRanLongAgo
|
|
if allowBackgroundRefresh() && shouldRun_ {
|
|
schedule()
|
|
let completeRefresh = completionHandler {
|
|
task.setTaskCompleted(success: true)
|
|
}
|
|
task.expirationHandler = { completeRefresh("expirationHandler") }
|
|
receiveMessages(completeRefresh)
|
|
} else {
|
|
schedule(interval: shouldRun_ ? bgRefreshInterval : runInterval)
|
|
logger.debug("BGManager.completionHandler: already active, not started")
|
|
task.setTaskCompleted(success: true)
|
|
}
|
|
}
|
|
|
|
func completionHandler(_ complete: @escaping () -> Void) -> ((String) -> Void) {
|
|
{ reason in
|
|
logger.debug("BGManager.completionHandler: \(reason)")
|
|
if !self.completed {
|
|
self.completed = true
|
|
self.chatReceiver?.stop()
|
|
self.chatReceiver = nil
|
|
self.bgTimer?.invalidate()
|
|
self.bgTimer = nil
|
|
self.timerCount = 0
|
|
suspendBgRefresh()
|
|
complete()
|
|
}
|
|
}
|
|
}
|
|
|
|
func receiveMessages(_ completeReceiving: @escaping (String) -> Void) {
|
|
if (!self.completed) {
|
|
logger.debug("BGManager.receiveMessages: in progress, exiting")
|
|
return
|
|
}
|
|
self.completed = false
|
|
DispatchQueue.main.async {
|
|
chatLastBackgroundRunGroupDefault.set(Date.now)
|
|
let m = ChatModel.shared
|
|
if (!m.chatInitialized) {
|
|
setAppState(.bgRefresh)
|
|
do {
|
|
try initializeChat(start: true)
|
|
} catch let error {
|
|
fatalError("Failed to start or load chats: \(responseError(error))")
|
|
}
|
|
}
|
|
activateChat(appState: .bgRefresh)
|
|
if m.currentUser == nil {
|
|
completeReceiving("no current user")
|
|
return
|
|
}
|
|
logger.debug("BGManager.receiveMessages: starting chat")
|
|
let cr = ChatReceiver()
|
|
self.chatReceiver = cr
|
|
cr.start()
|
|
RunLoop.current.add(Timer(timeInterval: 2, repeats: true) { timer in
|
|
logger.debug("BGManager.receiveMessages: timer")
|
|
self.bgTimer = timer
|
|
self.timerCount += 1
|
|
if cr.lastMsgTime.distance(to: Date.now) >= waitForMessages {
|
|
completeReceiving("timer (no messages after \(waitForMessages) seconds)")
|
|
} else if self.timerCount >= maxTimerCount {
|
|
completeReceiving("timer (called \(maxTimerCount) times")
|
|
}
|
|
}, forMode: .default)
|
|
}
|
|
}
|
|
}
|