• 如何使用EventKit框架中的EKAlarm给你的APP添加提醒功能
  • 发布于 2个月前
  • 354 热度
    0 评论
前言
昨天给朋友写个提醒 App,本来想用推送通知,结果他说能不能直接加到系统日历里?翻了下苹果文档发现系统框架中 EventKit 有个 EKAlarm 可以干这事。试了下还挺好用,就是文档写得有点绕。今天就来讲讲这个框架的使用。

EKAlarm 能干啥
简单说就是给日历事件加提醒:
时间提醒:提前多少分钟告诉你
地点提醒:到了地方或者走了就提醒
Mac 版本功能更多,能播放声音、发邮件,不过手机上用得少。

权限
用日历 API 最头疼的就是权限,苹果管得很严。不过也理解,谁都不想自己的日程被乱翻。
先在 Info.plist 里加这两行:
<key>NSCalendarsFullAccessUsageDescription</key>
<string>用来创建日程提醒</string>
<key>NSRemindersFullAccessUsageDescription</key>  
<string>管理你的提醒事项</string>
苹果现在权限分得很细,有三种:
完全访问:能读能写,功能最全
只写权限:只能创建不能读,比如车票 App
拒绝访问:啥都干不了
// 请求完全访问权限
let status = try await eventStore.requestFullAccessToEvents()
// 堆代码 duidaima.com
// 只要写入权限(iOS 17+)
let writeStatus = try await eventStore.requestWriteOnlyAccessToEvents()

怎么使用
最简单得就是提前几分钟提醒:
let alarm = EKAlarm(relativeOffset: -600) // 600 秒就是 10 分钟
event.addAlarm(alarm)
负数表示提前,正数表示延后。一个事件可以加多个提醒。

完整代码
import EventKit

class CalendarManager {
    let eventStore = EKEventStore()
    
    func createEvent(title: String, startDate: Date, endDate: Date) {
        guardlet calendar = eventStore.defaultCalendarForNewEvents else { return }
        
        let event = EKEvent(eventStore: eventStore)
        event.title = title
        event.startDate = startDate
        event.endDate = endDate
        event.calendar = calendar
        
        let alarm = EKAlarm(relativeOffset: -600) // 提前 10 分钟
        event.addAlarm(alarm)
        
        try? eventStore.save(event, span: .thisEvent)
    }
}

这样用户就能在日历 App 里看到事件,时间到了会有通知。
几个 Tips
多个提醒
重要事情我一般设 3 个提醒:
event.addAlarm(EKAlarm(relativeOffset: -86400)) // 一天前
event.addAlarm(EKAlarm(relativeOffset: -3600))  // 一小时前  
event.addAlarm(EKAlarm(relativeOffset: -600))   // 十分钟前
指定时间提醒
let alarm = EKAlarm(absoluteDate: someDate)
比如不管会议几点开,都在早上 9 点提醒下。
地点提醒
这个功能挺酷地,走到某个地方自动提醒:
let geoAlarm = EKAlarm()
geoAlarm.proximity = .enter
设置有点复杂,不过实现后挺有用得。走到公司门口就提醒你今天有啥安排。
在 SwiftUI 里用起来也很简单:
struct CalendarView: View {
    @Stateprivatevar permissionManager = CalendarPermissionManager()
    @Stateprivatevar showingAlert = false
    
    var body: some View {
        VStack {
            Text("日历权限: \(statusText)")
            
            Button("创建提醒") {
                Task {
                    if await requestPermissionIfNeeded() {
                        // 创建日历事件
                    } else {
                        showingAlert = true
                    }
                }
            }
        }
        .alert("需要日历权限", isPresented: $showingAlert) {
            Button("去设置") {
                // 跳转到设置页面
                iflet settingsUrl = URL(string: UIApplication.openSettingsURLString) {
                    UIApplication.shared.open(settingsUrl)
                }
            }
            Button("取消", role: .cancel) { }
        }
        .onAppear {
            permissionManager.checkCurrentStatus()
        }
    }
    
    privatevar statusText: String {
        switch permissionManager.calendarStatus {
        case .granted: return"已授权 ✅"
        case .denied: return"被拒绝 ❌"
        case .notDetermined: return"未决定 ⏳"
        }
    }
    
    privatefunc requestPermissionIfNeeded() async -> Bool {
        if permissionManager.calendarStatus == .granted {
            returntrue
        }
        return await permissionManager.requestCalendarAccess()
    }
}
写在最后
说实话这个功能还挺实用,集成到 App 里比推送自然多了。用户也习惯在日历里管理时间。就是适配有点烦,不过也没办法,苹果 API 年年改。你在项目中用过吗?感觉怎么样?
用户评论