Files
psu-schedule/ios/PSUSchedule/PSUScheduleApp.swift
Zakhar Timoshenko 170a44c377 Add date and time handling, models, schedule management, storage, and widget support
- Introduced `Dates.swift` for handling date and time in the university's timezone.
- Created `Models.swift` to define data structures for timetable, lessons, and directory search results.
- Implemented `Schedule.swift` for managing lesson slots and timetable functionalities.
- Developed `Storage.swift` for user settings and data persistence.
- Added `Widgets.swift` to create widgets displaying lessons and upcoming classes.
2026-10-18 20:26:12 +03:00

66 lines
2.3 KiB
Swift

import BackgroundTasks
import SwiftUI
import UIKit
@main
struct PSUScheduleApp: App {
@State private var model = AppModel()
@Environment(\.scenePhase) private var phase
var body: some Scene {
WindowGroup {
RootView()
.environment(model)
// The theme is set on the window, not with preferredColorScheme: sheets that are
// already open (the settings themselves) switch along with the screens behind them.
.onChange(of: model.settings.theme, initial: true) { old, new in Theme.apply(new, animated: old != new) }
.task { Background.schedule() }
}
.onChange(of: phase) { _, now in
// Back in the app after a while: a quiet refresh, as the Android client does on start.
if now == .active, model.settings.current != nil { Task { await model.refresh() } }
if now == .background { Background.schedule() }
}
// Every few hours iOS wakes the app to refresh the timetable, widgets and reminders.
.backgroundTask(.appRefresh(Background.identifier)) {
await Background.refresh()
}
}
}
enum Theme {
static func apply(_ mode: ThemeMode, animated: Bool) {
let style: UIUserInterfaceStyle = switch mode {
case .system: .unspecified
case .light: .light
case .dark: .dark
}
for scene in UIApplication.shared.connectedScenes.compactMap({ $0 as? UIWindowScene }) {
for window in scene.windows where window.overrideUserInterfaceStyle != style {
if animated {
UIView.transition(with: window, duration: 0.3, options: .transitionCrossDissolve) {
window.overrideUserInterfaceStyle = style
}
} else {
window.overrideUserInterfaceStyle = style
}
}
}
}
}
enum Background {
static let identifier = "org.xtimms.psu.schedule.refresh"
static func schedule() {
let request = BGAppRefreshTaskRequest(identifier: identifier)
request.earliestBeginDate = Date().addingTimeInterval(6 * 3600)
try? BGTaskScheduler.shared.submit(request)
}
static func refresh() async {
schedule()
await AppModel().refresh()
}
}