Resetting the app after deletion and cleanup settings (#372)

* Fixes #361 - Reset everything if the app has been deleted since the last run

* Cleaned up and renamed the ElementSettings to ApplicationSettings. Removed its singleton and moved it to the service locator.

* Moved the ApplicationSettings to the Application folder

* Merged together the BuildSettings and ApplicationSettings

* Reset the UserDefaults too when resetting the application state

* Rename ServiceLocator.applicationSettings to just settings

* Fix indentation

* Rename ApplicationSettings to AppSettings

* Various tweaks following code review

* Fix unit tests
This commit is contained in:
Stefan Ceriu
2022-12-16 10:02:22 +02:00
committed by GitHub
parent 5b90f37f2e
commit 6f974a4422
39 changed files with 259 additions and 272 deletions
@@ -52,23 +52,30 @@ class AppCoordinator: AppCoordinatorProtocol {
init() {
navigationRootCoordinator = NavigationRootCoordinator()
Self.setupServiceLocator(navigationRootCoordinator: navigationRootCoordinator)
Self.setupLogging()
stateMachine = AppCoordinatorStateMachine()
bugReportService = BugReportService(withBaseURL: BuildSettings.bugReportServiceBaseURL, sentryURL: BuildSettings.bugReportSentryURL)
bugReportService = BugReportService(withBaseURL: ServiceLocator.shared.settings.bugReportServiceBaseURL, sentryURL: ServiceLocator.shared.settings.bugReportSentryURL)
navigationRootCoordinator.setRootCoordinator(SplashScreenCoordinator())
ServiceLocator.shared.register(userNotificationController: UserNotificationController(rootCoordinator: navigationRootCoordinator))
backgroundTaskService = UIKitBackgroundTaskService {
UIApplication.shared
}
userSessionStore = UserSessionStore(backgroundTaskService: backgroundTaskService)
setupStateMachine()
// Reset everything if the app has been deleted since the previous run
if !ServiceLocator.shared.settings.hasAppLaunchedOnce {
AppSettings.reset()
userSessionStore.reset()
ServiceLocator.shared.settings.hasAppLaunchedOnce = true
}
setupLogging()
setupStateMachine()
Bundle.elementFallbackLanguage = "en"
@@ -91,7 +98,12 @@ class AppCoordinator: AppCoordinatorProtocol {
// MARK: - Private
private func setupLogging() {
private static func setupServiceLocator(navigationRootCoordinator: NavigationRootCoordinator) {
ServiceLocator.shared.register(userNotificationController: UserNotificationController(rootCoordinator: navigationRootCoordinator))
ServiceLocator.shared.register(appSettings: AppSettings())
}
private static func setupLogging() {
let loggerConfiguration = MXLogConfiguration()
loggerConfiguration.maxLogFilesCount = 10
@@ -270,7 +282,7 @@ class AppCoordinator: AppCoordinatorProtocol {
}
private func configureNotificationManager() {
guard BuildSettings.enableNotifications else {
guard ServiceLocator.shared.settings.enableNotifications else {
return
}
guard notificationManager == nil else {
@@ -0,0 +1,138 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import SwiftUI
/// Store Element specific app settings.
final class AppSettings: ObservableObject {
private enum UserDefaultsKeys: String {
case hasAppLaunchedOnce
case timelineStyle
case enableAnalytics
case isIdentifiedForAnalytics
case slidingSyncProxyBaseURLString
case enableInAppNotifications
case pusherProfileTag
}
private static var suiteName: String = InfoPlistReader.target.appGroupIdentifier
/// UserDefaults to be used on reads and writes.
private static var store: UserDefaults! = UserDefaults(suiteName: suiteName)
static func reset() {
store.removePersistentDomain(forName: suiteName)
}
static func configureWithSuiteName(_ name: String) {
suiteName = name
guard let userDefaults = UserDefaults(suiteName: name) else {
fatalError("Fail to load shared UserDefaults")
}
store = userDefaults
}
// MARK: - Application
/// Simple flag to check if app has been deleted between runs.
/// Used to clear data stored in the shared container and keychain
@AppStorage(UserDefaultsKeys.hasAppLaunchedOnce.rawValue, store: store)
var hasAppLaunchedOnce = false
let defaultHomeserverAddress = "matrix.org"
// MARK: - Notifications
var pusherAppId: String {
#if DEBUG
InfoPlistReader.target.baseBundleIdentifier + ".ios.dev"
#else
InfoPlistReader.target.baseBundleIdentifier + ".ios.prod"
#endif
}
let pushGatewayBaseURL = URL(staticString: "https://matrix.org/_matrix/push/v1/notify")
let enableNotifications = false
// MARK: - Bug report
let bugReportServiceBaseURL = URL(staticString: "https://riot.im/bugreports")
let bugReportSentryURL = URL(staticString: "https://f39ac49e97714316965b777d9f3d6cd8@sentry.tools.element.io/44")
// Use the name allocated by the bug report server
let bugReportApplicationId = "riot-ios"
let bugReportUISIId = "element-auto-uisi"
let bugReportGHLabels = ["Element-X"]
// MARK: - Analytics
#if DEBUG
/// The configuration to use for analytics during development. Set `isEnabled` to false to disable analytics in debug builds.
/// **Note:** Analytics are disabled by default for forks. If you are maintaining a fork, set custom configurations.
let analyticsConfiguration = AnalyticsConfiguration(isEnabled: InfoPlistReader.target.bundleIdentifier.starts(with: "io.element.elementx"),
host: "https://posthog.element.dev",
apiKey: "phc_VtA1L35nw3aeAtHIx1ayrGdzGkss7k1xINeXcoIQzXN",
termsURL: URL(staticString: "https://element.io/cookie-policy"))
#else
/// The configuration to use for analytics. Set `isEnabled` to false to disable analytics.
/// **Note:** Analytics are disabled by default for forks. If you are maintaining a fork, set custom configurations.
let analyticsConfiguration = AnalyticsConfiguration(isEnabled: InfoPlistReader.target.bundleIdentifier.starts(with: "io.element.elementx"),
host: "https://posthog.hss.element.io",
apiKey: "phc_Jzsm6DTm6V2705zeU5dcNvQDlonOR68XvX2sh1sEOHO",
termsURL: URL(staticString: "https://element.io/cookie-policy"))
#endif
/// Whether the user has already been shown the PostHog analytics prompt.
var hasSeenAnalyticsPrompt: Bool {
Self.store.object(forKey: UserDefaultsKeys.enableAnalytics.rawValue) != nil
}
/// `true` when the user has opted in to send analytics.
@AppStorage(UserDefaultsKeys.enableAnalytics.rawValue, store: store)
var enableAnalytics = false
/// Indicates if the device has already called identify for this session to PostHog.
/// This is separate to `enableAnalytics` as logging out leaves analytics
/// enabled, but requires the next account to be identified separately.
@AppStorage(UserDefaultsKeys.isIdentifiedForAnalytics.rawValue, store: store)
var isIdentifiedForAnalytics = false
// MARK: - Room Screen
@AppStorage(UserDefaultsKeys.timelineStyle.rawValue, store: store)
var timelineStyle = TimelineStyle.bubbles
// MARK: - Client
@AppStorage(UserDefaultsKeys.slidingSyncProxyBaseURLString.rawValue, store: store)
var slidingSyncProxyBaseURLString = "https://slidingsync.lab.element.dev"
// MARK: - Notifications
@AppStorage(UserDefaultsKeys.enableInAppNotifications.rawValue, store: store)
var enableInAppNotifications = true
/// Tag describing which set of device specific rules a pusher executes.
@AppStorage(UserDefaultsKeys.pusherProfileTag.rawValue, store: store)
var pusherProfileTag: String?
// MARK: - Other
let permalinkBaseURL = URL(staticString: "https://matrix.to")
}
@@ -1,80 +0,0 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
final class BuildSettings {
// MARK: - Bundle Settings
static var pusherAppId: String {
#if DEBUG
InfoPlistReader.target.baseBundleIdentifier + ".ios.dev"
#else
InfoPlistReader.target.baseBundleIdentifier + ".ios.prod"
#endif
}
// MARK: - Servers
static let defaultHomeserverAddress = "matrix.org"
static let defaultSlidingSyncProxyBaseURLString = "https://slidingsync.lab.element.dev"
static let pushGatewayBaseURL = URL(staticString: "https://matrix.org/_matrix/push/v1/notify")
// MARK: - Bug report
static let bugReportServiceBaseURL = URL(staticString: "https://riot.im/bugreports")
static let bugReportSentryURL = URL(staticString: "https://f39ac49e97714316965b777d9f3d6cd8@sentry.tools.element.io/44")
// Use the name allocated by the bug report server
static let bugReportApplicationId = "riot-ios"
static let bugReportUISIId = "element-auto-uisi"
static let bugReportGHLabels = ["Element-X"]
// MARK: - Analytics
#if DEBUG
/// The configuration to use for analytics during development. Set `isEnabled` to false to disable analytics in debug builds.
/// **Note:** Analytics are disabled by default for forks. If you are maintaining a fork, set custom configurations.
static let analyticsConfiguration = AnalyticsConfiguration(isEnabled: InfoPlistReader.target.bundleIdentifier.starts(with: "io.element.elementx"),
host: "https://posthog.element.dev",
apiKey: "phc_VtA1L35nw3aeAtHIx1ayrGdzGkss7k1xINeXcoIQzXN",
termsURL: URL(staticString: "https://element.io/cookie-policy"))
#else
/// The configuration to use for analytics. Set `isEnabled` to false to disable analytics.
/// **Note:** Analytics are disabled by default for forks. If you are maintaining a fork, set custom configurations.
static let analyticsConfiguration = AnalyticsConfiguration(isEnabled: InfoPlistReader.target.bundleIdentifier.starts(with: "io.element.elementx"),
host: "https://posthog.hss.element.io",
apiKey: "phc_Jzsm6DTm6V2705zeU5dcNvQDlonOR68XvX2sh1sEOHO",
termsURL: URL(staticString: "https://element.io/cookie-policy"))
#endif
// MARK: - Settings screen
static let settingsCrashButtonVisible = true
static let settingsShowTimelineStyle = true
// MARK: - Room screen
static let defaultRoomTimelineStyle: TimelineStyle = .bubbles
// MARK: - Other
static var permalinkBaseURL = URL(staticString: "https://matrix.to")
// MARK: - Notifications
static let enableNotifications = false
}
@@ -26,4 +26,10 @@ class ServiceLocator {
func register(userNotificationController: UserNotificationControllerProtocol) {
self.userNotificationController = userNotificationController
}
private(set) var settings: AppSettings!
func register(appSettings: AppSettings) {
settings = appSettings
}
}
@@ -1,82 +0,0 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
import SwiftUI
/// Store Element specific app settings.
final class ElementSettings: ObservableObject {
// MARK: - Constants
public enum UserDefaultsKeys: String {
case timelineStyle
case enableAnalytics
case isIdentifiedForAnalytics
case slidingSyncProxyBaseURLString
case enableInAppNotifications
case pusherProfileTag
}
static let shared = ElementSettings()
/// UserDefaults to be used on reads and writes.
static var store: UserDefaults {
guard let userDefaults = UserDefaults(suiteName: InfoPlistReader.target.appGroupIdentifier) else {
fatalError("Fail to load shared UserDefaults")
}
return userDefaults
}
private init() {
// no-op
}
// MARK: - Analytics
/// Whether the user has already been shown the PostHog analytics prompt.
var hasSeenAnalyticsPrompt: Bool {
Self.store.object(forKey: UserDefaultsKeys.enableAnalytics.rawValue) != nil
}
/// `true` when the user has opted in to send analytics.
@AppStorage(UserDefaultsKeys.enableAnalytics.rawValue, store: store)
var enableAnalytics = false
/// Indicates if the device has already called identify for this session to PostHog.
/// This is separate to `enableAnalytics` as logging out leaves analytics
/// enabled, but requires the next account to be identified separately.
@AppStorage(UserDefaultsKeys.isIdentifiedForAnalytics.rawValue, store: store)
var isIdentifiedForAnalytics = false
// MARK: - Room Screen
@AppStorage(UserDefaultsKeys.timelineStyle.rawValue, store: store)
var timelineStyle = BuildSettings.defaultRoomTimelineStyle
// MARK: - Client
@AppStorage(UserDefaultsKeys.slidingSyncProxyBaseURLString.rawValue, store: store)
var slidingSyncProxyBaseURLString = BuildSettings.defaultSlidingSyncProxyBaseURLString
// MARK: - Notifications
@AppStorage(UserDefaultsKeys.enableInAppNotifications.rawValue, store: store)
var enableInAppNotifications = true
@AppStorage(UserDefaultsKeys.pusherProfileTag.rawValue, store: store)
/// Tag describing which set of device specific rules a pusher executes.
var pusherProfileTag: String?
}
@@ -37,7 +37,7 @@ enum PermalinkBuilder {
throw PermalinkBuilderError.invalidUserIdentifier
}
let urlString = "\(BuildSettings.permalinkBaseURL)/#/\(userIdentifier)"
let urlString = "\(ServiceLocator.shared.settings.permalinkBaseURL)/#/\(userIdentifier)"
guard let url = URL(string: urlString) else {
throw PermalinkBuilderError.failedConstructingURL
@@ -75,7 +75,7 @@ enum PermalinkBuilder {
throw PermalinkBuilderError.failedAddingPercentEncoding
}
let urlString = "\(BuildSettings.permalinkBaseURL)/#/\(roomId)/\(eventId)"
let urlString = "\(ServiceLocator.shared.settings.permalinkBaseURL)/#/\(roomId)/\(eventId)"
guard let url = URL(string: urlString) else {
throw PermalinkBuilderError.failedConstructingURL
@@ -91,7 +91,7 @@ enum PermalinkBuilder {
throw PermalinkBuilderError.failedAddingPercentEncoding
}
let urlString = "\(BuildSettings.permalinkBaseURL)/#/\(identifier)"
let urlString = "\(ServiceLocator.shared.settings.permalinkBaseURL)/#/\(identifier)"
guard let url = URL(string: urlString) else {
throw PermalinkBuilderError.failedConstructingURL
@@ -30,7 +30,7 @@ final class AnalyticsPromptCoordinator: CoordinatorProtocol {
init(parameters: AnalyticsPromptCoordinatorParameters) {
self.parameters = parameters
viewModel = AnalyticsPromptViewModel(termsURL: BuildSettings.analyticsConfiguration.termsURL)
viewModel = AnalyticsPromptViewModel(termsURL: ServiceLocator.shared.settings.analyticsConfiguration.termsURL)
}
// MARK: - Public
@@ -55,7 +55,7 @@ struct AnalyticsPromptStrings {
// Replace the placeholder with a link.
var link = AttributedString(ElementL10n.analyticsOptInContentLink)
link.link = BuildSettings.analyticsConfiguration.termsURL
link.link = ServiceLocator.shared.settings.analyticsConfiguration.termsURL
optInContent.replaceSubrange(range, with: link)
self.optInContent = optInContent
@@ -114,7 +114,7 @@ struct AnalyticsPrompt: View {
// MARK: - Previews
struct AnalyticsPrompt_Previews: PreviewProvider {
static let viewModel = AnalyticsPromptViewModel(termsURL: BuildSettings.analyticsConfiguration.termsURL)
static let viewModel = AnalyticsPromptViewModel(termsURL: ServiceLocator.shared.settings.analyticsConfiguration.termsURL)
static var previews: some View {
AnalyticsPrompt(context: viewModel.context)
.tint(.element.accent)
@@ -61,7 +61,7 @@ class AuthenticationCoordinator: CoordinatorProtocol {
private func startAuthentication() async {
startLoading()
switch await authenticationService.configure(for: BuildSettings.defaultHomeserverAddress) {
switch await authenticationService.configure(for: ServiceLocator.shared.settings.defaultHomeserverAddress) {
case .success:
stopLoading()
showLoginScreen()
@@ -23,7 +23,7 @@ class ServerSelectionViewModel: ServerSelectionViewModelType, ServerSelectionVie
init(homeserverAddress: String, isModallyPresented: Bool) {
let bindings = ServerSelectionBindings(homeserverAddress: homeserverAddress,
slidingSyncProxyAddress: ElementSettings.shared.slidingSyncProxyBaseURLString)
slidingSyncProxyAddress: ServiceLocator.shared.settings.slidingSyncProxyBaseURLString)
super.init(initialViewState: ServerSelectionViewState(bindings: bindings,
isModallyPresented: isModallyPresented))
@@ -33,8 +33,8 @@ class ServerSelectionViewModel: ServerSelectionViewModelType, ServerSelectionVie
switch viewAction {
case .confirm:
if !state.bindings.slidingSyncProxyAddress.isEmpty,
state.bindings.slidingSyncProxyAddress != ElementSettings.shared.slidingSyncProxyBaseURLString {
ElementSettings.shared.slidingSyncProxyBaseURLString = state.bindings.slidingSyncProxyAddress
state.bindings.slidingSyncProxyAddress != ServiceLocator.shared.settings.slidingSyncProxyBaseURLString {
ServiceLocator.shared.settings.slidingSyncProxyBaseURLString = state.bindings.slidingSyncProxyAddress
}
callback?(.confirm(homeserverAddress: state.bindings.homeserverAddress))
@@ -68,7 +68,7 @@ class BugReportViewModel: BugReportViewModelType, BugReportViewModelProtocol {
let result = try await bugReportService.submitBugReport(text: context.reportText,
includeLogs: context.sendingLogsEnabled,
includeCrashLog: true,
githubLabels: BuildSettings.bugReportGHLabels,
githubLabels: ServiceLocator.shared.settings.bugReportGHLabels,
files: files)
MXLog.info("SubmitBugReport succeeded, result: \(result.reportUrl)")
callback?(.submitFinished)
@@ -17,7 +17,7 @@
import SwiftUI
struct RoomScreen: View {
@ObservedObject private var settings = ElementSettings.shared
@ObservedObject private var settings = ServiceLocator.shared.settings
@ObservedObject var context: RoomScreenViewModel.Context
@State private var showReactionsMenuForItemId = ""
@@ -35,7 +35,7 @@ enum TimelineStyle: String, CaseIterable {
// MARK: - Environment
private struct TimelineStyleKey: EnvironmentKey {
static let defaultValue = BuildSettings.defaultRoomTimelineStyle
static let defaultValue = TimelineStyle.bubbles
}
extension EnvironmentValues {
@@ -50,8 +50,6 @@ final class SettingsCoordinator: CoordinatorProtocol {
self.toggleAnalytics()
case .reportBug:
self.presentBugReportScreen()
case .crash:
self.parameters.bugReportService.crash()
case .logout:
self.callback?(.logout)
}
@@ -67,7 +65,7 @@ final class SettingsCoordinator: CoordinatorProtocol {
// MARK: - Private
private func toggleAnalytics() {
if ElementSettings.shared.enableAnalytics {
if ServiceLocator.shared.settings.enableAnalytics {
Analytics.shared.optOut()
} else {
Analytics.shared.optIn(with: parameters.userSession)
@@ -21,7 +21,6 @@ enum SettingsViewModelAction {
case close
case toggleAnalytics
case reportBug
case crash
case logout
}
@@ -33,13 +32,12 @@ struct SettingsViewState: BindableState {
}
struct SettingsViewStateBindings {
var enableAnalytics = ElementSettings.shared.enableAnalytics
var enableAnalytics = ServiceLocator.shared.settings.enableAnalytics
}
enum SettingsViewAction {
case close
case toggleAnalytics
case reportBug
case crash
case logout
}
@@ -51,8 +51,6 @@ class SettingsViewModel: SettingsViewModelType, SettingsViewModelProtocol {
callback?(.toggleAnalytics)
case .reportBug:
callback?(.reportBug)
case .crash:
callback?(.crash)
case .logout:
callback?(.logout)
}
@@ -19,7 +19,7 @@ import SwiftUI
struct SettingsScreen: View {
@State private var showingLogoutConfirmation = false
@Environment(\.colorScheme) private var colorScheme
@ObservedObject private var settings = ElementSettings.shared
@ObservedObject private var settings = ServiceLocator.shared.settings
@ScaledMetric private var avatarSize = AvatarSize.user(on: .settings).value
@ScaledMetric private var menuIconSize = 30.0
@@ -110,30 +110,20 @@ struct SettingsScreen: View {
.listRowSeparator(.hidden)
.foregroundColor(.element.primaryContent)
.accessibilityIdentifier("reportBugButton")
if BuildSettings.settingsCrashButtonVisible {
Button("Crash app",
role: .destructive) { context.send(viewAction: .crash)
}
.listRowInsets(listRowInsets)
.accessibilityIdentifier("crashButton")
}
}
}
@ViewBuilder
private var userInterfaceSection: some View {
if BuildSettings.settingsShowTimelineStyle {
Section {
Picker(ElementL10n.settingsTimelineStyle, selection: $settings.timelineStyle) {
ForEach(TimelineStyle.allCases, id: \.self) { style in
Text(style.description)
.tag(style)
}
Section {
Picker(ElementL10n.settingsTimelineStyle, selection: $settings.timelineStyle) {
ForEach(TimelineStyle.allCases, id: \.self) { style in
Text(style.description)
.tag(style)
}
.listRowInsets(listRowInsets)
.accessibilityIdentifier("timelineStylePicker")
}
.listRowInsets(listRowInsets)
.accessibilityIdentifier("timelineStylePicker")
}
}
@@ -49,14 +49,14 @@ class Analytics {
/// Whether to show the user the analytics opt in prompt.
var shouldShowAnalyticsPrompt: Bool {
// Only show the prompt once, and when analytics are enabled in BuildSettings.
!ElementSettings.shared.hasSeenAnalyticsPrompt && BuildSettings.analyticsConfiguration.isEnabled
!ServiceLocator.shared.settings.hasSeenAnalyticsPrompt && ServiceLocator.shared.settings.analyticsConfiguration.isEnabled
}
/// Opts in to analytics tracking with the supplied user session.
/// - Parameter userSession: The user session to use to when reading/generating the analytics ID.
/// The session will be ignored if not running.
func optIn(with userSession: UserSessionProtocol) {
ElementSettings.shared.enableAnalytics = true
ServiceLocator.shared.settings.enableAnalytics = true
startIfEnabled()
Task { await useAnalyticsSettings(from: userSession) }
@@ -64,7 +64,7 @@ class Analytics {
/// Stops analytics tracking and calls `reset` to clear any IDs and event queues.
func optOut() {
ElementSettings.shared.enableAnalytics = false
ServiceLocator.shared.settings.enableAnalytics = false
// The order is important here. PostHog ignores the reset if stopped.
reset()
@@ -76,7 +76,7 @@ class Analytics {
/// Starts the analytics client if the user has opted in, otherwise does nothing.
func startIfEnabled() {
guard ElementSettings.shared.enableAnalytics, !isRunning else { return }
guard ServiceLocator.shared.settings.enableAnalytics, !isRunning else { return }
client.start()
// monitoringClient.start()
@@ -96,8 +96,8 @@ class Analytics {
/// - Parameter userSession: The user session to read analytics settings from.
func useAnalyticsSettings(from userSession: UserSessionProtocol) async {
guard
ElementSettings.shared.enableAnalytics,
!ElementSettings.shared.isIdentifiedForAnalytics
ServiceLocator.shared.settings.enableAnalytics,
!ServiceLocator.shared.settings.isIdentifiedForAnalytics
else { return }
let service = AnalyticsService(userSession: userSession)
@@ -122,7 +122,7 @@ class Analytics {
// monitoringClient.reset()
MXLog.debug("Reset.")
ElementSettings.shared.isIdentifiedForAnalytics = false
ServiceLocator.shared.settings.isIdentifiedForAnalytics = false
// Stop collecting crash logs
// MXLogger.logCrashes(false)
@@ -147,7 +147,7 @@ class Analytics {
client.identify(id: id)
MXLog.debug("Identified.")
ElementSettings.shared.isIdentifiedForAnalytics = true
ServiceLocator.shared.settings.isIdentifiedForAnalytics = true
}
/// Capture an event in the `client`.
@@ -18,7 +18,7 @@ import PostHog
extension PHGPostHogConfiguration {
static var standard: PHGPostHogConfiguration? {
let analyticsConfiguration = BuildSettings.analyticsConfiguration
let analyticsConfiguration = ServiceLocator.shared.settings.analyticsConfiguration
guard analyticsConfiguration.isEnabled else { return nil }
let postHogConfiguration = PHGPostHogConfiguration(apiKey: analyticsConfiguration.apiKey, host: analyticsConfiguration.host)
@@ -22,7 +22,7 @@ class AuthenticationServiceProxy: AuthenticationServiceProxyProtocol {
private let authenticationService: AuthenticationService
private let userSessionStore: UserSessionStoreProtocol
private(set) var homeserver = LoginHomeserver(address: BuildSettings.defaultHomeserverAddress, loginMode: .unknown)
private(set) var homeserver = LoginHomeserver(address: ServiceLocator.shared.settings.defaultHomeserverAddress, loginMode: .unknown)
init(userSessionStore: UserSessionStoreProtocol) {
self.userSessionStore = userSessionStore
@@ -28,7 +28,7 @@ class BugReportService: BugReportServiceProtocol {
init(withBaseURL baseURL: URL,
sentryURL: URL,
applicationId: String = BuildSettings.bugReportApplicationId,
applicationId: String = ServiceLocator.shared.settings.bugReportApplicationId,
session: URLSession = .shared) {
self.baseURL = baseURL
self.sentryURL = sentryURL
@@ -81,7 +81,7 @@ class ClientProxy: ClientProxyProtocol {
await Task.dispatch(on: clientQueue) {
do {
let slidingSyncBuilder = try client.slidingSync().homeserver(url: ElementSettings.shared.slidingSyncProxyBaseURLString)
let slidingSyncBuilder = try client.slidingSync().homeserver(url: ServiceLocator.shared.settings.slidingSyncProxyBaseURLString)
let slidingSyncView = try SlidingSyncViewBuilder()
.timelineLimit(limit: 10)
@@ -89,12 +89,12 @@ class NotificationManager: NSObject, NotificationManagerProtocol {
do {
try await clientProxy.setPusher(pushkey: deviceToken.base64EncodedString(),
kind: .http,
appId: BuildSettings.pusherAppId,
appId: ServiceLocator.shared.settings.pusherAppId,
appDisplayName: "\(InfoPlistReader.target.bundleDisplayName) (iOS)",
deviceDisplayName: UIDevice.current.name,
profileTag: pusherProfileTag(),
lang: Bundle.preferredLanguages.first ?? "en",
url: BuildSettings.pushGatewayBaseURL.absoluteString,
url: ServiceLocator.shared.settings.pushGatewayBaseURL.absoluteString,
format: .eventIdOnly,
defaultPayload: [
"aps": [
@@ -113,7 +113,7 @@ class NotificationManager: NSObject, NotificationManagerProtocol {
}
private func pusherProfileTag() -> String {
if let currentTag = ElementSettings.shared.pusherProfileTag {
if let currentTag = ServiceLocator.shared.settings.pusherProfileTag {
return currentTag
}
let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
@@ -122,7 +122,7 @@ class NotificationManager: NSObject, NotificationManagerProtocol {
return String(chars[chars.index(chars.startIndex, offsetBy: offset)])
}.joined()
ElementSettings.shared.pusherProfileTag = newTag
ServiceLocator.shared.settings.pusherProfileTag = newTag
return newTag
}
}
@@ -132,7 +132,7 @@ class NotificationManager: NSObject, NotificationManagerProtocol {
extension NotificationManager: UNUserNotificationCenterDelegate {
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification) async -> UNNotificationPresentationOptions {
guard ElementSettings.shared.enableInAppNotifications else {
guard ServiceLocator.shared.settings.enableInAppNotifications else {
return []
}
guard let delegate else {
@@ -36,6 +36,12 @@ class UserSessionStore: UserSessionStoreProtocol {
MXLog.debug("Setup base directory at: \(baseDirectory)")
}
/// Deletes all data stored in the shared container and keychain
func reset() {
try? FileManager.default.removeItem(at: baseDirectory)
keychainController.removeAllRestorationTokens()
}
func restoreUserSession() async -> Result<UserSession, UserSessionStoreError> {
let availableCredentials = keychainController.restorationTokens()
@@ -25,6 +25,9 @@ enum UserSessionStoreError: Error {
}
protocol UserSessionStoreProtocol {
/// Deletes all data stored in the shared container and keychain
func reset()
/// Whether or not there are sessions in the store.
var hasSessions: Bool { get }
@@ -28,6 +28,10 @@ class UITestsAppCoordinator: AppCoordinatorProtocol {
mockScreens = UITestScreenIdentifier.allCases.map { MockScreen(id: $0, navigationRootCoordinator: navigationRootCoordinator) }
ServiceLocator.shared.register(userNotificationController: MockUserNotificationController())
AppSettings.configureWithSuiteName("io.element.elementx.uitests")
AppSettings.reset()
ServiceLocator.shared.register(appSettings: AppSettings())
}
func start() {