初めに
本記事は、iOS未経験のエンジニアがiOS + Google Cloud + Discord.bot + Alogliaを用いた57,000ステップにも及ぶ個人アプリを4回のリジェクトを通じてApp Storeリリースし学んだことを振り返りも込めて記したものです。
そのため、私の意見を強制するものではなく"そんな事例もあったんだね"くらいの共有レベルだと認識してください。
TL;DR
- 技術とビジネスの橋渡しが最も困難でありプロダクトを生かすも殺すも自分次第
- 仕様は念入りに決めてから作業してください
- App Store審査は著作権が絡むと非常に厳しいです
- App Storeガイドラインに満たしてるかどうかは運営目線ではなくユーザー目線で考えましょう
- CIはデグレ検知の最後の砦。敷きましょう
自己紹介
年齢:24歳
職種:Androidエンジニア(SIer)
趣味:チェス & ウィスキー & 技術系(LeetCode, 個人開発, 研究開発など)
個人アプリについて
どんなアプリか
タグを用いたAND検索を行うことで探したいゲーマーYouTuberを一瞬で探せるアプリです。
タグとはアプリ内でYouTuberに対して生成されるものであり当該人物の特徴を表すものです。
例えば、筆者である私の特徴で考えてみると"#Androidエンジニア, #チェス, #ウィスキー"など冒頭で述べた特徴がタグとして付与されます。
そして、アプリ内ではタグを複数組み合わせて検索します。
例えば、"#プロゲーマー, #関西弁, #アニメ声"などで組み合わせてそれらを満たすYouTuberを発見することが出来ます。
これは現代の検索が抽象的なキーワードで検索して自身でスクロールして探さないといけない、という弱点を逆手に取った発想です。
技術スタック
iOS
- Swift UI
- SwiftUI Shimmer
- WrappingHStack
- MVVM+Clean Architecture
- Swinject
- SwiftLint
- SwiftPackageManager
- GitHubActions(CI)
- PushNotifications
- Google Cloud reCAPTCHA Enterprise
- Firebase Authentication
- Firebase Cloud Functions
- Firebase Firestore
- Firebase AppCheck
- Fireabse Remote Config
- SwiftGen
- Quick+Nimble
- Alamofire
- Algolia Search Client
Google Cloud(APIとしての役割が主)
- Node.js22
- TypeScript 4.9
- Clean Architecture
- Express
- Zod
- ESLint
- Firebase Admin SDK
- Firebase Authentication
- Firebase Functions
- Firestore Vector Search
- Google Secret Manager
- YouTube Data APIv3
- OpenAI Embeddings API
- Anthropic Claude API
- Brave Search API
- Google Perspective API
Discord bot
- Node.js22
- TypeScript 4.9
- Clean Architecture+EventEmit(Observer)
- Discord.js v14
- ESLint
- Firebase Firestore
- Firebase Functions
- Fireabse Remote Config
- Google SecretManager
- Google Perspective API
Algolia
これはサービスを利用したものなので特に無し。
初期設定やったくらいです。
開発で学んだこと
仕様決めは丁寧に!doc化も忘れない!
正直、今回は"個人開発だから許されたムーブ"が多かったです。
私は仕様を二転三転させたせいで修正がたくさんありました。
仕様変更
⇩
ドメインモデル修正
⇩
ユースケース修正
⇩
データレイヤー修正
⇩
ビューレイヤー修正
⇩
テストコード修正(ここが特に重い...)
このパターン何度もやりました。
仕様を曖昧にしたまま突き進んだことが一番の原因です。
改めてビジネス要件の整理の重要さを身に染みて感じました。
一般的に、個人開発ではまず実装しろと言われますがそれは最低限仕様をしっかり定めてからにしましょう。
技術とビジネスの橋渡しが一番難しい
これが今回 最も伝えたいこと です。
本アプリではYouTuberのデータという著作権を持つデータを扱います。
案の定、App Store審査にて指摘をもらいリジェクト。
「第三者の情報を表示したら著作権侵害のおそれがある。問題ないことを証明するエビデンスを提出するなどしてください」とのこと。
これに対し二つの思いが交錯します。
- アプリ内で表示するYouTuberアイコンやチャンネル名は一時的に初期アイコンやイニシャル表記にしてリリース優先するか?
- ただそうするとプロダクトの価値が皆無になり提供したいUXは死ぬ
つまり「プロダクトの価値を維持したまま審査に通すことが出来ない」 という問題に直面しました。
そこで今回は下記のステップに沿って対応。
- 届けたいUXに必要な情報は何か?を整理
- 不要なYouTuberの情報は削除
- 情報収集元であるYouTube Data APIv3のガイドラインを精読
- "一般的に公開されている情報(チャンネル名など)を利用することは問題ない"と記載があったためこれを利用
これら修正+情報提供を行い無事に解決。
このようにビジネス要件でやりたいことがあってもそれが何かしらの要因で認められない時があります。
それを技術 / ビジネスどちら側で吸収するか?という判断が非常に難しいです。
ここで、審査を通すためにビジネス要件を変えるとプロダクトの価値は大幅に下がります。
これが冒頭で書いた「技術とビジネスの橋渡しが最も困難でありプロダクトを生かすも殺すも自分次第 」につながります
CIが温かい
改めてCIは強いと実感しました。
テストコード実装やCIパイプライン構築の工数はかかるもののデグレをしっかり検知してくれるのは非常に強力です。(ただし、しっかりテストコードを書いた前提)
これは何度もデグレを引き起こした人であれば経験あるかと思いますが、デグレが怖すぎて実装修正に異常に精神力を擦り減らすことなどがあるのです。
- 「既存コードを壊したらどうしよう...」
- 「またデグレ起こしたら怒られる...」
大袈裟かもしれませんが心理的安全性の低下を引き起こすのです。
それを防ぐのがCIです。デグレが発生したらしっかり拾ってくれるので自分で気が付けます。
それすら貫通する場合はそもそもテストコードの質の話なので別問題です。
4回のリジェクトをされた悲しきアプリ
各リジェクトでの指摘内容を記載していきたいのですが何故かApp Store Connect内から審査員とのチャット履歴が一部閲覧できず、1,3回目は記憶頼りになります...。
審査員とのチャットが消されてるっぽい...?
リジェクト - 1度目(記憶頼り)
Guideline 5.2.2 - Legal
先に述べた著作権問題です。
この時は既にAppStoreにある類似性の近いアプリを引き合いに出し、既存アプリと近しいことをしているのだから問題ないのでは?と主張。
リジェクト - 2度目
Guideline 5.2.2 - Legal
1度目のリジェクトに対して私がした反論に対し回答が来た。
内容は、「それはそれ、これはこれ」理論。類似アプリを引き合いに出すのはダメでした。
なので、前半で説明した通りの対応を実施して解決。
Guideline 2.1 - Information Needed
ログアウトするボタン無いから用意してくれ、ときた。
しかし、実際はしっかり用意してるので動線教えて解決。
Guideline 5.1.1 - Legal - Privacy - Data Collection and Storage
本アプリのコア機能にユーザー登録は不要なはずなのでアカウント無しでも使えるようにしてくれ、とのこと。
元々、登録必須系は厳しいと知っていたのでこちらは下記の内容で返信。
- アプリ内ではユーザーからの情報の申請依頼やログ情報を一部取得している
- これらはアプリの健全的な運営のために必須なのでアカウント登録は必要
リジェクト - 3度目(記憶頼り)
Guideline 5.1.1 - Legal - Privacy - Data Collection and Storage
Appleから「挙げてくれた理由はどれも運営側の都合。このアプリのコア体験にはどうしても必要とは思えないからアカウント登録無しでもできるように対応してほしい」と返信が来た。
確かに、振り返ってみると全て運営都合。
この時、自分はまだまだユーザファーストな考えが出来ていないことを思い知った。
ゲストアカウントを通じてログインできるように変更して対応して無事解消。
Guideline 4.0 - Design
iPadでレイアウト確認すると崩れてるから修正してほしい、とのこと。
しかし、Xcode/App Store Connect双方でiPadは対応デバイスに設定していないのでその旨で返信。
リジェクト - 4度目
Guideline 4.0 - Design
Appleから「iPadに対応していないことは知っているがAppleユーザーは全デバイスで使えることを期待してインストールするから対応してほしい」とのこと。
そうなると、なぜXcode/App Store Connectでサポートデバイスを指定するのか?という疑問が出てきたがAppleのユーザファーストっぷりを見れば納得できる一面もある。
Guideline 4.2.2 - Design - Minimum Functionality
Appleから「アプリの機能少なすぎない?ただ単純にYouTuberのチャンネルを紹介してるだけじゃん」と言われた。
しかし、本アプリのコアはタグを用いたAND検索によるものなのでそれを下記のように説明して解決。
TOEIC 795点の能力が活かせた...!
Guideline 2.3.10 - Performance - Accurate Metadata
アプリ内で"Android"という単語を使っていたのだがそれが指摘された。
これは消してもユーザに不便を与えることはないものだったので削除して解決。
ストア審査から学んだこと
SNSのような他者の情報(著作権)を扱うものはかなり審査が厳しい
今回、合計4回のreject喰らって5回目で通過しました。
期間も1ヶ月くらい要しました。(仕事もちょうど忙しく)
著作権周りが今回最も苦労しました。こう考えると、SNSやApple Musicなど著作権が絡むものを使う時は事前に問題ないかどうかチェックする必要がありますね。
開発走り切ったのに法的要件でNGにされたら絶望です。
アカウント登録強制は"ユーザー目線から見て"相応の理由がないと絶対に通らない
前々からアカウント登録の強制は指摘されやすいというのは知ってました。
が、アプリ内情報の収集やユーザーからの問い合わせなどアプリの健全的運営を理由に登録を必須にしてそれを盾にAppleへ返信したが前述の通りNG。
ユーザー目線から見て必須な理由がないとダメで、上記都合は全て運営である僕の理由だけだったことに気がついた。
toC向け個人プロダクト開発者としては失格だったなぁと反省。
まとめ
開発期間5ヶ月と長引きましたが無事リリースという一つの区切りまでやりきれてよかったです。
エラーハンドリングやAnalytics情報などまだまだ手が届いておらず荒削りな部分だらけです。(謙遜ではなく)
まずは、Facebookのように地元の友人などにアプリ使ってもらってフィードバック改善して範囲広げてくスモールスタートで長くやってこうと思います。