aboutsummaryrefslogtreecommitdiff
path: root/DomainDig/DataResetService.swift
blob: d4c5bfece01d12e8be86df8742448ad70fc63487 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import Foundation
import Security

enum DataResetService {
    enum ResetError: LocalizedError {
        case missingBundleIdentifier

        var errorDescription: String? {
            switch self {
            case .missingBundleIdentifier:
                "DomainDig could not determine its local storage identifier."
            }
        }
    }

    static func wipeAllLocalData(viewModel: DomainViewModel) async throws {
        let secretReferences = await MainActor.run {
            IntegrationService.shared.localSecretReferences() + LocalAPIService.shared.localSecretReferences()
        }

        try await Task.detached(priority: .userInitiated) {
            try performPersistentWipe(secretReferences: secretReferences)
        }.value

        await LookupRuntime.shared.clearCache()
        await LocalNotificationService.shared.clearAllNotifications()
        await UsageCreditService.shared.resetForCurrentVersion()

        await MainActor.run {
            IntegrationService.shared.resetAfterLocalWipe()
            LocalAPIService.shared.resetAfterLocalWipe()
            CloudSyncService.shared.resetLocalStateAfterWipe()
            PurchaseService.shared.resetCachedStateAfterLocalWipe()
            _ = DomainMonitoringScheduler.shared.syncSchedule()
        }

        await viewModel.applyLocalDataReset()
    }

    private nonisolated static func performPersistentWipe(secretReferences: [String]) throws {
        guard let bundleIdentifier = Bundle.main.bundleIdentifier else {
            throw ResetError.missingBundleIdentifier
        }

        for reference in secretReferences {
            deleteIntegrationSecret(reference: reference)
        }

        try removeTemporaryFiles()

        let defaults = UserDefaults.standard
        defaults.removePersistentDomain(forName: bundleIdentifier)
        defaults.synchronize()
    }

    private nonisolated static func removeTemporaryFiles() throws {
        let tempDirectory = FileManager.default.temporaryDirectory
        let urls = try FileManager.default.contentsOfDirectory(
            at: tempDirectory,
            includingPropertiesForKeys: nil,
            options: [.skipsHiddenFiles]
        )

        for url in urls {
            try? FileManager.default.removeItem(at: url)
        }
    }

    private nonisolated static func deleteIntegrationSecret(reference: String) {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: reference
        ]

        SecItemDelete(query as CFDictionary)
    }
}