Shuntiger Tech Diary

iOS/Androidエンジニアが気になる技術やガジェットなどを気のままお届けするブログ

AppStoreにKids Category向けのアプリを申請して、リジェクト対応した話

Kids Category とは

AppStoreに5才以下〜11才以下を対象とした"子供向け"というカテゴリーがあり、そこに掲載しているアプリのこと。 子供を対象にしたアプリを作成した場合、AppStoreConnectの年齢制限編集で"子供向けに制作に"チェックが必要になる。 そうしないとAppStoreの子供向けカテゴリーに表示されないため、子供が触る機会が少ないアプリになるので対応は必要になる。

f:id:hyaku-juu-ichi:20200209113122p:plain

ただ子供向けアプリは、17才以上を対象としたアプリと比べて審査が少し厳しくなる。 子供向けアプリは子供向けAppのApp Reviewガイドラインに準拠する必要がある。

子供向けAppのガイドライン

f:id:hyaku-juu-ichi:20200209115619p:plain

ガイドラインに記載してあるように次の通りにアプリを準拠しなければならない。

  • App外へのリンク、課金要素などに関しては子供が触れないようにペアレンタルゲートをつける必要がある。
  • 「子ども向け」カテゴリのAppには、他社製の分析機能や広告を組み込むことはできない。
  • IDFA(広告ID)を取得してはいけない
  • 子供を特定できる情報は取得してはいけない
    • 名前
    • 生年月日
    • メールアドレス など

リジェクト内容

  • You have selected the Kids category for your app, but it includes links out of the app or purchasing opportunities without first obtaining parental permission. Kids Categoryなのにアプリ内にペアレンタルゲートが組み込まれてない、という内容。

    • 対応: ペアレンタルゲートを追加で実装して対応。問題はランダムにする必要がある。他にリリースしてるアプリを参考に実装。
  • your app appears to use IDFA. アプリ内でIDFAを使っていますよ、という内容。 アプリ内ではIDFAは取得してはいないはずなのに...、ただFirebaseは使っているので、よく調べてみるとFirebase AnalytycsとClashlytycsでIDFAを送信している処理を行なっているみたい。 Firebase Help 「デバイスの識別」より"デフォルトでは、Firebase SDK はモバイル端末の識別子(例: Android の広告 ID や iOS の広告識別子)を収集し、Cookie に似たテクノロジーを利用します。" とのこと

アナリティクスとクラッシュログを使いたかったため、FirebaseからReproやAppsFlyerなどのサービスに乗り換えようと検討したが、 いずれもIDFAを取得するコードがSDK内に組み込まれているため、断念。

Repro SDKで取得しているデータ・情報について知りたい | Repro Knowledge Base
https://support.repro.io/ja/articles/2001441-repro-sdk%E3%81%A7%E5%8F%96%E5%BE%97%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E3%83%87%E3%83%BC%E3%82%BF-%E6%83%85%E5%A0%B1%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E7%9F%A5%E3%82%8A%E3%81%9F%E3%81%84

そもそもガイドラインには「子ども向け」カテゴリのAppには、他社製の分析機能や広告を組み込むことはできません。 と記載されているのでFirebase以外も組み込むのは難しそう。 子供を特定しない範囲でデータを収集したい場合は、自分たちでサーバーサイドの部分も実装が必要になるかと思います。

審査をする前に毎回以下の質問が聞かれていたので、ガイドラインに準拠するのは厳格に決められている感じがします。

  • Does your app include third-party analytics? If so, please provide details about what data is collected for this purpose.

アプリにサードパーティの分析が含まれていますか?その場合、この目的のために収集されるデータの詳細を入力してください。

  • Does your app include third-party advertising? If so, please provide a link to the ad network's publicly-documented practices and policies for kids apps.

アプリにサードパーティの広告が含まれていますか?その場合は、広告ネットワークで公開されている子供向けアプリの慣行とポリシーへのリンクを提供してください。

  • Will the data be shared with any third parties? If so, for what purposes and where will this information be stored?

データは第三者と共有されますか?その場合、この情報はどのような目的でどこに保存されますか?

  • Is your app collecting any user or device data for purposes beyond third-party analytics or third-party advertising? If so, please provide a complete and clear explanation of all planned uses of this data.

アプリは、サードパーティの分析やサードパーティの広告以外の目的でユーザーまたはデバイスのデータを収集していますか?その場合、このデータのすべての計画された使用法の完全かつ明確な説明を提供してください。

参考

Main.storyboardをリネームして使うときにハマったこと

Xcodeでプロジェクトを作成したときに元々用意されているMain.storyboardをリネームして使おうとしたときにハマった。

環境

  • Xcode11.1

 試したこと

  • Main.storyboardをProject Navigatorからリネーム
  • Info.plistからMain storyboard file base nameをリネーム
  • Main Interfaceを確認 リネーム後の名称に置き換わっていることを確認

ビルドすると"NSBundleでMain.storyboardが見つかりません"というエラーが表示される。

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Could not find a storyboard named 'Main' in bundle NSBundle </Users//Library/Developer/CoreSimulator/Devices/069B163A-4FE5-4849-9299-CBF8EB19FF02/data/Containers/Bundle/Application/6FE49B37-3698-4E8F-ACA7-C9AB43F6DBC0/<project_name>.app> (loaded)'

今まではこのやり方で問題なかったはずなのに🤔

解決方法

  • Info.plistにある"Storyboard Name"にあるValueがMainのままになっていたのでリネームしたら直った。

Info.plist>Application Scene Manifest>Scene Configuration>Application Session Role>Item 0 (Default Configuration)>Storyboard Name⬅︎ココ

f:id:hyaku-juu-ichi:20200209111603p:plain

  • 原因はXcode11, iOS13からInfo.plistに"Application Scene Manifest"の設定が追加されたことにあった。

参考

Github pagesで複数ページを作成する

AppStoreのサポートURL、プライバシーポリシーURL用にGithub pagesを使ってWebページを作成した。

以下のサイトの gh-pagesブランチを親なしで作成するを試した。

gh-pagesをサブディレクトリ内で管理 - Kludge Factory https://tyfkda.github.io/blog/2016/06/07/git-worktree.html

Tips

  • $ git checkout --orphan gh-pages でメインで作業しているブランチとは独立したブランチになる。

作業用ブランチとHTMLファイルのブランチを分けるのが目的。 ブランチを作成後はcheckoutしてxxx.htmlを作成してpushすればWebから見れるようになる。 root直下にhtmlファイルを置くと以下のようなURLになる。 https://<User>.github.io/<Repository_name>/xxx.html

以下の場合、supportprivacypolicyディレクトリ名。

https://.github.io/<Repository_name>/support/index.html https://.github.io/<Repository_name>/privacypolicy/index.html

root直下に support.htmlprivacypolicy.html として置いてもWebページが見れたので、 わざわざディレクトリを作ってhtmlファイルを分ける必要はなさそう。

こんなときに使える

  • AppStoreの申請に必須なサポートURL、プライバシーポリシーURLがコストがかからずに用意できる。
  • ポートフォリオページなど。

まとめ

gh-pages ブランチを作成して公開の他にも masterに /docs ディレクトリを作成などの公開方法もある。 /docs ディレクトリを使用して公開の方が簡単そう。 リポジトリをhtmlだけとはいえpublicにするのが不安だったため、gh-pagesの方法を採用したが、 特に問題ないようであれば次回は /docs の方でやってみる。

参考

Lets Build That App - AppStoreJSONAPIsコース #3

f:id:hyaku-juu-ichi:20200208234637p:plain f:id:hyaku-juu-ichi:20200208234648p:plain

学び

  • イニシャライザのテクニック
    • 戻り値がCGSizeの場合、CGSize(width: , height: ) と書くのではなく、.init(width: , height: ) で書いた方が簡潔に書ける。
    • UIEdgeInsetsなどの型の初期化に便利そう。
  • collectionviewのCellのサイズを変更するときはUICollectionViewDelegateFlowLayoutに準拠してからsizeForItemAtでCGSizeをreturnする。

Lets Build That App - AppStoreJSONAPIsコース #2

f:id:hyaku-juu-ichi:20200208233324p:plain

f:id:hyaku-juu-ichi:20200208233827p:plain

学び

  • refactorのテクニック
  • コード内で重複している所はメソッドに置き換える。
  • 重複しているところはコメントアウトして段階的にリファクタリングしていく。 入れ替えができ、ビルドエラーがなくなったり、想定通りに表示されたらコメントアウトしている箇所を削除する。

Lets Build That App - AppStoreJSONAPIsコース #1

Lets Build That Appで「AppStoreJSONAPIs」コースが25ドル安い$75ドルになっていたので人生初Paypalで購入した。(元値は$100) 時々こういうセールはあるみたい。 もともと気になっていたので、買うしかないと思った。 Lessonは 58個 最初の2レッスンで以下の画像なような感じになります。

まだtabBarControllerをセットした段階。 これからAppStoreに仕上がっていくとワクワクする。

AppStore JSON APIs | Lets Build That App https://www.letsbuildthatapp.com/course/AppStore-JSON-APIs

f:id:hyaku-juu-ichi:20200128001614p:plain

学び

  • viewControllersで表示するVCを管理する。 viewControllers - UITabBarController | Apple Developer Documentation https://developer.apple.com/documentation/uikit/uitabbarcontroller/1621185-viewcontrollers
  • Deployment InfoのMain Interfaceを削除しなくても、 AppdelegateのdidFinishLaunchingWithOptionsで以下のように書けば、コードが優先されること。

  • vimeoなのでYoutubeのように日本語字幕ができない。 自動翻訳ソフトを使えばなんとかなると思うが、英語でも半分くらいは意味が理解できる。

Could not launch "App名"のエラーが出たときの対処方法

f:id:hyaku-juu-ichi:20200127202555p:plain

 

実機でビルドした際に上記のタイトルのようにCould not launch "App名" でエラーが出た。

シミュレーターでビルドすると出ない。。

SchemeからRunとArchiveをDebug用にしたらエラーが出ない🤔

 

解決方法

Provisioning ProfileをDebug用に変更したらエラーが出なくなった。

RUNするときにRelease用のProvisionnig Profileだったからエラーが起きた。(AdHocやAppStoreなど)

 

 

まとめ

Releaseのプロビではブレークポイントで止めるなどデバッグできないことがわかった。

Release版を実機でビルドしたい、ブレークポイントで止めたい時はProvisioning ProfileがDebug用になっているか確認をする。