You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

161 lines
5.7 KiB

import Foundation
import EventKit
import Combine
// MARK: -
class CalendarManager: ObservableObject {
static let shared = CalendarManager()
private let eventStore = EKEventStore()
@Published var objectWillChange = ObservableObjectPublisher()
private init() {}
// MARK: -
func addEventToCalendar(calendarDetails: CalendarDetails, completion: @escaping (Bool, String?) -> Void) {
//
if #available(iOS 17.0, *) {
// iOS 17+ 使API
switch EKEventStore.authorizationStatus(for: .event) {
case .fullAccess, .writeOnly:
performAddEvent(calendarDetails: calendarDetails, completion: completion)
case .notDetermined:
requestCalendarAccess { [weak self] granted in
if granted {
self?.performAddEvent(calendarDetails: calendarDetails, completion: completion)
} else {
DispatchQueue.main.async {
completion(false, "calendar_permission_denied".localized)
}
}
}
case .denied, .restricted:
DispatchQueue.main.async {
completion(false, "calendar_permission_denied".localized)
}
@unknown default:
DispatchQueue.main.async {
completion(false, "calendar_permission_unknown".localized)
}
}
} else {
// iOS 16使API
switch EKEventStore.authorizationStatus(for: .event) {
case .authorized:
performAddEvent(calendarDetails: calendarDetails, completion: completion)
case .notDetermined:
requestCalendarAccess { [weak self] granted in
if granted {
self?.performAddEvent(calendarDetails: calendarDetails, completion: completion)
} else {
DispatchQueue.main.async {
completion(false, "calendar_permission_denied".localized)
}
}
}
case .denied, .restricted:
DispatchQueue.main.async {
completion(false, "calendar_permission_denied".localized)
}
@unknown default:
DispatchQueue.main.async {
completion(false, "calendar_permission_unknown".localized)
}
}
}
}
// MARK: -
private func requestCalendarAccess(completion: @escaping (Bool) -> Void) {
if #available(iOS 17.0, *) {
Task {
do {
let granted = try await eventStore.requestWriteOnlyAccessToEvents()
DispatchQueue.main.async {
completion(granted)
}
} catch {
DispatchQueue.main.async {
completion(false)
}
}
}
} else {
eventStore.requestAccess(to: .event) { granted, error in
DispatchQueue.main.async {
completion(granted)
}
}
}
}
// MARK: -
private func performAddEvent(calendarDetails: CalendarDetails, completion: @escaping (Bool, String?) -> Void) {
let event = EKEvent(eventStore: eventStore)
//
event.title = calendarDetails.summary.isEmpty ? "calendar_event".localized : calendarDetails.summary
//
if !calendarDetails.description.isEmpty {
event.notes = calendarDetails.description
}
//
if !calendarDetails.location.isEmpty {
event.location = calendarDetails.location
}
//
if let startDate = parseCalendarDate(calendarDetails.startTime) {
event.startDate = startDate
} else {
DispatchQueue.main.async {
completion(false, "calendar_invalid_start_time".localized)
}
return
}
//
if let endDate = parseCalendarDate(calendarDetails.endTime) {
event.endDate = endDate
} else {
// 1
event.endDate = event.startDate.addingTimeInterval(3600)
}
// 使
event.calendar = eventStore.defaultCalendarForNewEvents
// 15
let alarm = EKAlarm(relativeOffset: -900) // -15
event.addAlarm(alarm)
//
do {
try eventStore.save(event, span: .thisEvent)
DispatchQueue.main.async {
completion(true, nil)
}
} catch {
DispatchQueue.main.async {
completion(false, "calendar_save_failed".localized)
}
}
}
// MARK: -
private func parseCalendarDate(_ dateString: String) -> Date? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyMMdd'T'HHmmss"
if let date = dateFormatter.date(from: dateString) {
return date
}
//
dateFormatter.dateFormat = "yyyyMMdd"
return dateFormatter.date(from: dateString)
}
}