Multiple_simultaneous_push_notifications
こんにちは。株式会社EveryDaySoft代表の永田です。
今回は題名のPush通知について手順を公開します。 実施する内容は、アプリをインストールすると、AWS上でPush通知に必要なサブスクリプションを作成し、同時に複数端末にPush通知が送信されます。
アプリのインストールした際にデバイストークンを作成するコード 2. Print device token to use for PNs payloadsコメント部分でtokenを生成しています。tokenを生成するメソッドは、Apple標準のfunctionです。
import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate { func registerForPushNotifications() { UNUserNotificationCenter.current().delegate = self UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (granted, error) in print("Permission granted: \(granted)") // 1. Check if permission granted guard granted else { return } // 2. Attempt registration for remote notifications on the main thread DispatchQueue.main.async { UIApplication.shared.registerForRemoteNotifications() } } } func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. registerForPushNotifications() return true } // MARK: UISceneSession Lifecycle func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { // Called when a new scene session is being created. // Use this method to select a configuration to create the new scene with. return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { // Called when the user discards a scene session. // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. // Use this method to release any resources that were specific to the discarded scenes, as they will not return. } func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { // 1. Convert device token to string let tokenParts = deviceToken.map { data -> String in return String(format: "%02.2hhx", data) } let token = tokenParts.joined() // 2. Print device token to use for PNs payloads print(token) print("Device Token: \(token)") } func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { // 1. Print out error if PNs registration not successful print("Failed to register for remote notifications with error: \(error)") } }
APIのコード push通知 AWS Lambda APIGateWay
import boto3 import boto3 import json import datetime def lambda_handler(event, context): PLATFORM = 'default' TargetArn = 'TargetArn' dict = {'aps':{"alert":"Hallo World","badge":1,"sound":"default"}} apns_string = json.dumps(dict,ensure_ascii=False) message = {'default':'default message','APNS_SANDBOX':apns_string} messageJSON = json.dumps(message,ensure_ascii=False) client = boto3.client('sns') request = { 'TargetArn': TargetArn, 'Message': messageJSON, 'MessageStructure': 'json' } response = client.publish(**request)
APIのコード サブスクリプション作成 AWS Lambda APIGateWay
import boto3 import json import datetime def lambda_handler(event, context): client = boto3.client('sns') BUCKET_NAME = 'S3のBUCKET_NAME' s3 = boto3.client('s3') PRIVATE = 'PRIVATE.txt' res = s3.get_object(Bucket=BUCKET_NAME, Key=PRIVATE) body = res['Body'].read() # b'テキストの中身' bodystr = body.decode('utf-8') CERTIFICATE = 'CERTIFICATE.txt' res2 = s3.get_object(Bucket=BUCKET_NAME, Key=CERTIFICATE) body2 = res2['Body'].read() # b'テキストの中身' bodystr2 = body2.decode('utf-8') response = client.create_platform_application( Name='goto', Platform='APNS_SANDBOX', Attributes={ 'PlatformCredential': bodystr, 'PlatformPrincipal' : bodystr2 } ) response2 = client.create_platform_endpoint( PlatformApplicationArn=response['PlatformApplicationArn'], Token=event['push'], CustomUserData='abc' ) response3 = client.subscribe( TopicArn='AmazonSNSのTopicArn', Protocol='application', Endpoint=response2['EndpointArn'], ReturnSubscriptionArn=True|False ) return response3
S3に証明書をアップロード方法 Macのユーティリティ->キーチェーン->証明書を書き出し->ターミナルコマンド,ファイル作成->S3にアップロード
AppConnectでdevelop契約が前提です。AppConnect内で、iOSはAPNSの設定をします。 qiitaの記事を参照させていただきます。 https://qiita.com/b_a_a_d_o/items/e3bf9cd52b6cd9252088
ターミナルコマンド(macでP12ファイルと同じ階層で実施) openssl pkcs12 -in P12ファイル名 -nocerts -nodes -out 秘密鍵ファイル名 openssl pkcs12 -in P12ファイル名 -clcerts -nokeys -out 証明書ファイル名
S3にアップロードは簡単なので、割愛します。
この作業手順の他にもDeleteするAPIも作成する必要があります。アプリがアンインストールされた場合にDeleteAPIを使用する形になります。アプリをアンインストールされた場合の処理はcronなどで定期実行し、レスポンス結果により判定する設計にすると自動で対応できます。
APIのコード DeleteAPI作成 AWS Lambda APIGateWay
import boto3 import json import datetime def lambda_handler(event, context): client = boto3.client('sns') BUCKET_NAME = 's3のBUCKET_NAME' s3 = boto3.client('s3') PRIVATE = 'PRIVATE.txt' res = s3.get_object(Bucket=BUCKET_NAME, Key=PRIVATE) body = res['Body'].read() # b'テキストの中身' bodystr = body.decode('utf-8') # print(bodystr) CERTIFICATE = 'CERTIFICATE.txt' res2 = s3.get_object(Bucket=BUCKET_NAME, Key=CERTIFICATE) body2 = res2['Body'].read() # b'テキストの中身' bodystr2 = body2.decode('utf-8') response = client.create_platform_application( Name='goto', Platform='APNS_SANDBOX', Attributes={ 'PlatformCredential': bodystr, 'PlatformPrincipal' : bodystr2 } ) response2 = client.create_platform_endpoint( PlatformApplicationArn=response['PlatformApplicationArn'], Token=event['deleate'], CustomUserData='abc' ) response3 = client.subscribe( TopicArn='Amazon snsのTopicArn', Protocol='application', Endpoint=response2['EndpointArn'], ReturnSubscriptionArn=True|False ) response4 = client.unsubscribe( SubscriptionArn=response3['SubscriptionArn'] ) return response4
DataBaseの基本CRUDを意識して、アーキテクチャを作る事により、管理データが常に正しく運用できると思います。API側とiOS側に精通する事により、アプリ側ですべき内容、API側ですべき内容が分かります。少し先の未来ではMacアプリ、Android、Webも対応していきたいと考えています。 以上、貴重なお時間お読み下さいまして、誠にありがとうございます。