見出し画像

【iOSアプリ】「絶対に2度寝させない目覚まし時計」完成までの道のり

2024/3/10にiOSアプリ「絶対に2度寝させない目覚まし時計」の制作を開始。これは自身初のiOSアプリ開発となる。以後の制作過程を記録していく。

作成の動機

まず、「絶対に2度寝させない目覚まし時計」を作ろうと思った理由を紹介する。
私には高校生の妹がおり、毎朝学校まで2つのバスを乗り継いで通学している。しかし、学業や部活動で時間に追われていることや、そもそも朝に弱いことが原因で目覚ましアラームを複数かけても起きることができず、1つ目のバスの時間に間に合わないことが多々ある。バスの時間に間に合わないと親が2つ目のバスに乗り換えする停留所まで車で送り届けている。親の負担を減らすため、なんとか妹にはアラームが鳴ったら確実に起きるようにしてもらいたい。そのような思いで、「絶対に2度寝させない目覚まし時計」を開発に取り掛かった。妹は、目覚ましアラームが鳴ったら無意識のうちにアラームを止めてしまう癖がある。そのため、「絶対に2度寝させない目覚まし時計」としては、簡単にアラームを止めることができないような機能を実装する必要がある。具体的な構想としては、歩数データなどを利用して端末が完全に移動しないとアラームを解除できないようにすることを目標として開発を始める。

2024年

3/10

https://youtu.be/3v4OIds-OOg?si=vEBSOROuIwAeK_XS
この動画を参考に、SwiftとXcodeについて学び始める。動画が投稿された日は2020/12/30であり、当時主流であったStoryBoardで説明が行われている。しかし、2024年現在ではStoryBoardよりもSwiftUIの方が一般的となっており、私はそれに気づいていない。また、StoryBoardはオブジェクトを操作するので、今までやってきたプログラミングと全く異なり、その違いに戸惑う。

3/12

上の動画を参考に学習を進める。ところが、Storyboardとその中のUIと紐付けられているコードを並べて表示する方法がわからず苦戦。また、navigationBarの色を変更したところ、view controllerの色が変わらないという問題に直面する。調べると、Xcodeのバージョンが動画の人と違うことが問題で、バージョンによって操作方法が変わるとのこと。私のXcodeのバージョンは15.3であり、私と同じバージョンでの解決方法を調べるが、同じバージョンで紹介している記事は見つからない。そんな中、現在はStoryBordよりもSwiftUIの方が主流であることに気が付く。
それから、新たに、以下の動画で学習を始める。
https://youtu.be/EHdAqVVzAIE?si=Bk9AhUMTJnhM9BNv
実際に、HStack、VStack、ZStackなどの基本的な部分を学んだ。

3/13

同動画で学習を進める。また、技術的な学びを以下の記事に書き留めることを始めた。

ユーザのあらゆる動作を考え、バグを作らないようにすることが難しい。たとえば、入力した数値をDouble型に変える処理をするとき、入力が数値以外の場合にエラーになってしまわないよう、表示するキーボードを数値のみのものにするとか、型変換に失敗したときは0として続けるなど。

3/14

画面遷移について学ぶ。また、@Stateの2つの性質を理解できた。その性質は以下の通りである。

  • @Stateの変数はstructの中で変更できる。@Stateが付いてない変数は変更できない。

  • @Stateの変数は、常にモニタリングされていて、変更された時に参照しているViewが自動的に再描画される。

ダイアログ、アラート、リストについても学んだ。

3/15

Swiftの一通りの使い方を学び終え、実際に、「絶対に2度寝させない目覚まし時計」のアプリを開発し始める。
実装したい機能はあるが、その名称がわからず、調べるのが難しい。

3/16

前日につまづいた部分が解決し、Xcode15以降で@Bindingを使ったViewのプレビューを作成する方法についての記事を投稿。

また、Logging Errorが出て、調べても英語の記事しか見つからなかったため、環境変数の設定方法についても記事を投稿した。
https://qiita.com/sekibouya/items/3a6d53bc6b701da9f8dc

そして、基本的なUIは完成し、続いて時間の処理など、アラーム機能の実装に取り掛かる。

3/17

システムサウンドの連続再生、時間の処理を実装する。システムサウンドを連続再生するために、AudioServicesAddSystemSoundCompletionを使うのだが、第4引数に関数を指定するところに苦戦する。色々と調べるが、どれも最新のバージョンでないため、同じ書き方ができない。swiftはバージョンが変わるごとに書き方がコロコロ変わるのか、と、そんな雰囲気を味わった。

結局、このサイトを参考にするも、結構古いので大体なにをしたら良いのか大まかに理解する程度。

func AudioServicesAddSystemSoundCompletion(
    _ inSystemSoundID: SystemSoundID,
    _ inRunLoop: CFRunLoop?,
    _ inRunLoopMode: CFString?,
    _ inCompletionRoutine: AudioServicesSystemSoundCompletionProc,
    _ inClientData: UnsafeMutableRawPointer?
) -> OSStatus

公式ドキュメントによると、AudioServicesAddSystemSoundCompletionはこのような構成になっている。

typealias AudioServicesSystemSoundCompletionProc = (SystemSoundID, UnsafeMutableRawPointer?) -> Void

第4引数のAudioServicesSystemSoundCompletionProcはこのようになっている。
まるで理解できない。

Cannot convert value of type '()' to expected argument type 'AudioServicesSystemSoundCompletionProc' (aka '@convention(c) (UInt32, Optional<UnsafeMutableRawPointer>) -> ()')

このようなエラーがでたり、

A C function pointer can only be formed from a reference to a 'func' or a literal closure

ポインタやらクロージャやらのエラーが出たり。Swiftを触り始めて1週間の私にはさっぱり理解できなかった。

調査が難航してしまったが、結局、上のサイトを参考にしたら解決した。クロージャを指定するらしい。
また、ChatGPT先生にも聞いてみることに。
聞いてみると、Objective-Cが関係している??

{ (soundID, _) in
            ContentView().soundCallback(sID: soundID)
        }

GPTさんは第4引数をこのように書いている。

とりあえず、この解決方法について上の記事にまとめた。

そして、時間の処理に取り掛かる。

これらのサイトを参考に、指定した時間までのカウントダウン機能を実装できた。

3/19

アラームを鳴らす時間やタイマーの機能などを管理するMyTimerというclassにまとめた。

3/20

MyTimerのイニシャライザを書き忘れていたら、static method 'buildexpression' requires that 'toolbaritem<(), button<text>>' conform to 'view'というエラーが出た。
アラームが鳴り出したらアラームを止めるボタンを表示し、アラームを停止できるようにした。(アラートで実装)

アプリを簡素化するため、UIを大幅に変更。それに伴い、セットできるアラームの数を1つにした。
シンプルな目覚ましアプリの機能は完成した。次からは、歩数計などを使ってアラームの停止方法を工夫したい。

3/21

これらの記事を参考に歩数を取得する機能を実装した。歩数に関しては結構簡単に取得できる。

今まではアラートでアラームの停止を行なっていたが、歩数を表示する画面に遷移して、指定した歩数になるまでアラームを停止しないようにした。
学習不足だとは思うが、遷移先からアラームを停止することができなかったため、画面遷移せず、ContentViewの表示を変更するようにした。
続いて、この記事を参考にバックグラウンドでも動くようにしていく。
バックグラウンドに移行する時やフォアグラウンドに移行する時に処理を実行する方法はわかったが、バックグラウンドでもタイマーの処理を続ける方法が全くわからず、とりあえず、スリープさせないようにした。これにより、使い勝手の悪いアプリになってしまった。その代わり、アラームをセットしたら画面の明るさを自動的に暗くなるようにした。
そして、今まではアラーム音にシステムサウンドを使っていたのですが、端末がサイレントモードに設定されていると音が鳴らないため、AVAudioPlayerを使うように変更。
音楽は以前私が作成したものを使用した。非常にダサい音源である。

3/22

端末の音量が元々小さくてもアラームが鳴る時に強制的に音量を最大にするようにした。そのときに使った方法をこの記事にまとめた。この方法は、アプリから音量を変更できないという制限を強行突破しているため、いつ動かなくなってもおかしくない。

3/23

端末がライトモードであったとしてもこのアプリの画面はダークモードで表示されるようにした。あと、トグルのサイズを1.5倍にした。
とりあえず、完成!!!
完成したアプリはこちら

アラームをセット


10歩歩いたらアラームを停止

アラームに使用した自作の音楽はこちら
バックグラウンドの処理は実装できなかったけど、作りたかった機能は大体実装できたので良かった。
swift、結構難しかった。開発中に感じたことを次の章にまとめる。

3/28

アラームが一周目からうるさすぎて心臓に悪いという苦情が来たため1周目の音量は1/3にして2周目以降は最大なるよう変更した。

開発中に感じたこと

  • 変数の状態で動作やUIを制御しずらい。(しないほうが良い?)

  • 異なるViewや異なるファイル間での連携が難しい。単に変数の値を変更してもうまくいかない。@Bindingとかいろいろ

  • バージョンによって書き方がまるで違う。移り変わりが激しい?

その後

アプリが完成し、実際に妹に使わせたところ、ちゃんと起きることができ、朝のバスに乗ることができた。したがって、親の送り迎えの負担を減らすことができた。目的が達成できて良かった。若干、当初の構想とは異なる形になってしまった部分もあるので、引き続き学習を続けていきたい。

この記事が気に入ったらサポートをしてみませんか?