session-ios/SignalUtilitiesKit/Utilities/AppSetup.swift

110 lines
4.6 KiB
Swift

// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import Foundation
import GRDB
import SessionMessagingKit
import SessionUtilitiesKit
import SessionUIKit
import SessionSnodeKit
public enum AppSetup {
private static let hasRun: Atomic<Bool> = Atomic(false)
public static func setupEnvironment(
appSpecificBlock: @escaping () -> (),
migrationProgressChanged: ((CGFloat, TimeInterval) -> ())? = nil,
migrationsCompletion: @escaping (Result<Void, Error>, Bool) -> ()
) {
guard !AppSetup.hasRun.wrappedValue else { return }
AppSetup.hasRun.mutate { $0 = true }
var backgroundTask: OWSBackgroundTask? = OWSBackgroundTask(labelStr: #function)
DispatchQueue.global(qos: .userInitiated).async {
// Order matters here.
//
// All of these "singletons" should have any dependencies used in their
// initializers injected.
OWSBackgroundTaskManager.shared().observeNotifications()
// Attachments can be stored to NSTemporaryDirectory()
// If you receive a media message while the device is locked, the download will fail if
// the temporary directory is NSFileProtectionComplete
let success: Bool = OWSFileSystem.protectFileOrFolder(
atPath: NSTemporaryDirectory(),
fileProtectionType: .completeUntilFirstUserAuthentication
)
assert(success)
Environment.shared = Environment(
reachabilityManager: SSKReachabilityManagerImpl(),
audioSession: OWSAudioSession(),
proximityMonitoringManager: OWSProximityMonitoringManagerImpl(),
windowManager: OWSWindowManager(default: ())
)
appSpecificBlock()
/// `performMainSetup` **MUST** run before `perform(migrations:)`
Configuration.performMainSetup()
runPostSetupMigrations(
backgroundTask: backgroundTask,
migrationProgressChanged: migrationProgressChanged,
migrationsCompletion: migrationsCompletion
)
// The 'if' is only there to prevent the "variable never read" warning from showing
if backgroundTask != nil { backgroundTask = nil }
}
}
public static func runPostSetupMigrations(
backgroundTask: OWSBackgroundTask? = nil,
migrationProgressChanged: ((CGFloat, TimeInterval) -> ())? = nil,
migrationsCompletion: @escaping (Result<Void, Error>, Bool) -> ()
) {
// If the database can't be initialised into a valid state then error
guard Storage.shared.isValid else {
DispatchQueue.main.async {
migrationsCompletion(Result.failure(StorageError.databaseInvalid), false)
}
return
}
var backgroundTask: OWSBackgroundTask? = (backgroundTask ?? OWSBackgroundTask(labelStr: #function))
Storage.shared.perform(
migrationTargets: [
SNUtilitiesKit.self,
SNSnodeKit.self,
SNMessagingKit.self,
SNUIKit.self
],
onProgressUpdate: migrationProgressChanged,
onComplete: { result, needsConfigSync in
// After the migrations have run but before the migration completion we load the
// SessionUtil state and update the 'needsConfigSync' flag based on whether the
// configs also need to be sync'ed
if Identity.userExists() {
SessionUtil.loadState(
userPublicKey: getUserHexEncodedPublicKey(),
ed25519SecretKey: Identity.fetchUserEd25519KeyPair()?.secretKey
)
}
// Refresh the migration state for 'SessionUtil' so it's logic can start running
// correctly when called (doing this here instead of automatically via the
// `SessionUtil.userConfigsEnabled` property to avoid having to use the correct
// method when calling within a database read/write closure)
Storage.shared.read { db in SessionUtil.refreshingUserConfigsEnabled(db) }
migrationsCompletion(result, (needsConfigSync || SessionUtil.needsSync))
// The 'if' is only there to prevent the "variable never read" warning from showing
if backgroundTask != nil { backgroundTask = nil }
}
)
}
}