98
セミナー&ハンズオン ラボ シリーズ Windows Azure ハンズオン ラボ Windows Azure モバイル サービス iOS 日本マイクロソフト テクニカルエバンジェリスト 鈴木 章太郎 @shosuz Version: 1.0 Last updated: 2013/3

Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

  • Upload
    others

  • View
    1

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

セミナー&ハンズオン ラボ シリーズ

Windows Azure ハンズオン ラボ

Windows Azure

モバイル サービス iOS

日本マイクロソフト テクニカルエバンジェリスト 鈴木 章太郎 @shosuz

Version: 1.0

Last updated: 2013/3

Page 2: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

もくじ

概要 ........................................................................................................................................................ 1

目的 ........................................................................................................................................................ 1

演習内容 ................................................................................................................................................. 1

演習 1 : Windows Azure モバイル サービスの利用準備 .................................................................... 2

タスク 1 : Windows Azure 管理ポータルへのログイン .................................................................................................................... 3

タスク 2 : Windows Azure モバイル サービスの作成 ....................................................................................................................... 5

タスク 3 : Windows Azure モバイル サービスのフレームワークを取得 ................................................................................. 9

タスク 4 : Windows Azure モバイル サービスの URL とアプリケーションキーを取得 ................................................. 11

演習 2 : Windows Azure モバイル サービスに接続する iOS アプリの作成 .................................... 13

タスク 1 : Xcode プロジェクトの作成 ................................................................................................................................................... 14

タスク 2 : Windows Azure モバイル サービス SDK を追加 ......................................................................................................... 17

タスク 3 : ストーリーボードのビューを構成 .................................................................................................................................... 18

タスク 4 : ユーザー認証を設定 ................................................................................................................................................................ 20

タスク 5 : ユーザー認証画面をクライアントに追加 ...................................................................................................................... 24

タスク 6 : 投稿テーブルの作成とサーバースクリプトの CRUD 処理の追加 ...................................................................... 29

タスク 7 : タグ、投稿・タグの関連テーブルの作成とサーバースクリプトの CRUD 処理の追加 ............................ 33

タスク 8 : 投稿処理をクライアントに実装 ........................................................................................................................................ 39

タスク 9 : タグ関連処理をクライアントに実装 ............................................................................................................................... 55

タスク 10 : 公開投稿をクライアントに実装 ...................................................................................................................................... 62

タスク 11 : 投稿・タグ機能の動作確認 ............................................................................................................................................... 69

演習 3 : Windows Azure モバイル サービスを利用してプッシュ通知を実装 ................................. 74

タスク 1 : App ID の作成 ............................................................................................................................................................................. 75

タスク 2 : プッシュ通知の証明書作成 .................................................................................................................................................. 77

タスク 3 : プロビジョニングプロファイルの作成 .......................................................................................................................... 81

タスク 4 : 証明書の秘密鍵をモバイル サービスに登録............................................................................................................... 83

タスク 5 : デバイステーブルの作成とプッシュ通知処理の実装 .............................................................................................. 86

タスク 6 : プッシュ通知処理をクライアントに実装 ...................................................................................................................... 89

タスク 7 : APNS からのフィードバック処理を追加 ........................................................................................................................ 91

タスク 8 : プッシュ通知の動作確認 ...................................................................................................................................................... 92

演習の後に ........................................................................................................................................... 93

まとめ................................................................................................................................................... 95

リソース ............................................................................................................................................... 95

Page 3: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

1

概要

Windows Azure は、マイクロソフトのクラウド コンピューティング サービス基盤群であり、インターネットスケール

での Web サイト ホスティング、クラウド アプリケーション ホスティング (PaaS)、ならびに仮想マシン ホスティン

グ (IaaS) 環境を提供します。

これら Windows Azure サービス群を Windows ストアアプリやスマートデバイスアプリ用に使いやすくしたサービスで

ある Windows Azure モバイル サービスを使うと、それぞれ専用のフレームワークを利用して迅速にクラウドを利用し

たアプリを開発することが可能です。

このハンズオン ラボでは、Windows Azure モバイル サービスにフォーカスし、管理ポータルサイトを使用した,モバイ

ルサービスの作成および設定手順、Mac OS X での iOS SDK を用いた Xcode によるモバイル サービスを利用する iOS ア

プリの開発・実行手順について学習します。

目的

このハンズオン ラボでは以下のことを学習します。

Windows Azure 管理ポータルサイトにアクセスし Windows Azure モバイル サービスの構成を行う

Xcode を使用して Windows Azure モバイル サービスを利用した iOS アプリを作成する

演習内容

このハンズオン ラボは以下の演習で構成されています。

1. Windows Azure モバイル サービスの利用準備

2. Windows Azure モバイル サービスに接続する iOS アプリの作成

3. Windows Azure モバイル サービスを利用してプッシュ通知を実装

ソースコード一式は下記よりダウンロードできます。

http://sdrv.ms/XgHEZO

Page 4: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

2

演習 1 : Windows Azure モバイル サービスの利用準備

この演習では、Windows Azure 管理ポータルサイトを使用して Windows Azure モバイル サービスを使用可能な状態に

構成する方法を学習します。

注意:

本ハンズオン ラボでは、Windows Azure サブスクリプションを契約済みの Micosoftアカウントをご使用頂きます。このた

め、演習を始める前に必ず Windows Azureサブスクリプションの購買とアクティブ化を行っておいてください。

Windows Azure サブスクリプションの購入とアクティブ化の手順につきましては、「Windows Azureサブスクリプション

申し込み Step by step」を参照ください。

http://msdn.microsoft.com/ja-jp/windowsazure/ee943806

なお、MSDN サブスクリプション会員の方は MSDNサブスクリプション会員専用のWindows Azure ご利用プランを契約す

ることができます。

http://www.windowsazure.com/ja-jp/pricing/member-offers/msdn-benefits/

メモ:

Windows Azure の料金体系ならびに料金プランの詳細につきましては、以下のサイトを参照ください。

http://www.windowsazure.com/ja-jp/pricing/

Page 5: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

3

タスク 1 : Windows Azure 管理ポータルへのログイン

このタスクでは、Windows Azure の各サービスのセットアップや管理を行うためのポータルサイトである Windows

Azure 管理ポータルサイトにログインします。

1. ブラウザで Windows Azure 管理ポータルサイト (https://manage.windowsazure.com/) にアクセスし、表示され

るログイン画面にマイクロソフトアカウントのメールアドレスとパスワードを入力して [サインイン] ボタンを

クリックし、Windows Azure 管理ポータルにログインします。

(※ログインには Windows Azure のアクティブ化の際に使用したマイクロソフトアカウントを使用してくださ

い)

Page 6: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

4

ログインすると、下図の Windows Azure 管理ポータルサイト画面が表示されます。

Page 7: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

5

タスク 2 : Windows Azure モバイル サービスの作成

このタスクでは、Windows Azure 管理ポータルサイトを使用し、Windows Azure のモバイル サービスを作成します。

1. Windows Azure 管理ポータルサイトの左下部にある [+新規] をクリックします。

2. そして、表示された「新規」画面の左ペインの [コンピューティング] を選択し、右側に表示されるメニューか

ら [モバイル サービス] を選択し、最後に [作成] をクリックします。

Page 8: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

6

3. 続いてアプリケーションから接続する、インターネット上のモバイル サービスの URL とホスト先のデータセン

ター ロケーションを設定します。

[URL] 欄にユニークな URL プリフィックスを入力し、[地域] ドロップダウンリストで「West US」(アメリカ西)

を選択して、下部の [→] をクリックします。

注意:

URL欄に入力する URLプリフィックス名は、URLとして正しい形式かつ他のユーザーがまだ使用していないユニークな URL

プリフィックス名を入力する必要があります。本演習を行う際には任意の名前を設定します。

入力した URL プリフィックス名がユニークではない (すでに使用されている) 場合は、エラーメッセージ「この名前は既に

使用中です。別の名前を選択してください。」が表示されます。

メモ:

Windows Azure モバイル サービスの既定の DNS名 (URLのホスト名) は *.azure-mobile.net です。このため例えば URL プ

リフィックス名を sampleにした場合には、モバイルサービスの稼働環境の DNS 名は sample.azure-mobile.net になります。

メモ:

現在モバイル サービスが利用できるデータセンターの場所は、アメリカ東 (East US) / アメリカ西 (West US) / 北ヨーロッ

パ (North Europe) に限られています。本演習では日本に最も近いアメリカ西(West US) を選択しています。

Page 9: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

7

4. 続けてモバイル サービスが利用するデータベースの設定です。この実体は SQL Azure になります。から接続す

る、インターネット上のモバイル サービスの URL とホスト先のデータセンター ロケーションを設定します。

[名前] 欄に任意のデータベース名を入力します。 [サーバーURL] 欄にユニークな URLプリフィックスを入力し、

[サーバー] ドロップダウンリストで「新しい SQL データベース サーバー」を選択します。そうすると、[ログイ

ン名]、[パスワード]、[パスワードの確認入力]、[地域] 欄がそれぞれ表示されます。[ログイン名]と[パスワード]

のペアを入力します。ログイン名とパスワードは直接データベースの操作を行う際に必要なため控えておきます。

パスワードを再度、[パスワードの確認入力] 欄に入力し、[地域] はそのままにして、下部の [✔] をクリックし

ます。

メモ:

Windows Azure モバイル サービスが利用するデータベースの実体は、SQL データベース (旧 SQL Azure) になります。

Windows Azure 管理ポータルサイトの SQL データベースから使用状況の確認や操作を行うことができます。

Page 10: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

8

5. Windows Azure モバイル サービスの新規作成に成功すると、 [モバイル サービス] 一覧画面上に追加されてい

ることが確認できます。

Page 11: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

9

タスク 3 : Windows Azure モバイル サービスのフレームワークを取得

このタスクでは、Windows Azure モバイル サービスに接続する iOS アプリ開発のためにフレームワークを取得します。

1. Windows Azure 管理ポータルサイトで、[モバイル サービス] を表示します。作成したサービスを選択します。

2. モバイル サービスのクイック スタートを表示されます()。 [プラットフォームを選択する] で「iOS」を選択

し、[作業を開始する] で「新しい iOS アプリを作成する」をクリックします。

Page 12: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

10

メモ:

クイックスタートを他の画面から切り替えるときは、画面上部の をクリックします。

3. iOSアプリとして Todoアプリを作成する手順が表示されます。ここでは [3アプリをダウンロードして実行する]

の [ダウンロード] ボタンをクリックします。「<サービス名>.zip」という名前のアーカイブファイルがダウン

ロードされます。

Zip ファイルの解凍したルートの中身は下記のとおりとなり、WindowsAzureMobileServices.framework を利

用します。QuickStart はサンプルの Todo アプリの Xcode プロジェクトとソースコードです。

QuickStart.xcodeproj

QuickStart

WindowsAzureMobileServices.framework

Page 13: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

11

タスク 4 : Windows Azure モバイル サービスの URLとアプリケーションキーを取得

このタスクでは、Windows Azure モバイル サービスに接続するための URL とアプリケーションキーを取得します。

1. Windows Azure 管理ポータルサイトで、[モバイル サービス] を表示します。作成したサービスを選択します。

モバイル サービスの管理画面で、上部の [ダッシュボード] メニューをクリックします。[モバイル サービス

URL] をサービス URLとして控えておきます。

2. 画面下部のきー [ i キーの管理] をクリックします。[アクセス キーの管理]で、[アプリケーション キー] 横の

コピーボタンをクリックしてクリップボードにコピーし、アプリケーションキーとして控えておきます。

Page 14: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

12

注意:

アクセス キーは他人にわからないようにしてください。万が一漏れた場合は、再生成してクライアント側のキーも入れ替

えることになります。

Page 15: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

13

演習 2 : Windows Azureモバイル サービスに接続する iOS

アプリの作成

この演習では、Windows Azure モバイル サービスに接続するシンプルなミニブログのような iOS アプリを作成します。

Windows Azure モバイル サービスの主な機能であるデータストア・アクセス、ユーザー認証機能を実際に利用して制作

することで、各機能の理解と実装方法の学習をしていきます。

Windows Azure のアプリケーション開発では、Xcode 4.5 と iOS SDK 6 を使います。Simple View Application プロジェク

ト テンプレートを基に、WindowsMobileServices フレームワークを利用して、アプリを作成していきます。

この iOS アプリの作成には、配布物の parts.zip の中身を使います。

注意:

演習で作成するアプリでは、Windows Azure モバイル サービスなどネットワーク通信に際して、二重処理の防止などは割

愛しています。

Page 16: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

14

タスク 1 : Xcode プロジェクトの作成

このタスクでは、iOS アプリの Xcode プロジェクトを新規に作成し、必要なクラスやイメージファイルをプロジェクト

に取り込みます。

1. Xcode の [File] メニューで [New] → [Project…] をクリックします。

2. [iOS - Application] で、[Single View Application] を選択して、[Next] ボタンをクリックします。

3. [Product Name] 欄に「MiniBlog」を、 [Class Prefix] 欄に「MB」を入力します。[Devices] ドロップダウン

リストを「iPhone」を選択し、[Use Storyboards] と [Use Automatic Release Counting] に [✔] を入れます。

[Next] ボタンをクリックします。

Page 17: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

15

注意:

現行のWindows Azure モバイル サービスの iOS SDK は、ARC (Automatic Reference Counting) が有効の前提で作られてい

ます。

4. 任意のプロジェクト保存場所を選択し、[Create] ボタンをクリックし、プロジェクトの作成が完了します。

5. アプリに使うアイコンや拡張クラスファイルを取り込みます。配布物の「parts.zip」を任意の場所に解凍します。

Xcode の左ペインで Project Navigator の最上部のプロジェクトを選択します。コンテキストメニューから [Add

Files to “MiniBlog”…] をクリックします。解凍したフォルダ内のファイルをすべて選択して、[Add] ボタンを

クリックします。プロジェクト内のグループ分けは任意です。

アプリアイコン Icon.png [email protected]

アプリアイコン(通知用) Icon-Small.png [email protected]

投稿用タブアイコン tab_post_icon.png [email protected]

公開投稿用タブアイコン tab_public_post_icon.png [email protected]

タグ用タブアイコン tab_tag_icon.png [email protected]

Base64 エンコード用クラス NSData+Base64.h NSData+Base64.m

Page 18: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

16

Page 19: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

17

タスク 2 : Windows Azure モバイル サービス SDKを追加

このタスクでは、iOS アプリの Xcode プロジェクトに、Windows Azure モバイル サービスの iOS 用フレームワークを

追加します。

1. 左ペインで Project Navigator の最上部のプロジェクト名を選択します。TARGETS の [MiniBlog] を選択し、[Build

Phases] タブを選択します。[Link Binary With Libraries] の下部の [+] ボタンをクリックします。

2. [Choose frameworks and libraries to add:] ダイアログにおいて、下部の [Add Other…] ボタンをクリックします。

3. 演習1のタスク 3: でダウンロードした Zip アーカイブを展開し、WindowsAzureMobileServices.framework

を選択します。[Open] ボタンをクリックします。

Page 20: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

18

タスク 3 : ストーリーボードのビューを構成

このタスクでは、Xcode プロジェクトのストーリーボードにアプリの画面を構成します。

1. 左ペインで Project Navigator から MainStoryboard.storyboard を選択します。

2. ストーリーボードに下記のように 10 個のコントローラーを配置します。

画面 Objects Controllers 画面名(以降の呼び名)

A View Controller 初期ビュー

B Tab Bar Controller タブバーコントローラー

C Navigation Controller 投稿リストナビゲーションコントローラー

D Table View Controller 投稿リストテーブルビュー

E Navigation Controller 新規投稿ナビゲーションコントローラー

F Table View Controller 新規投稿ビュー

G Navigation Controller タグリストナビゲーションコントローラー

H Table View Controller タグリストテーブルビュー

I Navigation Controller 公開投稿リストナビゲーションコントローラー

J Table View Controller 公開投稿リストテーブルビュー

※画面 A は、デフォルトのビューコントローラーを利用してください。

A B

I J

C D E F

G H

Page 21: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

19

3. ナビゲーションコントローラーとタブバーコントローラーの関連を下記のとおり追加します。画面連結元を選択

後、Control キー押下しながらドラッグして、画面連結先にドロップします。

画面連結元 画面連結先 RelationShip Segue

C D root view controller

E F root view controller

G H root view controller

I J root view controller

B I view controllers

B C view controllers

B G view controllers

4. タブバーコントローラーに表示するアイコンとラベルを設定します。各ナビゲーションコントローラーのタブバ

ーのアイコンをそれぞれ選択し、Attributes Inspector の Bar Item で [Title] 欄と [Image] 欄に値を、下記のと

おりセットします。

画面 Title(任意) Image

I Public Posts tab_public_post_icon.png

C Posts tab_post_icon.png

G Tags tab_tag_post_icon.png

Page 22: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

20

タスク 4 : ユーザー認証を設定

このタスクでは、Windows Azure 管理ポータルサイトで、モバイル サービスに認証サービスとの連携を設定します。

1. Microsoft アカウントとの認証連携を行うため、ブラウザで [Live Connect デベロッパー センター - マイ アプ

リケーション] ページ (https://manage.dev.live.com/Applications/Index) を開いて、アプリケーションを登

録します。[アプリケーションの作成] をクリックします。

2. [アプリケーション名] 欄に任意のアプリケーション名を入力してください。[同意する] ボタンをクリックします。

Page 23: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

21

3. アプリケーションが登録されると、[API 設定] ページに遷移します。 [リダイレクト ドメイン] 欄に、モバイル

サービスの URL を入力します。 [モバイル クライアント アプリ] のラジオボタンで、[はい] を選択します。[保

存] ボタンをクリックします。

Page 24: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

22

4. 保存が完了したら、[クライアント ID]と[クライアント シークレット]の値をそれぞれ控えておきます。

Page 25: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

23

5. Windows Azure ポータルサイトに戻り、[モバイル サービス] の当該サービスの画面で、上部のメニュー [ID]

をクリックします。ここでは、Microsoft アカウントを使うため、[Microsoft アカウント設定] の [クライアント

ID] 欄、[クライアント シークレット] 欄に前の手順で取得した値をそれぞれ入力します。

6. 変更を加えると、画面下部のバーに [保存] ボタンが表示されるので、クリックして設定を保存します。

Page 26: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

24

タスク 5 : ユーザー認証画面をクライアントに追加

このタスクでは、iOS アプリに初期画面からユーザー認証処理の呼び出しを追加します。

1. 共通の Windows モバイル サービスへのアクセス機構を用意します。Xcode プロジェクトに MBServiceProvider

クラスを追加します。このクラスはシングルトンとして、sharedProvider プロパティで唯一のインスタンスを

提供します。下記のサービス URLおよびアプリケーションキーは、演習 1 のタスク 4: で取得したものをセット

します。

MBServiceProvider.h (Objective-C)

// // MBServiceProvider.h // MiniBlog // #import <Foundation/Foundation.h> #import <WindowsAzureMobileServices/WindowsAzureMobileServices.h> @interface MBServiceProvider : NSObject @property (nonatomic, readonly) MSClient *client; + (MBServiceProvider *)sharedProvider; - (void)decideUser; @end

MBServiceProvider.m (Objective-C)

// // MBServiceProvider.m // MiniBlog // #import "MBServiceProvider.h" @interface MBServiceProvider () { __strong MSClient *_client; } @end @implementation MBServiceProvider @synthesize client = _client; + (MBServiceProvider *)sharedProvider { // シングルトンとして扱う static MBServiceProvider *sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[MBServiceProvider alloc] init]; }); return sharedInstance; }

Page 27: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

25

- (id)init { self = [super init]; if (self) { // モバイルサービスクライアントをサービスの URLとアプリケーションキーから生成 _client = [MSClient clientWithApplicationURLString:@"<サービス URL>" withApplicationKey:@"<アプリケーションキー>"]; } return self; } - (void)decideUser { } @end

2. 認証連携を行うため、初期画面のコントローラーMBViewController クラスに下記の実装を追加します。

signinButtonPressed:メソッドは [Sign in] ボタンの Touch Up Inside 用アクションになります。

MBViewController.h (Objective-C)

// // MBViewController.h // MiniBlog // #import <UIKit/UIKit.h> @interface MBViewController : UIViewController - (IBAction)signinButtonPressed:(id)sender; @end

MBViewController.m (Objective-C)

// // MBViewController.m // MiniBlog // #import <WindowsAzureMobileServices/WindowsAzureMobileServices.h> #import "MBServiceProvider.h" #import "MBViewController.h" @interface MBViewController () { MSClient *_client; } @end @implementation MBViewController - (void)viewDidLoad { [super viewDidLoad]; client = [[MBServiceProvider sharedProvider] client]; }

Page 28: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

26

MSClient クラスの loginViewControllerWithProvider:completion:メソッドは、Windows Azure モバイル サー

ビスのユーザー認証連携用のログイン画面 MSLoginController インスタンスを返すので、そのままモーダルで

表示し、Blocks で結果の判定が可能です。この演習では Microsoft アカウントをプロバイダとしています。

ログイン後の遷移で使用する Segue「goPortal」は、後の手順においてストーリーボードで設定します。

メモ:

MSClient クラスの loginViewControllerWithProvider:completion:メソッドの providerに渡す文字列と連携先は下記のと

おりです。

Provider 引数文字列値 連携するアカウントサービス

microsoftaccount Microsoft アカウント

facebook Facebook

twitter Twitter

google Google

3. ストーリーボードを開き、初期ビューを選択します。View Controller を選択し、Identity Inspector の [Custom

Class] に「MBViewController」をセットします。下記のとおり、View にコントロールを配置および設定しま

す。

- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (IBAction)signinButtonPressed:(id)sender { // Microsoftアカウントのログイン画面を取得 MSLoginController *lc = [_client loginViewControllerWithProvider:@"microsoftaccount" completion:^(MSUser *user, NSError *error) { if (error) { NSLog(@"Authentication Error: %@", error); [self dismissViewControllerAnimated:YES completion:nil]; } else { // エラー無しログインユーザー確定 [[MBServiceProvider sharedProvider] decideUser]; // ログイン画面を閉じてポータル画面に遷移 [self dismissViewControllerAnimated:YES completion:^{ [self performSegueWithIdentifier:@"goPortal" sender:self]; }]; } }]; [self presentViewController:lc animated:YES completion:nil]; } @end

Page 29: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

27

コントロール 役割 設定

A Label アプリ名表示

B Round Rect Button Sign In ボタン Connections Touch up Inside →

signButtonPressed:

4. 左ペインの初期ビューの View Controller を選択します。そこから Control キーを押しながらドラッグして、タブ

バーコントローラーでドロップします。Manual Segue で、modal をクリックします。さらに関連アイコンを選

択して、Identifier に「goPortal」と入力します。

5. Xcode の Run ボタンをクリックして、iPhone 6.0 Simulator で動作確認を行います。

初期ビューで、[Sign In] ボタンをタップすると、Microsoft アカウントログイン画面が表示されます。正しいメ

ールアドレスとパスワードを入力して、[Sign in] ボタンをタップすると、タブバーコントローラーのポータルビ

ューが表示されることが確認できます。

A

B

Page 30: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

28

Page 31: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

29

タスク 6 : 投稿テーブルの作成とサーバースクリプトの CRUD処理の追加

このタスクでは、Windows Azure 管理ポータルサイトで、モバイル サービスに投稿テーブルとその挿入・読み取り処理

を追加します。

1. 「posts」という名前の投稿用テーブルを追加します。モバイル サービスの管理画面で、上部の [データ] メニ

ューをクリックします。画面下部の [+作成] をクリックします。

2. [新しいテーブルの作成] ダイアログで、[テーブル名] 欄に「posts」を入力し、各アクセス許可のドロップダウ

ンリストから「認証されたユーザーのみ」を選択して、[✔] ボタンをクリックします。

Page 32: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

30

メモ:

loginViewControllerWithProvider:completion:メソッドの providerに渡す文字列と連携先は下記のとおりです。

アクセス許可 説明

すべてのユーザー すべてのクライアントからのアクセスを受け付けます。

サービスURLが漏れるとDoSアタックにされる可能性があるの

で、基本的に使わない方が良いでしょう。

アプリケーションキーを持つユーザー アプリケーションキーがセットされたクライアントからのアク

セスを許可します。

例)公開データの読み取りに使います。

認証されたユーザーのみ ユーザー認証によって承認されたクライアントからのアクセス

のみ許可します。

例)特定ユーザーの情報および変更がかかる操作に使います。

スクリプトと管理者のみ 別テーブルの CRUD 処理のスクリプトからのアクセスと、管理

者による操作のみ許可する場合に設定します。

例)ユーザーが操作しないマスターデータなどに使います。

Page 33: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

31

3. posts テーブルが作成されたら、リストからテーブルをクリックして管理画面に移ります。

4. テーブル [posts] 管理画面で、上部の [スクリプト] メニューをクリックします。[操作] ドロップダウンリスト

を「挿入」に切り替えて、下記の定義に書き換えて、下部に表示される [保存] をクリックして、反映させます。

posts : 挿入 (JavaScript)

function insert(item, user, request) { item.userId = user.userId; request.execute({ success: function() { console.log("inserted post:", item); request.respond(); }, error: function(err) { console.error("Error inserting:", item, err); request.respond(); } }); }

user.userId は認証されたユーザーの ID が取得できます。すべてのレコードに入るようプリプロセッシングとし

て、userIdフィールドにこの値をセットしています。request.executeメソッドは、引数オブジェクトに success、

error キーで function を定義すると、それぞれ成功、エラー時のをハンドルすることができます。

メモ:

挿入処理の詳細は下記を参照

insert function (Windows Azure Mobile Services - Server script reference)

http://msdn.microsoft.com/en-us/library/windowsazure/jj554229.aspx

リクエスト操作の詳細は下記を参照

Request object (Windows Azure Mobile Services - Server script reference)

http://msdn.microsoft.com/en-us/library/windowsazure/jj554218.aspx

コンソール出力操作の詳細は下記を参照

Page 34: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

32

console object (Windows Azure Mobile Services - Server script reference)

http://msdn.microsoft.com/en-us/library/windowsazure/jj554209.aspx

5. [操作] ドロップダウンリストを「読み取り」に切り替えて、下記の定義に書き換えて、下部に表示される [保存]

をクリックして、反映させます。

Posts : 読み取り (JavaScript)

function read(query, user, request) { query.orderByDescending('id'); request.execute(); }

query. orderByDescending('id') は ID の降順、つまり新しいものが先に来るようにクエリを設定しています。

メモ:

読み取り処理の詳細は下記を参照

read function (Windows Azure Mobile Services - Server script reference)

http://msdn.microsoft.com/en-us/library/windowsazure/jj554224.aspx

クエリ操作に関するオブジェクトは下記を参照

Query object (Windows Azure Mobile Services - Server script reference)

http://msdn.microsoft.com/en-us/library/windowsazure/jj613353.aspx

Page 35: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

33

タスク 7 : タグ、投稿・タグの関連テーブルの作成とサーバースクリプトの CRUD 処理の追加

このタスクでは、モバイル サービスにタグ、投稿タグ用テーブルを作成し、さらにスクリプトによる拡張処理を実装し

ます。

1. 「tags」という名前のタグ用テーブルを追加します。モバイル サービスの管理画面で、上部の [データ] メニュ

ーをクリックします。画面下部の[+作成] をクリックします。

2. [新しいテーブルの作成] ダイアログで、[テーブル名] 欄に「tags」を入力し、各アクセス許可のドロップダウン

リストから「認証されたユーザーのみ」を選択して、[✔] ボタンをクリックします

3. 「posttags」という名前の投稿・タグの関連用テーブルを追加します。再度、画面下部の [+作成] をクリック

します。

[新しいテーブルの作成] ダイアログで、[テーブル名] 欄に「posttags」を入力し、各アクセス許可のドロップダ

ウンリストから「スクリプトと管理者のみ」を選択して、[✔] ボタンをクリックします

Page 36: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

34

メモ:

posttagsテーブルへの操作は、いずれも postsおよび tagsテーブルの操作スクリプト内に限るため、上記の設定にしてい

ます。

4. tags テーブルが作成されたら、リストからテーブルをクリックして管理画面に移ります。画面上部の [スクリプ

ト] メニューをクリックします。[操作] ドロップダウンリストを「挿入」に切り替えて、下記の定義に書き換え

て、下部に表示される [保存] をクリックして、反映させます。

tags : 挿入 (JavaScript)

function insert(item, user, request) { item.userId = user.userId; request.execute(); }

5. [操作] ドロップダウンリストを「削除」に切り替えて、下記の定義に書き換えて、下部に表示される [保存] を

クリックして、反映させます。

tags : 削除 (JavaScript)

Page 37: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

35

function del(id, user, request) { var posttagsTable = tables.getTable('posttags'); posttagsTable.where({ tagId: id }).read({ success: function(results) { if (results.length > 0) { results.forEach(function(item){ posttagsTable.del(item); }); } request.execute(); } }); }

タグ削除時にタグが付いている投稿からもタグの情報を削除する必要があります。posttags テーブルを操作する

ため table.getTable メソッドで対象オブジェクトを取得します。where メソッドに tagtId=id となるよう条件を

渡し、削除対象の投稿に関連するタグの情報を取得します。検索成功時は、関連が存在すれば posttags テーブ

ルから該当項目を削除してから、本来の削除処理を実行します。

メモ:

削除処理の詳細は下記を参照

delete function (Windows Azure Mobile Services - Server script reference)

http://msdn.microsoft.com/en-us/library/windowsazure/jj554215.aspx

別テーブル操作に関するオブジェクトは下記を参照

Table object (Windows Azure Mobile Services - Server script reference)

http://msdn.microsoft.com/en-us/library/windowsazure/jj554210.aspx

tables object (Windows Azure Mobile Services - Server script reference)

http://msdn.microsoft.com/en-us/library/windowsazure/jj614364.aspx

6. [操作] ドロップダウンリストを「読み取り」に切り替えて、下記の定義に書き換えて、下部に表示される [保存]

をクリックして、反映させます。

tags 読み取り (JavaScript)

function read(query, user, request) { query.where({ userId: user.userId }); request.execute(); }

タグは、クライアントの認証ユーザー ID と userId フィールド値が一致するものだけを取得するため、

query.where メソッドでその条件を定義しています。

7. テーブル [posts] 管理画面を表示します。上部の [スクリプト] メニューをクリックします。[操作] ドロップダ

ウンリストを「挿入」に切り替えて、下記の定義に書き換えて、下部に表示される [保存] をクリックして、反

映させます。

Page 38: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

36

posts : 挿入 (JavaScript)

function insert(item, user, request) { var posttagsTable = tables.getTable('posttags'); var tags = item.tags; delete item.tags; item.userId = user.userId; request.execute({ success: function() { console.log("inserted post:", item); var newPostId = item.id; tags.forEach(function(t) { posttagsTable.insert({ "postId": newPostId, "tagId": t.id }); }); request.respond(); }, error: function(err) { console.error("Error inserting:", item, err); request.respond(); } }); }

posttags テーブルを操作するため table.getTable メソッドで対象オブジェクトを取得します。Item には tags

フィールドにタグオブジェクトの配列が入っています。このまま処理するとエラーになってしまうため、ローカ

ル変数 tags に退避し、削除します。posts テーブルへの追加が完了したら、posttags テーブルに postId、tagI

dフィールドで posts テーブルの id、tags テーブルの id をそれぞれセットして関連を表すようにし、insert メ

ソッドでレコードを追加します。

8. [操作] ドロップダウンリストを「削除」に切り替えて、下記の定義に書き換えて、下部に表示される [保存] を

クリックして、反映させます。

posts : 削除 (JavaScript)

function del(id, user, request) { var posttagsTable = tables.getTable('posttags'); posttagsTable.where({ postId: id }).read({ success: function(results) { if (results.length > 0) { results.forEach(function(item){ posttagsTable.del(item); }); } request.execute(); } }); }

投稿削除時に関連するタグの情報も削除する必要があります。posttags テーブルを操作するため table.getTable

メソッドで対象オブジェクトを取得します。where メソッドに postId=id となるよう条件を渡し、削除対象の投

稿に関連するタグの情報を取得します。検索成功時は、関連が存在すれば posttags テーブルから該当項目を削

除してから、本来の削除処理を実行します。

Page 39: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

37

9. [操作] ドロップダウンリストを「読み取り」に切り替えて、下記の定義に書き換えて、下部に表示される [保存]

をクリックして、反映させます。

posts : 読み取り (JavaScript)

function read(query, user, request) { query.orderByDescending('id'); request.execute({ success: function(results) { if (results.length === 0) { request.respond(); return; } var postIds = []; results.forEach(function(p){ postIds.push(p.id); }); var sql= "SELECT posttags.postId, tags. id, tags.name FROM posttags, tags WHERE posttags.postId IN ("; sql += postIds.join(","); sql += ") AND posttags.tagId = tags.id"; console.log(sql); mssql.query(sql, { success: function(tags) { var tagmap = {}; tags.forEach(function(t){ if (t.postId in tagmap) { tagmap[t.postId].push({ id: t.id, name: t.name }); } else { tagmap[t.postId] = [{ id: t.id, name: t.name } ]; } }); results.forEach(function(p){ p.tags = tagmap[p.id]; }); request.respond(); }, error: function(err) { console.log(err); request.respond(); } }); }}); }

この操作では、対象の投稿を取得した後に、それらの投稿に紐づけタグとそのタグ名を一度に取得し、レスポン

スの投稿データに紐づけるようにします。mssql オブジェクトの query メソッドを使うと、SQL を発行してデー

タを取得することが可能になります。ここでは、下記の SQL ステートメントを発行しています。

SELECT posttags.postId, tags.id, tags.name

FROM posttags, tags

WHERE posttags.postId IN <投稿 ID のリスト> AND posttags.tagId = tags.id

この結果、postId (投稿 ID) , id (タグ ID) , name (タグ名) をフィールドに持つオブジェクトのコレクション(変数

tags)が取得できます。これを同一投稿に対する関連タグ情報をタグ ID, タグ名をまとめます。変数 tagmap が

postId をキーとして、 id (タグ ID) , name (タグ名) をフィールドに持つオブジェクトの配列を持つ、オブジェ

Page 40: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

38

クトになります。さらに最初に取得した投稿データに対して、ID から tagmap.postId の配列を、tags フィール

ドとしてセットします。

メモ:

mssql オブジェクトの具体的な使い方は下記を参照

mssql object (Windows Azure Mobile Services - Server script reference)

http://msdn.microsoft.com/en-us/library/windowsazure/jj554212.aspx

Page 41: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

39

タスク 8 : 投稿処理をクライアントに実装

このタスクでは、iOS アプリにモバイル サービスに接続して投稿の追加および取得する処理を実装します。

1. Xcode に戻り、サービスクラス共通で使う Blocks の定義をMBServiceDefinitions.h ヘッダファイルを追加して

定義します。

MBServiceDefinitions.h (Objective-C)

// // MBServiceDefinitions.h // MiniBlog // typedef void (^CompletionBlock) (NSArray *items); typedef void (^CompletionWithIndexItemBlock) (NSUInteger index, NSDictionary *item); typedef void (^CompletionWithIndexBlock) (NSUInteger index);

2. 投稿サービスを MBPostService クラスとして追加します。このクラスは内部で MSClient クラスによる HTTP ハ

ンドルを処理処理するため、MSFilter プロトコルに適合させます。コンストラクタとして initWithClient:メソッ

ドを追加します。さらに投稿一覧を取得する getItemsWithCompletion:メソッド、投稿を追加する

addPost:withImage:completion:メソッド、投稿を削除する deletePost:completion:メソッドを実装します。

MBPostService.h (Objective-C)

// // MBPostService.h // MiniBlog // #import <Foundation/Foundation.h> #import <WindowsAzureMobileServices/WindowsAzureMobileServices.h> #import "MBServiceDefinitions.h" @interface MBPostService : NSObject<MSFilter> @property (nonatomic, readonly) NSArray *items; - (id)initWithClient:(MSClient *)client; - (void)getItemsWithCompletion:(CompletionBlock)completion; - (void)addPost:(NSDictionary *)item withImage:(UIImage *)image completion:(CompletionWithIndexItemBlock)completion; - (void)deletePost:(NSDictionary *)item completion:(CompletionWithIndexBlock)completion; @end

MBPostService.m (Objective-C)

Page 42: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

40

// // MBPostService.m // MiniBlog // #import "MBPostService.h" #import "NSData+Base64.h" @interface MBPostService () { __strong MSClient *_client; __strong MSTable *_table; __strong NSMutableArray *_items; } @end @implementation MBPostService @synthesize items = _items; - (id)initWithClient:(MSClient *)client { self = [super init]; if (self) { // 本クラスで Filterの処理をするようにしてクライアントを複製 _client = [client clientwithFilter:self]; // 投稿テーブルを扱う MSTableインスタンスを取得 _table = [_client getTable:@"posts"]; } return self; } - (void)getItemsWithCompletion:(CompletionBlock)completion { if (_items) { // 取得済みの場合はローカルで保持しているものをハンドラーに渡す completion(_items); return; } // ログインユーザーIDと userId が一致する項目を検索するクエリを生成 NSString *userId = _client.currentUser.userId; NSPredicate *predicate = [NSPredicate predicateWithFormat:@"userId == %@", userId]; MSQuery *query = [_table queryWhere:predicate]; // モバイルサービスにテーブルから読み取る処理を要求 [query readWithCompletion:^(NSArray *items, NSInteger totalCount, NSError *error) { [self logErrorIfNotNil:error]; // ローカル用に変換しローカルで保持 _items = [self loadItems:items]; // ハンドラーに取得項目を渡す completion(_items); }]; } - (NSMutableArray *)loadItems:(NSArray *)items {

Page 43: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

41

// モバイルサービスから取得した項目群をローカルで使いやすくするように変更します NSMutableArray *items_ = [[NSMutableArray alloc] initWithCapacity:[items count]]; for (NSDictionary *item in items) { NSMutableDictionary *mitem = [item mutableCopy]; // image が存在する場合は Base64からデコードして UIImageとしてセットし直す NSString *imageBase64 = [mitem objectForKey:@"image"]; if ([[NSNull null] isEqual:imageBase64]) { [mitem removeObjectForKey:@"image"]; } else { UIImage *image = [UIImage imageWithData:[NSData dataFromBase64String:imageBase64]]; [mitem setObject:image forKey:@"image"]; } [items_ addObject:mitem]; } return items_; } - (void)addPost:(NSDictionary *)item withImage:(UIImage *)image completion:(CompletionWithIndexItemBlock)completion { // 追加項目を編集するために Mutable 変数を生成 NSMutableDictionary *newItem = [[NSMutableDictionary alloc] initWithDictionary:item]; if (image) { // 画像データは Base64エンコードして文字列として image としてセット NSData *imageData = UIImageJPEGRepresentation(image, 0.7); [newItem setObject:[imageData base64EncodedString] forKey:@"image"]; } else { [newItem removeObjectForKey:@"image"]; } // モバイルサービスにテーブルの追加処理を要求 [_table insert:newItem completion:^(NSDictionary *item, NSError *error) { [self logErrorIfNotNil:error]; // 割り当てられた IDを id にセット [newItem setObject:[item objectForKey:@"id"] forKey:@"id"]; if (image) { // 画像オブジェクトのまま image にセット [newItem setObject:image forKey:@"image"]; } // 先頭に新しい項目を追加 if ([_items count] == 0) { [_items addObject:newItem]; } else { [_items insertObject:newItem atIndex:0]; } // ハンドラーに追加位置と項目を渡す completion(0, newItem); }]; } - (void)deletePost:(NSDictionary *)item completion:(CompletionWithIndexBlock)completion { NSNumber *postId = [item objectForKey:@"id"];

Page 44: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

42

// モバイルサービスにテーブルの削除処理を要求 [_table deleteWithId:postId completion:^(NSNumber *itemId, NSError *error) { [self logErrorIfNotNil:error]; // 削除する項目位置を取得 NSUInteger index = [_items indexOfObjectIdenticalTo:item]; // 保持している配列から項目を削除 [_items removeObjectAtIndex:index]; // ハンドラーに削除項目の位置を渡す completion(index); }]; } - (void)logErrorIfNotNil:(NSError *) error { if (error) { NSLog(@"ERROR %@", error); } } #pragma mark - MSFilter methods - (void)handleRequest:(NSURLRequest *)request onNext:(MSFilterNextBlock)onNext onResponse:(MSFilterResponseBlock)onResponse { onNext(request, onResponse); } @end

テーブルへの操作は、MSClient クラスの getTable:メソッドで、テーブル名を指定して取得した MSTable イン

スタンスから行います。

読み取り操作は、条件なく取得する場合 MSTable クラスの readWithCompletion:メソッドが使えます。条件を

設定する場合は、NSPredicate クラスを生成して、MSTable クラスの queryWhere:メソッドに渡し、MSQuery

インスタンスから利用することもできます。この場合も同様の readWithCompletion:メソッドが使えます。

挿入操作は、MSTable クラスの insert:completion:メソッドに、JSON となる NSDictionary インスタンスを渡

して実行できます。

削除操作は、MSTable クラスの deleteWithId:completion:メソッドに、レコード ID を渡して実行できます。

メモ:

投稿の画像は、まず JPEG 化します。さらにバイナリデータではなく Base64 の文字列としてリクエストに渡します。

これは、JSON にバイナリがセットできないためです。Windows Azure モバイル サービス側でもそのまま文字列として扱

って、取得時には Base64 をデコードしています。別の方法として、Windows Azure モバイル サービス側のスクリプトに

よって、Windows Azure ストレージ サービスのブロブに画像をファイルとしてアップロードすることも可能です。

3. MBServiceProvider クラスに MBPostService インスタンスを postService プロパティで提供するように実装を

追加します。

Page 45: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

43

MBServiceProvider.h (Objective-C)

@property (nonatomic, readonly) MSClient *client; @property (nonatomic, readonly) MBPostService *postService; + (MBServiceProvider *)sharedProvider; - (void)decideUser; @end

MBServiceProvider.m (Objective-C)

@interface MBServiceProvider () { __strong MSClient *_client; __strong MBPostService *_postService; } @end @synthesize client = _client; @synthesize postService = _postService; - (void)decideUser { // ログインユーザーが確定したので各テーブル用サービスクラスを生成 _postService = [[MBPostService alloc] initWithClient:_client]; } @end

4. 新規投稿ビューを操作する UITableViewController クラスベースの MBNewPostViewController クラスを追加

します。 UIImagePickerController クラスを使って、写真の取り込みを行います。そのため、

UImagePickerControllerDelegate, UINavigationControllerDelegate プロトコルに適合させます。

result プロパティは投稿追加後に、投稿リストテーブルビューに結果を渡すためのものです。

photoButtonPressed:メソッドは写真撮影およびフォトアルバムからの選択の処理を、saveButtonPressed:メソ

ッドは投稿保存時のアクションをそれぞれ定義します。

MBNewPostViewController.h (Objective-C)

// // MBNewPostViewController.h // MiniBlog // #import <UIKit/UIKit.h> @interface MBNewPostViewController : UITableViewController <UIImagePickerControllerDelegate, UINavigationControllerDelegate> @property (weak, nonatomic) IBOutlet UITextView *textView; @property (weak, nonatomic) IBOutlet UIImageView *imageView;

Page 46: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

44

@property (strong, nonatomic) NSDictionary *result; - (IBAction)photoButtonPressed:(id)sender; - (IBAction)saveButtonPressed:(id)sender; @end

MBNewPostViewController.m (Objective-C)

// // MBNewPostViewController.m // MiniBlog // #import "MBNewPostViewController.h" #import "MBServiceProvider.h" #import "MBPostService.h" #import "MBTagService.h" @interface MBNewPostViewController () { __strong NSMutableArray *_selectedTags; NSArray *_tags; __strong UIImagePickerController *_imagePicker; } @end @implementation MBNewPostViewController - (void)viewDidLoad { [super viewDidLoad]; MBTagService *tagService = [[MBServiceProvider sharedProvider] tagService]; [tagService getItemsWithCompletion:^(NSArray *items) { _tags = items; NSUInteger count = [_tags count]; // タグ選択状態は初期はすべて未選択にする _selectedTags = [[NSMutableArray alloc] initWithCapacity:[items count]]; for (int i = 0; i < count; i++) { [_selectedTags addObject:[NSNumber numberWithBool:NO]]; } [self.tableView reloadData]; }]; } - (void)hideKeyboard { [self.textView resignFirstResponder]; } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // Return the number of sections.

Page 47: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

45

return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // Return the number of rows in the section. return [_tags count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; // タグの name をセルに表示 cell.textLabel.text = [[_tags objectAtIndex:indexPath.row] objectForKey:@"name"]; if ([[_selectedTags objectAtIndex:indexPath.row] boolValue]) { [cell setAccessoryType:UITableViewCellAccessoryCheckmark]; } else { [cell setAccessoryType:UITableViewCellAccessoryNone]; } return cell; } #pragma mark - Table view delegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { BOOL selected = [[_selectedTags objectAtIndex:indexPath.row] boolValue]; // タグ選択を反転させます。 [_selectedTags replaceObjectAtIndex:indexPath.row withObject:[NSNumber numberWithBool:!selected]]; [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; } -(void) scrollViewWillBeginDragging:(UIScrollView *)scrollView { [self hideKeyboard]; } - (IBAction)saveButtonPressed:(id)sender { NSLog(@"Save Button Pressed"); [self hideKeyboard]; // 選択したタグの配列を作ります NSMutableArray *tags = [[NSMutableArray alloc] init]; int index = 0; for (NSNumber *val in _selectedTags) { if ([val boolValue]) { [tags addObject:[_tags objectAtIndex:index]]; } index++;

Page 48: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

46

} MBPostService *postService = [[MBServiceProvider sharedProvider] postService]; // 新しい項目を投稿テーブルに追加する NSDictionary *newPost = @{ @"text": self.textView.text, @"tags": tags }; [postService addPost:newPost withImage:self.imageView.image completion:^(NSUInteger index, NSDictionary *item) { if (index != NSNotFound) { // 投稿リストに戻ったときに追加項目(item)とその挿入位置(index)を受け取れるようにします self.result = @{ @"item": item, @"index": [NSNumber numberWithInteger:index] }; // ビューを投稿リストへ戻します。 [self performSegueWithIdentifier:@"backPostList" sender:self]; } }]; } - (IBAction)photoButtonPressed:(id)sender { [self hideKeyboard]; UIImagePickerControllerSourceType sourceType; if ([sender tag] == 0) { sourceType = UIImagePickerControllerSourceTypeCamera; // 写真撮影 } else { // 写真アルバム sourceType = UIImagePickerControllerSourceTypePhotoLibrary; } if (![UIImagePickerController isSourceTypeAvailable:sourceType]) return; if (_imagePicker == nil) { // インスタンスを使い回す _imagePicker = [[UIImagePickerController alloc] init]; } _imagePicker.sourceType = sourceType; _imagePicker.delegate = self; // 正方形のサイズで取得します _imagePicker.allowsEditing = YES; // イメージピッカーを表示 [self presentViewController:_imagePicker animated:YES completion:nil]; } - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info { [[self parentViewController] dismissViewControllerAnimated:YES completion:nil]; UIImage *image = (UIImage *)[info objectForKey:UIImagePickerControllerEditedImage]; // 画像を 256x256のサイズに縮小します。 CGRect targetRect = CGRectMake(0.0, 0.0, 256.0, 256.0); UIGraphicsBeginImageContext(targetRect.size); [image drawInRect:targetRect]; UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext();

Page 49: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

47

self.imageView.image = newImage; } - (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker { [[self parentViewController] dismissViewControllerAnimated:YES completion:nil]; } @end

NSMutableArray インスタンス _selectedTags には、タグの数と同じ要素を持ち、それぞれの添え字によって、

選択状態を表す NSNumberインスタンスのブール値が入るようにしています。

viewDidLoad メソッドでタグサービスからタグを取得してテーブルビューに表示します。それと同時に

_seletedTags の初期化もしています。

画像は、投稿リストテーブルビューで表示するときを 128x128 サイズとして、Retina 表示を考慮し、256x256 サ

イズに縮小する処理を入れています。

5. ストーリーボードを開き、新規投稿ビューを選択します。Table View Controller を選択し、Identity Inspector の

[Custom Class] に「MBNewPostViewController」をセットします。Table View は、Prototypes にし、1つ

Prototype Cell を持ち、その Identifier は「Cell」にします。さらに、Table View のヘッダービューを配置し、そ

の中とナビゲーションバーに、下記のとおりコントロールを配置および設定します。

A

C

B

D

E F

Page 50: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

48

コントロール 役割 設定

A Text View アプリ名表示 Referencing

Outles

textView

B Round Rect Button Camera ボタン Connections Touch Up Inside →

photoButtonPressed:

C Image View 画像表示 Referencing

Outles

imageView

D Round Rect Button Photo Album

ボタン

Connections Touch Up Inside →

photoButtonPressed:

E Bar Button Item Cancel ボタン Connections action → saveButtonPressed:

F Bar Button Item Save ボタン Connections action → unwind

unwindNewPostViewForSegue:

6. 投稿リストテーブルビューを操作する UITableViewController クラスベースのMBPostListViewController クラ

スを追加します。UIActionSheetDelegate プロトコルに適合させて、アクションシートで [Delete] ボタンを表

示して削除処理を呼び出すようにします。loadItems:メソッドは、ユーザーの投稿を取得しテーブルビューに反

映する処理を定義します。アクションにしているのは、後述の公開投稿表示機能でオーバーライドして使うため

です。unwindNewPostViewForSeque:メソッドは、新規投稿ビューから戻ったときの処理を定義します。

MBPostListViewController.h (Objective-C)

// // MBPostListViewController.h // MiniBlog // // #import <UIKit/UIKit.h> @interface MBPostListViewController : UITableViewController <UIActionSheetDelegate> { __strong NSArray *_posts; } - (IBAction)loadItems:(id)sender; - (IBAction)unwindNewPostViewForSegue:(UIStoryboardSegue *)segue; @end

MBPostListViewController.m(Objective-C)

// // MBPostListViewController.m // MiniBlog // // #import "MBPostListViewController.h" #import "MBServiceProvider.h" #import "MBNewPostViewController.h"

Page 51: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

49

@implementation MBPostListViewController - (void)viewDidLoad { [super viewDidLoad]; [self loadItems:nil]; } - (IBAction)loadItems:(id)sender { // 投稿テーブルから項目を取得する MBPostService *postService = [[MBServiceProvider sharedProvider] postService]; [postService getItemsWithCompletion:^(NSArray *items) { // テーブルビューをリロード _posts = items; [self.tableView reloadData]; }]; } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // Return the number of sections. return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // Return the number of rows in the section. return [_posts count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; // セルを構成する各パーツの参照を取得 UILabel *textView = (UILabel *)[cell viewWithTag:1]; UIImageView *imageView = (UIImageView *)[cell viewWithTag:2]; UILabel *tagLabel = (UILabel *)[cell viewWithTag:3]; NSDictionary *post = [_posts objectAtIndex:indexPath.row]; // 投稿の text,imageをセット textView.text = [post objectForKey:@"text"]; imageView.image = [post objectForKey:@"image"]; // 投稿 tags配列からタグ名を空白で連結してセット NSArray *tags = [post objectForKey:@"tags"]; if ([tags count] > 0) { NSMutableString *tagstr = [[NSMutableString alloc] init]; for (NSDictionary *tag in tags) {

Page 52: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

50

[tagstr appendString:[tag objectForKey:@"name"]]; [tagstr appendString:@" "]; } tagLabel.text = tagstr; } else { tagLabel.text = nil; } return cell; } #pragma mark - Table view delegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // 削除を操作するアクションシートを表示 UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Delete" otherButtonTitles:nil]; actionSheet.actionSheetStyle = UIActionSheetStyleBlackTranslucent; [actionSheet showInView:[UIApplication sharedApplication].keyWindow]; } - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex { MBPostService *postService = [[MBServiceProvider sharedProvider] postService]; NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow]; NSDictionary *targetPost = [_posts objectAtIndex:selectedIndexPath.row]; if (buttonIndex == 0) { // 選択項目を投稿テーブルから削除する [postService deletePost:targetPost completion:^(NSUInteger index) { // 削除位置に従いテーブルビューを更新 [self.tableView deleteRowsAtIndexPaths:@[selectedIndexPath] withRowAnimation:UITableViewRowAnimationFade]; }]; } [self.tableView deselectRowAtIndexPath:selectedIndexPath animated:YES]; } // ストーリーボードで新規投稿ビューからの戻り時の処理 - (IBAction)unwindNewPostViewForSegue:(UIStoryboardSegue *)segue { NSDictionary *result = [[segue sourceViewController] result]; if (result) { // 新規投稿ビューに追加結果が存在するとき NSInteger index = [[result objectForKey:@"index"] integerValue]; NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0]; // 追加位置に従いテーブルビューを更新 [self.tableView beginUpdates]; [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; [self.tableView endUpdates]; } } @end

viewDidLoad メソッドで投稿サービスから認証ユーザーの投稿を取得して、テーブルビューに表示します。テ

Page 53: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

51

ーブルビューのセル上のコントロールは Tag 値から参照を取得して、投稿内容をセットします。セルをタップし

たときは、アクションシートを表示して、[Delete]から投稿サービスの削除処理を呼べるようにします。

7. ストーリーボードを開き、投稿リストテーブルビューを選択します。Table View Controller を選択し、Identity

Inspector の [Custom Class] に「MBPostListViewController」をセットします。Table View は、Dynamic

Prototypes にし、1つ Prototype Cell を持ち、その Identifier は「Cell」にします。その Table View Cell 内とナ

ビゲーションバーに、下記のとおりコントロールを配置および設定します。

コントロール 役割 設定

A Label 投稿文字列表示 Tag 1

B Image View 投稿画像表示 Tag 2

C Label タグ表示 Tag 3

D Bar Button Item Compose ボタン Identifier Compose

Connections action → modal

新規投稿ナビゲーション

コントローラー

A

B

D

C

Page 54: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

52

8. 新規投稿ビューから投稿リストテーブルビューへの戻るときの Segue を設定します。新規投稿ビューを選択しま

す。下部の3つのアイコンのうち Exitアイコン(下記図 A)を選択します。さらに Connections Inspector を表示

します。unwindNewPostViewForSegue: の [+](下記図 B)をドラッグして、新規投稿ビュー(下記図 C)に

ドロップします。manual とポップアップで出るので、それをクリックします。

A

B

C

Page 55: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

53

9. 作成した Segue に ID を割り振ります。左ペインの新規投稿ビューの [Unwind segue from New Post to Exit] を

選択します。さらに Attributes Inspector を表示します。[Identifier] に「backPostList」と入力します。

Page 56: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

54

10. 新規投稿ビューの [Cancel] ボタンを選択します。さらに Connections Inspector を表示します。の action の [+]

(下記図 B)をドラッグして、新規投稿ビュー下部の3つのアイコンのうち Exit アイコン(下記図 C)にドロッ

プします。ポップアップで表示された [unwindNewPostViewForSegue:] をクリックします。

A

B

C

Page 57: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

55

タスク 9 : タグ関連処理をクライアントに実装

このタスクでは、iOS アプリにタグ管理サービスとそのビューを実装します。

1. tags テーブルに対する処理をまとめた MBTagService クラスを追加します。このクラスは内部で MSClient クラ

スによる HTTP ハンドルを処理処理するため、MSFilter プロトコルに適合させます。コンストラクタとして

initWithClient:メソッドを追加します。さらにタグ一覧を取得する getItemsWithCompletion:メソッド、タグを

追加する addTagWithName:completion:メソッド、タグを削除する removeTagItem:completion:メソッドを実

装します。

MBTagService.h (Objective-C)

// // MBTagService.h // MiniBlog // #import <Foundation/Foundation.h> #import <WindowsAzureMobileServices/WindowsAzureMobileServices.h> #import "MBServiceDefinitions.h" @interface MBTagService : NSObject<MSFilter> - (id)initWithClient:(MSClient *)client; - (void)getItemsWithCompletion:(CompletionBlock)completion; - (void)addTagWithName:(NSString *)name completion:(CompletionWithIndexItemBlock)completion; - (void)removeTagItem:(NSDictionary *)item completion:(CompletionWithIndexBlock)completion; @end

MBTagService.m (Objective-C)

// // MBTagService.m // MiniBlog // #import "MBTagService.h" #import "MBServiceProvider.h" @interface MBTagService () { __strong MSClient *_client; __strong MSTable *_table; __strong NSMutableArray *_items; } @end @implementation MBTagService - (id)initWithClient:(MSClient *)client { self = [super init];

Page 58: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

56

if (self) { // 本クラスで Filterの処理をするようにしてクライアントを複製 _client = [client clientwithFilter:self]; // 投稿テーブルを扱う MSTableインスタンスを取得 _table = [_client getTable:@"tags"]; } return self; } - (void)getItemsWithCompletion:(CompletionBlock)completion { if (_items) { // 取得済みの場合はローカルで保持しているものをハンドラーに渡す completion(_items); return; } [_table readWithCompletion:^(NSArray *items, NSInteger totalCount, NSError *error) { [self logErrorIfNotNil:error]; // ローカルに変更可能な配列として保持 _items = [items mutableCopy]; // ハンドラーに取得項目を渡す completion(_items); }]; } -(void)addTagWithName:(NSString *)name completion:(CompletionWithIndexItemBlock)completion { NSDictionary *newItem = @{ @"name": name }; // モバイルサービスにテーブルの追加処理を要求 [_table insert:newItem completion:^(NSDictionary *item, NSError *error) { [self logErrorIfNotNil:error]; // 追加後のインデックスを調べる NSUInteger index = [_items count]; // 新しい項目を最後に追加 [_items addObject:item]; // ハンドラーに追加位置と項目を渡す completion(index, item); }]; } - (void)removeTagItem:(NSDictionary *)item completion:(CompletionWithIndexBlock)completion { NSNumber *targetId = [item objectForKey:@"id"]; // モバイルサービスにテーブルの削除処理を要求 [_table deleteWithId:targetId completion:^(NSNumber *itemId, NSError *error) { [self logErrorIfNotNil:error]; // 削除する項目位置を取得 NSUInteger targetIndex = [_items indexOfObjectIdenticalTo:item]; // 保持している配列から項目を削除

Page 59: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

57

[_items removeObjectAtIndex:targetIndex]; // ハンドラーに削除項目の位置を渡す completion(targetIndex); }]; } - (void)logErrorIfNotNil:(NSError *) error { if (error) { NSLog(@"ERROR %@", error); } } #pragma mark - MSFilter methods - (void)handleRequest:(NSURLRequest *)request onNext:(MSFilterNextBlock)onNext onResponse:(MSFilterResponseBlock)onResponse { onNext(request, onResponse); } @end

メモ:

取得操作で、投稿の時と違い、直接 MSTableクラスの readWithCompletion:メソッドを呼んでいるのは、サーバーサイド

で、認証ユーザーのものに限定するようにしているためです。

2. MBServiceProvider クラスにMBTagService インスタンスを tagService プロパティで提供するように実装を追

加します。

MBServiceProvider.h (Objective-C)

@property (nonatomic, readonly) MSClient *client; @property (nonatomic, readonly) MBPostService *postService; @property (nonatomic, readonly) MBTagService *tagService; + (MBServiceProvider *)sharedProvider; - (void)decideUser; @end

MBServiceProvider.m (Objective-C)

@interface MBServiceProvider () { __strong MSClient *_client; __strong MBPostService *_postService; __strong MBTagService *_tagService; } @end @synthesize client = _client;

Page 60: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

58

@synthesize postService = _postService; @synthesize tagService = _tagService; - (void)decideUser { // ログインユーザーが確定したので各テーブル用サービスクラスを生成 _postService = [[MBPostService alloc] initWithClient:_client]; _tagService = [[MBTagService alloc] initWithClient:_client]; } @end

3. タグ管理画面にタグサービスを使って表示処理を実装します。タグリストテーブルビューを操作する

UITableViewController クラスベースの MBTagListViewController クラスを追加します。UIAlertViewDelegate

プロトコルに適合させて、addButtonPressed:メソッドをトリガーに、新規タグ名の入力するアラートビューを

表示します。

MBTagListViewController .h (Objective-C)

// // MBTagListViewController.h // MiniBlog // #import <UIKit/UIKit.h> @interface MBTagListViewController : UITableViewController <UIAlertViewDelegate> - (IBAction)addButtonPressed:(id)sender; @end

MBTagListViewController .m (Objective-C)

// // MBTagListViewController.m // MiniBlog // #import "MBTagListViewController.h" #import "MBServiceProvider.h" #import "MBTagService.h" @interface MBTagListViewController () { NSArray *_tags; } @end

Page 61: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

59

- (void)viewDidLoad { [super viewDidLoad]; // タグテーブルからログインユーザーのタグを取得 MBTagService *tagService = [[MBServiceProvider sharedProvider] tagService]; [tagService getItemsWithCompletion:^(NSArray *items) { _tags = items; [self.tableView reloadData]; }]; }

#pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { // Return the number of sections. return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { // Return the number of rows in the section. return [_tags count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath]; // タグの name をセルに表示 cell.textLabel.text = [[_tags objectAtIndex:indexPath.row] objectForKey:@"name"]; return cell; } - (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { if (editingStyle == UITableViewCellEditingStyleDelete) { // 指定された項目をタグテーブルから削除する MBTagService *tagService = [[MBServiceProvider sharedProvider] tagService]; [tagService removeTagItem:[_tags objectAtIndex:indexPath.row] completion:^(NSUInteger index) { // 削除位置に従いテーブルビューを更新 [tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade]; }]; } } #pragma mark - Table view delegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

Page 62: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

60

{ } - (IBAction)addButtonPressed:(id)sender { // 新しいタグ名を入力を要求するアラートビューを表示 UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"New Tag" message:@"" delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil]; [alertView setAlertViewStyle:UIAlertViewStylePlainTextInput]; [alertView show]; } - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { if (buttonIndex == 1) { NSString *newTagName = [[alertView textFieldAtIndex:0] text]; // 新しい項目をタグテーブルに追加する MBTagService *tagService = [[MBServiceProvider sharedProvider] tagService]; [tagService addTagWithName:newTagName completion:^(NSUInteger index, NSDictionary *item) { // 項目の追加位置に従いテーブルビューを更新 [self.tableView beginUpdates]; NSIndexPath *indexPath = [NSIndexPath indexPathForItem:index inSection:0]; [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; [self.tableView endUpdates]; }]; } } @end

4. ストーリーボードを開き、タグリストテーブルビューを選択します。Table View Controller を選択し、Identity

Inspector の [Custom Class] に「MBTagListViewController」をセットします。Table View は、Dynamic

Prototypes にし、1つ Prototype Cell を持ち、その Identifier は「Cell」にします。ナビゲーションバーに、下

記のとおりコントロールを配置および設定します。

Page 63: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

61

コントロール 役割 設定

A Bar Button Item + ボタン Identifier Add

Connections action →

addButtonPressed:

A

Page 64: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

62

タスク 10 : 公開投稿をクライアントに実装

このタスクでは、iOS アプリに投稿の共有機能と、共有された投稿を公開投稿として表示する機能を実装します。

1. 更新機能 updatePost:completion メソッドと、 shared フラグが真である項目だけ取得する機能

getPublicItemsWithCompletion メソッドを、MBPostService クラスに追加します。

MBPostService.h (Objective-C)

// // MBPostService.h // MiniBlog // - (void)getItemsWithCompletion:(CompletionBlock)completion; - (void)getPublicItemsWithCompletion:(CompletionBlock)completion; - (void)addPost:(NSDictionary *)item withImage:(UIImage *)image completion:(CompletionWithIndexItemBlock)completion; - (void)updatePost:(NSDictionary *)item completion:(CompletionWithIndexItemBlock)completion; - (void)deletePost:(NSDictionary *)item completion:(CompletionWithIndexBlock)completion; @end

MBPostService.m (Objective-C)

// // MBPostService.m // MiniBlog // - (void)getPublicItemsWithCompletion:(CompletionBlock)completion { // shared が真である項目を検索するクエリを生成 NSPredicate *predicate = [NSPredicate predicateWithFormat:@"shared == YES"]; MSQuery *query = [_table queryWhere:predicate]; // モバイルサービスにテーブルから読み取る処理を要求 [query readWithCompletion:^(NSArray *items, NSInteger totalCount, NSError *error) { [self logErrorIfNotNil:error]; // ローカル用に変換してハンドラーに渡す completion([self loadItems:items]); }]; } - (void)updatePost:(NSDictionary *)item completion:(CompletionWithIndexItemBlock)completion

Page 65: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

63

{ NSInteger itemId = [[item objectForKey:@"id"] integerValue]; // id 値を比較して更新項目のインデックスを調べる NSUInteger index = [_items indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { if ([[obj objectForKey:@"id"] integerValue] == itemId) { *stop = YES; return YES; } return NO; }]; // 更新項目の変更可能の変数を取得 NSMutableDictionary *currentItem = [[_items objectAtIndex:index] mutableCopy]; // モバイルサービスにテーブルの更新処理を要求 [_table update:item completion:^(NSDictionary *item, NSError *error) { [self logErrorIfNotNil:error]; // id 値以外は更新処理から戻った値をセット for (NSString *key in item) { if (![key isEqualToString:@"id"]) { [currentItem setObject:[item objectForKey:key] forKey:key]; } } // 保持している配列の項目を入れ替える [_items replaceObjectAtIndex:index withObject:currentItem]; // ハンドラーに項目の位置と更新後の項目を渡す completion(index, item); }]; }

テーブルへの更新操作は、MSTable クラスの updete:completion:メソッドに、JSON となる NSDictionary イン

スタンスを渡して実行できます。この NSDictionary には、id フィールドと更新するフィールドだけセットしま

す。

2. 投稿リストビューの MBPostListViewController クラスに、共有状態を示すラベルとアクションシートから共有

の切り替えをするボタンを表示するように実装を追加します。

MBPostListViewController.m (Objective-C)

// // MBPublicPostListViewController.m // MiniBlog // - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];

Page 66: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

64

// セルを構成する各パーツの参照を取得 UILabel *textView = (UILabel *)[cell viewWithTag:1]; UIImageView *imageView = (UIImageView *)[cell viewWithTag:2]; UILabel *tagLabel = (UILabel *)[cell viewWithTag:3]; UILabel *sharedLabel = (UILabel *)[cell viewWithTag:4]; NSDictionary *post = [_posts objectAtIndex:indexPath.row]; // 投稿の text,imageをセット textView.text = [post objectForKey:@"text"]; imageView.image = [post objectForKey:@"image"]; // 投稿 tags配列からタグ名を空白で連結してセット NSArray *tags = [post objectForKey:@"tags"]; if ([tags count] > 0) { NSMutableString *tagstr = [[NSMutableString alloc] init]; for (NSDictionary *tag in tags) { [tagstr appendString:[tag objectForKey:@"name"]]; [tagstr appendString:@" "]; } tagLabel.text = tagstr; } else { tagLabel.text = nil; } // 共有ラベルがセルにあれば shared 値に応じて表示 if (sharedLabel) { [sharedLabel setHidden:![self getSharedFromItem:post]]; } return cell; } - (BOOL)getSharedFromItem:(NSDictionary *)item { NSNumber *isShared = [item objectForKey:@"shared"]; if ([isShared isEqual:[NSNull null]]) { return NO; } return [isShared boolValue]; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // 削除と共有を操作するアクションシートを表示 NSString *shareButtonName = [self getSharedFromItem:[_posts objectAtIndex:indexPath.row]] ? @"Unshare" : @"Share"; UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:nil delegate:self cancelButtonTitle:@"Cancel" destructiveButtonTitle:@"Delete" otherButtonTitles:shareButtonName, nil]; actionSheet.actionSheetStyle = UIActionSheetStyleBlackTranslucent; [actionSheet showInView:[UIApplication sharedApplication].keyWindow]; } - (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {

Page 67: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

65

MBPostService *postService = [[MBServiceProvider sharedProvider] postService]; NSIndexPath *selectedIndexPath = [self.tableView indexPathForSelectedRow]; NSDictionary *targetPost = [_posts objectAtIndex:selectedIndexPath.row]; if (buttonIndex == 0) { // 選択項目を投稿テーブルから削除する [postService deletePost:targetPost completion:^(NSUInteger index) { // 削除位置に従いテーブルビューを更新 [self.tableView deleteRowsAtIndexPaths:@[selectedIndexPath] withRowAnimation:UITableViewRowAnimationFade]; }]; } else if (buttonIndex == 1) { // 共有の設定もしくは解除をする NSNumber *isShared = [targetPost objectForKey:@"shared"]; BOOL shared = NO; if (![isShared isEqual:[NSNull null]]) { shared = [isShared boolValue]; } NSDictionary *upItem = @{ @"id": [targetPost objectForKey:@"id"], @"shared": [NSNumber numberWithBool:!shared] }; // 選択項目の新しい shared 値を投稿テーブルに対して更新する [postService updatePost:upItem completion:^(NSUInteger index, NSDictionary *item) { // 更新位置に従いテーブルビューを更新 [self.tableView reloadRowsAtIndexPaths:@[selectedIndexPath] withRowAnimation:UITableViewRowAnimationNone]; }]; } [self.tableView deselectRowAtIndexPath:selectedIndexPath animated:YES]; }

共有を示すブール値 shared フィールドは新規作成時には、存在しないフィールドなので、存在しないもしくは

NSNull インスタンス値以外の場合に取得するようにしています。

テーブルビューの項目タップ時に表示するアクションシートに「Delete」に加えて、「Share」(既に共有済みの

場合は「Unshare」)を表示するようにします。shared フィールド値を反転して、MBPostService クラスの

updatePost:completion:メソッドを呼ぶようにします。

3. ストーリーボードを開き、投稿リストテーブルビューを選択します。Table View Cell 内に、下記のとおりコント

ロールを追加で配置および設定します。

Page 68: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

66

コントロール 役割 設定

A Label Shared 表示 Tag 4

4. 公開投稿リストビューの MBPostListViewController クラスをベースとする MBPublicPostListViewController

クラスを追加します。

MBPublicPostListViewController.h (Objective-C)

// // MBPublicPostListViewController.h // MiniBlog // #import "MBPostListViewController.h" @interface MBPublicPostListViewController : MBPostListViewController @end

MBPublicPostListViewController.m (Objective-C)

A

Page 69: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

67

// // MBPublicPostListViewController.m // MiniBlog // #import "MBPostListViewController.h" #import "MBServiceProvider.h" #import "MBPublicPostListViewController.h" @implementation MBPublicPostListViewController - (IBAction)loadItems:(id)sender { MBPostService *postService = [[MBServiceProvider sharedProvider] postService]; // 投稿テーブルから共有項目を取得する [postService getPublicItemsWithCompletion:^(NSArray *items) { // テーブルビューをリロード _posts = items; [self.tableView reloadData]; }]; } @end

5. ストーリーボードを開き、公開投稿リストテーブルビューを選択します。Table View Controller を選択し、Identity

Inspector の [Custom Class] に「MBPublicPostListViewController」をセットします。Table View は、Dynamic

Prototypes にし、1つ Prototype Cell を持ち、その Identifier は「Cell」にします。その Table View Cell 内とナ

ビゲーションバーに、下記のとおりコントロールを配置および設定します。

コントロール 役割 設定

A Label 投稿文字列表示 Tag 1

B Image View 投稿画像表示 Tag 2

C Label タグ表示 Tag 3

A

B

D

C

Page 70: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

68

D Bar Button Item 更新ボタン Identifier Refresh

Connections action → loadItems:

Page 71: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

69

タスク 11 : 投稿・タグ機能の動作確認

このタスクでは、iOS アプリに実装した投稿機能・タグ管理機能・公開投稿表示を、実際に動作させて試します。

1. Xcode から実行します。初期ビューからログインして、ポータル画面に遷移します。

2. タブバーの [Tags] をタップして、Tags を表示します。

Page 72: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

70

3. 右上の [+] ボタンをタップして、「メモ」を追加します。

4. 追加した項目をスワイプすると、[削除] ボタンが表示されることが確認できます。

Page 73: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

71

5. 続けて、タブバーの [Posts] をタップして、Posts を表示させます。右上の Compose ボタンをタップします。

6. New Post が表示されます。テキストを入力して、タグ「メモ」を選択して、[Save] ボタンをタップします。

Page 74: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

72

7. Posts に戻り、タグとともに投稿内容が表示されます。

8. 投稿をタップし、アクションシートから [Share] をタップすると、Shared マークが付きます。

Page 75: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

73

9. 続けて、タブバーの [Public Posts] をタップして、Public Posts を表示させます。右上の Refresh ボタンをタップし

ます。共有した投稿が表示されます。

Page 76: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

74

演習 3 : Windows Azure モバイル サービスを利用してプ

ッシュ通知を実装

この演習では演習2で作成したアプリに、Windows Azure モバイル サービスのプッシュ通知機能を利用して、APNS と

組み合わせたプッシュ通知機能を実装します。プッシュ通知を、実際に利用して制作することで、各機能の理解と実装

方法の学習をしていきます。

Page 77: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

75

タスク 1 : App ID の作成

このタスクでは、iOS アプリの App ID を iOS Developer Center で登録します。

1. ブラウザから iOS Developer Center にログインして、Provisioning Portal を表示します。[App IDs]をクリック後、

[New App ID] ボタンをクリックします。

2. App ID を登録します。[Description] 欄、[Bundle Seed ID] ドロップダウンリスト、[Budle Identifier] 欄を入

力・選択して、[Submit] ボタンをクリックします。

Page 78: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

76

メモ:

これまでのアプリ開発においては、ワイルドカードの App ID が使えるため、別途 App ID を登録する必要はありません。プ

ッシュ通知を行うには、ユニークな App ID が必要になります。

0

0

Page 79: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

77

タスク 2 : プッシュ通知の証明書作成

このタスクでは、iOS アプリのプッシュ通知用証明書を iOS Developer Center で作成します。

1. [Apps ID] で生成した App ID の [Configure] ボタンをクリックします。

2. [Configure Apps ID] で生成した [Enable for Apple Push Notification service] に [✔ ] を入れます。

[Development Push SSL Certificate] の [Configure] ボタンをクリックします。

3. [Generate a Certificate Signing Request] の指示通り CSRファイルをMac OS Xのキーチェーンアクセスでを使っ

て、生成します。

4. ユーティリティのキーチェーンアクセスを開きます。アプリケーションメニューから [証明書アシスタント] の

[認証局に証明書を要求…] をクリックします。

Page 80: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

78

5. [証明書アシスタント] で、[ユーザのメールアドレス] 欄に自分のメールアドレスを、[通称] 欄に任意の証明書の

名称を入力します。[要求の処理] の「ディスクに保存」を選択して、[続ける] ボタンをクリックします。ファイ

ル保存のダイアログが表示されるので、任意の場所に証明書要求ファイルを保存します。

6. ブラウザの [Generate a Certificate Signing Request]に戻り、[Continue] ボタンをクリックします。

Page 81: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

79

7. [ファイルを選択] ボタンをクリックして、キーチェーンアクセスで作成した証明書要求ファイルを選択して、

[Generate] ボタンをクリックします。

8. APNS の SSL 証明書の作成に成功したことを確認し、[Continue] ボタンをクリックします。

Page 82: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

80

9. [Downlaod] ボタンをクリックして、「aps_development.cer」ファイルをダウンロードします。[Done] ボタンを

クリックして、ウィザードを終了します。

Page 83: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

81

タスク 3 : プロビジョニングプロファイルの作成

このタスクでは、iOS アプリの App ID と開発する実機に紐づくプロビジョニングプロファイルを作成し、Xcode にイン

ポートして設定します。

1. プロビジョニングプロファイルを作成します。Provisioning Portal で [Provisioning] を選択し、[Development] タ

ブをクリックして、[New Profile] ボタンをクリックします。

2. [Profile Name] 欄に任意の名前、[Certificates] の自分の証明書に [✔] を入れます。[App ID] ドロップダウンリ

ストから先ほど作成したものを選んで、[Devices] の使用する実機をに [✔] を入れて、[Submit] ボタンをクリ

ックします。

3. [Development Provisioning Profiles] で作成したプロファイルの [Status] が「Active」になったら、[Download] ボ

タンをクリックして、ローカルにダウンロードします。ダウンロードしたファイルをダブルクリックすると、

Xcode に取り込まれます。この手順は Xcode の Organizer で同期することでも可能です。

4. Xcode に戻ります。左ペインで Project Navigator の最上部のプロジェクト名を選択します。中央ペインで、

PROJECT のアプリ名項目を選択します。さらに [Build Settings] タブを選択し、[Code Signing] 内の [Code

Signing Identity] を開きます。 [Debug] の [Any iOS SDK] をクリックして、取り込んだプロビジョニングプロ

ファイルを選択します。 [Release] の [Any iOS SDK] も同様に選択します。

Page 84: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

82

Page 85: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

83

タスク 4 : 証明書の秘密鍵をモバイル サービスに登録

このタスクでは、プッシュ通知用証明書を p12 形式で書き出して、モバイル サービスにアップロードし登録します。

1. 前タスクの最後にダウンロードした「aps_development.cer」をダブルクリックして、キーチェーンに登録します。

キーチェーンアクセスを開きます。[ログイン] を選択し、[Apple Development iOS Push Sevices: <設定した

Bundle App ID>] を選択して、コンテキストメニューを表示します。[Apple Development iOS Push Sevices:

<設定した Bundle App ID>を書き出す…] をクリックします。

2. ファイル保存のダイアログが表示されるので、任意の名前で p12 形式の証明書ファイルを保存します。書き出す

際に証明書の読み込み時に必要なパスワードを [パスワード] 欄と [確認] 欄に入力して、[OK] ボタンをクリッ

クします。最後にキーチェーンにの OS のログインパスワードを入力します。

Page 86: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

84

注意:

証明書ファイルとパスワードは、Windows Azure 管理ポータルサイトにおいて、証明書ファイルを登録するときに使います。

またこのペアが漏れると、プッシュ送信元に成りすますことができるため管理に気を付けてください。

3. ブラウザで Windows Azure 管理ポータルサイトを表示します。モバイル サービスの管理画面で上部の[]メニュ

ーをクリックします。

4. [証明書のアップロード] を [ファイル] に書き出した p12形式の証明書ファイルを選択します。[パスワード] 欄

に書き出し時に設定したパスワードを入力します。[✔] ボタンをクリックします。

5. [証明書のアップロード] を [ファイル] に書き出した p12形式の証明書ファイルを選択します。[パスワード] 欄

に書き出し時に設定したパスワードを入力します。[✔] ボタンをクリックします。

Page 87: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

85

Page 88: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

86

タスク 5 : デバイステーブルの作成とプッシュ通知処理の実装

このタスクでは、プッシュ通知用に iOS デバイストークンを管理するテーブルをモバイル サービスに追加します。さら

に、プッシュ通知用サーバーサイドスクリプトの実装と APNS からのフィードバック処理も実装します。

1. 「devices」という名前のデバイストークン登録用テーブルを追加します。モバイル サービスの管理画面で、上

部の [データ] メニューをクリックします。画面下部の [+作成] をクリックします。

2. [新しいテーブルの作成] ダイアログで、[テーブル名] 欄に「devices」を入力し、各アクセス許可のドロップダ

ウンリストから [挿入アクセス許可] のみ「認証されたユーザーのみ」を、それ以外は「スクリプトと管理者の

み」を選択して、[✔] ボタンをクリックします。

メモ:

devicesテーブルへの操作は外部からは登録(挿入アクセス)のみで、読み取りは postsテーブルの更新処理、削除は APNS

のフィードバック処理のときであるため、上記の設定にしています。

Page 89: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

87

3. プッシュ通知する対象のデバイスを登録するサーバースクリプトを devices の挿入操作に追加します。一度登録

済みのトークンは二重登録しないように先に検索をかけてチェックしています。

devices : 挿入 (JavaScript)

function insert(item, user, request) { var devicesTable = tables.getTable('devices'); devicesTable.where({ token: item.token }).read({ success: insertTokenIfNotFound }); function insertTokenIfNotFound(existingTokens) { if (existingTokens.length > 0) { request.respond(200, existingTokens[0]); } else { item.userId = user.userId; request.execute(); } } }

4. プッシュ通知を posts テーブルの更新操作に追加します。更新項目の shared フィールドが真である(つまり共

有が行われた)ときにプッシュ通知を行います。devices テーブルから投稿の userId とは一致しない(自分自身

のデバイスは通知先から除外)レコードを検索し、各レコードの token 値をデバイストークンとして

push.apns.send メソッドを呼び出し、プッシュ通知を APNS に依頼します。

posts : 更新 (JavaScript)

function update(item, user, request) { request.execute({ success: function() { request.respond(); if (item.shared) { sendNotifications(user.userId); } } }); function sendNotifications(ownerUserId) { var devicesTable = tables.getTable('devices'); devicesTable.where(function(currentUserId){ return this.userId !== currentUserId; }, ownerUserId).read({ success: function(devices) { // not required setTimeout for demonstratation. //setTimeout(function() { devices.forEach(function(device) { push.apns.send(device.token, { alert: "New shared post arrived.", sound: "default", payload: { "inAppMessage": "New shared post arrived." } }); }); //}, 2500); }}); } }

Page 90: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

88

注意:

上記は実運用時の実装になります。1つの端末でプッシュ通知を試す場合は、devicesTagble.whereメソッドの第1引数の

無名関数で常に trueを返し、コメントアウトされている setTimeout(function(){ … }, 2500) を有効にしてください。

メモ:

更新処理の詳細は下記を参照

update function (Windows Azure Mobile Services - Server script reference)

http://msdn.microsoft.com/en-us/library/windowsazure/jj554214.aspx

apns オブジェクトの詳細は下記を参照

apns object (Windows Azure Mobile Services - Server script reference)

http://msdn.microsoft.com/en-us/library/windowsazure/jj839711.aspx

Page 91: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

89

タスク 6 : プッシュ通知処理をクライアントに実装

このタスクでは、iOS アプリにプッシュ通知のための実装を追加します。

1. Xcode にもどり、MBServiceProvider クラスにアプリ起動時のデバイストークンを受け取るための

deviceToken プロパティを追加します。decideUser メソッドで deviceToken に値が入っていれば、モバイル サ

ービスの devices テーブルに登録するようにします。

MBServiceProvider.h (Objective-C)

@property (nonatomic, readonly) MSClient *client; @property (nonatomic, readonly) MBPostService *postService; @property (nonatomic, readonly) MBTagService *tagService; @property (nonatomic, strong) NSString *deviceToken; + (MBServiceProvider *)sharedProvider; - (void)decideUser; @end

MBServiceProvider.m (Objective-C)

@synthesize client = _client; @synthesize postService = _postService; @synthesize tagService = _tagService; @synthesize deviceToken; - (void)decideUser { // ログインユーザーが確定したので各テーブル用サービスクラスを生成 _postService = [[MBPostService alloc] initWithClient:_client]; _tagService = [[MBTagService alloc] initWithClient:_client]; if (deviceToken) { // devicesテーブルにトークンを登録 MSTable *devicesTable = [_client getTable:@"devices"]; NSDictionary *item = @{ @"token": deviceToken }; [devicesTable insert:item completion:^(NSDictionary *item, NSError *error) { }]; } } @end

2. MBAppDelegate クラスの application:didFinishLaunchingWithOptions:メソッドで、アプリ起動時にプッシュ

通知を登録します。application:didRegisterForRemoteNotificationsWithDeviceToken:メソッドで、登録後処

理のデバイストークンを MBServiceProvider クラスにセットするようにします。

MBAppDelegate.m (Objective-C)

Page 92: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

90

// // MBAppDelegate.m // MiniBlog // #import "MBServiceProvider.h" #import "MBAppDelegate.h" @implementation MBAppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // プッシュ通知の登録 [[UIApplication sharedApplication] registerForRemoteNotificationTypes: UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound]; return YES; } - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { // デバイストークンをサービスプロバイダーに渡す NSCharacterSet *angleBrackets = [NSCharacterSet characterSetWithCharactersInString:@"<>"]; NSString *deviceTokenStr = [[[deviceToken description] stringByTrimmingCharactersInSet:angleBrackets] stringByReplacingOccurrencesOfString:@" " withString:@""]; [[MBServiceProvider sharedProvider] setDeviceToken:deviceTokenStr]; } - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { // アラートでプッシュ通知メッセージを表示 NSString *msg = [userInfo objectForKey:@"inAppMessage"]; UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Notification" message:msg delegate:nil cancelButtonTitle:nil otherButtonTitles:@"OK", nil]; [alert show]; } @end

application:didReceiveRemoteNotification:メソッドでは、アプリ起動中に受信したプッシュ通知をアラートで

表示するように実装しています。

Page 93: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

91

タスク 7 : APNS からのフィードバック処理を追加

このタスクでは、APNS から無効になったデバイストークンを管理対象から外す処理を実装します。

1. APNS サービスから無効になったデバイストークンのフィードバックを受け取り、devices テーブルから削除す

るようにします。モバイル サービス管理画面上部の [プッシュ] メニューをクリックします。[apple プッシュ

通知の設定] の [スクリプト編集→] をクリックします。[apns フィードバック スクリプト] を下記のように変

更します。

apns フィードバック スクリプト (JavaScript)

// The processFeedback method will be invoked periodically to // allow you to call the APNS service to collect feedback. // You can use the results of the push.apns.getFeedback invocation // to retire any device tokens appropriately function processFeedback() { push.apns.getFeedback({ success: function(results) { // results will be an array of objects, each with 'deviceToken' and 'time' properties if (results.length > 0) { // TODO - update your storage to mark or delete devices appropriately console.warn("APNS feedback received for the following device tokens.", results); var devicesTable = tables.getTable('devices'); results.forEach(function(item){ devicesTable.where({ token: item.deviceToken }) .read({ success: function(devices) { if (devices.length > 0) { devicesTable.del(devices[0]); } }}); }); } } }); }

下部の [保存] ボタンをクリックして反映します。

メモ:

APNS は通知できなかったデバイスのトークンを管理していて、アプリケーションのサービスに定期的にフィードバックを

します。これはそのフィードバックを受け取ったときの処理になります。実際のサービス運用上は必須ですが、この演習の

範囲でフィードバックが実際に発生する可能性は、ほとんどないでしょう。

Page 94: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

92

タスク 8 : プッシュ通知の動作確認

このタスクでは、iOS アプリに実装したプッシュ通知を実際に実機で動作させて試します。

1. 実機を繋いで、Xcode から実行します。

2. 初回は、最初にプッシュ通知を許可するかどうかのアラートが表示されます。[OK] ボタンをタップします。

3. ログイン後、Posts で任意の投稿を作成します。その投稿をタップして、アクションシートの [Share] をタップ

します。

4. すぐにホームボタンを押して、ホーム画面に戻ると、プッシュ通知が下記のように表示されます。

Page 95: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

93

演習の後に

演習で作成した Windows Azure モバイル サービスを削除し、Windows Azure 管理ポータルサイトからサインアウトし

てください。

1. Windows Azure 管理ポータルサイトで、[モバイル サービス] を表示します。作成したサービスを選択します。

2. 下部メニューの [削除] ボタンをクリックします。

3. [削除の確認] でデータをどうするか決めます。今回はデータをすべて削除するので、二つともチェックボックス

に [✔] を入れて、右下の [✔] をクリックします。

Page 96: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

94

4. モバイル サービス一覧に画面は遷移し、削除中となり完了すると一覧から消え、進捗状況に「次のモバイル サ

ービスが削除されました:’サービス名’。」が表示されます。

以上で本ハンズオン ラボの演習は終了です。お疲れさまでした。

Page 97: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。

95

まとめ

このハンズオン ラボでは、Windows Azure 管理ポータルサイトを使用して、Windows Azure モバイル サービスのセッ

トアップを行い、Windows Azure モバイル サービスに接続する、シンプルなミニブログのような iOS アプリを作成しま

した。モバイル サービスの主要な機能と iOS アプリ用のフレームワークを含めたその具体的な使い方に対して、理解を

深めました。

リソース

Windows Azure 情報サイト

http://www.windowsazure.com/

Windows Azure 管理ポータルサイト

https://manage.windowsazure.com/

Windows Azure 開発ツール ダウンロード

http://www.windowsazure.com/ja-jp/develop/downloads/

Windows Azure 管理ツール ダウンロード

http://www.windowsazure.com/ja-jp/manage/downloads/

Windows Azure Mobile Services server script reference

http://msdn.microsoft.com/en-us/library/windowsazure/jj554226.aspx

WindowsAzure/azure-mobile-services - GitHub

https://github.com/WindowsAzure/azure-mobile-services/

Page 98: Windows Azure クラウド サービス入門編download.microsoft.com/download/5/2/B/52BAC8F7-A179-4EC0-A02… · このハンズオン ラボは以下の演習で構成されています。