The Pragmatic Ball boy

iOSを中心にやってる万年球拾いの老害エンジニアメモ

Phrasieを支える技術

個人開発で2023年5月末にiOSアプリをリリースしました!

apps.apple.com

有料アプリなので課金はしなくてもよいですが(してもいいですよ!)、星5をなにとぞお願いします!!!


どんなアプリかというと、英語勉強を支援するアプリです。 機能としては、日本語で日記を書き、それを英語に自動で翻訳し、翻訳された英語の発音を聞いてシャドーイングの練習をするというアプリとなっています。

これまで個人開発では無料のアプリをつくってきましたが、今回は有料のAPIを用いているというのもあり個人開発では初の月額課金のアプリとしています。

使っている技術

  • フルSwiftUI
    • 個人開発ということでサポートOSを最新のみとしてSwiftUIをフル活用できるようにしました
  • Firestore
    • データはFirestoreで永続化しています。最初はお金がかからないのでCoreDataにしようかと思ったのですが、APIがアレなので 有料サービスということもあり機種変時のデータ移行とかもちゃんとできないといけなかったりするので、そのへんのテストはCoreDataだと確認が結構めんどくさいなということでFirestoreにしました。(リリースしたのがiOS17が発表される前だったのでもう少し遅ければSwiftDataを使ってみたさで意思決定は変わっていたかもしれません。。)
    • ちなみにRealmはアップデートが頻繁すぎて個人開発で最新追従し続けるのがたいへんなので選択肢からははずしています。
  • FirebaseAuthentication
    • データの引き継ぎのためにアカウントと紐付ける必要がありFireStoreと同様にFirebaseのFirebaseAuthenticationを利用しました。
  • RevenueCat
    • 課金周りは自分でサーバー側やるのはたいへんなのでRevenueCatを利用しました。
  • 英語周りのところ
    • こちらに関しては企業秘密ということで・・

ハマりどころ

AppStoreでのリジェクト

その1

"次のサブスクリプション期間の支払いが自動的に開始されることを明確にしていません" と怒られました。結構ちゃんと書いてたつもりでしたが完璧は難しいですね・・

We noticed that one or more of your auto-renewable subscriptions is marketed in the purchase flow in a manner that may mislead or confuse users about the subscription terms or pricing. Specifically: 

- Your app offers a free trial or introductory period but does not make it clear that a payment will be automatically initiated for the next subscription period.

Next Steps

To resolve this issue, please revise your auto-renewable subscription purchase flow to clearly indicate how long the free trial lasts and the amount that will be billed after the free trial is over. 

その2

App Store Connectのプライバシーでユーザー追跡をしているにチェックを入れていたら、AppTrackingTransparencyのポップアップの表示が必須ということでした。

The app privacy information you provided in App Store Connect indicates you collect data in order to track the user, including Product Interaction, Other Usage Data, Customer Support, Search History, Browsing History, User ID, Purchase History, and Crash Data. However, you do not use App Tracking Transparency to request the user's permission before tracking their activity.

Starting with iOS 14.5, apps on the App Store need to receive the user’s permission through the AppTrackingTransparency framework before collecting data used to track them. This requirement protects the privacy of App Store users.

その3

スクショにアプリの画面入れるのが面倒だったので、最初はなくていいかーと思ってたら、ダメ出しされました・・

We noticed that your screenshots do not sufficiently show your app in use. Specifically, your screenshots do not show the actual app in use in the majority of the screenshots.

To help users understand your app’s functionality and value, your screenshots should highlight your app's core concept. For example, a gaming app should feature screenshots that capture actual gameplay within the app.

Next Steps

Please revise your screenshots to ensure that they accurately reflect the app in use on the supported devices.

Keep in mind the following requirements: 

- Marketing or promotional materials that do not reflect the UI of the app are not appropriate for screenshots.
- The majority of the screenshots should highlight your app's main features and functionality.
- Confirm that your app looks and behaves identically in all languages and on all supported devices.
- Make sure that the screenshots show your app in use on the correct device. For example, iPhone screenshots should be taken on iPhone, not on iPad. 

その4

AppleIDログインを必須にしていたらリジェクトされました。 新規登録時には登録不要にしてあとからAppleID連携できるように変更しました。ただ、AppleID連携は機種変時のアカウント移行をトラブルなく行うために必要不可欠にしたかったのでこのリジェクトは結構痛かったです。

We noticed that your app requires users to register with personal information to purchase in-app purchase products that are not account based. 

Apps cannot require user registration prior to allowing access to app content and features that are not associated specifically to the user. User registration that requires the sharing of personal information must be optional or tied to account-specific functionality.

Next Steps

To resolve this issue, please revise your app to not require users to register before purchasing in-app purchase products that are not account based. You may explain to the user that registering will enable them to access the purchased content from any of their iOS devices and provide them a way to register at any time, if they wish to later extend access to additional devices.

Please note that although App Store Review Guideline 3.1.2 requires an app to make subscription content available to all the iOS devices owned by a single user, it is not appropriate to force user registration to meet this requirement; such user registration must be optional.

ここについては初回起動時はFirebase Authenticationの匿名ログインを使ってユーザーを作成し、アカウント移行のために別途AppleIDログインを用意することで対応しました。

実装についてはこちらに記事に書きました。

qiita.com

技術的なところ

Firestore

ちょっとDB設計に戸惑いました。

今回の場合、各ユーザーが作成したユーザーのみが閲覧できる日記データを持つといった構造になります。

RDBの場合だと雑に考えるとdiaryテーブルとuserテーブルをつくってdiaryのカラムにuser_idもたせるみたいな感じになるかなと思います。

Firestoreの場合セキュリティルールで自分に関するデータしか見れないようにしたりしたいので、

- users
  - userID
    - diaries(サブコレクション)

このようなデータスキームにすることで、ユーザーIDに対しそのユーザーの日記のみを紐付け、必要な日記データのみを取ってこれるようにしました。Firestoreは読み取り、書き込み、削除したドキュメントの数で料金がかかってくるので、無駄な読み込みが減りコスト削減にもなっているかなと思います。

日記数も、countをつかうとデータを全部舐める必要が出てくると思うので日記数も保存するようにするなどといったこともやりました。

RevenueCat

RevenueCatはものの数十行で課金処理ができちゃうのでめちゃくちゃ便利でした。 実装自体はすごい簡単なのですが、どちらかというと設定のほうに苦労しました。 というのもRevenueCatはiOS/Androidといったプラットフォームによる課金の違いを別の概念で抽象化して表現しているので、その概念を理解するのにちょっと戸惑いました。

その他

アプリを公開するにあたって、プライバシーポリシーや利用規約などを用意しなければならず、これらのWebページをどうやって用意するかがちょっとこれまでは面倒でした。 今回Notionを使って試しにやってみたところ、普通に審査でも突っ込まれず、実装や運用コストもかからずめっちゃ楽で最高でした。

失敗したなというところ

Firestoreで検索ができない

like検索くらいはできるだろうと思っていたら、あとから検索機能をつけようとしたらないことが判明して結構困っております。。 ElasticやAlgoliaなどの3rd partyのサービスと連携すればできるみたいですが、ちょっと個人開発だと財政面できびしそうです

firebase.google.com

ローカライズ

海外展開すれば人口で単純計算するとダウンロード数めっちゃ増えるだろうと思って頑張って韓国語、中国語対応もいれたのですが、びっくりするほどダウンロードされていない、というか閲覧すらされていないので全然コスパよくなかったです。。 単純に翻訳しただけでは駄目という学び。

最後に

今回はじめて無料ではなく有料アプリをリリースしてみました。 幸い数名の方にご利用いただきとてもありがたいです。 今後も少しずつ改善していきたいなと思っています!