たまにXcodeをさわらなければいけない機会があるのだが、本腰入れて開発するわけではないため、知識がまったく身につかない。毎回Google先生のお世話になる。最近はChatGPT先生にもお世話になっている。
ただ、それらで調べた結果がすべて正しいとも限らない。古い情報だったりもする。公式のリファレンスを当たるのが一番なのだろうけれど、ド素人には手っ取り早いサンプルコードが欲しいものである。
今回、ロック中でもタイマーを動かしたい要件があったためネットでいろいろ調べていたのだが、振り回されてしまってちょっと苦労したため、備忘録としてここにメモしておく。
無操作タイムアウト処理がちゃんと動かない
規定時間アプリが無操作だった場合にアプリの初期画面に戻すという処理があり、実機デバッグ時はちゃんと動いていたのだが、通常実行時に画面ロックがかかると、時間を過ぎてロック解除しても初期画面に戻っていないという現象があった。
ホームボタンを押して、アプリをバックグラウンドにしていた場合も同様。
ロック中にタイマーは動き続けるのか?
単純にscheduledTimerWithTimeIntervalで規定時間を指定し、selectorには初期画面に戻す処理を書いていたのだが、このタイマーがロック中に止まってしまっているらしい。
ロック中NSTimerはどうなるのかを調査。アプリがバックグラウンドになったとき、NSTimerは動いているのか?止まっているのか?動いているとの記事も見かけたが、結論としては「デバッグ実行時のみ動いている」ようだ。実機をMacから切り離し、アプリを実行した場合はロック中タイマーが動いていなかった。
UIBackgroundTaskIdentifier
ちょっと調べたら、UIBackgroundTaskIdentifierを使うとバックグラウンドでも処理を継続できるということがわかった。
[Objective-C] バックグラウンドで継続して処理を実行する
すごくわかりやすいサンプルコード。ド素人の私でもすぐに開発中のアプリに組み込むことができた。サンプルではNSLogしているところを、1秒ごとにカウンター用変数をカウントアップしていく処理に変えただけ。
しかし、永遠に動いてくれているわけではないようだ。
これはちょっと延命するだけのようで、バックグラウンドに入ってから30秒でタイマーが止まってしまった。今回の場合は30秒だったけれど、特に決まっているわけではないらしい。
iOSではアプリがバックグラウンドに移行したらプロセスがすべて一時停止するのでタイマーも止まってしまいます。基本的に後ろで長時間動き続けるという動作はできません。こちらを参照してください。
beginBackgroundTaskWithNameで一時停止するまでの時間を延長することはできますが、どれくらいiOSが待ってくれるか明確な時間は決まっていません。
引用元:https://teratail.com/questions/123950#reply-188862
Background Modes
CapabilityにBackground Modesというのもあるけれど、これは今回無関係のよう。
よくわからず全部チェック入れてやっていたが、それでも上記のように30秒でタイマーが止まっていた。
これはオーディオ再生や位置情報アプリなど限られた用途にしか対応しておらず、NSTimerで汎用的にバックグラウンド処理ができるようになるものではないらしい。
結局どうしたか
バックグラウンドで経過時間をカウントし続けるのは無理なので、バックグラウンドに入った時刻を記憶し、復帰したときに経過時間を算出、それをカウンター変数に加算することで、経過時間をカウントし続けたように見せることができた。
(バックグラウンドに入るイベントはUIApplicationWillResignActiveNotification、復帰イベントはUIApplicationDidBecomeActiveNotificationで作成)
(手っ取り早いサンプルコードが欲しい、と言いつつ、ここにサンプルコードを提示できないのは、単純化したサンプルコードを作るスキルすらないため…ごめんなさい)