2014년 2월 27일 목요일

[iOS] 캘린더 일정 이벤트 등록하기 EventKit 예제

앱에서 캘린더(Calender)나 미리알림(Reminder) 등에 이벤트를 등록 할 때는 EventKit 프레임워크를 이용 할 수 있다. 이 포스트는 캘린더용 일정을 앱에서 등록하는 방법에 대한 예제이다.

우선 프로젝트 타겟 설정에 EventKit.framework를 추가해주자. 최신 버전의 Xcode 에는 자동으로 프레임워크를 추가하는 기능이 있으니 별로 신경쓰지 않아도 된다.

이벤트 킷을 이용하기 위해 헤더 파일을 추가한다.
#import <EventKit/EventKit.h>
이제 이벤트스토어를 생성한다.
EKEventStore *eventStore = [[EKEventStore alloc] init];
이벤트스토어는 한번 만들어 두고 재활용해도 된다.

이제 실제로 이벤트를 등록하는 방법이다.
[eventStore requestAccessToEntityType:EKEntityTypeEvent
                           completion:^(BOOL granted, NSError *error) {
    if (granted == NO) {
        NSLog(@"Failed to grant");
        return;
    }

    EKEvent *e = [EKEvent eventWithEventStore:eventStore];
    e.title = @"Calendar Event";
    e.startDate = [NSDate date];
    e.endDate = [NSDate date];
    e.calendar = [eventStore defaultCalendarForNewEvents];

    NSError *err = nil;
    [eventStore saveEvent:e
                    span:EKSpanThisEvent
                  commit:YES
                   error:&err];

    if (err) {
       NSLog(@"Failed to add calendar: %@", [err description]);
       return;
    }
}];
이 코드가 실행되면 최초에 한번 사용자에게 캘린더 엑세스를 허용 할 것인지 팝업으로 물어온다. 즉 여기서 허용하지 않으면 completion 블럭의 granted에 NO가 들어와서 에러처리를 해야한다. 정상적이라면 granted 값이 참(YES, TRUE 등)이 들어올 것이다.

EKEvent 는 캘린더 이벤트 아이템 클래스이다 이걸 이용해서 타이틀과 시작 및 끝 날짜를 정해 줄 수 있다. 상세한 내용은 EKEvent의 Documentation을 살펴보자.

위의 것 만으로도 사실상 이벤트를 등록하는 것은 끝난다. 실제로 위 코드가 실행되고 나면 아래 스크린샷 처럼 캘린더 앱에 이벤트가 등록된 것을 확인 할 수 있다.



하지만 좀 심심하기에 이번엔 이벤트 편집창을 띄워보도록 해 보자. 이번엔 EventKitUI 라는 프레임워크가 필요하다. EventKitUI.framework를 추가하고 아래 헤더 파일을 임포트하자.
#import <EventKitUI/EventKitUI.h>
위의 등록하는 코드에서 일부를 수정한다.
[eventStore requestAccessToEntityType:EKEntityTypeEvent
                           completion:^(BOOL granted, NSError *error) {
    if (granted == NO) {
        NSLog(@"Failed to grant");
        return;
    }

    EKEvent *e = [EKEvent eventWithEventStore:eventStore];
    e.title = @"Calendar Event";
    e.startDate = [NSDate date];
    e.endDate = [NSDate date];
    e.calendar = [eventStore defaultCalendarForNewEvents];

    dispatch_async(dispatch_get_main_queue(), ^{
       EKEventEditViewController *controller = [[EKEventEditViewController alloc] init];
       controller.event = e;
       controller.eventStore = eventStore;
       controller.editViewDelegate = self;

       [self presentViewController:controller animated:YES completion:nil];
    });

}];
바뀐 내용은 이벤트스토어 객체를 이용해 saveEvent 를 호출하는 부분이 사라지고 대신 EKEventEditViewController 를 띄우는 코드가 들어갔다는 점이다. 그리고 여기에 앞에서 생성한 EKEvent 객체를 넣어준 것이다.

그런데 여기서 (굵은 글자로 표기한 부분에) dispatch_async를 통해 메인스레드에서 이벤트 에디트 뷰 컨트롤러를 띄우고 있다. dispatch_async가 여기서 꼭 필요한 코드는 아니다. 다만 completion 블럭이 백그라운드에서 불리는 것으로 예상되어서 만약 이렇게 하지 않으면 상황에 따라 UI가 제대로 업데이트 되지 않거나 얼어버리는(freezing) 경우도 발생 할 수도 있어서 일부러 이렇게 예제를 만들었다. 이 뷰 컨트롤러를 띄우는 곳이 메인스레드에 해당한다면 dispatch_async가 굳이 필요하진 않다.

코드를 보면 알겠지만 EKEventEditViewController 에 delegate를 self로 할당했다. 따라서 EKEventEditViewDelegate 라는 프로토콜을 인터페이스에 정의해 두고 프로토콜을 구현해야한다.
- (void)eventEditViewController:(EKEventEditViewController *)controller
          didCompleteWithAction:(EKEventEditViewAction)action
{
    [self dismissViewControllerAnimated:YES completion:nil];
}
창을 닫아주는 역활만 하는 코드 예제이다. 필요하다면 action 을 이용해 몇 가지 처리를 할 수 있을 것이다.
이렇게 해서 실행시키면 아래와 같은 이벤트 등록 창이 뜬다.


개인적으론 이렇게 사용자에게 어필해 주는 스타일이 좋다고 생각된다.

여기서는 캘린더 일정 이벤트 만으로 줄이지만 미리알림(Reminder)에 등록하기 위해서는 EKReminder와 관련된 부분을 문서에서 찾아보자. 비슷하기 때문에 쉽게 구현이 가능하다.

댓글 없음 :