EventKit을 사용해 디바이스 내 캘린더 접근하기

오늘은 앱에서 디바이스 내 캘린더에 접근하고 일정을 확인 혹은 추가 , 삭제할 수 있는 방법에 대해서 알아보려고 한다.

 

먼저 그렇게 하기 위해서는 EventKit을 사용해야하는데 이 EventKit 프레임워크는 사람들이 앱에서 캘린더 항목을 만들고, 검색, 편집할 수 있도록 데이터에 대한 엑세스를 제공한다.

 

내가 어떤 앱을 만들었을때 캘린더 관련 기능을 사용하면서 디바이스 내 캘린더도 동시에 관리할 수 있게 되는 아주 아주 유용한 방식이니 한번 알아보자!

 

먼저 당연하게 이용자의 캘린더에 접근할 수 있는 권한이 필요하다. 그렇기 때문에 infoPlist로 들어가 다음과 같은 설정을 넣어주면 된다!

 

  • Key : NSCalendarsUsageDescription
  • Value : “앱에서 캘린더에 접근해 이벤트를 관리합니다”


이렇게 값을 넣어 주게 되면 해당 권한이 실행되는 시점에 이용자에게 캘린더 접근 권한에 대해 물어보고 권한을 설정할 수 있도록 설정이 완료된다.

 

그 다음엔 Manager를 구성해주면 된다. EventKit은 EKEventStore에 접근해 사용해야하기 때문에 해당 내용을 설정하고 권한 값과 가져온 이벤트 데이터를 담아줄 수 있는 인스턴스를 구성한다.

private let eventStore = EKEventStore()

@Published var events: [EKEvent] = []
@Published var authorizationStatus: EKAuthorizationStatus = .notDetermined

해당 내용을 설정한 다음 권한 설정을 요청하기 위한 메서드를 구현한다.

func checkCalendarAuthorizationStatus() {
    authorizationStatus = EKEventStore.authorizationStatus(for: .event)
    
    switch authorizationStatus {
    case .notDetermined:
        requestAccessToCalendar() // 아직 권한을 요청하지 않았으므로 권한 요청
    case .authorized:
        fetchEvents() // 이미 권한이 있으므로 이벤트 가져오기
    case .denied, .restricted:
        print("캘린더 접근 권한 거부")
    @unknown default:
        break
    }
}

권한이 설정되지 않았을 때 권한을 요청하고 권한이 승인이 된다면 바로 데이터를 가져올 수 있도록 requestAccessToCalendar를 구현해주면 된다.

func requestAccessToCalendar() {
    eventStore.requestAccess(to: .event) { [weak self] granted, error in
        if granted {
            DispatchQueue.main.async {
                self?.fetchEvents() // 권한이 부여되었으므로 이벤트 가져오기
                self?.authorizationStatus = .authorized
            }
        } else {
            DispatchQueue.main.async {
                self?.authorizationStatus = .denied // 권한이 거부됨
            }
        }
    }
}

이렇게 구현하면 EventKit이 구현되는 곳에서 권한 설정을 물어보는 팝업을 띄우고 권한이 주어지면 바로 Event를 페치해올 수 있도록 한다.

 

권한이 설정되지 않으면 당연히 페치가 되지 않기 때문에 추가적으로 권한을 설정할 수 있도록 유도하는 로직을 구현하는게 좋다.

 

이제 권한 설정도 됐고 이벤트 데이터를 페치하면 되니까 해당 로직을 구현해보자

func fetchEvents() {
    let startDate = Date() // 현재 날짜
    if let endDate = Calendar.current.date(byAdding: .month, value: 1, to: startDate) { // 1달 뒤 날짜 설정
        let predicate = eventStore.predicateForEvents(withStart: startDate, end: endDate, calendars: nil)
        let events = eventStore.events(matching: predicate)
        
        DispatchQueue.main.async {
            self.events = events // 이벤트 리스트를 events 프로퍼티에 저장
        }
    } else {
        print("유효한 종료 날짜가 없습니다.")
    }
}

이벤트 데이터를 가져올 날짜의 시작 날짜와 종료 날짜를 선택한 뒤 특정 캘린더만 가져오던지 혹은 모두 가져오던지 설정할 수 있다 모든 데이터를 가져오려면 calendar: nil로 설정해 가져온다.

그 다음 eventStore에 접근해 데이터를 가져와 넣어주면 구현이 완료된다.

 

여기까지 캘린더의 데이터를 가져오는 방식이 완료되었고 이제 데이터를 가져오는 건 구현했으니 캘린더에 이벤트를 추가하는 로직을 구현해보자

func addEvent(title: String, startDate: Date, endDate: Date) {
    let event = EKEvent(eventStore: eventStore)
    event.title = title // 이벤트 제목 설정
    event.startDate = startDate // 이벤트 시작 시간
    event.endDate = endDate // 이벤트 종료 시간
    event.calendar = eventStore.defaultCalendarForNewEvents // 새 이벤트가 추가될 기본 캘린더
    
    do {
        try eventStore.save(event, span: .thisEvent) // 이벤트 저장
        fetchEvents() // 저장 후 최신 이벤트 리스트 다시 가져오기
    } catch {
        print("이벤트 저장 오류")
    }
}

EKEvent는 캘린더의 이벤트를 나타내는 클래스로 이 클래스의 속성인 (eventStore: )를 활용해 새로운 이벤트를 캘린더에 추가할 수 있다!

event의 제목 시작, 종료 등 일정의 정보를 넣어주고 defaultCalendarForNewEvents 를 설정해 새로운 이벤트가 설정될 기본 캘린더를 설정해준다. 그 이후 save(event, span: .tihsEvent를 활용해 캘린더에 해당 값을 저장하면 캘린더에서 추가하지 않아도 자동으로 일정이 추가되어있다!

 

다른 앱에서 캘린더 앱을 컨트롤할 수 있다는게 너무 신기하고 잘 활용하면 일정을 캘린더에서 추가하는게 아닌 인앱에서 해결할 수 있다는 점이 은근 사용할만한 기능이라고 생각했다.

 

오늘은 EvnetKit에 대해 알아보았다!!