Objective-C で日付の比較をしようとしたところ、どうも、うまくいかない。
if ( now > item.date )
こういうのはだめ。
なんで?と調べて見たところ、compare 使えと。
[now compare:item.date] --> 1 ............ now の方が最近
[now compare:item.date] --> -1 ........... now の方が古い
isEqual といい、入念に用意してあるね。
Objective-C で日付の比較をしようとしたところ、どうも、うまくいかない。
if ( now > item.date )
こういうのはだめ。
なんで?と調べて見たところ、compare 使えと。
[now compare:item.date] --> 1 ............ now の方が最近
[now compare:item.date] --> -1 ........... now の方が古い
isEqual といい、入念に用意してあるね。
時刻を設定すると、その時刻になるとアラームを出すアプリを作成中。こういった処理は、UNNotification を使って作成するのが iOS 10 からの流れのようで、UINotification の時代から大して慣れていない中、やっとこさっとこ意味のある機能を作ることができました。
端末が通知を許可しているか、許可していないかを調べる方法がわからなくて、色々試行錯誤してようやくわかりました。そもそもなんで許可しているかどうかを知りたかったかと言えば、タイマーをセットしようとして、時間が来たらアラートを出すのですが、端末が許可されていないと、fire しても発火しない(アラートが出ない)からです。
具体的な方法は、AppDelegate.m に以下のメソッドを定義します。
- (void) isNotificationsAuthorization:(void (^)(BOOL isActive)) handler
{
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings* _Nonnull settings)
{
//settings.alertStyle == UNAlertStyleNone;
NSLog(@"settngs: %ld", settings.authorizationStatus);
if ( settings.authorizationStatus == UNAuthorizationStatusAuthorized )
{
// NSLog(@"Authorized");
handler(YES);
}
else
{
// NSLog(@"Denied");
handler(NO);
}
}];
}
このメソッドを呼び出すと、YES が返って来れば、端末は通知を許可していることになります。一方、NO が返って来れば、許可していないということです。このメソッドを呼び出すのは次のようにします。
AppDelegate* appdelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appdelegate isNotificationsAuthorization:^(BOOL isActive)
{
if ( isActive )
{
NSLog(@"notification setting: YES");
}
else
{
NSLog(@"notification setting: NO");
}
}];
isActive の状況に応じて分岐できるので、例えば設定されていないことが分かれば、そこで「設定してください」のアラートを出すことができます。
というわけで、こんな感じでできることがわかって、良かった・よかったなんですが、余談もひとつ。
最近ネット上に Objective-C の記述が少なくて、そろそろ情報が増えて来た Swifit にしようかなと思い始めたところです。でも、Swift のコードを見ると、クロージャとかの記述は Objective-C とあんまり変わらず見にくい(醜い)ので、そうであれば、Objective-C の方がまだわかりやすくていいなぁと思いました。
SKLabelNode で画面中央に時計を表示した時のこと。時計は、シンプルに次のような形で HH:MM:SS。これ、何秒かおきに微妙右にずれ、左にずれを繰り返します。理由は、画面左右の中央に時計を表示しますが、時計の数字フォントの幅が、数によって微妙に異なるため。例えば、1 の横幅と 0 の横幅は違うということ。
これ、TeX の時も謎だったんだよなぁ。TeX で表組みする時、表に数字データが入っていると、数字によって幅が異なるので、縦に並べようとするとガタガタになっちゃう。回避策は、数字を表示したいときは数式モードにしてやると、0-9 まで全部の width が揃う(というか、kernning されなくなる)ので、表組みしてもガタガタしない。
SKLabelNode の場合、kernning を解除する指定は無いようなので、さてどうしたものかと試行錯誤した結果、horizonalAlignmentMode というプロパティがあり、縦方向に揃えようとする時、どこで揃えるかを指定できることが判明。今回の場合、SKLabelHorizonalAlignmentModeLeft を指定して、label の左端を揃える位置として指定してやることで、ずれなくなりました。デフォルトは center になっているので、数字の組み合わせによって時計全体の幅が変わるのでガタガタしていた、という訳だ。
細かく指定できていいけど、ちょっとめんどくさいね。
textView は曲者だ。以前もハマった記憶がある。
事象は、textView に文字を設定しようとしても設定できない状況。textView.text = @"hoge"; で設定できるはずなのに、全く反応なし。かなり調べて、例えば、.editable は頭に指定しないで、text の変更をしてから指定する・・・だめだ、うまくいかない。textView.text = @""; としてから、textView.text = @"hoge"; とする・・・、だめだ、うまくいかない。
結局、storyboard に設定した textView を一度削除して、もっかいオブジェクトを配置し、ヘッダーファイルにアウトレット登録したらうまくいきました。
なんかよくわからない。バグですか?
5 月にトレイルのレースがあり、距離は 37Km。レース前に同じぐらいの距離を走っておきたく、今日は休日だったので朝から走ることに。
自宅からつくば駅まで 35 Km あり、朝 7:30 分に走り始めて、つくば駅に着いたのが 11:40 頃。実に 4 時間以上かかったわけですが、30 Km 過ぎてからペースが上がらず、歩き歩きになってしまいました。レースに向けてこれまで練習した中で、最長は 27 Km。今日は 30 Km 以上の距離になり、未知の世界感満載。途中、自販機とコンビニで2 回程給水。気温が割とあるのと、日陰があまりなかったので、結構喉が乾きました。
つま先走りに変えてから、35 Km も保つのか不安でしたが、特に問題なかったのが良かったです。これぐらい長距離を走ると、いつもは太ももの前面が痛くて痛くてたまらなくなるのですが、つま先走りにしてからは、太ももの前面が全然痛くなりません。代わりに、足の裏、母子球付近が痛くなってしまいますが、ま、これは仕方ないでしょうね。明日、どこが筋肉痛になるか注意して、レース本番までに補強をしたいと思います。
ゴールした後、コンビニで 100 円ドリップコーヒーと、アイスクリーム買って食べました。すごくうまかったです。昼も食べて帰ろうかと思っていたのですが、疲れ過ぎて食べるのが辛そうなのでやめました。回数券の自販機で割安の切符を買ったら、¥200 近く安くてびっくり。
レース本番では今日よりも距離があるのと、アップダウンもあるわけですが、硬いアスファルトではなく土なので、足の裏のダメージは少なくて済みそう。
それにしても、何時間でゴールできるのか・・・
CoreData が automatic になったことは既報の通りです。実際使用するにあたり、どういう違いが出てくるのか、実際プログラムして試してみました。
結論から言うと、automatic の方が便利です。理由は、開発の段では database の schema は変動的なので、フィールドの追加、フィールド名の変更などが頻繁に発生するもので、その度にファイルを作り直す必要がないので(コンパイル時に自動的に作ってくれるので)そこが便利。
あと、従来の方法で、フィールドの変更などが起こると、アプリ起動時にアプリが落ちてしまうことが大半で、お?何のエラー?意味わかんね?と言うことがしばしばでしたが、automatic なら、落ちることも少なくなり精神衛生上好ましいと思います。それではフィールド名の変更やフィールドの追加があったとするとどうなるのかと言うと、変更前のレコードはそのまま取り残され、次にデータを保存したタイミングでは新しいスキーマに基づいたレコードが保存されることになります。落ちなくなったものの、すでに保存してしまったデータを活かすなら従来通り migration は必要なので、そこは、注意が必要でしょうか。
それでは、新しい CoreData はどうやって使うかについて言及しましょう。まずヘッダーファイルで以下の指定をします。
#import <CoreData/CoreData.h>
#import "your_entity_name+CoreDataProperties.h"
データの追加は次のような形です。
AppDelegate* appdelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
NSManagedObjectContext* context = appdelegate.persistentContainer.viewContext;
これで、context ができたので、データを保存するなら、こんな感じです。
NSManagedObject* entityNameObj = [NSEntityDescriptioninsertNewObjectForEntityForName:@"your_entity_name"
inManagedObjectContext:context];
[entityNameObj setValue:mesg forKey:@"your_fieldname"];
[((AppDelegate*)[[UIApplication sharedApplication] delegate]) saveContext];
と言うわけで、
appdelegate.persistentContainer.viewContext
って何ですか?と言うのはありますが、これは覚えるしかないのでしょうね。
事の発端はアプリから SSL 通信で CGI にアクセスし、JSON データを受領しようとした事。
NSURLRequest を使って、簡単・簡単、と思ったら、推奨していないので、NSURLSession を使えと叱られる。うわーめんどくセーと思いながら渋々書き換えたところ、error 発生。どういうわけか、ここだけ日本語でエラー表示。
Error Domain=NSURLErrorDomain Code=-1200 "SSLエラーが起きたため、サーバへのセキュリティ保護された接続を確立できません。
何かいつもと様子が違うので、ググってみたら、iOS 9 から TSL v1.2 となっていて、Web サーバ側での対応がとれてていないのでエラーになっている感じ。Web サーバは当方管轄外なので、回避策はないのかさらに調べたところ、まず、console で通信しようとしている Web サーバの対応状況を調べ、次に、info.plist に例外記載すればよいらしい。
console で調査:
/usr/bin/nscurl --ats-diagnostics https://some_domain
TLSv1.2 with PFS disabled and insecure HTTP allowed
Result : FAIL
---
---
TLSv1.1 with PFS disabled and insecure HTTP allowed
Result : FAIL
---
---
TLSv1.0 with PFS disabled and insecure HTTP allowed
Result : PASS
はいはい、v1.0 じゃないとダメなのね、ということで、今度は info.plist に以下の記載を追加。
App Transport Security Settings, dictionary
Exception Domains, dictionary
domain_name, dictionary
NSIncludesSubdomains, Boolean, YES
NSThirdPartyExceptionRequiresForwardSecrecy, Boolean, NO
NSThirdPartyExceptionMinimumTLSVersion, string, TLSv1.0
どこに追加するのかよくわからなかったので、Information Property List のすぐ下に追加しましたが、ちゃんと設定されたようで、うまく通信できました。
これ、普通わかんないよね。
ふうぅぅぅ。