|
@@ -40,9 +40,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|
DBDebugToolkit.setupCrashReporting()
|
|
DBDebugToolkit.setupCrashReporting()
|
|
|
|
|
|
let console = ConsoleDestination()
|
|
let console = ConsoleDestination()
|
|
|
|
+ console.format = "$DHH:mm:ss.SSS$d $C$L$c $M" // see https://docs.swiftybeaver.com/article/20-custom-format
|
|
logger.addDestination(console)
|
|
logger.addDestination(console)
|
|
dcContext.logger = DcLogger()
|
|
dcContext.logger = DcLogger()
|
|
- logger.info("launching")
|
|
|
|
|
|
+ logger.info("➡️ didFinishLaunchingWithOptions")
|
|
|
|
|
|
window = UIWindow(frame: UIScreen.main.bounds)
|
|
window = UIWindow(frame: UIScreen.main.bounds)
|
|
guard let window = window else {
|
|
guard let window = window else {
|
|
@@ -65,8 +66,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|
setStockTranslations()
|
|
setStockTranslations()
|
|
|
|
|
|
reachability.whenReachable = { reachability in
|
|
reachability.whenReachable = { reachability in
|
|
|
|
+ // maybeNetwork() shall not be called in ui thread;
|
|
|
|
+ // Reachability::reachabilityChanged uses DispatchQueue.main.async only
|
|
logger.info("network: reachable", reachability.connection.description)
|
|
logger.info("network: reachable", reachability.connection.description)
|
|
- self.dcContext.maybeNetwork()
|
|
|
|
|
|
+ DispatchQueue.global(qos: .background).async { [weak self] in
|
|
|
|
+ self?.dcContext.maybeNetwork()
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
reachability.whenUnreachable = { _ in
|
|
reachability.whenUnreachable = { _ in
|
|
@@ -94,6 +99,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|
// `open` is called when an url should be opened by Delta Chat.
|
|
// `open` is called when an url should be opened by Delta Chat.
|
|
// we currently use that for handling oauth2 and for handing openpgp4fpr
|
|
// we currently use that for handling oauth2 and for handing openpgp4fpr
|
|
func application(_: UIApplication, open url: URL, options _: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
|
|
func application(_: UIApplication, open url: URL, options _: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
|
|
|
|
+ logger.info("➡️ open url")
|
|
|
|
+
|
|
// gets here when app returns from oAuth2-Setup process - the url contains the provided token
|
|
// gets here when app returns from oAuth2-Setup process - the url contains the provided token
|
|
if let params = url.queryParameters, let token = params["code"] {
|
|
if let params = url.queryParameters, let token = params["code"] {
|
|
NotificationCenter.default.post(name: NSNotification.Name("oauthLoginApproved"), object: nil, userInfo: ["token": token])
|
|
NotificationCenter.default.post(name: NSNotification.Name("oauthLoginApproved"), object: nil, userInfo: ["token": token])
|
|
@@ -115,9 +122,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|
// we have 30 seconds time for our job, leave some seconds for graceful termination.
|
|
// we have 30 seconds time for our job, leave some seconds for graceful termination.
|
|
// also, the faster we return, the sooner we get called again.
|
|
// also, the faster we return, the sooner we get called again.
|
|
func application(_: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
|
|
func application(_: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
|
|
- logger.info("---- background-fetch ----")
|
|
|
|
|
|
+ logger.info("➡️ performFetchWithCompletionHandler")
|
|
increaseDebugCounter("notify-local-wakeup")
|
|
increaseDebugCounter("notify-local-wakeup")
|
|
|
|
|
|
|
|
+ // if we're in foreground, there is nothing to do
|
|
|
|
+ if self.appIsInForeground {
|
|
|
|
+ logger.warning("➡️ app already in foreground")
|
|
|
|
+ completionHandler(.newData)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // we're in background, run IO for a little time
|
|
dcContext.maybeStartIo()
|
|
dcContext.maybeStartIo()
|
|
dcContext.maybeNetwork()
|
|
dcContext.maybeNetwork()
|
|
|
|
|
|
@@ -135,51 +150,43 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|
}
|
|
}
|
|
|
|
|
|
func applicationWillEnterForeground(_: UIApplication) {
|
|
func applicationWillEnterForeground(_: UIApplication) {
|
|
- logger.info("---- foreground ----")
|
|
|
|
|
|
+ logger.info("➡️ applicationWillEnterForeground")
|
|
appIsInForeground = true
|
|
appIsInForeground = true
|
|
dcContext.maybeStartIo()
|
|
dcContext.maybeStartIo()
|
|
- if reachability.connection != .none {
|
|
|
|
- self.dcContext.maybeNetwork()
|
|
|
|
- }
|
|
|
|
|
|
|
|
- if let userDefaults = UserDefaults.shared, userDefaults.bool(forKey: UserDefaults.hasExtensionAttemptedToSend) {
|
|
|
|
- userDefaults.removeObject(forKey: UserDefaults.hasExtensionAttemptedToSend)
|
|
|
|
- let nc = NotificationCenter.default
|
|
|
|
|
|
+ DispatchQueue.global(qos: .background).async { [weak self] in
|
|
|
|
+ guard let self = self else { return }
|
|
|
|
+ if self.appIsInForeground {
|
|
|
|
+ if self.reachability.connection != .none {
|
|
|
|
+ self.dcContext.maybeNetwork()
|
|
|
|
+ }
|
|
|
|
|
|
- DispatchQueue.main.async {
|
|
|
|
- nc.post(
|
|
|
|
- name: dcNotificationChanged,
|
|
|
|
- object: nil,
|
|
|
|
- userInfo: [:]
|
|
|
|
- )
|
|
|
|
|
|
+ if let userDefaults = UserDefaults.shared, userDefaults.bool(forKey: UserDefaults.hasExtensionAttemptedToSend) {
|
|
|
|
+ userDefaults.removeObject(forKey: UserDefaults.hasExtensionAttemptedToSend)
|
|
|
|
+ DispatchQueue.main.async {
|
|
|
|
+ NotificationCenter.default.post(
|
|
|
|
+ name: dcNotificationChanged,
|
|
|
|
+ object: nil,
|
|
|
|
+ userInfo: [:]
|
|
|
|
+ )
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- func applicationDidEnterBackground(_: UIApplication) {
|
|
|
|
- logger.info("---- background ----")
|
|
|
|
- appIsInForeground = false
|
|
|
|
- maybeStop()
|
|
|
|
|
|
+ func applicationWillResignActive(_: UIApplication) {
|
|
|
|
+ logger.info("⬅️ applicationWillResignActive")
|
|
|
|
+ registerBackgroundTask()
|
|
}
|
|
}
|
|
|
|
|
|
- private func maybeStop() {
|
|
|
|
- DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
|
|
|
|
- let app = UIApplication.shared
|
|
|
|
- logger.info("state: \(app.applicationState) time remaining \(app.backgroundTimeRemaining)")
|
|
|
|
-
|
|
|
|
- if app.applicationState != .background {
|
|
|
|
- // only need to do sth in the background
|
|
|
|
- return
|
|
|
|
- } else if app.backgroundTimeRemaining < 10 {
|
|
|
|
- self.dcContext.stopIo()
|
|
|
|
- } else {
|
|
|
|
- self.maybeStop()
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ func applicationDidEnterBackground(_: UIApplication) {
|
|
|
|
+ logger.info("⬅️ applicationDidEnterBackground")
|
|
|
|
+ appIsInForeground = false
|
|
}
|
|
}
|
|
|
|
|
|
func applicationWillTerminate(_: UIApplication) {
|
|
func applicationWillTerminate(_: UIApplication) {
|
|
- logger.info("---- terminate ----")
|
|
|
|
|
|
+ logger.info("⬅️ applicationWillTerminate")
|
|
closeDatabase()
|
|
closeDatabase()
|
|
|
|
|
|
reachability.stopNotifier()
|
|
reachability.stopNotifier()
|
|
@@ -254,32 +261,54 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|
|
|
|
|
func installEventHandler() {
|
|
func installEventHandler() {
|
|
DispatchQueue.global(qos: .background).async {
|
|
DispatchQueue.global(qos: .background).async {
|
|
- self.registerBackgroundTask()
|
|
|
|
let eventEmitter = self.dcContext.getEventEmitter()
|
|
let eventEmitter = self.dcContext.getEventEmitter()
|
|
while true {
|
|
while true {
|
|
guard let event = eventEmitter.getNextEvent() else { break }
|
|
guard let event = eventEmitter.getNextEvent() else { break }
|
|
handleEvent(event: event)
|
|
handleEvent(event: event)
|
|
}
|
|
}
|
|
- if self.backgroundTask != .invalid {
|
|
|
|
- self.endBackgroundTask()
|
|
|
|
- }
|
|
|
|
|
|
+ logger.info("⬅️ event emitter finished")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// MARK: - BackgroundTask
|
|
// MARK: - BackgroundTask
|
|
|
|
|
|
|
|
+ // let the app run in background for a little while
|
|
|
|
+ // eg. to complete sending messages out and to react to responses.
|
|
private func registerBackgroundTask() {
|
|
private func registerBackgroundTask() {
|
|
- logger.info("background task registered")
|
|
|
|
|
|
+ logger.info("⬅️ registering background task")
|
|
backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
|
|
backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
|
|
- self?.endBackgroundTask()
|
|
|
|
|
|
+ // usually, the background thread is finished before in maybeStop()
|
|
|
|
+ logger.info("⬅️ background expirationHandler called")
|
|
|
|
+ self?.unregisterBackgroundTask()
|
|
|
|
+ }
|
|
|
|
+ maybeStop()
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private func unregisterBackgroundTask() {
|
|
|
|
+ if backgroundTask != .invalid {
|
|
|
|
+ UIApplication.shared.endBackgroundTask(backgroundTask)
|
|
|
|
+ backgroundTask = .invalid
|
|
}
|
|
}
|
|
- assert(backgroundTask != .invalid)
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- private func endBackgroundTask() {
|
|
|
|
- logger.info("background task ended")
|
|
|
|
- UIApplication.shared.endBackgroundTask(backgroundTask)
|
|
|
|
- backgroundTask = .invalid
|
|
|
|
|
|
+ private func maybeStop() {
|
|
|
|
+ DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
|
|
|
|
+ let app = UIApplication.shared
|
|
|
|
+ if app.applicationState != .background {
|
|
|
|
+ logger.info("⬅️ no longer in background")
|
|
|
|
+ self.unregisterBackgroundTask()
|
|
|
|
+ } else if app.backgroundTimeRemaining < 10 {
|
|
|
|
+ logger.info("⬅️ few background time, \(app.backgroundTimeRemaining), stopping")
|
|
|
|
+ self.dcContext.stopIo()
|
|
|
|
+ DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
|
|
|
|
+ logger.info("⬅️ few background time, \(app.backgroundTimeRemaining), done")
|
|
|
|
+ self.unregisterBackgroundTask()
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ logger.info("⬅️ remaining background time: \(app.backgroundTimeRemaining)")
|
|
|
|
+ self.maybeStop()
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -294,12 +323,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|
// register for showing notifications
|
|
// register for showing notifications
|
|
UNUserNotificationCenter.current()
|
|
UNUserNotificationCenter.current()
|
|
.requestAuthorization(options: [.alert, .sound, .badge]) { [weak self] granted, _ in
|
|
.requestAuthorization(options: [.alert, .sound, .badge]) { [weak self] granted, _ in
|
|
- logger.info("Notifications: Permission granted: \(granted)")
|
|
|
|
-
|
|
|
|
if granted {
|
|
if granted {
|
|
// we are allowed to show notifications:
|
|
// we are allowed to show notifications:
|
|
// register for receiving push notifications
|
|
// register for receiving push notifications
|
|
|
|
+ logger.info("Notifications: Permission granted: \(granted)")
|
|
self?.maybeRegisterForRemoteNotifications()
|
|
self?.maybeRegisterForRemoteNotifications()
|
|
|
|
+ } else {
|
|
|
|
+ logger.info("Notifications: Permission not granted.")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -341,7 +371,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|
let endpoint = "https://notifications.delta.chat/register"
|
|
let endpoint = "https://notifications.delta.chat/register"
|
|
#endif
|
|
#endif
|
|
|
|
|
|
- logger.verbose("Notifications: POST token: \(tokenString) to \(endpoint)")
|
|
|
|
|
|
+ logger.info("Notifications: POST token: \(tokenString) to \(endpoint)")
|
|
|
|
|
|
if let url = URL(string: endpoint) {
|
|
if let url = URL(string: endpoint) {
|
|
var request = URLRequest(url: url)
|
|
var request = URLRequest(url: url)
|
|
@@ -353,9 +383,12 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|
logger.error("Notifications: cannot POST to notification server: \(error)")
|
|
logger.error("Notifications: cannot POST to notification server: \(error)")
|
|
return
|
|
return
|
|
}
|
|
}
|
|
- logger.info("Notifications: request to notification server succeeded with respose, data: \(String(describing: response)), \(String(describing: data))")
|
|
|
|
|
|
+ if let httpStatus = response as? HTTPURLResponse, httpStatus.statusCode == 200 {
|
|
|
|
+ logger.info("Notifications: request to notification server succeeded")
|
|
|
|
+ } else {
|
|
|
|
+ logger.error("Notifications: request to notification server failed: \(String(describing: response)), \(String(describing: data))")
|
|
|
|
+ }
|
|
self.notifyToken = tokenString
|
|
self.notifyToken = tokenString
|
|
-
|
|
|
|
}
|
|
}
|
|
task.resume()
|
|
task.resume()
|
|
} else {
|
|
} else {
|
|
@@ -387,9 +420,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|
_ application: UIApplication,
|
|
_ application: UIApplication,
|
|
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
|
|
didReceiveRemoteNotification userInfo: [AnyHashable: Any],
|
|
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
|
|
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
|
|
- logger.verbose("Notifications: didReceiveRemoteNotification \(userInfo)")
|
|
|
|
|
|
+ logger.info("➡️ Notifications: didReceiveRemoteNotification \(userInfo)")
|
|
increaseDebugCounter("notify-remote-receive")
|
|
increaseDebugCounter("notify-remote-receive")
|
|
|
|
|
|
|
|
+ // didReceiveRemoteNotification might be called if we're in foreground,
|
|
|
|
+ // in this case, there is no need to wait for things or do sth.
|
|
|
|
+ if self.appIsInForeground {
|
|
|
|
+ logger.warning("➡️ app already in foreground")
|
|
|
|
+ completionHandler(.newData)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // we're in background, run IO for a little time
|
|
dcContext.maybeStartIo()
|
|
dcContext.maybeStartIo()
|
|
dcContext.maybeNetwork()
|
|
dcContext.maybeNetwork()
|
|
|
|
|
|
@@ -425,17 +467,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
|
|
|
|
|
|
// MARK: - Handle notification banners
|
|
// MARK: - Handle notification banners
|
|
|
|
|
|
- // This method will be called if an incoming message was received while the app was in forground.
|
|
|
|
|
|
+ // This method will be called if an incoming message was received while the app was in foreground.
|
|
// We don't show foreground notifications in the notification center because they don't get grouped properly
|
|
// We don't show foreground notifications in the notification center because they don't get grouped properly
|
|
func userNotificationCenter(_: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
|
func userNotificationCenter(_: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
|
- logger.info("forground notification")
|
|
|
|
|
|
+ logger.info("Notifications: foreground notification")
|
|
completionHandler([.badge])
|
|
completionHandler([.badge])
|
|
}
|
|
}
|
|
|
|
|
|
// this method will be called if the user tapped on a notification
|
|
// this method will be called if the user tapped on a notification
|
|
func userNotificationCenter(_: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
|
|
func userNotificationCenter(_: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
|
|
if !response.notification.request.identifier.containsExact(subSequence: Constants.notificationIdentifier).isEmpty {
|
|
if !response.notification.request.identifier.containsExact(subSequence: Constants.notificationIdentifier).isEmpty {
|
|
- logger.info("handling notifications")
|
|
|
|
|
|
+ logger.info("Notifications: notification tapped")
|
|
let userInfo = response.notification.request.content.userInfo
|
|
let userInfo = response.notification.request.content.userInfo
|
|
if let chatId = userInfo["chat_id"] as? Int,
|
|
if let chatId = userInfo["chat_id"] as? Int,
|
|
let msgId = userInfo["message_id"] as? Int {
|
|
let msgId = userInfo["message_id"] as? Int {
|