iAd 実装が iOS7 から恐ろしく簡単になってる件
iAd on iOS 7
iOS 6 までは iAd を表示しようとすると、ADBannerView をインスタンス化して layout してなど色々と面倒でした。
iOS 7 では iAd.framework に以下のカテゴリが追加されています。
UIViewController (iAdAdditions)
これを用いる事で、今までのような煩わしいことをほとんど気にせずに iAd を表示する事が出来る様になります。
ユースケース毎に見ていきましょう。
バナー広告を表示したい
以下のプロパティをセットするだけ。
///(1)フレームワークをimport @import iAd; ///(2)初期化時などに self.canDisplayBannerAds = YES;
これだけでバナーが画面下に表示されます。
Interstitial 広告を表示したい
事前準備として以下を実行すると Interstitial 広告の情報をプリフェッチしておいてくれます。
//起動時などに
[UIViewController prepareInterstitialAds];
自動的に出すだけならカテゴリで追加された UIViewController の以下のセットするだけです。
@import iAd;
self.interstitialPresentationPolicy = ADInterstitialPresentationPolicyAutomatic;
自動だとさすがに煩わしい、意図したタイミングで、という場合は以下の様に
@import iAd; self.interstitialPresentationPolicy = ADInterstitialPresentationPolicyManual; //意図したタイミングで [self requestInterstitialAdPresentation];
iAd という選択肢
「iAd だけじゃやってけない」、「SSP 使って出し分けしたいし」、「広告興味ありません」などの意見には、
上記だけでは充分なメリットはないですが、
「広告導入に手間はかけずに、プロダクトに注力したい」「けど最低限の運営費は欲しい」みたいな開発者には一つの手段には成り得ると思います。
ということで知らない間に驚くほど簡単になってた iAd の紹介でした。
iAd Production Beginner’s Guide
- 作者: Ben Collier
- 出版社/メーカー: Packt Publishing
- 発売日: 2012/02/23
- メディア: Kindle版
- この商品を含むブログを見る
Framework Bundle について
framework ファイルってナニモノ?
iOS 開発をするうえで、プロジェクト作成時点で Foundation.framework や UIKit.framework などの
Apple から提供されている framework が既に追加されているかと思います。
普段あまり疑問に思わずこれらの framework を使わせてもらっていますが、
いざこれを作ろうと思うと、そもそも .framework ファイルとはナニモノで、
どうやって作れば良いのか分からなかったので、この記事を書きました。
上記で .framework ファイルと呼んでいたものは正式には Framework Bundle と言います。
指定されたディレクトリ階層にリソースファイルや Static Library やドキュメントなど、
色々なものをパッケージングしてカプセル化して提供しているディレクトリを Framework Bundle と呼ばれています。
Framework Bundle で提供するメリット
前回の記事でも紹介した Static Library で提供するよりも、
Framework Bundle の方が以下のようなメリットがあります。
- インストールが手軽
Static Library はヘッダやライブラリなど複数ファイルを追加しないといけないですが、
Framework Bundle は、それ自身を追加するだけ済みます。
- 様々なリソースが追加可能
画像やドキュメントなども追加できます。
- 複数バージョンを含む事が可能
同じ Framework Bundle 内に、 複数のバージョンを持つ事ができるので
この Framework を使用するプロジェクトの古いプログラムに対しての下位互換を可能にします。
このように Framework Bundle は良い事ずくめです。
しかし Framework Bundle で提供する際のデメリットもあります。
Xcode には iOS 向けの Framework 用のプロジェクトテンプレートが存在しないので、
自身で Framework Bundle を生成しないといけない点です。
Mac には Cocoa Framework テンプレートがあるのですが、iOS にはありません。
しかし、その部分は実績のある OSS が代替してくれるので心配は不要です。
以下からは Framework Bundle の構造を読み解き、どのような方針でそれを作成すれば良いかをみていきます。
ではまず、Framework Bundle がどんなデータ構造を取っているのかを見てみましょう。
Framework Bundle のファイル構造
ファイル構造といってもそこまで難しい事は何もありません。
早速みてみましょう。
MyFramework.framework/ MyFramework -> Versions/Current/MyFramework Resources -> Versions/Current/Resources Versions/ A/ MyFramework Resources/ English.lproj/ InfoPlist.strings Info.plist B/ MyFramework Resources/ English.lproj/ InfoPlist.strings Info.plist Current -> B
階層毎に見ていきます。
MyFramework.framework 配下には実体のあるディレクトリは Versions のみです。
ではその Versions には何が入っているかというと、
Version 毎に分かれた A, B というディレクトリ、そして現在の Current を示すシンボリックリンクです。
この Current を通して、一番上の階層にあった MyFramework と Resources は参照している訳です。
これはほんの一例です。詳細は以下のリンクを読んでみてください。
Framework Bundle の作り方
では実際に Framework Bundle をどう作るのか、という一番大事なところに触れましょう。
自分でスクリプトを作成し、 Framework Bundle を生成する事も出来ますが、
以下の OSS を用いる事で簡単に出来ます。
上記の OSS を用いて、iOS 用のプロジェクトテンプレートを作成するだけです。
長くなってしまうので、 iOS-Universal-Framework の
実際の使い方などは別の記事に回したいと思います。
参考
Xcode 5 で作る Universal Static Library
なぜ Static Library なのか
全てのソースコードを GitHub にアップロードし、Cocoapods に podspec をマージしてもらって、
他のデベロッパー達とのコラボレーションを楽しむのが現在の iOS 界隈の Social Coding の主流になっているのかなと思います。
しかし、以下のような要因などによっては上記のようなアクションを取り辛い場合があると思います。
コード自体は自由に使ってもらいたいが、実装ファイルは見られたくない
ファイルが山ほどあるので、それらを使いやすくパッケージ化したうえで共有したい
そんなときに有効な手段の一つとなるのが Static Library です。
Static Library は指定した header file とコンパイル済みの archive file (.a 拡張子)がインターフェースとなります。
そのため、実装部分が利用者には見えなくなり隠蔽できるうえ、利用者にも扱いやすくなります。
プロジェクトの作成
では実際に Xcode 5 を用いて Static Library を作成してみましょう。
まずは Static Library 作成用のプロジェクトを新規に作成します。
Xcode を起動し、メニューから File -> New… -> Project を選択します。
左ペインの iOS セクションから Framework & Library を選択し、以下の Cocoa Touch Static Library を選択します。
適当な Product Name を付けたら完成です、早速ビルドしてみましょう。
Product フォルダに libProject.a という archive file ができていると思います。
これを選択状態にし、 Show in Finder を選択して実際にどんなファイルが生成されているかを確認してみましょう。
Product フォルダ内にある lib
それを選択して右クリックメニューから Show in Finder です。
上記でも説明した、header file と archive file の2種類が生成されています。
手順としてはこれだけです。
あとは生成された Static Library を、利用したいプロジェクトに追加し適切にリンクするだけです。
Universal Library
実は上記の手順だけだと、一つ問題があります。
このままだとビルドした時に選択している Architecture 用にコンパイルされた Static Library しか生成されないからです。
例えば iOS Simulator を選択してビルドした Static Library は iPhone など実機のArchitecture とは異なるため使用できません。
Xcode が Universal Library 用のテンプレートなりを用意してくれても良い気がするのですが、そんなものはありません。
なので lipo コマンドを使い、各 Architecture 毎に生成した Static Library を結合する処理を追加します。
手順をみていきましょう。
まず先ほど作成した Static Library 作成用プロジェクトに Aggregate ターゲットを追加します。
Aggregate ターゲットは、複数のターゲットを一度にビルドしてくれ、コマンドラインのスクリプトを記述できるターゲットです。
ソースコードをコンパイルしたり、ライブラリをリンクするなど普通のターゲットとは違うので、ターゲット同士を結合するための非常にシンプルかつ透明性のあるターゲットという位置付けになっています。
ターゲットが作成できたら、下記のようにメニューを辿ってスクリプトを追加するための項目を以下のようにして追加します。
スクリプトは以下のようなものを追加しましょう。
# Universal Libraryを配置する場所 UNIVERSAL_OUTPUTFOLDER=${BUILD_DIR}/${CONFIGURATION}-universal # 実機用ライブラリ生成 xcodebuild -target TestTarget ONLY_ACTIVE_ARCH=NO -configuration ${CONFIGURATION} -sdk iphoneos BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}” # iOS Simulator用ライブラリ生成 xcodebuild -target TestTarget -configuration ${CONFIGURATION} -sdk iphonesimulator -arch i386 BUILD_DIR="${BUILD_DIR}" BUILD_ROOT="${BUILD_ROOT}" # Universal Library用フォルダ生成 mkdir -p "${UNIVERSAL_OUTPUTFOLDER}" # lipo コマンドで実機・iOS Simulatorライブラリを結合 lipo -create -output "${UNIVERSAL_OUTPUTFOLDER}/lib${PROJECT_NAME}.a" "${BUILD_DIR}/${CONFIGURATION}-iphoneos/lib${PROJECT_NAME}.a" "${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/lib${PROJECT_NAME}.a" # lipoコマンドにより生成されたUniversal Libraryをコピー cp -R "${BUILD_DIR}/${CONFIGURATION}-iphoneos/include" "${UNIVERSAL_OUTPUTFOLDER}/"
簡単に言うと、iOS Simulator 向けの i386, iOS Device の向けの arm の Architecture を生成し、それらを lipo で結合してアウトプットを作っています。
これでビルドするターゲットを Aggregate に設定すれば Universal ビルドを作成出来るようになります。
終わりに
ここまで読んで頂いた方は気付いたかもしれませんが、私たちが SDK 普段使っているなどで使用している Static Library は凄い簡単に作れます。
次回はもう一つ先を行った Framework の作り方を書こうと思います。
参考
メモリ管理・レイアウトの観点からみた UIViewController の view の扱い
self.view
iOS 開発において、UIViewController の view の振る舞いは一番理解しておきたい点の一つです。
今回はその view に対して、メモリ管理とレイアウトの2つの視点を交えてアプローチを行い、
UIViewController の subclass を作成する上で、
UIViewController の各メソッドにどんな処理を書くべきか、そして何を書くべきでないか
を説明出来ればなと思っています。
iOS 6 以降からを対象として考えていますので、 iOS 5 以前は取り扱いません。
self.view の振る舞い
扱いを学ぶには、まず対象の振る舞いを把握する事からです。
ライフサイクルとレイアウトサイクルの2点から簡単に復習します。
self.view のライフサイクル
UIViewController の view がどのようなタイミングで生成されメモリ上に展開し、
どのようにメモリから消されていくか、を Apple が提供している図を元に簡単に把握しましょう。
では上記の要点を簡単に。
view はアクセスされたタイミングで初めて生成される
アプリケーションからビューを要求されてはじめて、UIViewController は view をメモリ上に展開し、
自身の view プロパティにも保持するようになります。
UIViewController の提供されているメソッドを用いたロードサイクルは上記の図の通りです。
システムの命令により、UIViewController は自身の view を window からアンロード
UIViewController は自身が破棄されるまでは retain 属性の view プロパティを保持し続けるのがデフォルトの動作です。
メモリ警告が起きると、以下のメソッドが呼ばれてシステムにより UIViewController の view が
何らかの理由で window にアタッチされている場合にはアンロードされてしまいます。
(2014/02/01 修正 - ブックマークコメントで指摘を頂いた箇所を修正しました。)
(2014/02/03 追加 - ブックマークコメントの詳しい修正を以下のブログに書いて頂きました。ありがとうございます! )
何らかの理由で window からデタッチされている場合にはアンロードされてしまいます。
- (void)didReceiveMemoryWarning;
詳しくは以下が参考になると思います。
viewDidUnloadがdeprecatedになった理由を考察
self.view のレイアウトサイクル
UIViewController の view の frame は、様々な外部要因によって決定されます。
簡単に例を挙げると以下の通りです。
UIWindow の rootViewController
表示されている window の frame
ステータスバーの有無、通話中など Background Mode が作動時の高さの変更
デバイスの傾き
UITabBarController, UINavigationController などの Container ViewController
上記の Child ViewController は、親の Container ViewController の frame に従い設定されます。
開発者はこれらを意識したレイアウトの実装を UIViewController に行い、
それぞれに適したレイアウトを、 UIViewController の外部からは意識せずとも実現させるのが理想です。
では肝心の UIViewController は、上記の frame 決定プロセスにどのように関与できるのでしょうか?
大まかなメソッドが呼ばれる流れは以下の通りです。
簡略化のため、UIViewAutoresizing と Auto Layout は省いています。
///(1) self.view の frame がシステムによって変更される ///(2) 以下が呼ばれる - (void)viewWillLayoutSubviews; ///(3) self.view の layoutSubviews が呼ばれる ///(4) 以下が呼ばれる - (void)viewDidLayoutSubviews;
駆け足で UIViewController のメモリ管理とレイアウトの流れを復習しました。
では実際に、それらに関した処理をどのメソッドに書くべきかを見ていきましょう。
実装メソッド
- (id)init
initializer では、その UIViewController が適切な振る舞いを行うための全ての状態の設定を書くべきです。
具体的にはカレンダーの画面を表示する ViewController だったら NSCalendar、
メモ帳の詳細画面だったらメモの内容はここで設定するのが良いでしょう。
逆に、 view やそれの subview の初期化は行うべきではありません。理由は後述。
- (void)loadView
loadView はシステムによって UIViewController の view プロパティにアクセスされ、
まだ view プロパティが nil の場合に発火します。
override している loadView 内で
[super loadView];
を行うことで初めて view プロパティに何も設定されていない UIView が入ります。
ここでは self.view と、その subviews の load の一点だけを行うべきです。
self.view の frame を用いて、 subviews の frame 調整は行うべきではありません。理由は後述。
- (void):viewDidLoad
loadView が完了されると呼ばれるメソッドです。
名前の通り、UIViewController が管理する全ての view がロード済みであることが保証されています。
なので、ここでは view に対して初期化以外の処理を行うべきです。
具体的には view へのデータセットや Target-Action などの通知設定などがあたります。
- (void):viewDidLayoutSubviews
loadView の項で frame 調整は行うべきではない と書きましたが、それはここで行うべきだからです。
self.view の subviews.frame の調整、すなわちレイアウト処理は全てここで記述するべきです。
なぜなら、このメソッドが発火するタイミングは、self.view の layoutSubviews の後、
要するにシステムや Container ViewController による self.view の frame 調整の後だからです。
このメソッド内で得られる self.view.frame がそのまま画面上に描画されるので、
Navigation Bar があるとか、Tab Bar が無いとか、デバイスが横向きとか全てが反映された後の frame になっています。
- (void):didReceiveMemoryWarning
メモリ警告が発生した場合に呼ばれるメソッドです。
ここでは不要なプロパティや self.view を解放するべきです。
このメソッド内で self.view に nil をセットする事で、
次回以降この UIViewController の view にアクセスがあった際に loadView が発火します。
loadView では view の初期化処理のみを行うべきと書いたのはそれが理由です。
サンプル
最後に GoodViewController と BadViewController という名前でサンプルを書いてみました。
BadViewController は「べきでない」を実践している最悪な UIViewController、
そして GoodViewController は上記の「べき」を実践した UIViewController。
サンプルはあくまでイメージですので、動作や値の保証は致しません。
viewDidLoad 全記述原理主義者を目撃した際は、このブログを教えてあげて下さい。
参考
view | UIViewController Class Reference
Resource Management in View Controllers | View Controller Programming Guide
Resizing the View Controllers’s Views | View Controller Programming Guide
コーディング規約要らずの Objective-Clean
コーディング規約
プログラミングを行う上でコーディング規約は一つの指針になり、
定める事でそれをもとにコードレビューなどの場で指摘などがし易くなります。
Objective-C で、しかも公開されているだけでも、ざっと探してみてこれだけの量があります。
企業
開発者
これだけの量のコーディング規約があることからも分かりますが、
それぞれの中で定められている事柄には、結構なばらつきがあるものも多いです。
そのような環境の中で、以下のような事象が発生してしまうのも事実です。
- コーディング規約を定めたものの、守れないコードが生まれてしまう
- コードレビュー内でコーディング規約に沿わないコードを見つけるコストが高い
- コーディング規約に沿わないだけのコードに対して PR をするのが億劫
- コーディング規約の陳腐化
今回紹介する Objective-Clean はそのようなコーディング規約のイチ文書化を、
技術的視点から解決してくれるツールです。
Objective-Clean
Objective-Clean が行ってくれるのは以下の2つです。
- コーディング規約設定ファイルの生成
- ビルド時に上記ファイルに反するコードへの警告
これによって、今までとは違い、定義したコーディング規約となるファイルが
毎回のビルド時に反映され、Xcode 内で警告を表示してくれるようになります。
具体的には設定ファイルは plist 形式で生成されるので、それをプロジェクト内に組み込み、
一度インストールすると、Project Settings に Run Script が追加されるので、
恐らくそれが plist の内容を読み取り、Clang の静的解析 を拡張してくるのかなと思っています。
では実際の使い方は以下のページを観て頂くとして、
面白かったコーディング規約の生成方法をちょっと触れます。
コーディング規約生成方法
Objective-Clean は現時点(2014/01/29 v1.4)で、
コーディング規約の取得に際して3つの方法を提供しています。
Custom
Objective-Clean には Survey 機能があり、
一問一答形式で数十個の質問に答えていくと、自分にあったコーディング規約のファイルが生成されます。
少し面倒ですが、普段の自分がどういう意識でコードを書いているかが明確になるような気がするのでオススメです。
Stackoverflow
Objective-Clean は Survey 作成に際して、
Facebook, Twitter, Stackoverflow でのログイン機能を提供しています。
その中でもコーディングに関して著しく親和性の高い Stackoverflow でログインしたユーザの作った
コーディング規約の平均を取ったコーディング規約を使用する事が出来ます。
User Average
上記と似ていますが、こちらは Survey を行ったユーザ全ての平均になります。
おわりに
コンパイラの設定ファイルとして常にビルド時に参照する事で、
コーディング規約にワンランク上のステージが訪れたようにも思います。
まだまだ発展途上のツールですが、最近は Cocoapods 対応など入り盛り上がっているようなので使い続けてみようと思います。
ちなみに現在半額セール中なので、買うなら今ですね!
そして僕のコーディング規約ファイルは以下ですので、
興味がある方はのぞいてみて、あれやこれや言って下さい。