こんにちは。株式会社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も対応していきたいと考えています。 以上、貴重なお時間お読み下さいまして、誠にありがとうございます。