2016年6月27日月曜日

GoogleのAdMobを組み込む

iAdが廃止になり、サードパーティ製の広告サービスの組み込みが必須になったので、GoogleのAdMobというのを組み込むことにした。
はやりのインターステーシャル広告とかなんとかにも対応してる。

以前はリリース済みアプリじゃないと広告が表示できなかったようだが、現在はリリース前のものにも組み込めるようになったようだ。(リリース後にAdMobの管理画面でAppStoreの情報とリンクさせる必要はある)

概要

やることは以下のように単純。
  • AdMobのFrameworkをプロジェクトにインポートする
  • クラスの先頭で import GoogleMobileAds する
  • StoryboardもしくはコードからGADBannerViewクラスのオブジェクトを作って表示させる
Firebaseというモバイルサービスプラットフォームの一部なのだそうだが、説明が英語なのでよくわかってない。
とりあえずFirebaseを設定しなくても広告は表示できるようなので、わかったら追記したい。

手順

AdMobのアカウントを作成

AdMobのサイトでアカウントを作成する。
細かいやり方は略す。

アプリを追加

  • 新しいアプリの収益化
    • AppStoreにリリース済みのアプリを、アプリ名や開発者名で検索して選択
  • 広告フォーマットを選択
    • 通常のバナー広告(画面隅に出しとくアレ)
    • インターステーシャル広告(アプリの場面切り替え時などに表示する全面広告)
    • ネイティブ広告(デバイスに最適な広告を表示してくれるらしいが、まだよくわからん)
    • ……などを選ぶ
  • Firebaseアナリティクスの設定
    • 新しい広告向けのアクセス解析サービスだが、設定方法がまだよくわからん
  • SDKのインストール
    • 詳細は以下に

SDKのインストール

「表示の設定方法」にリンクされてるGoogle Mobile Ads SDKのページで googlemobileadssdkios.zip ってのをダウンロード。

次にXcodeにインストールするのだが…
以前は日本語でとってもやさしく説明してくれてたページ(この辺?)があったんだけど、現バージョンについてはFirebaseの英語ページしか出てこん!? 探す!
とりあえずこちらのページを参考にする。



必ずコピーすること
Xcodeの左側のファイルインスペクタからアプリ名を右クリックして、Add Files to "アプリ名"...を選び、ダイアログから、ダウンロードしたAdMobSDKのフォルダの中のGoogleMobileAds.frameworkをインストールする。
この際、ダイアログ下にあるOptionsボタンを押し、Copy items if neededをチェックすること。そうしないとframeworkへのリンクが貼られるだけとなるため、frameworkのファイルを移動したり捨てたりすると機能しなくなっちゃう。

とりあえずしなくてもいいこと?

GoogleService-Info.plistを入手

正式にはアプリに関する構成情報を格納するGoogleService-Info.plistというファイルを使用するそうである。
入手するにはFirebase consoleにログインしてアプリを登録するか、もしくはサンプルの同.plistファイルをダウンロードし、プロジェクトと同じディレクトリーに保存するのだそうだ。
重要:GoogleService-Info.plistファイルは広告のUnit IDを保存しません。あなたは他の手段で広告のUnit IDを格納、読み出さなければなりません。 
しかし、Firebaseの方も英語の説明しかないので、よくわからない。
とりあえずFirebaseとか.plistファイルとかを考えなくても広告は表示できるようなので、無視しておこう。

Podfileを作る

プロジェクトと同じディレクトリに、以下の内容を含むPodfileというファイルを作れとある。
source 'https://github.com/CocoaPods/Specs.git'

platform :ios, '7.0'

target 'BannerExample' do
  pod 'Firebase/Core'
  pod 'Firebase/AdMob'
end

pod installの実行

ターミナルから同じディレクトリのpod installを実行してください。
インストールが終わったらプロジェクトを閉じ、プロジェクト名.xcworkspaceを開いてください。
あなたのプロジェクトファイルはFirebaseとAdMobのために新しいPodsプロジェクトを含まねばなりません。
…とあるんだが、これも無視してもできたようだ。

コードからGADBannerViewを参照する

こっからはまたちゃんと作業すること。

広告を表示するViewControllerの先頭でGoogleMobileAdsをimportする。

  • Swiftの場合
    • import GoogleMobileAds
  • Objective-Cの場合
    • #import <GoogleMobileAds/GoogleMobileAds.h>

StoryboardでAdMobのUIViewを貼る方法と、コードから作る方法と2通りあるのはiOSの他の部品と一緒だ。
Storyboardで貼るなら、UIViewをドラッグして配置し、カスタムクラスを GADBannerView に設定する。Outletも引っ張って変数を作っとくこと。

アプリIDを書き込む

AppDelegate.swiftにアプリIDを書き込む。AdMobにアプリを登録した時に表示されるやつ。
なくても表示されるみたいなんだけど、入れとけ。
注意:次に説明する広告ユニットIDとは別物!
アプリID
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    GADMobileAds.configure(withApplicationID: "ca-app-pub-xxxxxxxxxxxxxxxx~xxxxxxxxxx")
    return true
}

広告をGADBannerViewに読み込む

最後に、バナー広告を表示させるViewControllerにコードを加えよう。

Storyboardで作った場合

override func viewDidLoad() {
  super.viewDidLoad()

  print("Google Mobile Ads SDK version: " + GADRequest.sdkVersion())
  adMobBanner.adUnitID = "ca-app-pub-xxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  adMobBanner.rootViewController = self
  adMobBanner.loadRequest(GADRequest())
}

コードから作る場合

上記コードを以下のように置き換える。
//スマートバナーにしておけば画面横幅に(だいたい)合わせてくれる
adMobBanner = GADBannerView(adSize: kGADAdSizeSmartBannerPortrait)
//広告ユニットID
adMobBanner.adUnitID = "ca-app-pub-xxxxxxxxxxxxxxxxx/xxxxxxxxxx"
//ルートのViewControllerを指定。だいたい self でいいのでは。
adMobBanner.rootViewController = self;
//バナーの位置を画面サイズと自分の大きさから決定(画面下に貼る場合)
adMobBanner.frame = CGRectMake(CGRectGetMidX(view.frame) - adMobBanner.frame.size.width / 2, CGRectGetMaxY(view.frame) - adMobBanner.frame.size.height, adMobBanner.frame.size.width, adMobBanner.frame.size.height) //中央に配置
//viewに追加
view.addSubview(adMobBanner)
adMobBanner.delegate = self //Delegateの設定
//広告を読み込んで表示させる
adMobBanner.loadRequest(GADRequest())

上記コードの3カ所を変更する。

  1. 上記コード中のadUnitID(ca-app-pub-xxxxxxxxxxxxxxxxx/xxxxxxxxxxの部分)を、広告ユニットを作った際に表示された広告ユニットIDに置き換える。
    広告ユニットID
  2. バナーを表示するためのルートのViewControllerを指定する。このViewControllerは広告がクリックされたときに重ねて表示される際に使われる。とりあえず self でいんじゃね?
  3. GADBannerViewのloadRequest関数をGADRequest()オブジェクトでコール。これで広告が読み込まれ、表示される。

Delegateの設定

iAd同様、広告が表示できたりできなかったりの時に呼ばれるDelegate関数がいくつかある。

当然、クラス先頭でDelegateの宣言をする。
class ViewController: UIViewController, GADBannerViewDelegate { … }

関数名はadView〜で始まるので、入力すると補完表示してくれるのでそれで調べて。

//Delegate関数
func adViewDidReceiveAd(bannerView: GADBannerView!) {
    print("AdMob表示できた😄")
}
  
func adView(bannerView: GADBannerView!, didFailToReceiveAdWithError error: GADRequestError!) {
    print("AdMob表示できず🤔\(error)")
}

didFailToReceiveAdWithErrorの方は表示失敗時だと思うんだが、表示失敗しなくても(この関数が呼ばれなくても)広告が表示されない場合があるようだ。この辺はよくわからん。

テスト用広告の設定

開発中に、誤タップや表示回数の統計に影響しないよう、本物の広告のかわりにテスト用広告を表示するように指示されている。
そのためにiOSシミュレータもしくは実機のテストデバイスIDを設定する必要がある。

シミュレータはすでにAdMobの方で定数kGADSimulatorIDとして宣言されてるのでそれを使う。

実機の方はデバイスごとに違うので、一度実機でAdMobを表示させた場合、Xcodeの下部のメッセージ欄に表示される以下のようなメッセージの「xxxxxxxxxxxxxxxxxxxxxxxxxxx」の部分を使う。

2016-06-28 00:29:04.837 alien[8831:7496717] <Google> To get test ads on this device, call: request.testDevices = @[ @"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" ];
前述したコードの
//広告を読み込んで表示させる
adMobBanner.loadRequest(GADRequest())
の部分を以下に置き換えればいい。

//テスト広告表示用
let request:GADRequest = GADRequest()
if TARGET_OS_SIMULATOR == 1 {
    request.testDevices = [kGADSimulatorID] //iOSシミュレータ用
} else {
    request.testDevices = ["xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"] //実機用
}
//広告を読み込んで表示

adMobBanner.loadRequest(request)

要はGADRequestクラスのプロパティを変更し、loadRequestしているわけだ。
シミュレータと実機のどっちで実行されてるのかは TARGET_OS_SIMULATOR で判定できる。
(1がシミュレータ、0が実機)
値はXcodeでシミュレータと実機のどちらを選択しているかで変化するので、Will never executedというWarningが出るけど気にしないでいい。

リリース時はこのままのコードでかまわない。
なぜなら、テストデバイスIDが実行デバイスと異なる場合には本物の広告が表示される仕組みだからだ。

Objective-Cの場合のコード

Objective-Cで書く場合も参考までにざっと載せとく。
- (void)adMob
{
    //いくつかサイズがあるが、スマートバナーにしておけば画面横幅に(だいたい)合わせてくれる
    adMobBanner = [[GADBannerView alloc]initWithAdSize:kGADAdSizeSmartBannerPortrait];
    //広告ユニットIDを指定する
    adMobBanner.adUnitID = @"ca-app-pub-**********************"; //WebAdMobの広告設定時に書かれてたもの
    adMobBanner.rootViewController = self;
    adMobBanner.frame = CGRectMake(CGRectGetMidX(self.view.frame) - adMobBanner.frame.size.width / 2, 0.0,
                                    adMobBanner.frame.size.width, adMobBanner.frame.size.height); //中央に配置
    [self.view addSubview:adMobBanner];
    //広告を読み込んで表示
    GADRequest *request = [GADRequest request];
    //テスト広告の設定
    if (TARGET_OS_SIMULATOR == 1) {
        request.testDevices = @[kGADSimulatorID]; //iOSシミュレータ用
    } else {
        request.testDevices = @[@"******************************"]; //実機用
    }
    [adMobBanner loadRequest:request];
}

変なメッセージが出る

うちでは出なかったんだけど、以下の変なメッセージが出る場合があるようだ。
This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes.  This will cause an exception in a future release.
「バックグラウンドのスレッドでオートレイアウトをいじってるようだな? 将来痛い目にあうぞ!」という内容だと思う。
どうやらViewが確定する前のviewDidLoadメソッドとかでAdMobを初期設定したりすると出るようで。
以下のようにdispatch_asyncのブロック中に入れておけば大丈夫だそうだ。(以下はObjective-Cのコード)
dispatch_async(dispatch_get_main_queue(), ^{
    ここにadMob設定コードを書く

});

審査提出時の注意

AdMobはターゲット広告を利用するようなので、iTunesConnectから審査提出時に最後に聞かれる「IDFAを使ってるかどうか」などという項目に必ずチェックを入れること。
IDFA使ってるのにチェックを入れないと、リジェクトされて新しいバイナリを求められるってさ。
「下記の目的で…」のチェックは自分の場合、一番上の「App内で広告を出す」だけで良かったみたい。他の項目は何言ってるのかわかんないしね。

2016年6月24日金曜日

Gitでバージョン管理

よくわからんのでまとめとく。
間違ってたらすまなんだ。

Gitってなんだ?

ジットと読み、Linux発でソースコードのバージョンを管理するための仕組み。
ソースコードの差分、ディレクトリ構造などの変更履歴を取っておくことで、変更前に戻すことができる。
XcodeではXcode 4から実装している。

リポジトリ

ファイルやディレクトリの状態を記録する場所。

  • ローカルリポジトリ
    • 一人で開発してる場合、端末上に作る
    • Xcodeではプロジェクト作成時の保存ダイアログ上で「Create Git repository on My Mac」にチェックを入れることで作る。(デフォルトでチェックされてる)
  • リモートリポジトリ
    • 複数で開発してる場合、サーバ上に作る
    • Xcodeでの作り方はよくわかんないんで略

コミット

リポジトリに変更履歴を記録すること。
1の次に2、2の次に3といった形で順番に記録される。
XcodeではメニューのSource Control/Commit...。
ソースファイル名の横にMとかAとか付いてるのがコミット可能なファイル。
Commitの画面で、どこをどう直したかなど、コメント入力必須。

ブランチ

大きな変更がある際など、コミットの流れを分岐させること。
3でブランチした場合、3’の流れが新たに作られ、3’を4’に変更しても、3の流れの方は影響を受けない。
分岐させたブランチはマージ(merge/合流)させることができる。
最初のコミットで作られるのがマスター( master)というブランチ。この例で言えば「’」のつかない1〜3の流れ。

チェックアウト

今いるブランチを出て、別のブランチに移ること。

プッシュ/プル

  • プッシュ
    • リモートリポジトリに対して変更を書き込む
    • XcodeではSource Control/Push...
  • プル
    • リモーとリポジトリから変更を読み込む
    • XcodeではSource Control/Pull...
リモートリポジトリについてはよくわかんないので略。

2016年6月14日火曜日

アプリリリース方法

前にもどっかにまとめたはずなんだけど見つかんないし、iTunesConnectやXcodeのデザインも変わってると思うので、あらためて簡単に。
細かいところがちょこちょこ変わるので、2016年12月現在の方法に更新しました。

新規App

iTunesConnectにログインし、新規App登録を開始する。
2016年12月現在
  • 名前
    • アプリ名です。
  • プライマリ言語
    • 他言語対応の場合、Englishにしといた方がいいらしい。
  • バンドルID
    • これはXcodeで設定したのを見て入れること。→ 登録ダイアログ中のリンクにあるDeveloper Portal(次項参照)で、事前登録が必要。登録後、プルダウンメニューに表示されるのでそれを選ぶ。
  • SKU
    • 他のアプリとかぶらないものならなんでもいいけど、俺は英文字+通し番号にしてる。

Developer Portalでの事前登録

  • App ID Description
    • アプリ名を英文字で入れる
  • App ID Prefix
    • チームIDが表示される
  • App ID Suffix
    • Bundle IDにXcodeで設定したBundle Identifier(ドメインを逆さにして入れたアレ)を入れる。間違っちゃダメ。間違うとこの設定はたぶんゴミとして残る。そうなったら無視するしかない。
上記Prefix、Suffix、アプリ名の順に「.」でつなげたものがApp IDになるらしい。

  • App Services
    • アプリで必要とするサービスがあればチェックする。
    • Apple Pay、HealthKit、iCloud、SiriKitとか、そういうやつね。この項目は後から再編集できる。

APP STORE情報

  • App情報
    • AppStore上のカテゴリなどを入れる
  • 価格および配信状況
    • AppStoreの価格などの設定
      • 価格をプルダウンメニューから選ぶ
      • 開始日、終了日は最初は変更できないっぽい
    • Volume Purchase Program
      • 教育機関とかがまとめ買いした場合に割り引くかどうか
    • Bitcode自動再コンパイル
      • よくわからんのでデフォルトのままでいいか

iOS APP

左側に「1.0 提出準備中」ってなってるのを開くと、スクリーンショットとかの登録ページに移る。
書いてある内容を読んで必要なものを記入、アップロードしていくこと。

  • バージョン情報
    • App プレビューとスクリーンショット
      • AppStoreで表示する説明画像をiPhoneおよびiPad用を登録する。最も大きい画面サイズのデバイス(iPhoneなら5.5インチのもの)を一種類登録すれば全部のサイズで有効になる。
    • 概要
      • AppStoreでのアプリ説明文
    • キーワード
      • AppStoreでの検索用
    • サポートURL
      • アプリをサポートするためのサイトやSNS
    • マーケティングURL(オプション)
      • なんのこっちゃ?
  • iMessage App
    • 必要な場合だけ
  • Apple Watch
    • 必要な場合だけ
  • ビルド
    • 一番最後にやる。Xcodeからアーカイブしたアプリをアップロードしてからここを設定する(次項参照)
  • App 一般情報
    • App アイコン
      • 1024×1024じゃないとダメ
    • バージョン
      • 1.0からね
    • 年齢制限指定
      • 暴力表現や性的表現があるかなどの項目に答えていく
    • Copyright
      • 著作権表示。テキトーに。
    • 通商代表連絡先情報(韓国App Storeのみなので普通はいらない)
      • 英語で入力。番地は逆さに(山田町1丁目2番地なら1-2 Yamadacho)
      • 電話番号は先頭に国番号を付け、市外局番の頭の0を取ったものにしないといけない。たとえば080-12345678だった場合、+81-80-12345678となる。
    • Routing App Coverage File(オプション)
      • アプリがサポートする地理的領域をファイルで指定だって? 使ったことない。
  • Game Center
    • 必要な場合だけ
  • App Reviewに関する情報
    • 連絡先情報
      • 名前と電話番号とメルアド
        • 電話番号は先頭に国番号を付け、市外局番の頭の0を取ったものにしないといけない。たとえば080-12345678だった場合、+81-80-12345678
    • デモアカウント
      • 審査の時に使う全部の機能が確認できるアカウント情報。デフォルトでチェックされてるから不要なら外す。
    • メモ
      • アプリ審査時に役立つ情報。なければ空欄で。
  • バージョンのリリース
    • 自動でリリースするか、手動でリリースするかなど。普通は自動で。
並行してXcodeからアップロード作業をする。

Xcodeでアーカイブとアップロード

  • メニューProduct/Scheme/Edit Scheme...(これいらないかも?)
    • Build ConfigurationをDebug → Releaseに
  • メニューProduct/Archive
    • 右のカラムのValidate...
    • provisioningを選択し、Validate。SuccessfulになったらDone
  • 右のカラムのUpload to App Store...
    • provisioningを再度選択し、Upload
    • これでiTunesConnectにアップロードされる。
    • アップロード後、手直しして再アップの場合、XcodeのGeneral/IdentityのBuildナンバーを変えないとダメ。
    • Upload SuccessfulになったらDone
数分するとアップロードがiTunesConnectに反映され、iTunesConnectのビルドに出るようになる。
最近は反映された際にメールで知らせてくれるようだが、なかなか出ない場合はiTunesConnectのページを再読み込みするといいようだ。

審査提出

iTunesConnectで必要な項目を全て書き込み、スクリーンショットなどもアップロードしたら、一番上の「審査に提出」。

最後に、
  • 中で暗号使ってないか?
  • 使ってる画像とかがちゃんと著作権クリアしてるか?
  • ターゲット広告使ってないか?(IDFAとか言うらしい)
    • GoogleのAdMobはターゲット広告なのでここをチェックした
などと聞いてくるのでそれぞれ答える。

最近は一両日中に結果を出してくれるなど、かなり早く審査してくれるようだ。

最後に一言…

Xcodeから審査提出できるようにせいや!
いちいちiTunesConnectと行ったり来たりは面倒なんじゃあ!!!
ああすっきりした。

すべてのiPhoneに対応させない

注意:結論から言うと、うまくいきませんでした。

解像度が多すぎる

アンドロイドスマホほどじゃないとは思うが、iPhoneは解像度とアスペクト比のバリエーションが多い。
執筆時現在、以下の画面サイズに対応させなければいけない。
  • RetinaのiPhone4sで640×960
  • 縦にサイズが伸びたiPhone5が640×1136
  • iPhone6では4.7インチで750×1334
  • 最新のiPhone6 Plusでは5.5インチで1242×2208

このうちiPhone4sだけは縦が短いので、うまく画面を作らないと、それ以外の画面で横がはみ出したり上下に黒い帯を表示させることになってしまう。

正直、いちいちこんないっぱい対応してられるか! と思う。
これにiPadの対応までするとなるとますますだ。(iPadはアスペクト比が一緒だからレイアウト変更などは一種類で済むが)
俺のせこいアプリなんかはiPhone4sの解像度でだけ作って、iPhone6s Plusだろうがスケーリングモードで拡大して表示してくれるとありがたい。
要はiPhoneのアプリをiPadで使う時みたいに、多少表示が粗く見えても2倍とかに拡大してくれればいいのだ。

そういう風に作れないだろうか?
さらに、それでアップルの審査をパスできるだろうか?(最低iPhone5のサイズに対応せんと怒られるのではないか?)

対応させないためには

みなさん頑張って各解像度に対応させてるのか、検索してもスケーリングモードで表示させる方法があまり出てこない。
どうやらアプリ起動時に表示されるLaunch Screen(スプラッシュスクリーンともいうのだな)をどの解像度(機種)の分をインストールするか/しないかで対応解像度を選べるらしい…ということだけ風の便りで聞いていた。


そこでLaunch ScreenにiPhone4s(3.5インチ)用のLaunchScreenだけ入れiTunesConnectにアップロードし、スクリーンショットも3.5インチ画面のみをアップロードしてみた。
しかし「審査提出」ボタンを押すと「アプリが他のサイズ用に対応してるから、それ用のスクリーンショットもアップロードしろ」と出てしまった。

チェックボックス全てOFFにしてみる

最低限のLaunch Image
XcodeのLaunchImageの画面でスクリーンショットの入ってない枠が残ってるせいかと思い、LaunchImageのインスペクタでチェックボックスを全てOFFにしてみたが、やっぱりiTunesConnectで同じエラーになる。

もしかしたらちゃんとしたやり方あるんだろうとは思うけど、近い将来サポートされなくなる3.5インチにこだわって作っててもしょうがないのであきらめることにした。
あ〜、全部の解像度対応してやりますよ! このヤロー!(TДT)(iPadを除く)

やっぱりサポートされなくなった

2016/6/14のアップルのイベントでiOS10について発表があり、iPhone4sが非対応になったようだ。
これで、若干のアスペクト比の違いはあれど全て縦長になったので(iPadを除く)、デザインはしやすくなったかな。

iPadのスケーリング表示はどうなる?

iPadってiPhoneしか対応してないアプリを使う場合、現状ではiPhone4sの解像度をスケーリング表示させてるけど、これからはどうなるんだろうな。
iPhone5の解像度をスケーリング表示するようになるの?

iPad対応版を作るんであれば、どのみち今までどおり縦長なiPhone、正方形に近いiPadの両方のデザインを作らないといけませんな。こっちはもう早くからあきらめてる。