それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

リマインダ(Reminders.app)をターミナルから使うためにEventKitを使う(2)

リマインダのアイテムを一覧する。

前回で、リマインダのアイテムを追加することが出来たので、次はリマインダのアイテムを取得してみる。

リマインダのアイテム一覧取得は「検索」することになる、ちょっと考えることが多い。

リマインダの検索はNSPredicateを使ってアクセスすることになる。

リマインダの全アイテムを列挙する

//
//  main.m
//  reminders
//
//  Created by takuya on 20150619.
//  Copyright (c) 2015年 takuya. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <EventKit/EventKit.h>
int main(int argc, const char * argv[]) {
  @autoreleasepool {
    EKEventStore *eventStore = [[EKEventStore alloc] init];
    __block BOOL busy = YES;
    __block NSArray *reminders;
    NSPredicate *predicate = [eventStore predicateForRemindersInCalendars:nil];
    [eventStore fetchRemindersMatchingPredicate:predicate completion:^(NSArray *list) {
      reminders = list;
      busy = NO;
    }];
    while( busy ){ sleep(0.1);}
    for( EKReminder *reminder in reminders){
      printf("%s\n", [reminder.title UTF8String]);
    }

  }
    return 0;
}

やっていることは、次の通り

  • 検索用にNSPredicateを作成
  • NSPredicate をつかって一覧取得
  • NSPredicate だとコールバックになるので Waitを入れる。
  • リマインダ・アイテムを配列に入れてループ

追加部分のコードについて

NSPredicate *predicate = [eventStore predicateForRemindersInCalendars:nil];

NSPredicate を作る。NSPredicate を作成するpredicateForRemindersInCalendarsメソッドをコール。predicateForRemindersInCalendarsは引数に nil を指定するとなにも検索条件を指定しない、つまり全件を取得することになる。

 [eventStore fetchRemindersMatchingPredicate:predicate completion:^(NSArray *list) {}];

fetchRemindersMatchingPredicate で Predicateを使った検索をする。検索結果はコールバック用の関数を指定することになる。ここで関数内はブロックが変わり、変数スコープが変わる。したがって変数スコープを越えて変数にアクセスするために__block をつけて宣言する。

またここで指定した関数は ansync で呼ばれる、変数スコープの他にスレッドを意識した処理が必要。

__block NSArray *reminders;

変数 *remindersに検索を結果を入れるのだけど、スコープを超えなくちゃいけないため__blockをつけている。

また、ループが終わるのをWaitingしたいので、フラグを使った。

    __block BOOL busy = YES;

GUIを持つアプリケーションであればGUIを更新するので、このような処理は不要だと思うし、スレッドをきっちり扱えばいいんだろうけど。とりあえず動くシンプルなものを重視して、単純にブロックない処理でメインをブロッキングする用に書いた。

    while( busy ){ sleep(0.1);}

検索中ならそこでsleepして待つ

リマインダ・リストの一覧

リマインダはカレンダリスト毎に管理される。このリマインダ・リストのリストを取得したり指定する事も必要になる。だからリマインダのリストを一覧するように書いた。

//
//  main.m
//  reminders
//
//  Created by takuya on 20150619.
//  Copyright (c) 2015年 takuya. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <EventKit/EventKit.h>
int main(int argc, const char * argv[]) {
  @autoreleasepool {
    EKEventStore *eventStore = [[EKEventStore alloc] init];
    NSArray *calendars = [eventStore calendarsForEntityType:EKEntityTypeReminder];
    for (EKCalendar *calendar in calendars)
    {
      printf("%s\n", [calendar.title UTF8String]);
    }
  }
    return 0;
}

ここは、特に問題なく取得できる。

[eventStore calendarsForEntityType:EKEntityTypeReminder];

calendarsForEntityType で取得する。このときリマインダの絞るためにEKEntityTypeReminderを指定する。

指定したリマインダ・リストの中身を列挙する

ここまでで、リマインダ・アイテム検索して一覧する、リマインダ・リストを一覧する方法がわかったので、この2つを組み合わせて戦うことにする。

//
//  main.m
//  reminders
//
//  Created by takuya on 20150619.
//  Copyright (c) 2015年 takuya. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <EventKit/EventKit.h>
int main(int argc, const char * argv[]) {
  @autoreleasepool {
      //指定したリマインダのTODOを一覧する。
    NSString *name = @"お買い物";
    EKEventStore *eventStore = [[EKEventStore alloc] init];
    EKCalendar *cal = [eventStore defaultCalendarForNewReminders];
    NSArray *calendars = [eventStore calendarsForEntityType:EKEntityTypeReminder];

    for (EKCalendar *calendar in calendars)
    {
      if ([calendar.title isEqualToString:name]) {
        printf("「%s」のカレンダを発見\n", [calendar.title UTF8String]);
        cal = calendar;
      }
    }
    // アイテムを検索
    __block BOOL busy = YES;
    __block NSArray *reminders;
    NSPredicate *predicate = [eventStore predicateForRemindersInCalendars:[NSArray arrayWithObject:cal]];
    [eventStore fetchRemindersMatchingPredicate:predicate completion:^(NSArray *list) {
      reminders = list;
      busy = NO;
    }];
    while( busy ){ sleep(0.1);}
    for( EKReminder *reminder in reminders){
      printf("%s\n", [reminder.title UTF8String]);
    }
    return 0;
  }
}

やっていることは、単純で、

      if ([calendar.title isEqualToString:name]) {
        printf("「%s」のカレンダを発見\n", [calendar.title UTF8String]);
        cal = calendar;
      }

if 文を使い指定のリマインダ・リストを名前でマッチングして見つけたものをcal変数に格納

リマインダ・アイテムの検索をするPredicate作成時に、cal 変数を検索条件として指定する。

NSPredicate *predicate = [eventStore predicateForRemindersInCalendars:[NSArray arrayWithObject:cal]];

この部分の検索条件として配列を指定する箇所に

[NSArray arrayWithObject:cal]

として、取っておいたcal変数を指定してある。

リマインダ・アイテムの中身を見る。

リマインダ・リスト ➝ リマインダ・アイテム ➝ アイテム詳細 の順に辿れたので、リマインダのアイテム詳細をもう少し見ておくことにする。

    for( EKReminder *reminder in reminders){
      printf("%s\n", [reminder.title UTF8String]);
      printf("追加日 %s \n", [[NSDateFormatter
                            localizedStringFromDate:reminder.lastModifiedDate dateStyle:NSDateFormatterFullStyle timeStyle:NSDateFormatterShortStyle
                            ] UTF8String]);
      printf("場所 %s\n", [reminder.location UTF8String]);
      printf("URL %s\n", [[reminder.URL absoluteString] UTF8String]);
      printf("完了 %d\n", reminder.completed);
      printf("識別子(EX) %s\n", [reminder.calendarItemExternalIdentifier UTF8String]);
      printf("識別子(local) %s\n", [reminder.calendarItemIdentifier UTF8String]);
      printf("パーマリンク(local) x-apple-reminder://%s\n", [reminder.calendarItemIdentifier UTF8String]);
    }

コンソールアプリケーションで作成したためprintfが並んでいるので面倒だが、実行すると

「お買い物」のカレンダを発見
牛乳買いに行くのを忘れないでね
追加日 2015-06-19(金) 15:31 
場所 (null)
URL (null)
完了 0
識別子(EX) FFFFFFFF-FFFF-4B50-A567-A0EB22DB5DE1
識別子(local) FFFFFFFF-FFFF-437D-8FF8-AA08873184AC
パーマリンク(local) x-apple-reminder://FFFFFFFF-FFFF-437D-8FF8-AA08873184AC

このような結果を得ることが出来る。

ここで、パーマリンクと書いている部分は

takuya@rena:~/Desktop$ open x-apple-reminder://FFFFFFFF-FFFF-DDDD-8FF8-AA08873184AC

のようにすると、open で直接リマインダ・アイテムを開くことが出来てとても便利になる魔法のリンクである。

f:id:takuya_1st:20150619153318p:plain

識別子があることで別のアプリケーションと連携を取りやすくなって嬉しい。

一覧ができた。

ここまでで

  • リマインダ・アイテムの検索結果一覧
  • リマインダ・リストの一覧
  • リマインダ・リスト中のアイテム一覧
  • リマインダ・アイテムのプロパティを閲覧

とREAD系のアレコレをガッツリ扱えるようになった。

次はリマインダの削除

次はリマインダ削除をやろうと思う。

長くなったので、別エントリに書きます。

⇐前(1) | まとめ⇒次(3)