AuthManager.swift 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. import Foundation
  2. import Combine
  3. /// Manages authentication state, user session, profile management, and audio sync access authorization.
  4. final class AuthManager: ObservableObject {
  5. static let shared = AuthManager()
  6. @Published private(set) var currentUser: UserAccount?
  7. @Published var isProcessing: Bool = false
  8. @Published var authErrorMessage: String?
  9. @Published var isSyncing: Bool = false
  10. @Published var lastSyncDate: Date?
  11. @Published var syncErrorMessage: String?
  12. var isLoggedIn: Bool {
  13. currentUser != nil
  14. }
  15. /// Auth repository interface. Can be swapped for `RemoteAuthServiceStub()` or mock services.
  16. private let authService: AuthServiceProtocol
  17. init(authService: AuthServiceProtocol = LocalAuthRepository()) {
  18. self.authService = authService
  19. self.currentUser = authService.getCurrentUser()
  20. }
  21. // MARK: - Auth Actions
  22. /// User login via username/email/phone & password.
  23. func login(identifier: String, password: String, completion: ((Bool, String?) -> Void)? = nil) {
  24. isProcessing = true
  25. authErrorMessage = nil
  26. let req = LoginRequest(identifier: identifier, password: password)
  27. authService.login(request: req) { [weak self] result in
  28. DispatchQueue.main.async {
  29. guard let self = self else { return }
  30. self.isProcessing = false
  31. switch result {
  32. case .success(let user):
  33. self.currentUser = user
  34. completion?(true, nil)
  35. case .failure(let error):
  36. self.authErrorMessage = error.localizedDescription
  37. completion?(false, error.localizedDescription)
  38. }
  39. }
  40. }
  41. }
  42. /// User registration.
  43. func register(username: String, identifier: String, password: String, completion: ((Bool, String?) -> Void)? = nil) {
  44. isProcessing = true
  45. authErrorMessage = nil
  46. let req = RegisterRequest(username: username, identifier: identifier, password: password)
  47. authService.register(request: req) { [weak self] result in
  48. DispatchQueue.main.async {
  49. guard let self = self else { return }
  50. self.isProcessing = false
  51. switch result {
  52. case .success(let user):
  53. self.currentUser = user
  54. completion?(true, nil)
  55. case .failure(let error):
  56. self.authErrorMessage = error.localizedDescription
  57. completion?(false, error.localizedDescription)
  58. }
  59. }
  60. }
  61. }
  62. /// Update profile information (nickname, email, phone, avatar).
  63. func updateProfile(username: String?, email: String?, phoneNumber: String?, avatarURL: String?, completion: ((Bool, String?) -> Void)? = nil) {
  64. isProcessing = true
  65. authErrorMessage = nil
  66. let req = UpdateProfileRequest(username: username, email: email, phoneNumber: phoneNumber, avatarURL: avatarURL)
  67. authService.updateProfile(request: req) { [weak self] result in
  68. DispatchQueue.main.async {
  69. guard let self = self else { return }
  70. self.isProcessing = false
  71. switch result {
  72. case .success(let user):
  73. self.currentUser = user
  74. completion?(true, nil)
  75. case .failure(let error):
  76. self.authErrorMessage = error.localizedDescription
  77. completion?(false, error.localizedDescription)
  78. }
  79. }
  80. }
  81. }
  82. /// Change password.
  83. func changePassword(oldPassword: String, newPassword: String, completion: ((Bool, String?) -> Void)? = nil) {
  84. isProcessing = true
  85. authErrorMessage = nil
  86. let req = ChangePasswordRequest(oldPassword: oldPassword, newPassword: newPassword)
  87. authService.changePassword(request: req) { [weak self] result in
  88. DispatchQueue.main.async {
  89. guard let self = self else { return }
  90. self.isProcessing = false
  91. switch result {
  92. case .success:
  93. completion?(true, nil)
  94. case .failure(let error):
  95. self.authErrorMessage = error.localizedDescription
  96. completion?(false, error.localizedDescription)
  97. }
  98. }
  99. }
  100. }
  101. /// Logout current user.
  102. func logout() {
  103. authService.logout { [weak self] in
  104. DispatchQueue.main.async {
  105. self?.currentUser = nil
  106. }
  107. }
  108. }
  109. // MARK: - Audio Sync
  110. /// Triggers audio synchronization. Requires authenticated user.
  111. func syncRecordings(onRequireAuth: @escaping () -> Void, onComplete: @escaping (Bool) -> Void) {
  112. guard isLoggedIn else {
  113. onRequireAuth()
  114. return
  115. }
  116. isSyncing = true
  117. syncErrorMessage = nil
  118. // Mock sync delay
  119. DispatchQueue.main.asyncAfter(deadline: .now() + 1.2) { [weak self] in
  120. guard let self = self else { return }
  121. self.isSyncing = false
  122. self.lastSyncDate = Date()
  123. onComplete(true)
  124. }
  125. }
  126. }