uka.apple のすべての投稿

実録!アプリ申請リジェクト — その後

あまりだらだら続けてもしょうがないので、その後の経緯をざっくりまとめ、最後に対処方法を書いてみます。

その後の経緯:

 App Store Connectサイトでアプリのメタデータを更新できなかった件については、その後1か月くらい経過した時点でいつの間にか更新できるようになっていました。さすがに開発者皆が利用しているサイトなので、どこかの時点でAppleが対応せざるを得なくなったと推察します。

「あなたのID」は、いつどこで利用するのですか?の質問ですが、これに関しては画面遷移図を作成し、トップ画面からどのような経路で「あなたのID」がある画面にたどり着くか、また「あなたのID」部分を赤まるで囲って、画像を見た瞬間に誰でもわかる形にしたところOKになりました。驚くことに、その後再度「あなたのID」は、いつどこで利用するのですか?と同じ質問が来たりしてびっくりしましたが、再度同じ画像を提示して事なきを得ました。

「利用規約(EULA)」と「プライバシーポリシー」の件についても、画面遷移図を作成し、赤まるで囲って”ここにあるよ”と明示したら通りました。

その後も、具体的な内容は省きますが上記以外の質問を5,6問ほど次々にされました。それも一度にではなくこちらが回答を返す度に、モグラたたきのように目についたから今回はこの質問!というような形でリジェクトと共に質問を投げられました。この審査員の方の仕事のやり方には疑問を感じざるを得ません。

対処方法:

アプリ審査員は当たり外れがある気がします。過去5,6アプリほど申請してリジェクトを喰らうことは全くなかったのですが、今回は急にリジェクトの嵐でした。

肝心の対処方法ですが、私が今回当たった審査員の場合、相手をスマホをさわったことのないIT素人だと思ったらうまくいきました。

こちらで当然だと思うことは全く通じないので回答として直感的に理解できる画像を多用しました。

明らかに審査員の落ち度だと思ってもそれを指摘せず、無感情で淡々と、誰にでもわかりやすい表現(画像)で説明しました。

付け加えると、こちら(開発者)に寄り添うような姿勢はみじんもなかったので、まじめな方ほど感情的になってしまい審査がうまくいかない気がします。

感情を殺して、素人相手だと思って過剰すぎるほど分かり易い回答を淡々と返すのが最善かと思います。

以上です。アプリ開発者の方に少しでもこの経験がお役に立てば幸いです。

実録!アプリ申請リジェクト — 初回の3つのリジェクト理由つづき 

初回の申請では3つのリジェクト理由を言い渡されました。前回のとおりそれぞれの理由に対して回答を返しましたが、Appleからの回答は、回答にならない回答でした。

以下の赤字が、私の回答に対するAppleの回答です。

リジェクト理由その1:

「あなたのID」は、いつどこで利用するのですか?

リジェクト理由1への回答:

・ユーザーがアプリを再インストールしたときや、iPhoneやiPadを買い替えたときです。 
・トップメニューの「そのほか」を選択後、「別デバイスの変更」からこのIDを利用します。

→ この回答に対する、Appleの回答:

続けるにはより多くの情報が必要です。

リジェクト理由その2:

あなたのアプリは、Paid Applications 契約の Schedule 2, Section 3.8(b) に規定される自動更新サブスクリプションの条件をすべて満たしていないことに気づきました。

以下の必要な情報が見つかりませんでした。
・プライバシーポリシーへの機能的なリンク
・利用規約(EULA)への機能的なリンク

リジェクト理由2への回答:

・トップメニューの「そのほか」を選択した後で表示される画面で「利用規約(EULA)」と「プライバシーポリシー」を表示するようにしました。

→この回答に対する、Appleの回答:

続けるにはより多くの情報が必要です。

リジェクト理由その3:

アプリのメタデータに次の必須項目が見つかりませんでした。
・使用条件(EULA)への機能的なリンク

リジェクト理由3への回答:

この2週間、何回も App Store Connect の「App情報」「使用許諾契約」からアプリのメタデータを変更しようとしましたが、 以下のエラーメッセージが表示されて変更することが出来ませんでした。
「変更内容を保存できませんでした。しばらくしてからもう一度お試しください。」

→この回答に対する、Appleの回答:

アプリのメタデータに使用条件(EULA)への機能的なリンクが見つかりませんでした。

・・・この回答を見た時「あ、そういう対応か・・・」と思いました。

面倒ごとはゴメンだ。そっちですべてなんとかしろよ。なんらかの揺るがない証拠を出してこない限りこっちは何もしないよ。

という感じでしょうか。

リジェクト理由1と2に関しては、こちらが説明しているにもかかわらず情報を更に寄越せと言ってきました。たぶんアプリを何も見てくれてないか、日本語が読めない方なのかもしれません。

リジェクト理由3に関しては、初回の回答と全く変化がありません。システムの不具合調査は面倒なので知らん、という感じでしょうか。

次回へ続く・・・

実録!アプリ申請リジェクト — 初回の3つのリジェクト理由

初回の申請では3つのリジェクト理由を言い渡されました。その内の1つ目のリジェクト理由は、以下のような簡潔な一文でした。

リジェクト理由その1:

「あなたのID」は、いつどこで利用するのですか?

「あなたのID」とは、スマホを買い替えた時などデータ移行に必要となるIDです。

「あなたのID」を表示する画面には、注釈で「※利用デバイスを移行する際に必要となります。」としっかり記述してあります。また、そのすぐ下の「別デバイスのIDに変更」のリンクで利用するようになっています。

このリジェクト理由をみて私は思いました。たぶん日本語がわからない人が審査してるのかな、と・・・

リジェクト理由その2:

あなたのアプリは、Paid Applications 契約の Schedule 2, Section 3.8(b) に規定される自動更新サブスクリプションの条件をすべて満たしていないことに気づきました。

以下の必要な情報が見つかりませんでした。
・プライバシーポリシーへの機能的なリンク
・利用規約(EULA)への機能的なリンク

プライバシーポリシーに関しては作成済みで、こんな文章です。アプリ内課金を実装したアプリは、アプリ内からこの文章へのリンクを追加する必要があるようです。

利用規約(EULA)に関しては未作成だったため頑張って作成しました。Steam向けの利用規約生成ツールなどが出回っていたため、それを利用したりしました。

リジェクト理由その3:

アプリのメタデータに次の必須項目が見つかりませんでした。
・使用条件(EULA)への機能的なリンク

リジェクト理由3に関しては、App Store Connect の「App情報」「使用許諾契約」からアプリのメタデータにEULAを記述する必要があります。しかしいくら記述して更新しようとしても、 以下のエラーメッセージが表示されて変更出来ませんでした。

「変更内容を保存できませんでした。しばらくしてからもう一度お試しください。」

なにか不備があるのか?と思いましたが何度チェックしてやり直したり、時間を置いてみても(2週間近くも!)ダメでした。単にApp Store Connect のサイトのバグのようでした。(開発者の皆さんなら経験があるかと思いますが、開発者が利用するApp Store Connect のサイトは比較的バグが多いです・・・)

わたしは利用規約(EULA)を作成後、プライバシーポリシーと利用規約(EULA)をアプリ内からリンクして表示可能にし、Appleに対して以下のように回答すると共に新しいアプリを申請しました。

リジェクト理由1への回答:

・ユーザーがアプリを再インストールしたときや、iPhoneやiPadを買い替えたときです。
・トップメニューの「そのほか」を選択後、「別デバイスの変更」からこのIDを利用します。

リジェクト理由2への回答:

・トップメニューの「そのほか」を選択した後で表示される画面で「利用規約(EULA)」と「プライバシーポリシー」を表示するようにしました。

リジェクト理由3への回答:

この2週間、何回も App Store Connect の「App情報」「使用許諾契約」からアプリのメタデータを変更しようとしましたが、 以下のエラーメッセージが表示されて変更することが出来ませんでした。
「変更内容を保存できませんでした。しばらくしてからもう一度お試しください。
• 使用許諾契約の変更を保存できませんでした。しばらくしてからもう一度お試しください。」

この時はこれでアプリ修正に関しては全て完了し、リジェクト理由3に関してはAppleが App Store Connectサイトのバグを認めて対処してくれると信じていました・・・

次回へ続く・・・

実録!アプリ申請リジェクト — アプリ内課金編

先月末、アプリ内課金を組み込んだテキスト漫画がリリースされました。

実は、リジェクトを8回受け、リリースまでになんと半年もかかりました。


リジェクトのショックで精神的に作業を行う気になれず、対応の間が空いてしまったこともありますが・・・

 
    /\ ⌒ヽ ペタン
   / /⌒)ノ ペタン
  ∧∧\/ ((  ∧_∧
 (´ Д ) ^ ) ))(・∀・;)
 / ⌒ノ(⌒ヽ⊂⌒ヽ
(  ノ ) ̄ ̄(0_ )
 ))_)  (   ) (_(
    アップル    開

アプリ内課金(月額)の実装ということもあり、通常よりもアプリ審査が厳しかったのかもしれません。

しかし、審査が厳しい=アプリの品質が上がるということですので、開発者は大変かもしれませんが、一般ユーザにとっては嬉しいことだと思います。

ここでは、開発者の皆さんのアプリ申請がスムーズに行くことを願い、アップルのリジェクト理由を記述しようと思います。

~~\(゚-゚*)バサッ

次回へ続く・・・

テキスト漫画 お気に入り機能の使い方

テキスト漫画アプリにお気に入り機能を追加※しました。

利用手順は以下の通りです。

※月額利用料金(最低限)がかかります。

まず、トップ画面から「そのほか」を選択します。

「App内課金」をタップします。

お気に入り機能を購入します。

購入できたら、画面下の「閉じる」をタップします。

トップ画面に戻ると、メニューに「4.お気に入り」が追加されているので、タップします。

初期状態ではお気に入りに1つも登録されておらず何も表示されません。メニュー1〜3で気に入ったテキスト漫画の「☆」をタップしましょう。

お気に入りに登録したテキスト漫画は「☆」が「★」に変化します。

「ペシ!ペシ!」を登録

メニュー4に戻ると、先ほど登録したテキスト漫画が表示されます。

これで、たくさんのテキスト漫画の中からあなただけのコレクションを作成できます!!

テキスト漫画アプリを気に入っていただけましたら是非ご利用ください。

テキスト漫画アプリ 機種変更時等に利用する移行コードについて

近日、テキスト漫画に「お気に入り」や「広告オフ」の機能追加を予定しています。

アップデート申請時にAppleより、機種変更時等に利用する移行コードについて、1つのコードだけでの認証ではダメで(今までテキスト漫画では「あなたのID」というコードだけで移行可能でした)、2つ以上のコードが必要との指摘を受けました。

よって、次回バージョンからは「あなたのID」に加えて「ニックネーム」も必要になるため予めご承知おきください。

テキスト漫画アプリへのお気に入り機能や広告オフ機能の追加について

現在、テキスト漫画アプリに気に入ったテキスト漫画を登録していつでも呼び出せる「お気に入り機能」や「広告オフ機能」を月額課金(最低限の価格)で利用可能にすべく、アップルにアプリのアップデートを申請しておりますが、

5、6回ほど

リジェクトを喰らっておりアップデートできておりません。

 
    /\ ⌒ヽ ペタン
   / /⌒)ノ ペタン
  ∧∧\/ ((  ∧_∧
 (´ Д ) ^ ) ))(・∀・;)
 / ⌒ノ(⌒ヽ⊂⌒ヽ
(  ノ ) ̄ ̄(0_ )
 ))_)  (   ) (_(
    アップル    開

現在リジェクトされないよう鋭意アプリ改善中ですので、新機能を利用したいユーザ様はしばらくお待ちください・・・

     -ーー ,,_
   r'”      `ヽ,__
   \       ∩/ ̄ ̄ ヽつ
  ノ ̄\ /”ヽ/ ”   ノ   ヽi
 |  \_)\ .\    ・  ・ |\
 \ ~ )     \ .\_  ( _●_)\_つ
    ̄       \_つ

StoreKit2でシンプルな課金処理 第7回

今回は購入情報の復元(リストア)処理を行います。

※裏作業でSwiftUIにより、UI上部に”購入情報の復元(リストア)”のリンクを追加しました。課金処理の範囲外になるので「SwiftUI」の記述は詳しく行いませんが、第4回で紹介したAppleのサンプルコードが参考になると思います。

もちろんエラー処理は必要になりますが、購入情報の復元(リストア)処理を行うコードは、基本的に以下の1行だけです!

let result = try? await AppStore.sync()

課金アイテムの取得、課金処理、に続いて今回も1行だけです!

このコードが実行されると、ログインしているAppleIDに紐づいた課金情報が復元されるはずです。ログインしていない場合、ログインを促されると思います。

復元後、UIが更新されるはずですが、サンプルコードにはUIを更新するコードが一見、見当たりません。

おそらく、以下の

updateListenerTask = listenForTransactions()

このコードにより、トランザクションリスナーが開始されているため、課金関連処理が行われるとこのリスナーに検知されてUIが自動で変わる仕組みだと思います。(もし違ったら、詳しい方、コメント欄で教えてください!)

心配な場合は、リストア後にUIを更新するコードを念のため書いておくと良いかと思います。


以上、第1回から今回までで、課金に必要な処理は一通り説明しました。少し前までは、課金処理はとても複雑・面倒で、手を出しにくかった開発者の方も多かったかと思います。特に個人開発者の場合、できれば余分な実装に手間をかけたく無いですよね。

今回、StoreKit2で手軽に課金処理を実装可能になりましたので、いままで手を出せなかった方もどんどん手を出していただければと思います!

(੭ ˃̣̣̥ ω˂̣̣̥)੭ु⁾⁾

StoreKit2でシンプルな課金処理 第6回

今回は課金アイテムの購入処理を行います。

※実は裏作業で、「SwiftUI」により課金用のUIを作り込みました。課金処理の範囲外になるので「SwiftUI」の記述は詳しく行いませんが、第4回で紹介したAppleのサンプルコードが参考になると思います。

価格が表記されたボタン押下時に課金処理コードを記述します。

もちろんエラー処理は必要になりますが、基本的に課金処理を行うコードは以下の1行だけです。

let result = try await item.purchase()

※”item” は前回取得した課金アイテムです。

このコードが実行されると、

上記の画像のように、課金アイテムの詳細表示と、購入を促すUIが自動で表示されます。

以上です!

(੭ ˃̣̣̥ ω˂̣̣̥)੭ु⁾⁾

あとは、購入済みアイテムの場合、価格ボタンを購入済みと分かる表示に切り替える処理などが必要ですが、この部分も第4回で紹介したAppleのサンプルコード(function isPurchasedの部分)が参考になると思います。

Appleのサンプルコードは一見コード量が多いですが、課金を行うStoreKit2の中核部分は本当にシンプルなコードです。

UIの部分は皆さん好みがあると思いますので、Appleのサンプルコードを参考に独自に実装していただければと思います。

次回は、”リストア”処理を行います!!

(੭ ˃̣̣̥ ω˂̣̣̥)੭ु⁾⁾

Javascript と PHP での iOS(iPad)判定

iOS13以降、HTTP_USER_AGENTだけではiPadとPCの区別がつかなくなりました。

これは、9.7インチ以上のiPadの場合にHTTP_USER_AGENTに”iPad”の文字列が含まれなくなり、PCのブラウザ(MacのSafari)からのアクセスとほぼ同じになるケースが発生したからです。

PHPの場合、以前は以下のようなコードでiOS判定が可能でしたが、現在は無理です。

function is_ios() {
    $ua = $_SERVER['HTTP_USER_AGENT'];
    // iOSと判定する文字リスト
    $ua_list = array('iPhone', 'iPad', 'iPod');
    foreach ($ua_list as $ua_smt) {
        if (strpos($ua, $ua_smt) !== false) {
           return true;
        }
    } return false;
}

Javascript ならば、HTTP_USER_AGENTに加えて ‘ontouchend’ なども利用する事で判別可能です。

const ua = window.navigator.userAgent.toLowerCase();
const isIOS = ua.indexOf("iphone") >= 0 
   || ua.indexOf("ipad") >= 0 
   || (ua.indexOf('macintosh') > -1 
       && 'ontouchend' in document) /* iPad */
   || ua.indexOf("ipod") >= 0;

PHP内で判定するには、JavascriptなどからPHPに判定済みの情報を渡すことが必要です。

2022年2月現在、Mac、Windows、iPadからのHTTP_USER_AGENTは以下となります。

[Mac の Safari] と [iPad(9.7インチ以上)の Safari] の違いがほぼ無いことが確認出来ます。

[Mac の Safari]
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Safari/605.1.15:

[Mac の Chrome]
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.80 Safari/537.36

[Windows の Chrome]
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.82 Safari/537.36

[11インチiPad Pro(第2世代) の Safari]
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.1 Safari/605.1.15

[11インチiPad Pro(第2世代) の Chrome]
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) Mozilla/5.0 (iPad; CPU OS 15_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/97.0.4692.84 Mobile/15E148 Safari/604.1

[iPad mini(第6世代) の Safari]
Mozilla/5.0 (iPad; CPU OS 15_2_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.2 Mobile/15E148 Safari/604.1