Browse Source

finish app initialization as soon as the access to the keychain is available

cyberta 2 years ago
parent
commit
fe7dc4b027

+ 2 - 1
DcCore/DcCore/Helper/KeychainManager.swift

@@ -3,6 +3,7 @@ import Security
 
 public enum KeychainError: Error {
     case noPassword
+    case accessError(message: String, status: OSStatus)
     case unhandledError(message: String, status: OSStatus)
 }
 
@@ -86,7 +87,7 @@ public class KeychainManager {
         case errSecItemNotFound:
             throw KeychainError.noPassword
         case errSecInteractionNotAllowed:
-            throw KeychainError.unhandledError(message: "Keychain not yet available.", status: status)
+            throw KeychainError.accessError(message: "Keychain not yet available.", status: status)
         default:
             throw KeychainError.unhandledError(message: "Unknown error while querying secret for account \(id):",
                                                status: status)

+ 1 - 1
DcShare/Controller/ShareViewController.swift

@@ -90,7 +90,7 @@ class ShareViewController: SLComposeServiceViewController {
                 if !dcContext.open(passphrase: secret) {
                     logger.error("Failed to open database.")
                 }
-            } catch KeychainError.unhandledError(let message, let status) {
+            } catch KeychainError.unhandledError(let message, let status), KeychainError.accessError(let message, let status) {
                 logger.error("KeychainError. \(message). Error status: \(status)")
             } catch {
                 logger.error("\(error)")

+ 22 - 8
deltachat-ios/AppDelegate.swift

@@ -23,6 +23,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     var window: UIWindow?
     var notifyToken: String?
     var applicationInForeground: Bool = false
+    private var launchOptions: [UIApplication.LaunchOptionsKey: Any]?
 
     // purpose of `bgIoTimestamp` is to block rapidly subsequent calls to remote- or local-wakeups:
     //
@@ -49,13 +50,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
     // 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 {
+        logger.info("➡️ didFinishLaunchingWithOptions")
         // 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
         signal(SIGPIPE, SIG_IGN)
 
         DBDebugToolkit.setup(with: []) // empty array will override default device shake trigger
         DBDebugToolkit.setupCrashReporting()
-        
+
+        let webPCoder = SDImageWebPCoder.shared
+        SDImageCodersManager.shared.addCoder(webPCoder)
+        let svgCoder = SDImageSVGKCoder.shared
+        SDImageCodersManager.shared.addCoder(svgCoder)
+
         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)
@@ -64,6 +71,13 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
         dcAccounts.openDatabase()
         migrateToDcAccounts()
 
+        self.launchOptions = launchOptions
+        finishAppInitialization()
+        return true
+    }
+
+    // finishes the app initialization which depends on the successful access to the keychain
+    func finishAppInitialization() {
         if let sharedUserDefaults = UserDefaults.shared, !sharedUserDefaults.bool(forKey: UserDefaults.hasSavedKeyToKeychain) {
             // we can assume a fresh install (UserDefaults are deleted on app removal)
             // -> reset the keychain (which survives removals of the app) in case the app was removed and reinstalled.
@@ -81,6 +95,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
                     if !dcContext.open(passphrase: secret) {
                         logger.error("Failed to open database for account \(accountId)")
                     }
+                } catch KeychainError.accessError(let message, let status) {
+                    logger.error("Keychain error. \(message). Error status: \(status)")
+                    return
                 } catch KeychainError.unhandledError(let message, let status) {
                     logger.error("Keychain error. \(message). Error status: \(status)")
                 } catch {
@@ -93,8 +110,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
            fatalError("Could not initialize a new account.")
         }
 
-        logger.info("➡️ didFinishLaunchingWithOptions")
-
         window = UIWindow(frame: UIScreen.main.bounds)
         guard let window = window else {
             fatalError("window was nil in app delegate")
@@ -153,11 +168,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
             registerForNotifications()
         }
 
-        let webPCoder = SDImageWebPCoder.shared
-        SDImageCodersManager.shared.addCoder(webPCoder)
-        let svgCoder = SDImageSVGKCoder.shared
-        SDImageCodersManager.shared.addCoder(svgCoder)
-        return true
+        launchOptions = nil
     }
 
     // `open` is called when an url should be opened by Delta Chat.
@@ -209,6 +220,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
 
     func applicationProtectedDataDidBecomeAvailable(_ application: UIApplication) {
         logger.info("➡️ applicationProtectedDataDidBecomeAvailable")
+        if launchOptions != nil {
+            finishAppInitialization()
+        }
     }
 
     func applicationProtectedDataWillBecomeUnavailable(_ application: UIApplication) {