普段はWebアプリケーションを開発したり、設計したり色々している中の人が、初めてiOSアプリを作った時の話をまとめました。iOSアプリを作る中でQiitaに幾度となく助けられたこともあり、同じような境遇の方に何かしら恩返しができればと思い、やったことやハマったことを中心に書いています。
概要
Swift初心者である開発者が、約3ヶ月でiOSアプリを作った時に、お勉強タスクとしてやったことや、開発中にハマったことをまとめました。コーディングの細かいところというよりは、WebアプリとiOSアプリの違いによるハマりポイント、検討ポイントを中心にまとめています。
想定している読者
- Webアプリは開発したことはあるが、iOSアプリは開発したことがない人
- 一定程度のプログラミングの経験がある人(超初心者向けではありません)
書かないこと
- サーバサイドの話
- Swiftのプログラミングのコアな難しい話や細かい話
開発開始時の中の人のスペック
- Webアプリケーションは最低限何かしらのものを作ることができる
- XcodeはHomebrewを入れる時にインストールしただけで、触ったことはない
- iOSアプリの開発は一切したことがない
- もちろんAndroidアプリも作ったことはない
事前のお勉強タスクとしてやったこと
ここからは、アプリを開発するにあたって事前に行ったことや、アプリをリリースした後に振り返ってみて「やっておけばよかったこと」を書いていきます。
本でのお勉強
TECHNICAL MASTER はじめてのiOSアプリ開発
この本を2週間くらいかけてひたすら写経しました。
この本では、サーバとの通信を行う(APIで取得したデータを利用する)アプリケーションを一通り作ることができるため、習うより慣れろの精神でひたすら写経しました。
内容は簡潔にまとまっており、また前半部分はリファレンスとしても活用することもできるため、非常に良い本だと思いました。他の本も幾つか中身は見ましたが、この本が一番自分のレベルに合っていたため、他の本は使っていません。
また、この本一つで何とかなりそうな気がしたので、ドットインストール等のWeb教材系も特に使っていません。
やっておけばよかったこと
UIコンポーネントの理解を深めておく
iOSアプリには多数のUIコンポーネントが標準で準備されており、多くの場合は標準のUIコンポーネントだけで良い感じのアプリを作ることができます。
とはいえ、どのようなUIコンポーネントがあるのかを知らなければ、せっかくのUIコンポーネントを使うことができません。
ググったりリファレンスを読んだりするときの検索ワードとしてもUIコンポーネント名は重要ですので、事前に一通り学んでおく方がよかったと感じました。
また、Appleが提供しているiOSヒューマンインターフェイスガイドラインは事前に読み込んでおくべきでした。それぞれのコンポーネントの使い方や目的などが詳細に書かれており、非常に参考になりました
日本語訳のものがないため、こちらの記事なども参考になるかと思います。
ライブラリについての調査を事前に行っておく
RubyやPythonと同様に、Swift(Objective-c)でも多数のライブラリがあります。
iOSアプリの場合はブラウザで利用するWebアプリよりもできることが多く、ライブラリの種類も多方面にわたります。その分、一つの方面ごとのデファクトスタンダードと呼べるライブラリが多くないため、ライブラリの調査に結構時間を使いました。
ライブラリの調査の際には、こちらの記事などを参考にさせていただきました。
また、他のアプリが利用しているライブラリ一覧を見ることができるケースもあるので、普段使っているアプリで素敵なものがあれば、そのアプリのライブラリ一覧を参考にするのも良いと思います。
ディレクトリ構成を事前に考えておく
Swiftの場合、Ruby on Railsなどと異なり、ある程度自由にディレクトリの構成を決めることができます。逆の言い方をすれば、自由度が高い分、すぐに煩雑なディレクトリ構成になってしまいます。
それらを防ぐためにも、自身が作りたいアプリと似たようなアプリのコードを探し、事前に読み込んでおき、それらのディレクトリ構成を参考にすればよかったと思いました。(とはいえ、アプリのやりたいことによって構成が異なると思うので、一概にどれがベストプラクティスと言い難いとは思います)
今回のアプリ作成では、上述の本に載っていたディレクトリ構成をベースに色々調整したものの、ベストプラクティスとは言い難い状況になってしまいました。一人で開発していたのでそこまで困りませんでしたが、複数人での開発だとカオスになったことが想像されます。
審査ガイドラインを事前に読んでおく
後述しますが、iOSアプリの公開にはAppleの審査が必要です。
作るアプリの種類によっては、「この機能がないと審査を通さない」といったケースもあるため、事前に審査ガイドラインを一読しておくことをオススメします。
iOSアプリ開発でハマったこと
ここからが本題の、Swift初心者が開発時にハマったことや、よくわからなかったこと、悩んだことを中心に書いていきます。
Swiftのバージョンの違いにハマる
Swiftでコードをに書いていて一番ハマったのが、Swiftのバージョンに違いです。
RubyやPythonやもちろんJavaは、古いバージョンのコードであってもなんとなく動くことが多いのですが、Swiftは動かないケースが多いです。良い感じのサンプルコードをネットの記事などで見つけたとしても、それをコピペしてそのまま動くケースはかなり少なかった印象です。
例えば、HTMLでいう input type="text"
を表示するコードですが、ググって出て来たsample1のコードはSwift3では動かなかったりします。Swift3で動かすためには、sample2のように書く必要があります。
myTextField = UITextField(frame: CGRectMake(posX, posY, tWidth, tHeight))
myTextField = UITextField(frame: CGRect(x: posX, y: posY, width: tWidth, height: tHeight))
Swiftに慣れている人なら一目でわかりますが、初心者からすると凄くハマるポイントでした。動かない理由の多くはSwiftのバージョンの違いによるものなのですが、初心者にはそのような違いがわかるわけもなく、途方にくれる日々でした。
この場合の解決方法はシンプルで、素直に公式のリファレンスを読むというだけです。とはいえ、情報として色々まとまっている分Qiitaの記事とかの方がわかりやすいことが多いので、そこはケースバイケースかなと思います。
挙動とコードが結びつかなくてハマる
例えば、「スワイプで削除」という処理自体は標準で用意されています。
ですがこれを実現するためには、下記のようなコードを書かなければいけません。
func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
こんなコード、知らないと書けるわけがありません。
Swiftでコードを書いている時間の多くはこのようなものを調べる時間に使ってしまいました。知っていれば30秒で終わることが、知らなければ数時間調べるなんてこともザラでした。
そのため、事前にリファレンスを読み込んだり、標準コンポーネントでできることを調べたりして、「こんなことをしたいときは、こんな処理を書く必要がある」ということをざっくりでいいので頭に入れておくと、開発がスムーズに進むかと思います。
このような事情もあり、逆引きSwiftさんには非常にお世話になりました。また、逆引き系の書籍は一冊手元に置いて置いても良いかもしれません。バージョンが違うと使い物になりませんが、最低限の手がかりにはなるので。
ストーリーボードにハマる
Webアプリケーション開発者にとって一切馴染みがないのがストーリーボードです。
ざっくりいうと、アプリのコンポーネントの配置や色などと画面遷移をGUIで設定できるツールです。デザインツールのように、コンポーネントを画面に配置していくことで、画面の設計ができてしまうという優れもののようです。
ここで「ようです」と他人事になっているのは、結果的にストーリーボードを利用しなかったためです。世の中にはストーリーボードライク派とアンチストーリーボード派がいるようで、私はアンチストーリーボード派になってしまいました。
ストーリーボード利用しなかった理由は大きく以下の三つです。
- どこを変更したのかDiffを見てもよくわからなかった
- 設定内容をマウスでクリックしないと表示/編集できないのが面倒だった
- 画面数が増えるとよくわからなくなった
この辺りは慣れやノウハウの問題もあると思うので、ケースバイケースだと思います。色々な人の意見を見ていると、全てストーリーボードで書く派や、画面遷移だけをストーリーボードで書く派がいたりと、世の中的にもベストプラクティスが出ていない領域にも思えました。
ストーリーボードを利用しない開発
もちろん、ストリーリーボードを利用しなくても開発は問題なく行えます。
とはいえ、コンポーネントの設定内容を全てコードに書く必要があるので、コードがかなり煩雑になってしまいます。
例えば、WebアプリでいうとHTMLにimgタグを配置するだけのコードでも、ストーリーボードを使わない場合は最低限これくらい書く必要がでてきます。
// viewにimgタグを配置するイメージ
self.view.addSubview(imageView)
// cssで位置やサイズを設定するイメージ
imageView.snp.updateConstraints{ make in
make.top.equalToSuperview()
make.left.equalToSuperview()
make.width.height.equalTo(64)
}
慣れてしまえば大したことないコードですが、私が書いたコードの半分以上はこのようなコードだったこともあり、ストーリーボードを使っていた方が楽だったのでは、、、と思うこともありました。もちろん、diffを見たり、細かい調整をするにあたっては楽な部分も多かったので、ケースバイケースとしか言いようがありません。
なお、ストーリーボードを利用しない場合のコードは、こちらなどを参考にさせていただきました。
アプリのライフサイクルでハマる
Webアプリではほとんど意識しませんが(並列処理などを行う場合などは多少考えますが)、iOSアプリの場合は「処理の順序」をどうするかが非常に重要になってきます。この処理の順序のことを、「ライフサイクル」と呼ぶようです。
たとえば、アプリの起動時にする処理、アプリがバックグラウンドに呼ばれた時にする処理、アプリがフォアグラウンドに来た時にする処理、、、といったように、どのタイミングで何をするのかを指定することができます。
しかし、本来書くべき場所にその処理を書いていなかった場合、当たり前ですがうまく動きません。コードを書いているにもかかわらずうまく動作しない場合、ライフサイクルに合わない場所に処理を書いているケースが多々ありました。
また、ややこしいことに、「アプリ全体」のライフサイクルや、使っているコンポーネントごとのライフサイクルがあったりして、その辺りの観点からも色々とハマることがありました。
なお、ライフサイクルについては、公式ドキュメントやこちらの記事などを参考にさせていただきました。
端末へのデータの保存はどうするか
Webアプリと大きく異なる音が、端末へのデータ保存です。
Webアプリの場合、永続的なデータはサーバサイドでデータベース等に保存し、一時的な少量の情報のみクッキーやセッションに入れるというのが通常だと思います。つまり、端末へのデータ保存ということを前提にして作るケースはほとんどありません。
しかし、iOSアプリの場合は、端末にデータを保存することを前提としてアプリを作る方が効率が良いケースが多いです。通信が不要になる分、端末へのデータの保存や呼び出しの方が、サーバに保存するよりもはるかにパフォーマンスが高いからです。
とはいえ、どうやって端末へのデータ保存については、Webアプリ開発者には謎が多い領域だと思います。
ざっくりまとめると大きく4つの保存方法があり、それぞれの保存方法に特徴があるため、場合によって使い分けが必要になります。
UserDefaultsへの保存
ユーザの設定情報など、ちょっとしたデータを保存しておきたい時に利用するのがUserDefaultsです。UserDefaultsにデータを保存するとアプリを終了した後もデータが残ることや、簡単に記述できることもあり、非常に使い勝手の良い印象です。
コードも下記のようにシンプルでわかりやすいと思います。
let userDefaults = UserDefaults.standard
userDefaults.set("fugafuga", forKey: "hogehoge")
userDefaults.synchronize()
とはいえ、セキュアな保存方法ではないことや、大量データの保存(RDBのような使い方)には不向きであるため、使用箇所は考える必要があります。
UserDefaultsの利用にあたっては、こちらの記事などを参考にさせていただきました。
Keychainへの保存
Keychainは、セキュアな情報をiOSで取り扱う仕組みです。
アプリ開発者以外でも名前は聞いたことがあるかもしれません。
UserDefaultsとの大きな違いは下記の三つで、ログイン情報などのUserDefaultsにはセキュリティ上持たせられないようなデータの保存に向いています。
- アプリが削除された後もデータが残る
- データが暗号化される
- 同じKeychainを利用しているアプリ同士でアクセスできる
なお、普通に利用するとコード量がなかなか多くなるので、KeychainAccessを利用しました。
こちらのライブラリを使うと、かなりシンプルにKeychainへのアクセス処理を書くことができ、非常に助かりました。
let keychain = Keychain(service: "hogehoge")
keychain["fugafuga"] = String(id)
キャッシュへの保存
ユーザのプロフィール画像など、頻繁に利用するデータはキャッシュとして端末に保存することができます。キャッシュとして端末に保持することで、サーバへのアクセス回数を減らすことができ、よりユーザビリティの高いアプリにすることができます。(この辺りはWebアプリでも同じですね)
Swiftでのキャッシュの保存を簡単にするライブラリはいくつかあるのですが、私はこちらの記事などを参考に、AlamofireImageを利用しました。
基本的にはライブラリがいい感じに処理してくれるため、アプリを書く上であまりキャッシュを意識することはありませんでした。
大きいデータの保存
作るアプリによっては、設定情報などだけでなく、ある程度のサイズ感のデータを端末に保存する必要があるかと思います。また、Webアプリで使い慣れたRDBのような形で端末にデータを保存したくなるケースもあるかと思います。
今回作成したアプリにおいても、端末内にデータを保存し、それをある程度柔軟に呼び出す必要があったため、Realmというアプリ用のデータベースを利用しました。
使い方も非常にシンプルで、Ruby on RailsのActiveRecordのような感じでデータ取得や保存の処理を書くことができます。
let realm = try! Realm()
let fruits = realm.objects(Fruits.self).sorted(byKeyPath: "priority", ascending: false)
Realmの公式ドキュメントが非常にわかりやすく、こちらを読めば処理の記述は問題ないと思います。
なお、日本語訳されているドキュメントは最新のバージョンのものではないのですが、最新版と大きな差異があるわけではないため、最低限の動作においては日本語訳のドキュメントで必要十分だと思いますので、その点でも利用のハードルは低いかなと思います。(必要に応じてお使いのバージョンのドキュメントを見る必要はあります)
ログの保存を考える
サービスをグロースさせていくにあたり、何かしらの分析は欠かせません。そのためにも、どのようにしてユーザの行動(ログ)を取得し、分析をしていくのかということが重要になります。
Webアプリの場合、サーバサイドで自前のログを仕込んだり、MixPanelなどを利用したり、極論適当な処理でprint
しておけば良かったりしますが、iOSアプリの場合はWebサーバが必ずしも必要でないこともあり、端末ごとのログを何かしらの手法で取得する必要があります。
また、サーバサイドの処理があるiOSアプリだったとしても、端末側でどのような操作がなされたのかを知るためには、端末側で何らかのログを記録する仕組みが必要となります。
こちらも幾つかの方法があるのですが、今回はFirebaseを使いました。
Firebase自体は色々なことが出来すぎて何が何だかもはやわかりませんが、アプリのログを簡単に記録してくれる仕組みも提供しています。
ちなみにログの記録は、下記のようなコードを書くだけです。
あとは放っておけば、Firebaseの管理画面でログを見て分析することができます。
Analytics.logEvent(AnalyticsEventSignUp, parameters: [
AnalyticsParameterSignUpMethod: service,
])
Firebaseは公式ドキュメントがしっかりしていて、ドキュメント通りに設定していけば問題なく利用できます。
ログの処理とかどうすれば良いのかわからずに途方にくれていましたが、Firebaseを使えば本当に一瞬だったのでオススメです。もっと細かい分析が必要な場合は別の手法の方が良い気もしますが、最低限のイベントやアクションのログの記録だけであれば、必要十分だと思います。
アプリがクラッシュした時にどうするか
ログの記録と関連しますが、iOSアプリの場合はWebアプリと異なり、アプリがクラッシュ(Webアプリでいう500エラー)した場合のログを簡単に取得することはできません。クラッシュした際のログを取得するために、何かしらの仕組みを導入する必要があります。
実はこちらもFirebaseがいい感じにやってくれていて、Firebaseを導入するとあとは管理画面側でクラッシュレポートを確認することができます。(本当にこれといって何かの設定も必要がなかったため、クラッシュレポートのタブを開いて感激しました)
Firebaseのクラッシュレポートでは、特に何かの設定をしなくても、そのユーザがクラッシュに至るまでの操作の流れを見ることができ、非常に便利だと感じました。
アプリの公開前のテストをどうするか
Webアプリの場合は所謂ステージング環境を作ったりして、アプリの公開前に何らかのテストを行うことが多いと思います。iOSアプリの場合も「テストフライト」というものを利用して、似たようなことが出来ます。
テストフライトについては、こちらの記事に丁寧にまとまっていますので、こちらを見ていただく方が良いかと思います。
非常にざっくりした内容を書くと、テストを行うにあたってAppleの審査が必要なケース(外部テスター)と不要なケース(内部テスター)があります。そのため、今すぐテストをしたいと思ったとしても、簡単に開始できないという点がWebアプリと異なります。
また、どちらのケースにせよ、アプリのデータのアップロード等が必要なため、テスト開始までに数時間はかかるという点にも注意しておいた方が良いと思います。
内部テスターの場合
- 審査は不要
- ただし25名まで
外部テスターの場合
- Appleの審査に通らないとテストができない
- 10,000名まで可能
- 審査は2-3日程度必要
Appleへの申請・審査
最後に、Webアプリ開発者でもなんとなく聞いたことがあるAppleの審査について書きます。
Webアプリの場合、開発者が自由にデプロイして公開することができますが、iOSアプリの場合はアプリの公開にAppleの審査が必要です。
Appleへの申請については実際にやってみると大したことない作業なのですが、実際にやるまではちょっと不安を感じていました。その辺りの不安が解消できればと思います。
ちなみに、私は何回かリジェクトされましたが、無事公開できましたし、Appleの中の人は非常に好意的でしたし、何回かやりとりしていると、「頑張って!」的なメッセージをくれたので、個人的には好印象です。
なお、先にも述べましたが、審査ガイドラインは事前に読み込んでおいた方が良いです。
今回はSNSのようなアプリを作ったのですが、この場合は「ユーザのブロック」機能や「投稿の通報」機能がないとリジェクトされてしまいます。リリース直前になってこれらの機能を追加するのは骨が折れるため、事前に確認しておくことをオススメします。
審査の時に記載する内容
Appleへの申請を怖がっていた理由に、審査時に記載する文章を英語で書く必要があると勘違いしていたことがあげられます。
実際は、日本語で記載した文章でも問題なく審査を行っていただけました。
とはいえ、審査に関してのAppleからの返信は全て英語のため、英語アレルギーの人には一つの山かもしれません。(非常に簡易的な英語なので、Google翻訳等で問題なく対応できます)
審査にかかる日数
申請〜審査完了までの期間は、2017/9時点では非常に早く、1-2日程度でした。
審査に関してAppleとメッセージをやりとりする場合があるのですが、この場合もだいたい半日〜1日で返事が来ていました。(早い時は1時間くらいできました)
Appleへの申請がリジェクトされた時
リジェクトされると、上述の審査ガイドラインのどこに引っかかったかということと、簡単なリジェクトの理由が書かれた英文がAppleの担当者から送られてきます。
基本的にはその文面に従って対応すればOKでした。
ちなみに私は下記の二つの理由でリジェクトされました。
必要な機能がないない
上述のように、「ユーザのブロック」といった機能がないという理由でリジェクトされました。といっても、その機能自体は作っていたので、「この画面のこのボタンからブロックできます」といったことをAppleに返信すれば、審査は通りました。
SNSでのログインを提供している場合
SNSでのログイン機能を提供している場合、テスト用のSNSのアカウントをAppleに提供する必要があります。テスト用のアカウントについてはApple側で準備していただけないため、開発者側からIDとパスワードを提供する必要があります。
Twitterログインの場合は何かしらのメールアドレスを利用してアカウントを取れば良いのですが、電話番号認証が必要なLINEログインやFacebookログインの場合は心理的に面倒でした。
審査に必要ということでやむなくプライベートで利用しているアカウント情報を提供したのですが、この辺り、他の開発者の方はどうやって対応されているのでしょうか。今のところ情報漏洩的なことは起こっていませんが、どのような対応がベターなのか気になっています。
(追記)
Facebookの場合、こちらの方法でテスト用のアカウントが作れるようです。
コメントでのご指摘ありがとうございました!
https://developers.facebook.com/docs/apps/test-users#managetool
また、Twitterログインであったとしても、Twitter側が気を利かせて「普段とは違う端末、または場所からTwitterにログインされました。」といったメッセージを出した場合、Twitterの認証で使う電話番号等をApple側に伝えておかなければ、「ログインできなかったよ!」的な理由でリジェクトされてしまうため注意が必要です。
おわりに
かなり長くなりましたが、普段はWebアプリを書いている人が、初めてSwiftでiOSアプリを書いた時にハマった点、Webアプリとの違いを感じた点を中心にまとめました。何かの参考になればと思います。
また、お勉強時間を含めてだいたい3ヶ月で作ったのが下記のアプリです。(サーバサイドやWebサイトも作っていたりしたので、アプリの開発自体に費やした時間はもう少し短いです)
筋トレ好きな人をターゲットにしたアプリなので、トレーニング好きな方は是非使っていただけると嬉しいです。