読者です 読者をやめる 読者になる 読者になる

addObserver に UserDefaults を登録できる

プログラムを作るときに KVO って便利なんだけど、監視対象にするオブジェクトとして、UserDefaults を指定できることを知らなかった・・・

随分遠回りな実装になってしまったところでできることに気がついて、その後、余分なコードを消し消ししてそっちの方が大変だった。

実装は次のような感じ。

    defaults = [NSUserDefaults standardUserDefaults];    

    [defaults addObserver:self forKeyPath:@"fireTime"

                  options:( NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld )

                  context:nil];

便利、便利。

UserNotifications について

時計とアラームをアプリに組み込んでいて、時間が来たら Alerm を出すわけですが、以前は、Notification とか使っていたんですけど、いつの間にか、UNNotificationCenter なる Framework が追加になっていた。

Swift ベースの記述が多くて難儀しましたが、いろいろ調べたところ、今までよりも格段に使いやすくなっていることが分かりました。後学のため、組み込み方をメモします。

以下のような記述が必要ですが、ひとまずは Appdelegage.m の applicationDidFinishLaunchingWithOptions に記載。

    //set NotificationCenter instance

    UNUserNotificationCenter* notificationCenter = [UNUserNotificationCenter currentNotificationCenter];

    

    // set delegate

    notificationCenter.delegate = self;

    

    //setting type of notification

    // use Sound and Alert

    [notificationCenter requestAuthorizationWithOptions:UNAuthorizationOptionSound|UNAuthorizationOptionAlert

                                      completionHandler:^( BOOL granted, NSError * _Nullable error )

     {

         // none

     }];

 

 

フォアグラウンドで通知を受けた場合の処理は delegate method が呼ばれる。

該当するクラスに、delegate を設定し、.delegate = self; を忘れずに、以下のメソッドを記入。

 

 

- (void) userNotificationCenter:(UNUserNotificationCenter *)center

        willPresentNotification:(UNNotification *)notification

          withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler

{

 

    // アプリがフォアグランドにいた場合の通知動作を指定する。

    completionHandler(UNNotificationPresentationOptionBadge |

                      UNNotificationPresentationOptionSound |

                      UNNotificationPresentationOptionAlert);

}

 

はまってしまったのは、AppDelegate.m に記載しなければならないのかと思ったのですが、普通に delegate method だったので、delegate の宣言をして、呼んで欲しいクラスに書けば OK。

 

ボタンを押したら SpriteKit に切り替えるには・・・

アプリを作成中なんですが、今のままの機能だけでは reject 必至だろうということで、ミニゲームの機能も持たせることにしました。アプリの画面にボタンを用意して、ボタンを押したら spritekit の画面に切り替えようとしたところエラー発生・・・

呼び出し元のボタンでは、以下のように呼びます。

   [self presentViewController:gameViewController animated:YES completion:nil];

呼ばれた ViewController は spritekit の Scene を呼ぶわけですが、この ViewController の実態は UIViewController。テンプレまんまで、以下の通り。

SKView* skView = (SKView *)self.view;

これではダメで、以下の通りでようやくうまくいきました。

    

    CGRect applicationFrame = [[UIScreen mainScreen] bounds];

    SKView *skView = [[SKView alloc] initWithFrame:applicationFrame];

    self.view = skView;

 

実は以前もトライして、うまくいかなかったんですが、今にしてようやくなるほどと。

Xcode 8 では CoreData 用のファイルは automatic になったんだって

仕事でアプリを作る必要があり、CoreData も使おうと。ようやく作り方にも慣れてきたところで、まずはモデリングして、次に、Editor -> Create NSManagedObject Subclassess... を選んでファイルを生成してと、慣れた手順でファイルを作ったら、build でエラー発生・・・

エラー内容は、以下の通り。

 

 

Showing All Messages

ld: 4 duplicate symbols for architecture x86_64

 

Showing All Messages

clang: error: linker command failed with exit code 1 (use -v to see invocation)

 

ナンジャコリャ。重複しているファイルがあるので、linker がエラーこきました、とのこと。しっかりしろよ、clang。

調べてみたら、このエラーは必要なライブラリを指定していない時に出るよとのことで、あぁ、そう言えば CoreData を linked framework に指定していなかったなぁ、ってか、そんなこと最近しなくてもよくなった気がしていたんだけど・・と思って、指定してもエラー解消せず・・・

最近 Xcode がアップデートしたので、ひょっとして Xcode の bug じゃないのか?と思って、さらに調べたところ、Xcode 8 から CoreData 用にモデリングしたファイルは自動生成されるようになったんで、Subclassess で生成してしまうと duplicate error が出てしまうと。

自動生成するかの指定は、.xcdatamodeld を選択して、Show the file Inspector の Tools

 Version を見るとわかります。Automatic (Xcode 8.0) と書いてあるので、自動生成だと。

全然知りませんでした。Release Note をよく読めってことですかね・・・

でも、便利な機能であることは間違いないなと思いました。これによって、アプリのバージョンアップの際、CoreData のモデルに変更があるとデータが失われてしまうので、migration 必要とかも解消するのかなぁ・・・

 

LM5102 と LM4102 の比較

Lee の 102 が好きで、いつもこれなんですが、数年前に買ったものが、ついに、お尻の辺りが薄くなってきて、このままではまずいなと。

アメ横の 610 のセールはもうすぐだった気がするけれども、待てそうにないので買うことにしました。おまけで、Lee のマグカップもらえて嬉しかったです。

今売っている 102 は、細かくいうと LM05102 というモデル。数年前に買ったのは LM4102。数字からしてアップデートされているのですが、LM04102 のお尻の生地が破れてしまう前に、両モデルを重ねてスタイルを比較してみました。

重ねてみると、ほとんど変わりません。膝の絞り込み、裾の広がり具合、レングス、同じです。宣伝見ると、生地が厚くなったのか?とも思っていましたが、それも変わりません。

大きく違うのは股上の長さ。LM05102 の方が断然長く、チャックの長さも 2cm 以上長いです。これは、履き心地にも大きな違いとして感じられます。履きやすさでいうと断然 LM5102 ですが、おそらく、履いた姿がカッコよく見えるのは LM04102 でしょうね。

あと、宣伝で見て知っていたのですが、ベルトループが 5 → 7 本に増えました。効果のほどは、んーー、ズボンに入れたシャツが出にくくなる?かもしれませんね、まだわかりません。

そのほか、Lee のレザータグがちょっと変わってます。色が少し濃くなってます。これは正直 LM04102 の方が良かったかな。ベルトするときは、中に通さないので、誰にも見えない部分ですから、実はどうでもよいことなのですが・・・また、見えないことですが、ジッパーのつまみの形状が変わりましたね。今までは真四角でしたが角がカットしてあり、全体的に大きくなっています。使い勝手がよくなった感じです。

ってことで、しばらく履き込んでみます。

CloudKit にマスターデータを登録するには・・・

CloudKit にマスターデータを登録するにはどうするのだろう。Dashboard をいくら探してもファイルをアップロードする仕組みはない。色々調べてみても、その辺りについて書いてある記述もない。まさかの、その仕組みナシ・・・という気がしてならない。

日本で観察できる野鳥 600 種類ぐらいなのでそれほど多くないこともあり、CoreData に登録済みのデータを for 文で回しながら一件ずつ CloudKIt に登録。すると、一件も登録されない。for 文の最後で break かけると 1 件だけ登録された。どうやら制限があって、あんまり高速にアップロードを繰り返すとキャンセルされるようだ。

ウエイトの掛け方:

        [NSThread sleepForTimeInterval:0.5f];

強制的にスリープを送りながら実行するとうまくいきました。ってか、600 件もあるので、5 分ぐらいかかるんですが・・・一回だけなので、ま、許す・・・

CloudKit: N から 1 のレコードを参照してみました

CloudKit で relation の定義の仕方がわかったので、今度は、N のテーブルから 1 のテーブルを参照する方法を試してみました。

Apple 謹製のドキュメントはすごく素性がよくて(当たり前?)、その通りに作ればその通りに動きました。ただ、これ、実装するとなるとかなりキツイね。理由は手順を追うとわかります。

  1. まず、N テーブルのレコードを query して recordID を特定。
  2. 次に、そのレコードに定義してある Referene 型のフィールドの値を得る。
  3. それを手掛かりとして、1 のレコードが定まるので、フィールドの値を得る。

正直、CloudKit 使わないで、自 server に仕組みを素で書くのと、手間は大して変わりません。以前 4D 使ってプログラム書いている時とは雲泥の差。なんか、PythonCGI としてプログラムするのと大差ない。いや、SQLite3 なら、フィールドに色々制約をつけられるので、恩恵を受けられるけれども、CloudKit はそんな仕組みがないので、その面では厄介か。なんでこんなこと言うかといえば、CoreData の素晴らしさはどこ行ったんですか?と言うことを確認したい。

で、さらに、サーバーとやりとりする都合、1 で block を使い、さらに、3 のために block を使うという、block の中に block を書く形になるので、変態的な code になってしまう関係上、こんな仕組みやめて自 server で書いちゃった方がむしろ判読性?可読性?メンテナンス性?は高いんじゃないかと思いました。

と言いつつも、自 server を建てるとなると、それなりに費用がかかりますので、そんなお金はない当方としては、これを使うしかないなぁと。

あと、まだよくわかんないけど、簡単に重複したレコードを作れちゃうのは、そうならないような配慮も自分でしないとならないからなのでしょうか。RDB なら普通にある機能なんだけど、自分で実装するとなると・・・きついね。

ふぅぅぅぅ。

 

さて、次は、マスターにデータを一括して登録するにはどうするのか・・・ファイルアップロードして一括登録したかったんだけど、仕組みが・・・ない? アプリから作る感じなのかなぁ・・・