Jelajahi Sumber

Merge pull request #1096 from deltachat/doc-app-delegate

update documentation, add some notify measures to debug view
bjoern 4 tahun lalu
induk
melakukan
07416d8a7c

+ 3 - 6
DcCore/DcCore/DC/Wrapper.swift

@@ -172,16 +172,13 @@ public class DcContext {
         dc_stop_ongoing_process(contextPointer)
     }
 
-    public func getInfo() -> [[String]] {
+    public func getInfo() -> String {
         if let cString = dc_get_info(contextPointer) {
             let info = String(cString: cString)
             dc_str_unref(cString)
-            logger?.info(info)
-            return info.components(separatedBy: "\n").map { val in
-                val.components(separatedBy: "=")
-            }
+            return info
         }
-        return []
+        return "ErrGetInfo"
     }
 
     public func getContactEncrInfo(contactId: Int) -> String {

+ 70 - 17
deltachat-ios/AppDelegate.swift

@@ -19,6 +19,16 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     var window: UIWindow?
     var appIsInForeground = false
 
+    // `didFinishLaunchingWithOptions` is the main entry point
+    // that is called if the app is started for the first time
+    // or after the app is killed.
+    //
+    // `didFinishLaunchingWithOptions` creates the context object and sets
+    // up other global things.
+    //
+    // `didFinishLaunchingWithOptions` is _not_ called
+    // when the app wakes up from "suspended" state
+    // (app is in memory in the background but no code is executed, IO stopped)
     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
         // explicitly ignore SIGPIPE to avoid crashes, see https://developer.apple.com/library/archive/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/CommonPitfalls/CommonPitfalls.html
         // setupCrashReporting() may create an additional handler, but we do not want to rely on that
@@ -66,8 +76,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             logger.error("Unable to start notifier")
         }
         
-        let notificationOption = launchOptions?[.remoteNotification]
-        logger.info("Notifications: remoteNotification: \(String(describing: notificationOption))")
+        if let notificationOption = launchOptions?[.remoteNotification] {
+            logger.info("Notifications: remoteNotification: \(String(describing: notificationOption))")
+            increaseDebugCounter("notify-remote-launch")
+        }
 
         if dcContext.isConfigured() && !UserDefaults.standard.bool(forKey: "notifications_disabled") {
             registerForNotifications()
@@ -76,6 +88,8 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         return true
     }
 
+    // `open` is called when an url should be opened by Delta Chat.
+    // we currently use that for handling oauth2 and for handing openpgp4fpr
     func application(_: UIApplication, open url: URL, options _: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
         // gets here when app returns from oAuth2-Setup process - the url contains the provided token
         if let params = url.queryParameters, let token = params["code"] {
@@ -91,13 +105,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         return true
     }
 
-
+    // `performFetchWithCompletionHandler` is called on local wakeup.
+    // this requires "UIBackgroundModes: fetch" to be set in Info.plist
+    // ("App downloads content from the network" in Xcode)
+    //
+    // 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.
     func application(_: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
         logger.info("---- background-fetch ----")
+        increaseDebugCounter("notify-local-wakeup")
+
         dcContext.maybeStartIo()
+        dcContext.maybeNetwork()
 
-        // 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.
         DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
             if !self.appIsInForeground {
                 self.dcContext.stopIo()
@@ -254,8 +274,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         backgroundTask = .invalid
     }
 
+
     // MARK: - PushNotifications
 
+    // `registerForNotifications` asks the user if they want to get notifiations shown.
+    // if so, it registers for receiving push notifications.
     func registerForNotifications() {
         UNUserNotificationCenter.current().delegate = self
 
@@ -265,13 +288,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             logger.info("Notifications: Permission granted: \(granted)")
 
             if granted {
-                // we are allowd to show notifications:
+                // we are allowed to show notifications:
                 // register for receiving push notifications
                 self?.maybeRegisterForRemoteNotifications()
             }
         }
     }
 
+    // register on apple server for receiving push notifications
+    // and pass the token to the app's notification server.
+    //
+    // on success, we get a token at didRegisterForRemoteNotificationsWithDeviceToken;
+    // on failure, didFailToRegisterForRemoteNotificationsWithError is called
     private func maybeRegisterForRemoteNotifications() {
         UNUserNotificationCenter.current().getNotificationSettings { settings in
             logger.info("Notifications: Settings: \(settings)")
@@ -279,8 +307,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             switch settings.authorizationStatus {
             case .authorized, .provisional, .ephemeral:
                 DispatchQueue.main.async {
-                  // on success, we get a token at didRegisterForRemoteNotificationsWithDeviceToken;
-                  // on failure, didFailToRegisterForRemoteNotificationsWithError is called
                   UIApplication.shared.registerForRemoteNotifications()
                 }
             case .denied, .notDetermined:
@@ -288,7 +314,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             }
         }
     }
-    
+
+    // `didRegisterForRemoteNotificationsWithDeviceToken` is called by iOS
+    // when the call to `UIApplication.shared.registerForRemoteNotifications` succeeded.
+    //
+    // we pass the received token to the app's notification server then.
     func application(
       _ application: UIApplication,
       didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
@@ -322,26 +352,49 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         }
     }
 
+    // `didFailToRegisterForRemoteNotificationsWithError` is called by iOS
+    // when the call to `UIApplication.shared.registerForRemoteNotifications` failed.
     func application(
       _ application: UIApplication,
-        didFailToRegisterForRemoteNotificationsWithError error: Error) {
+      didFailToRegisterForRemoteNotificationsWithError error: Error) {
         logger.error("Notifications: Failed to register: \(error)")
     }
-    
+
+    // `didReceiveRemoteNotification` is called by iOS when a push notification is received.
+    //
+    // we need to ensure IO is running as the functionb may be called from suspended state
+    // (with app in memory, but gracefully shut down before; sort of freezed).
+    // if the function was not called from suspended state,
+    // the call to maybeStartIo() did nothing, therefore, interrupt and force fetch
     func application(
         _ application: UIApplication,
         didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
         logger.verbose("Notifications: didReceiveRemoteNotification \(userInfo)")
+        increaseDebugCounter("notify-remote-receive")
 
-        // startIO as this function may be called from suspended state
-        // (with app in memory, but gracefully shut down before; sort of freezed)
         dcContext.maybeStartIo()
-
-        // if the function was not called from suspended state,
-        // the call to maybeStartIo() did nothing, therefore, interrupt and force fetch
         dcContext.maybeNetwork()
     }
-    
+
+    private func increaseDebugCounter(_ name: String) {
+        let nowDate = Date()
+        let nowTimestamp = Double(nowDate.timeIntervalSince1970)
+        let startTimestamp = UserDefaults.standard.double(forKey: name + "-start")
+        if nowTimestamp > startTimestamp + 60*60*24 {
+            let cal: Calendar = Calendar(identifier: .gregorian)
+            let newStartDate: Date = cal.date(bySettingHour: 0, minute: 0, second: 0, of: nowDate)!
+            UserDefaults.standard.set(0, forKey: name + "-count")
+            UserDefaults.standard.set(Double(newStartDate.timeIntervalSince1970), forKey: name + "-start")
+        }
+
+        let cnt = UserDefaults.standard.integer(forKey: name + "-count")
+        UserDefaults.standard.set(cnt + 1, forKey: name + "-count")
+        UserDefaults.standard.set(nowTimestamp, forKey: name + "-last")
+    }
+
+
+    // MARK: - Handle notification banners
+
     private func userNotificationCenter(_: UNUserNotificationCenter, willPresent _: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
         logger.info("forground notification")
         completionHandler([.alert, .sound])

+ 16 - 6
deltachat-ios/Controller/SettingsController.swift

@@ -469,19 +469,29 @@ internal final class SettingsViewController: UITableViewController, ProgressAler
     }
 
     private func showDebugToolkit() {
-        var info: [DBCustomVariable] = dcContext.getInfo().map { kv in
-            let value = kv.count > 1 ? kv[1] : ""
-            return DBCustomVariable(name: kv[0], value: value)
+        var info = ""
+
+        for name in ["notify-remote-launch", "notify-remote-receive", "notify-local-wakeup"] {
+            let cnt = UserDefaults.standard.integer(forKey: name + "-count")
+
+            let startInt = UserDefaults.standard.double(forKey: name + "-start")
+            let startStr = startInt==0.0 ? "" : " since " + DateUtils.getExtendedRelativeTimeSpanString(timeStamp: startInt)
+
+            let timestampInt = UserDefaults.standard.double(forKey: name + "-last")
+            let timestampStr = timestampInt==0.0 ? "" : ", last " + DateUtils.getExtendedRelativeTimeSpanString(timeStamp: timestampInt)
+
+            info += "\(name)=\(cnt)x\(startStr)\(timestampStr)\n"
         }
 
         #if DEBUG
-        info.append(DBCustomVariable(name: "DEBUG", value: "1"))
+        info += "DEBUG=1\n"
         #else
-        info.append(DBCustomVariable(name: "DEBUG", value: "0"))
+        info += "DEBUG=0\n"
         #endif
 
+        info += "\n" + dcContext.getInfo()
 
-        DBDebugToolkit.add(info)
+        DBDebugToolkit.add(DBCustomVariable(name: "", value: info))
         DBDebugToolkit.showMenu()
     }
 }