Skip to content

Commit

Permalink
let NSE-fetch and MainApp run mutually exclusive
Browse files Browse the repository at this point in the history
  • Loading branch information
r10s committed Mar 20, 2024
1 parent c4a1316 commit d928de7
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 2 deletions.
19 changes: 19 additions & 0 deletions DcCore/DcCore/Extensions/UserDefaults+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,26 @@ public extension UserDefaults {
static var hasExtensionAttemptedToSend = "hasExtensionAttemptedToSend"
static var hasSavedKeyToKeychain = "hasSavedKeyToKeychain"
static var upgradedKeychainEntry = "upgradedKeychainEntry_"
static var mainAppRunningKey = "mainAppRunning"
static var nseFetchingKey = "nseFetching"

static var shared: UserDefaults? {
return UserDefaults(suiteName: "group.chat.delta.ios")
}

static var mainAppRunning: Bool {
return shared?.bool(forKey: mainAppRunningKey) ?? false
}

static func setMainAppRunning(_ value: Bool = true) {
shared?.setValue(value, forKey: mainAppRunningKey)
}

static var nseFetching: Bool {
return shared?.bool(forKey: nseFetchingKey) ?? false
}

static func setNseFetching(_ value: Bool = true) {
shared?.setValue(value, forKey: nseFetchingKey)
}
}
10 changes: 10 additions & 0 deletions DcNotificationService/NotificationService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,24 @@ class NotificationService: UNNotificationServiceExtension {
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
guard let bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) else { return }

if UserDefaults.mainAppRunning {
contentHandler(silenceNotification(bestAttemptContent))
return
}
UserDefaults.setNseFetching()

// as we're mixing in notifications from accounts without PUSH and we cannot add multiple notifications,
// it is best to move everything to the same thread - and set just no threadIdentifier

dcAccounts.openDatabase(writeable: false)
let eventEmitter = dcAccounts.getEventEmitter()

if !dcAccounts.backgroundFetch(timeout: 25) {
UserDefaults.setNseFetching(false)
contentHandler(bestAttemptContent)
return
}
UserDefaults.setNseFetching(false)

var messageCount = 0
var uniqueChats: [String: String] = [:]
Expand Down Expand Up @@ -70,6 +79,7 @@ class NotificationService: UNNotificationServiceExtension {

// For Delta Chat, it is just fine to do nothing - assume eg. bad network or mail servers not reachable,
// then a "You have new messages" is the best that can be done.
UserDefaults.setNseFetching(false)
}

private func silenceNotification(_ bestAttemptContent: UNMutableNotificationContent) -> UNMutableNotificationContent {
Expand Down
19 changes: 17 additions & 2 deletions deltachat-ios/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD

logger.info("➡️ didFinishLaunchingWithOptions")

// The NSE ("Notification Service Extension") must not run at the same time as the app.
// The other way round, the NSW is not started with the app running.
UserDefaults.setMainAppRunning()
var pollSeconds = 0
while UserDefaults.nseFetching && pollSeconds < 30 {
logger.info("➡️ wait for NSE to terminate")
usleep(1_000_000)
pollSeconds += 1
}
UserDefaults.setNseFetching(false) // NSE terminated unexpectedly, do not always wait 30 seconds

let webPCoder = SDImageWebPCoder.shared
SDImageCodersManager.shared.addCoder(webPCoder)
let svgCoder = SDImageSVGKCoder.shared
Expand Down Expand Up @@ -231,6 +242,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
func applicationWillEnterForeground(_: UIApplication) {
logger.info("➡️ applicationWillEnterForeground")
applicationInForeground = true
UserDefaults.setMainAppRunning()
dcAccounts.startIo()

DispatchQueue.global().async { [weak self] in
Expand Down Expand Up @@ -271,6 +283,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
// applicationDidBecomeActive() is called on initial app start _and_ after applicationWillEnterForeground()
func applicationDidBecomeActive(_: UIApplication) {
logger.info("➡️ applicationDidBecomeActive")
UserDefaults.setMainAppRunning()
applicationInForeground = true
}

Expand All @@ -286,6 +299,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD

func applicationWillTerminate(_: UIApplication) {
logger.info("⬅️ applicationWillTerminate")
UserDefaults.setMainAppRunning(false)
uninstallEventHandler()
dcAccounts.closeDatabase()
if let reachability = reachability {
Expand Down Expand Up @@ -326,6 +340,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
} else if app.backgroundTimeRemaining < 10 {
logger.info("⬅️ few background time, \(app.backgroundTimeRemaining), stopping")
self.dcAccounts.stopIo()
UserDefaults.setMainAppRunning(false)

// to avoid 0xdead10cc exceptions, scheduled jobs need to be done before we get suspended;
// we increase the probabilty that this happens by waiting a moment before calling unregisterBackgroundTask()
Expand Down Expand Up @@ -424,8 +439,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
private func performFetch(completionHandler: ((UIBackgroundFetchResult) -> Void)? = nil) {
// `didReceiveRemoteNotification` as well as `performFetchWithCompletionHandler` might be called if we're in foreground,
// in this case, there is no need to wait for things or do sth.
if appIsInForeground() {
logger.info("➡️ app already in foreground")
if appIsInForeground() || UserDefaults.nseFetching {
logger.info("➡️ app already in foreground or NSE running")
pushToDebugArray("OK1")
completionHandler?(.newData)
return
Expand Down

0 comments on commit d928de7

Please sign in to comment.