それマグで!

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

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

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

リマインダのデータ更新のイベントをハンドリングする

リマインダはEventStoreに保存されており、EventStoreは自身のデータが更新された時に、更新を通知する仕組みを備えている。NSNotificationCenterを使った通知と起動をする。

NSNotificationCenter の仕組み

GOFデザインパターンのObserverパターンを利用している。

と、一言で言えば大丈夫そう。

  • NSNotificationCenterに自分を登録しておく。(◯◯イベントが起きたら教えて)
  • 誰かがNSNotificationCenterにイベント発生を報告する(◯◯イベントが起きました)
  • NSNotificationCenterが登録した皆に通知する(◯◯イベントが起きたので通知するわ)

具体的には、NSNotificationCenterに イベントの種類+コールバック関数リファレンス のセットを登録して待っておく。

コンソールアプリケーションからNSNotificationCenterを使うには

そもそも通知待ちってイベントドリブンだし、イベント通知受信にはスレッド制御が移される必要があるので、専用のループで待つ必要がある。

while( [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]){
 //なにもしない
 ;
}

いわば、win32api のメインループみたいなもんと思うことにする。

 //win32 dispacthMessage
 while (GetMessage(&msg , NULL , 0 , 0)) DispatchMessage(&msg);

コンソールアプリケーションでイベント通知待ちってあまりしないし、イベント通知で表示を更新するってGUI処理だしな。コマンドラインではあまり用はないんだけど、Daemonで待ち受けしておくのは楽しそう。

EventStoreの更新通知で一覧を取得する。

main 側には、メインループと通知登録するクラスの初期化処理だけを書くことにした。

//
//  main.m
//  reminder_test
//
//  Created by takuya on 20150611.
//  Copyright (c) 2015年 takuya. All rights reserved.
//
#import "MyClass.h"

int main(int argc, const char * argv[]) {
  @autoreleasepool {
    MyClass *my = [[MyClass alloc]init];
    [my add];
    int count =0;;
    while(!my.terminated && [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]){
              printf("called  %d times \n", count++);
      ;
    }
  }
    return 0;
}

更新通知を受け取るクラスMyClassを作る。

MyClassには、

  • メインループ離脱用のフラグ
  • EvnetStore へのリファレンス
  • NSNotificationCenterに登録するメソッド
  • NSNotificationCenterから通知を受け取るメソッド

の4つを定義している。

とくに、EventStoreへのリファレンスは大事、コレを忘れると参照がdisalloc されちゃって通知を受け取る元がなくなる。EventStoreへの参照がなくなると通知が受け取れなくなる。NSNotificationCenterへの参照は特に保存しなくてもいいみたい。

//
//  MyClass.h
//  reminder_test
//
//  Created by takuya on 20150617.
//  Copyright (c) 2015年 takuya. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <EventKit/EventKit.h>

@interface MyClass : NSObject

@property (strong, nonatomic) EKEventStore *eventStore;
@property (nonatomic) BOOL terminated=NO;

-(void)recieve:(NSNotificationCenter *)nc;
-(void)add;
@end

MyClassのメソッド実装はこんな感じ。

//
//  MyClass.m
//  reminder_test
//
//  Created by takuya on 20150617.
//  Copyright (c) 2015年 takuya. All rights reserved.
//

#import "MyClass.h"
#import "MyReminderAccess.h"
#import <EventKit/EventKit.h>


@implementation MyClass
-(void)recieve:(NSNotificationCenter *)nc{
  @autoreleasepool {
  printf("recieved\n");
    //[MyReminderAccess list_of_remidners_in_calendar_with_name:@"inbox"];
  }
}
-(id)init {
  @autoreleasepool{
    self.eventStore = [[EKEventStore alloc] init];
    self.terminated=NO;
    printf("init\n");
    return self;
  }
}
-(void)add{
  printf("add Observer\n");
  NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
  [center addObserver:self selector:@selector(recieve:) name:EKEventStoreChangedNotification object:self.eventStore];
}
@end
  • init 初期化処理で、EventStore参照を取得し保存
  • add は通知を登録する処理
  • recieve は通知を受け取る。

通知を受け取るように、登録処理は個々の2行になる。

 NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
[center addObserver:self selector:@selector(recieve:) name:EKEventStoreChangedNotification object:self.eventStore];
  • NSNotificationCenterへの参照を作る
  • @selector(メソッド名)で関数を渡す
  • EKEventStoreChangedNotificationという型の通知を登録する
  • 監視対象は self.eventStore で参照を保持しているEventStore

あとはコレをビルドすると・・・

メインループでブロッキングされて、イベント通知待ちになる。

そこで、Reminders.appからリマインダを作成削除するとイベント通知がもらえる。

イベント通知がもらえたところ

f:id:takuya_1st:20150619163708p:plain

ただし、イベント通知は大量に発生するのである程度絞り込んだほうがイイ。

  • EventStoreの変更
  • Itemの削除
  • Itemの新規作成
  • Itemの編集
  • Itemの保存

などあらゆる通知が区別なく飛んでくる・・・

Posted whenever changes are made to the Calendar database, including adding, removing, and changing events or reminders. Individual changes are not described. When you receive this notification, you should refetch all EKEvent and EKReminder objects you have accessed, as they are considered stale. If you are actively editing an event and do not wish to refetch it unless it is absolutely necessary to do so, you can call the refresh method on it. If the method returns YES, you do not need to refetch the event.

AppleのDocumentにもそう書いてある。もし更新(削除)だけがほしい時は通知のrefresh==YESの時だけに絞れとのこと。

私はターミナルから使えればイイので、ここはコレ以上調べないことにしました。

ですが、どうやるかご存じの方がいれば教えて下さい。

以上でリマインダを扱える。

ここまででリマインダを自由に扱えるようになった。

あとはコレを利用してRaisのApplicationやRedmineEvernoteのTODOリストなどと組みわせて戦うことにする。

感想:リマインダはicloud経由で同期される

iCloud経由で同期されるので、同期関連をプログラミングしなくていいし。データ保存を気にしなくていいし、サーバーのリソースも暗号化も気にしなくてイイ。

iCloud同期がファイル管理や保存通信の代替処理に使う。このようなプログラミングがこれからの主流になるだろうと思っています。だから、リマインダをベースに触っておいた。

GoogleApp EngineやHerokuよりも、このリマインダ・プログラミングようなのが使いやすくしばらく主流になると思う。サーバでホスティングするよりApp側で自動同期されるストレージに放り込むのが楽。つまりiCloudの自動保存・同期の処理をメインに使う方が圧倒的に楽。

また、リマインダはURL・HTMLノート・日付処理・一覧処理といったプログラミングの基本的要素が多分に含まれるので学習には最適かもしれない。データを扱うにしてもReminders.appという完成したインターフェースが存在するのでデータを変更・削除・追加を手軽に確認することが出来てこれもまた学習には向いているかもしれない。

⇐前(3) | まとめ