| class CalendarManager: ObservableObject {
static let shared = CalendarManager()
private let eventStore = EKEventStore()
@Published var authorizationStatus: EKAuthorizationStatus = .notDetermined
private init() {
checkAuthorizationStatus()
}
// MARK: - Authorization
func checkAuthorizationStatus() {
authorizationStatus = EKEventStore.authorizationStatus(for: .event)
}
func requestAccess() async -> Bool {
do {
let granted = try await eventStore.requestFullAccessToEvents()
await MainActor.run {
checkAuthorizationStatus()
}
return granted
} catch {
print("Error requesting calendar access: \(error)")
return false
}
}
// MARK: - Calendar Events
func addEntryToCalendar(_ entry: SpotEntry) async -> Result<String, CalendarError> {
// Check authorization
let status = EKEventStore.authorizationStatus(for: .event)
switch status {
case .notDetermined:
let granted = await requestAccess()
if !granted {
return .failure(.accessDenied)
}
case .denied, .restricted:
return .failure(.accessDenied)
case .fullAccess, .authorized:
break
case .writeOnly:
break
@unknown default:
return .failure(.accessDenied)
}
// Create the event
let event = EKEvent(eventStore: eventStore)
event.title = entry.playName
event.startDate = entry.dateTime
// Assume shows are about 2.5 hours
event.endDate = entry.dateTime.addingTimeInterval(2.5 * 60 * 60)
// Build location string
var locationParts: [String] = []
if !entry.cuisine.isEmpty {
locationParts.append(entry.cuisine)
}
if !entry.address.isEmpty {
locationParts.append(entry.address)
}
event.location = locationParts.joined(separator: ", ")
// Add notes with confirmation link
var notes = "Added from Spots"
if !entry.confirmationLink.isEmpty {
notes += "\n\nConfirmation: \(entry.confirmationLink)"
}
event.notes = notes
// Add an alert 1 hour before
event.addAlarm(EKAlarm(relativeOffset: -60 * 60))
// Use default calendar
event.calendar = eventStore.defaultCalendarForNewEvents
do {
try eventStore.save(event, span: .thisEvent)
return .success(event.eventIdentifier)
} catch {
print("Error saving calendar event: \(error)")
return .failure(.saveFailed(error.localizedDescription))
}
}
func removeEntryFromCalendar(eventIdentifier: String) -> Bool {
guard let event = eventStore.event(withIdentifier: eventIdentifier) else {
return false
}
do {
try eventStore.remove(event, span: .thisEvent)
return true
} catch {
print("Error removing calendar event: \(error)")
return false
}
}
// Check if an event already exists for this entry
func eventExists(for entry: SpotEntry) -> Bool {
let startOfDay = Calendar.current.startOfDay(for: entry.dateTime)
let endOfDay = Calendar.current.date(byAdding: .day, value: 1, to: startOfDay) ?? entry.dateTime
let predicate = eventStore.predicateForEvents(
withStart: startOfDay,
end: endOfDay,
calendars: nil
)
let events = eventStore.events(matching: predicate)
return events.contains { $0.title == entry.playName }
}
} | `CalendarManager` class | Defines the `CalendarManager` class. Conforms to ObservableObject. |