AWS（SNS, Cognito）を用いてiOSアプリにプッシュ通知するまでの手順（アプリ編）


以前投稿した、[AWS（SNS, Cognito）を用いてiOSアプリにプッシュ通知するまでの手順（サーバー編）](http://qiita.com/MJeeeey/items/c11cbebd542f06e68719) のアプリ側の実装部分をこちらにまとめました。

# AWS SDK の導入

Podfile を作成して、以下の内容を記述します。

~~~:Podfile
source 'https://github.com/CocoaPods/Specs.git'

pod 'AWSSNS'
pod 'AWSCognito'
~~~

ターミナルで以下の pod コマンドを実行します。

~~~
pod update
pod install
~~~

これで必要なライブラリがインストールされます。
今まで `SampleApp.xcodeproj` からプロジェクトを起動していたとすると、これ以降は新たに生成された `SampleApp.xcworkspace` から起動してください。

# プッシュ通知に関する実装

以降は AppDelegate.m への実装のみで完結させています。

参考にしたサンプルコードを紹介しておきます。
[https://github.com/awslabs/aws-sdk-ios-samples/tree/master/SNS-MobileAnalytics-Sample](https://github.com/awslabs/aws-sdk-ios-samples/tree/master/SNS-MobileAnalytics-Sample)

サンプルでは SNS のみの使用で、Cognito は扱っていませんが、AWS の Cognito の設定画面でサンプルコードを載せてくれているので、そちらをコピペすればOKです。（後述の initAWSCognito 関数にあたります。）


## 宣言と定義

以下は Prefix Header で宣言してもらっても構いません。

~~~objectivec:AppDelegate.m
#import <AWSSNS/AWSSNS.h>
#import <AWSCognito/AWSCognito.h>
~~~

以下は NSString で定義してもらっても構いません。

~~~objectivec:AppDelegate.m
// Identity Pool Id
#define Cognito_Identity_Pool_Id @"ap-northeast-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"

// Application ARN (Development, Product)
#define SNS_Platform_Application_Arn @"arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:app/APNS_SANDBOX/[Application Name]"
//#define SNS_Platform_Application_Arn @"arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:app/APNS/[Application Name]"

// Topic ARN
#define SNS_Topic_Arn @"arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:[Topic name]"
~~~

リージョンは「東京」で設定します。

~~~objectivec:AppDelegate.m
AWSRegionType const Cognito_Region_Type = AWSRegionAPNortheast1;
AWSRegionType const Default_Service_Region_Type = AWSRegionAPNortheast1;
~~~

## アプリ起動時の処理

各ユーザーが所有する端末のデバイストークンを AWS 側に登録するための処理を行います。

流れは以下のようになります。

1. プッシュ通知の呼び出し設定
1. （デバイストークンの取得後）AWS への登録処理
 1. Cognito の認証処理
 1. SNS への登録処理

処理に成功すると、AWS の Cognito と SNS に登録処理が行われます。

### プッシュ通知の呼び出し設定

アプリ起動時に呼び出される application:didFinishLaunchingWithOptions メソッド内に記述します。

~~~objectivec:AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{

	/* 中略 */

	[self registRemoteNotification:YES];

	return YES;
}

// プッシュ通知の有効／無効設定
- (void)registRemoteNotification:(BOOL)isRegist
{
	UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
	UIUserNotificationSettings *notificationSettings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
	if (isRegist) {
		[[UIApplication sharedApplication] registerUserNotificationSettings:notificationSettings];
		[[UIApplication sharedApplication] registerForRemoteNotifications];
	} else {
		[[UIApplication sharedApplication] unregisterForRemoteNotifications];
	}
}
~~~

### Cognito の認証処理／SNS への登録処理

registerForRemoteNotifications メソッドの呼び出し後、以下のどちらかのメソッドが呼び出されます。
特に問題なければ、Cognito, SNS への登録処理を行います。

~~~objectivec:AppDelegate.m
// APNsにデバイスを登録できたときに呼ばれるメソッド
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
	// デバイストークンの取得
	NSString *deviceTokenString = [[[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]] stringByReplacingOccurrencesOfString:@" " withString:@""];

	[self initAWSCognito];

	[self initAWSSns:deviceTokenString];
}

// Amazon Cognito credentials provider の初期化
- (void)initAWSCognito
{
	AWSCognitoCredentialsProvider *credentialsProvider = [[AWSCognitoCredentialsProvider alloc] initWithRegionType:Cognito_Region_Type
																									identityPoolId:Cognito_Identity_Pool_Id];
	
	AWSServiceConfiguration *configuration = [[AWSServiceConfiguration alloc] initWithRegion:Default_Service_Region_Type credentialsProvider:credentialsProvider];
	
	[AWSServiceManager defaultServiceManager].defaultServiceConfiguration = configuration;
}

- (void)initAWSSns:(NSString *)deviceTokenString
{
	AWSSNS *sns = [AWSSNS defaultSNS];
	// エンドポイントの登録
	AWSSNSCreatePlatformEndpointInput *endpointInput = [AWSSNSCreatePlatformEndpointInput new];
	endpointInput.token = deviceTokenString;
	endpointInput.platformApplicationArn = SNS_Platform_Application_Arn;
	endpointInput.customUserData = @"任意の文字列";
	[[sns createPlatformEndpoint:endpointInput] continueWithBlock:^id(AWSTask *task) {
		if (task.error != nil) {
			NSLog(@"task.error: %@", task.error);
		} else {
			AWSSNSCreateEndpointResponse *endPointResponse = task.result;
			// トピックへの登録
			AWSSNSSubscribeInput *subscribeInput = [AWSSNSSubscribeInput new];
			subscribeInput.topicArn = SNS_Topic_Arn;
			subscribeInput.protocols = @"application";
			subscribeInput.endpoint = endPointResponse.endpointArn;
			[[sns subscribe:subscribeInput] continueWithBlock:^id(AWSTask *task2) {
				NSLog(@"task2.error: %@", task2.error);
			}];
		}
	}];
}

// APNsにデバイスを登録できなかったときに呼ばれるメソッド
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
	NSLog(@"Failed to register with error: %@", error);
}
~~~

以上がプッシュ通知の事前処理です。

## プッシュ通知の受信処理

AWS の SNS にてプッシュ通知の送信処理を行うと、各端末にプッシュ通知が届きます。端末側でアクションを行ったときにアプリ側で呼び出されるメソッドが以下になります。

~~~objectivec:AppDelegate.m
// プッシュ通知を受け取ったときに呼ばれるメソッド
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
	if (application.applicationState == UIApplicationStateActive) {
		NSLog(@"Foreground");
	} else if (application.applicationState == UIApplicationStateInactive) {
		NSLog(@"Background");
	}

	NSLog(@"userInfo: %@", userInfo);

	/* 実行したい何かを実装 */

}
~~~

以上です。iOSはシンプルですね。