feat: add complete Mayday iOS Xcode project

- Swift 6, SwiftUI, MVVM + async/await architecture
- iOS 17.0 minimum deployment target
- Two targets: Mayday app + MaydayLiveActivity widget extension
- Models: UserResponse, TokenPair, AppNotification, SessionResponse, AlertAttributes
- Services: HTTPClient (actor), AuthService, KeychainService, NotificationsAPIService, PushNotificationService
- ViewModels: AuthViewModel, NotificationsViewModel, SettingsViewModel
- Views: Login/Register/VerifyEmail, NotificationsList/Detail, Settings/ChangePassword/Sessions
- APNs push notifications with UIApplicationDelegate
- ActivityKit Live Activities for Dynamic Island + Lock Screen
- Keychain (Security framework) token storage
- 30-second polling with pagination for notifications
- Xcode project file (project.pbxproj) with correct build phases for both targets

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-03-13 23:04:35 +00:00
parent 0bb4d89a09
commit 1eb21c71ce
33 changed files with 2605 additions and 0 deletions
@@ -0,0 +1,46 @@
import Foundation
import UIKit
actor NotificationsAPIService {
static let shared = NotificationsAPIService()
private let client = HTTPClient.shared
private init() {}
func getNotifications(page: Int = 1, perPage: Int = 20) async throws -> NotificationsPage {
try await client.request(.getNotifications(page: page, perPage: perPage))
}
func markAsRead(id: UUID) async throws {
let _: AppNotification = try await client.request(.markAsRead(id: id))
}
func getSessions() async throws -> [SessionResponse] {
try await client.request(.getSessions)
}
func deleteSession(id: UUID) async throws {
let _: EmptyResponse = try await client.request(.deleteSession(id: id))
}
func logoutAll() async throws -> Int {
let response: LogoutAllResponse = try await client.request(.logoutAll)
return response.revokedSessions
}
func changePassword(current: String, new: String) async throws -> UserResponse {
try await client.request(.changePassword(current: current, new: new))
}
func updateAppBadge(_ count: Int) async {
await UIApplication.shared.setApplicationIconBadgeNumber(count)
}
}
struct LogoutAllResponse: Decodable {
let revokedSessions: Int
enum CodingKeys: String, CodingKey {
case revokedSessions = "revoked_sessions"
}
}