Memo

メモ > 技術 > サービス: AmazonSNS > アプリ起動時にデバイストークンをサーバ側に記録

アプリ起動時にデバイストークンをサーバ側に記録
「アプリからHTTPリクエストする例」の内容も踏まえて、アプリ起動時にデバイストークンをサーバ側に記録する例。 Androidについては、ChatGPTが「OkHttp」ではなく「Volley」を使ったコードを提示してきたので、いったんそのまま採用している。 Androidの通信ライブラリの歴史を振り返る #Java - Qiita https://qiita.com/Reyurnible/items/33049c293c70bd9924ee ※実際の実装では、さらに「デバイストークン」「サーバ側で発行した一意なID」をデバイス内に保存しておくなどの処理もあると良さそう ■サーバサイド(save_device_token.php)
<?php header('Content-Type: application/json; charset=utf-8'); if (file_put_contents('log/' . date('YmdHis') . '.log', print_r($_POST, true)) === false) { echo json_encode(array( 'status' => 'NG', )); } else { echo json_encode(array( 'status' => 'OK', 'datetime' => date('Y/m/d H:i:s'), )); } exit;
■アプリ(Android)
package net.refirio.pushtest1 import android.Manifest import android.content.Context import android.content.pm.PackageManager import android.os.Bundle import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.ManagedActivityResultLauncher import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.Button import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.tooling.preview.Preview import androidx.core.content.ContextCompat import com.android.volley.Request import com.android.volley.toolbox.StringRequest import com.android.volley.toolbox.Volley import com.google.firebase.messaging.FirebaseMessaging import net.refirio.pushtest1.ui.theme.PushTest1Theme class MainActivity : ComponentActivity() { private var tokenState: MutableState<String?>? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { PushTest1Theme { val localTokenState = remember { mutableStateOf<String?>(null) } tokenState = localTokenState Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding -> MainScreen(modifier = Modifier.padding(innerPadding), localTokenState) } } } // Firebaseのトークンを取得 FirebaseMessaging.getInstance().token.addOnCompleteListener { task -> if (!task.isSuccessful) { Log.w("MainActivity", "Fetching FCM registration token failed", task.exception) return@addOnCompleteListener } // トークンを取得 val token = task.result Log.d("MainActivity", "FCM Token: $token") tokenState?.value = token // サーバにトークンを送信 sendDeviceToken(token) } } private fun sendDeviceToken(token: String) { val url = "https://example.com/app/save_device_token.php" // URLエンコード形式のデータを作成 val postBody = "device_token=${java.net.URLEncoder.encode(token, "UTF-8")}" // リクエストを送信 val request = object : StringRequest( Request.Method.POST, url, { response -> // 成功時の処理 Log.d("MainActivity", "Token saved successfully: $response") }, { error -> // エラー時の処理 Log.e("MainActivity", "Failed to save token: $error") } ) { override fun getHeaders(): Map<String, String> { val headers = HashMap<String, String>() headers["Content-Type"] = "application/x-www-form-urlencoded" // URLエンコード形式 return headers } override fun getBody(): ByteArray { return postBody.toByteArray(Charsets.UTF_8) // ボディにエンコード済みのデータを設定 } } // リクエストキューに追加 Volley.newRequestQueue(this).add(request) } } fun checkAndRequestPermissions( context: Context, permissions: Array<String>, launcher: ManagedActivityResultLauncher<Array<String>, Map<String, Boolean>> ) { if ( permissions.all { ContextCompat.checkSelfPermission( context, it ) == PackageManager.PERMISSION_GRANTED } ) { // パーミッションが与えられている Log.d("MainActivity", "Already granted") } else { // パーミッションを要求 launcher.launch(permissions) } } @Composable fun MainScreen(modifier: Modifier = Modifier, tokenState: MutableState<String?>) { val context = LocalContext.current val permissions = arrayOf( Manifest.permission.POST_NOTIFICATIONS ) val launcherMultiplePermissions = rememberLauncherForActivityResult( ActivityResultContracts.RequestMultiplePermissions() ) { permissionsMap -> val areGranted = permissionsMap.values.reduce { acc, next -> acc && next } if (areGranted) { // パーミッションが与えられている Log.d("MainActivity", "Already granted") } else { // パーミッションを要求 Log.d("MainActivity", "Show dialog") } } Column(modifier = modifier) { Text("FCM Token: ${tokenState.value ?: "Loading..."}") Button( onClick = { checkAndRequestPermissions( context, permissions, launcherMultiplePermissions ) } ) { Text("通知を許可(Android13用)") } } } @Preview(showBackground = true) @Composable fun MainScreenPreview() { var tokenState: MutableState<String?>? = null val localTokenState = remember { mutableStateOf<String?>(null) } tokenState = localTokenState MainScreen(modifier = Modifier, localTokenState) }
■アプリ(iOS)
import SwiftUI struct ContentView: View { let publisher = NotificationCenter.default.publisher(for: .deviceToken) @State var deviceToken: String = "" var body: some View { VStack { Text("DeviceToken") TextField("Device Token is Empty", text: $deviceToken) .padding() } .onReceive(publisher) { message in if let deviceToken = message.object as? String { sendDeviceToken(deviceToken) } } } private func sendDeviceToken(_ token: String) { guard let url = URL(string: "https://example.com/app/save_device_token.php") else { return } var request = URLRequest(url: url) request.httpMethod = "POST" request.httpBody = "device_token=\(token)".addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)?.data(using: .utf8) let task = URLSession.shared.dataTask(with: request) { data, response, error in if let error = error { print("Error: \(error)") return } guard let data = data else { return } do { let object = try JSONSerialization.jsonObject(with: data, options: []) print(object) DispatchQueue.main.async { self.deviceToken = token } } catch { print("JSON parsing error: \(error)") } } task.resume() } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }

Advertisement