apple_kit

Apple-native services for Radroots iOS and macOS apps
git clone https://radroots.dev/git/apple_kit.git
Log | Files | Refs | README | LICENSE

RadrootsAppLocalStateReset.swift (2766B)


      1 import Foundation
      2 import Security
      3 
      4 public struct RadrootsAppLocalStateResetRequest: Sendable, Equatable {
      5     public let appIdentifier: String
      6     public let keychainServiceNames: [String]
      7 
      8     public init(appIdentifier: String, keychainServiceNames: [String] = []) {
      9         self.appIdentifier = appIdentifier
     10         self.keychainServiceNames = keychainServiceNames
     11     }
     12 }
     13 
     14 public enum RadrootsAppLocalStateReset {
     15     public static func reset(_ request: RadrootsAppLocalStateResetRequest) throws {
     16         try clearApplicationSupport(appIdentifier: request.appIdentifier)
     17         for serviceName in request.keychainServiceNames {
     18             try clearKeychainService(serviceName)
     19         }
     20     }
     21 
     22     public static func clearApplicationSupport(
     23         appIdentifier: String,
     24         fileManager: FileManager = .default
     25     ) throws {
     26         let trimmed = appIdentifier.trimmingCharacters(in: .whitespacesAndNewlines)
     27         guard !trimmed.isEmpty else {
     28             throw RadrootsAppLocalStateResetError.invalidRequest
     29         }
     30         do {
     31         let root = try fileManager.url(
     32             for: .applicationSupportDirectory,
     33             in: .userDomainMask,
     34             appropriateFor: nil,
     35             create: true
     36         ).appendingPathComponent(trimmed, isDirectory: true)
     37         if fileManager.fileExists(atPath: root.path) {
     38             try fileManager.removeItem(at: root)
     39         }
     40         } catch let error as RadrootsAppLocalStateResetError {
     41             throw error
     42         } catch {
     43             throw RadrootsAppLocalStateResetError.fileSystemFailure
     44         }
     45     }
     46 
     47     public static func clearKeychainService(_ serviceName: String) throws {
     48         let trimmed = serviceName.trimmingCharacters(in: .whitespacesAndNewlines)
     49         guard !trimmed.isEmpty else {
     50             throw RadrootsAppLocalStateResetError.invalidRequest
     51         }
     52         let query: [String: Any] = [
     53             kSecClass as String: kSecClassGenericPassword,
     54             kSecAttrService as String: trimmed,
     55         ]
     56         let status = SecItemDelete(query as CFDictionary)
     57         guard status == errSecSuccess || status == errSecItemNotFound else {
     58             throw RadrootsAppLocalStateResetError.keychainFailure
     59         }
     60     }
     61 }
     62 
     63 public enum RadrootsAppLocalStateResetError: Error, Equatable, Sendable {
     64     case invalidRequest
     65     case fileSystemFailure
     66     case keychainFailure
     67 }
     68 
     69 extension RadrootsAppLocalStateResetError: LocalizedError {
     70     public var errorDescription: String? {
     71         switch self {
     72         case .invalidRequest: "The local-state reset request is invalid."
     73         case .fileSystemFailure: "The local application state could not be reset."
     74         case .keychainFailure: "The secure local state could not be reset."
     75         }
     76     }
     77 }