PUSH API · v1a
iOS SDK
確認日:2026-09-11。外部コンソールの項目名は変更される場合があります。公式ガイドのリンクと実際のSDKバージョンも確認してください。
始める前に:3つの接続を区別する
SDKの導入、プッシュサービスの設定、端末登録は別の手順です。ライブラリのビルド成功だけでは通知の配信を確認できません。ご自身の端末1台で順番に確認してください。
- 組織・プロジェクト・アプリ環境を選びます。AndroidとiOSでは、それぞれのアプリとプッシュサービス設定を使います。
- 設定 → プッシュサービス接続でFCMまたはAPNsを設定し、検証状態を確認します。
/app-environments/{appEnvironmentId}/providers - 設定 → SDK接続から同じ環境の公開キーを用意します。既存アプリはstorageNamespaceを維持します。顧客向けSDKの接続先は固定で、モバイルアプリにサーバーAPIキーは埋め込まないでください。
/app-environments/{appEnvironmentId}/settings/sdk
0. Apple APNsとアプリ署名を準備する
- Apple Developer AccountでアプリのBundle IDとチームを確認し、Certificates, Identifiers & Profiles → Keysから承認済みのAPNs .p8キーを用意します。
- 新しいキーが必要で作成権限がある場合は、Keys → 新規キー追加 → Apple Push Notifications service (APNs)を選択 → Configureへ進みます。EnvironmentはSandbox、Production、Sandbox & Productionから、アプリの署名済みaps-environmentを許可する値を選びます。Key RestrictionはTeam Scoped (All Topics)とTopic Specificの必要範囲を検討し、アプリのBundle ID/topicが許可されていることを確認してください。
- Kitsのプッシュサービス接続で.p8ファイル・Key ID・Team ID・アプリのBundle ID/topic・APNs環境を確認します。Key IDとTeam IDは別の値です。ファイル名から読み取ったKey IDもAppleアカウントと照合してください。
- XcodeのアプリtargetでSigning & Capabilities → + Capability → Push Notificationsを追加し、正しいTeamを選びます。通常の表示通知のためだけにBackground Modesを無条件で追加する必要はありません。バックグラウンド処理には別の要件と制限があります。
- 署名後のaps-environment、SDKのKitsAPNsEnvironment(.sandboxまたは.production)、プッシュサービス接続のAPNs環境を一致させます。Debug/ReleaseやKitsのアプリ環境の種類だけで判断しないでください。
共通のプッシュサービス設定画面を見る · 画面例はAndroid FCMです。iOSではJSONファイルではなく、APNs .p8・Key ID・Team IDを使用します。
新しい管理型SDKの簡単連携
import KitsPush
// Once on MainActor, after wiring the AppDelegate bridge:
try Kits.initialize(publicKey: publicKey)
// Development-signed app instead:
// try Kits.initialize(publicKey: publicKey, options: KitsOptions(ios: KitsIOSOptions(apnsEnvironment: .sandbox)))SDKが本番URLと既定ストレージを提供し、認証設定をダッシュボードから読みます。更新版SDKはユーザー・イベント・活動の収集を既定で有効にし、保存済みの収集拒否を維持します。同意前はcollectionEnabled=falseで収集を保留できます。既存アプリは更新や鍵交換で以前のnamespaceを維持してください。APNsの既定はproductionで、開発署名ではsandboxを明示します。
APNsトークンの転送にはKitsPushAppDelegateの継承またはSDK READMEのAppDelegate転送ブリッジが必要です。SwiftUIはUIApplicationDelegateAdaptorを使い、Sceneを使う場合はクリックも転送します。初期化だけでアプリdelegateは置き換わりません。
| 共通の任意設定 | 意味・既定値 |
|---|---|
collectionEnabled | 収集の統合設定・true。falseで開始すると収集を保留。既定のtrueで保存済みの拒否は解除しません |
foregroundPresentationPolicy | フォアグラウンド表示方針・PRESENT / .present |
shouldDisplay | 任意の表示コールバック・省略時は許可、エラー時は非表示 |
identityAuthProvider | VERIFIEDのアカウント証明プロバイダー・省略 |
audienceCollectionEnabled | 基本ユーザー情報・セッション収集・true。明示的なfalseは維持 |
notificationEventsEnabled | 実際の受信・タップ報告・true。メッセージのtrack_eventsも必要 |
observationOptions | persistNotificationEvents・true / activityDistributionEnabled・true |
同意前に収集を保留するには、初期化オプションでcollectionEnabled=falseを指定します。同意後はKits.setCollectionEnabled(true)で開始し、falseで停止します。通知の受信拒否やサーバーに保存済みの記録の削除とは別です。個別機能で明示したfalseは統合設定で有効にしても維持します。iOS拡張の受信報告は別途設定します。
Android固有設定はandroid: KitsAndroidOptions(FCMモード・通知リソース)、iOS固有設定はios: KitsIOSOptions(APNs環境・delegate連携)にまとめます。shouldDisplayはフォアグラウンド表示と新Android SDKによる表示を制御しますが、iOSや旧Android SDKでOSが表示するバックグラウンド通知は遮断できません。初期化だけでは権限を要求しません。
以下のcore専用設定・OSコールバック例は手動の低レベル連携を維持する場合のみ使います。上記の管理型初期化は、この連携をSDKが担当します。
対応条件はiOS 15以上、Swift 6、Xcode 16.4以上です。macOS上でテストできることは、macOSアプリ向けの製品対応を意味しません。
1. Swift Packageを追加する
運営者から検証済みのkits-iosソースまたは承認済みのパッケージの場所を入手してください。この文書では、未確認の公開リポジトリやリリースURLは案内していません。XcodeのAdd Package Dependencies → Add Localで/path/to/kits-iosを選び、アプリのターゲットにKitsPushを追加します。
2. 設定とAPNs環境を確認する
import KitsPush
import UIKit
// Legacy core integration only: keep this app's existing namespace unchanged.
// New integrations use Kits.initialize(publicKey:); the SDK manages storage.
let configuration = try KitsPushConfiguration(
publicKey: configuredPublicKey,
storageNamespace: configuredStorageNamespace,
apnsEnvironment: configuredAPNsEnvironment
)
let kitsPush = try await KitsPushClient.configure(configuration)
await MainActor.run {
UIApplication.shared.registerForRemoteNotifications()
}configuredPublicKeyとconfiguredStorageNamespaceには、対象のアプリ環境の設定値を使います。configuredAPNsEnvironmentは、署名のaps-environmentに合わせてKitsAPNsEnvironment.sandboxまたは.productionにします。アプリにはPush Notifications capabilityと、対応する署名設定が必要です。
3. 今回の起動で取得したAPNsトークンを渡す
起動するたびにregisterForRemoteNotificationsを呼び出し、その起動のdelegateコールバックで得たトークンを渡してください。保存済みの古いトークンでは代用しません。以下は既存のdelegateに追加する部分例です。クライアントのライフサイクルはアプリ側で管理します。
// In your existing UIApplicationDelegate; kitsPush is the configured client.
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
Task { _ = await kitsPush.registerAPNsToken(deviceToken) }
}
// Observe acknowledgement separately from the local .queued result.
for await state in kitsPush.syncStates() {
if case .synced = state {
// Latest desired device state acknowledged by Kits.
}
}権限の例:実際の選択を伝える
import KitsPush
import UserNotifications
// In a host-owned async action after explaining why notifications are useful.
// kitsPush is the same retained client configured at app startup.
let center = UNUserNotificationCenter.current()
let before = await center.notificationSettings()
if before.authorizationStatus == .notDetermined {
do { _ = try await center.requestAuthorization(options: [.alert, .badge, .sound]) }
catch { /* Show a safe error; do not pretend permission was granted. */ }
}
let settings = await center.notificationSettings()
let permission: KitsPushPermission
switch settings.authorizationStatus {
case .notDetermined: permission = .notDetermined
case .denied: permission = .denied
case .authorized: permission = .authorized
case .provisional: permission = .provisional
case .ephemeral: permission = .ephemeral
@unknown default: permission = .notDetermined
}
_ = await kitsPush.reportPermission(permission)
// Independently forward the user's real in-app choice:
// _ = await kitsPush.setPushPreference(.optedIn) or .optedOut
// Re-read settings after returning from iOS Settings, without prompting again.画像通知:Xcodeの拡張targetを追加する
SDKは画像の取得・検証ヘルパーを提供しますが、署名済みのアプリ拡張を代わりに作成できません。画像通知にはホストアプリに含めたNotification Service Extensionが必要です。テキスト通知のみなら省略できます。
- XcodeのFile → New → Target → Notification Service Extensionで追加し、適切なTeamで署名してアプリへの組み込みを確認します。
- 拡張targetにKitsPushNotificationService製品を追加します。アプリ本体のKitsPushとは別です。生成されたNotificationServiceクラスは次のように置き換えられます。
import KitsPushNotificationService
// Image-only default; no APNs private key or device bearer belongs here.
final class NotificationService: KitsNotificationServiceExtension {}NSExtensionPrincipalClassがこのクラスを指し、APPLICATION_EXTENSION_API_ONLY=YESであることを確認します。リダイレクトしない公開HTTPS PNG/JPEG URLを使い、失敗時のテキスト通知へのフォールバックも確認します。サイズ制限と手動転送例は下の任意機能の節にあります。
5. ダッシュボードと端末で最後まで確認する
- SDKの同期後、同じアプリ環境のオーディエンス → プッシュ購読でインストールを探します。ID・プラットフォーム・権限・受信設定を確認し、トークンの原文は共有しません。
- ご自身のインストールだけをテスト端末に指定し、直接送信で選びます。テストとして記録しても実際に通知され、権限・同意・プッシュサービス検証は省略されません。まず画像なしのSERVICE通知1件で確認します。
- 配信管理 → 送信済みメッセージでリクエストと個別配信結果を確認します。ダッシュボード/APIの送信元と通常/テストの目的は別項目です。受付やプッシュサービスの受理は端末表示・タップの成功ではありません。
- フォアグラウンド・バックグラウンド・通知タップによるコールドスタート・拒否後の再許可を別々に確認します。画像・タグ・多言語・受信観測は基本配信を確認してから追加します。
失敗している手順から確認する
| 症状 | 最初の確認 |
|---|---|
| インストールが表示されない | アプリ環境・公開キー・APIオリジン・ネットワーク・SDKエラーを確認します。configure/Queuedはサーバーの成功ではありません。最初の対処としてDBやアプリデータを削除しないでください。 |
| APNs token / BadDeviceToken | 今回の起動時のAPNs delegateトークン・署名entitlement・Bundle ID/topic・APNs環境の一致を確認します。古いトークンや自作トークンで代用しないでください。 |
| 受け付けたのに通知が来ない | 個別配信結果 → プッシュサービスのエラー → OS権限と受信設定 → チャネル/集中モード → フォアグラウンド表示の順に確認します。最初の結果を見る前に繰り返し送信しないでください。 |
| 通知が重複する | OneSignalとKitsの両方が送信していないか、OS表示の通知をアプリが再表示していないか確認します。各サーバーイベントの担当を既存プラットフォームとKitsのどちらかに明示してください。 |
4. 通知delegateをアプリで管理する
- UNUserNotificationCenterDelegateはアプリ側で管理します。フォアグラウンド通知ではhandleWillPresent、通知への応答ではhandleNotificationResponseを呼び出します。
- 戻り値がnilならKitsの通知ではないため、既存のハンドラーに渡します。OSのcompletion handlerは、どの経路でも必ず1回だけ呼び出してください。画面遷移を待つ間、呼び出しを保留しないでください。
- 実際のOS権限をreportPermissionに、アプリ内の受信設定をsetPushPreferenceに渡します。SDKが代わりに同意を得たり、リンクを開いたりすることはありません。
- queuedはローカルへの保存完了を示します。サーバーへの反映はsyncStatesのsyncedで確認し、必要に応じてsynchronizeで同期を要求してください。
任意:実際の受信・タップの観測
収集の既定値を更新したSDKソースではnotificationEventsEnabledとpersistNotificationEventsが既定でtrueです。公開済みの旧版とは異なる場合があるため、導入するリリースを確認してください。メッセージのtrack_events=trueは別途必要で、省略したAPIフィールドや保存済み下書きのfalseは自動変更しません。保存済みの収集拒否とcollectionEnabled=falseが優先されます。アプリの同意・プライバシー表示も確認してください。
// Optional argument in KitsPushConfiguration, after host consent:
notificationEventsEnabled: true
// Forward actual OS callbacks:
kitsPush.handleWillPresent(notification)
kitsPush.handleNotificationResponse(response)メッセージに{"track_events": true}を追加します。報告にはサーバー発行のkits_delivery_idと正確なバージョンマーカーが必要です。この予約IDを自作しないでください。CLICKEDからRECEIVEDは作られず、プッシュサービスの受理や表示を受信として推定しません。
独自の連携ではawait reportNotificationReceived(payload)またはreportNotificationClicked(payload)を使えます。willPresentはRECEIVED、閉じる操作以外の実際のresponseはCLICKEDを観測します。バックグラウンド受信には、画像なしの通常通知でも受信報告を設定したNotification Service Extensionが必要です。OSの完了処理を独自に遅らせず、SDKの時間制限付き処理を使ってください。
認証拒否(401)などの恒久的な失敗を無制限に再試行しません。記録の報告は通知の配信や分析の完了状態を変更しません。
メッセージ詳細のSDK記録は自動で読み込み、表示中は最大5分間、5秒ごとに更新します。グラフはサーバーに記録が届いた時刻を使うため、端末の実際のクリック時刻とは異なる場合があります。更新したり後から開いたりしても保存済みの時間別記録を確認できます。
画像なし通知のバックグラウンド受信
- 署名済みのNotification Service Extensionターゲットをアプリに含め、KitsPushNotificationServiceをリンクします。SDKライブラリがターゲットを自動追加することはできません。
- ダッシュボードで記録収集とiOSバックグラウンド受信収集を両方有効にします。APIではtrack_events=trueとtrack_receipts=trueです。サーバーが通常のAPNs alertにmutable-contentと配信専用の報告トークンを付けます。
- KitsNotificationServiceExtensionのreceiptConfigurationで、拡張から読める現在の同意を確認します。許可された場合のみKitsNotificationReceiptConfiguration(reportingEnabled: true)、拒否・不明ならnilを返します。アプリSDKの収集拒否は別プロセスの拡張へ自動共有されないため、App Groupなどの共有設定が必要です。
任意:画像拡張機能の追加
- XcodeでアプリにNotification Service Extensionターゲットを追加します。そのターゲットにはKitsPushNotificationServiceのみを追加し、アプリ用のKitsPushは追加しません。APPLICATION_EXTENSION_API_ONLY=YESと正しい署名・プロビジョニングを維持してください。
- 以下のヘルパーを保持し、受信と期限切れの両メソッドを渡します。image_urlは通常のメッセージフィールドで送り、Kitsがmutable-contentを設定します。実機で画像と失敗時のテキストを確認してください。
import KitsPushNotificationService
import UserNotifications
final class NotificationService: UNNotificationServiceExtension {
private let images = KitsNotificationService()
override func didReceive(_ request: UNNotificationRequest,
withContentHandler handler: @escaping (UNNotificationContent) -> Void) {
images.didReceive(request, withContentHandler: handler)
}
override func serviceExtensionTimeWillExpire() {
images.serviceExtensionTimeWillExpire()
}
}任意:アプリで登録するiOSボタン
タイトルを翻訳し、category/actionをアプリ内で定義します。他のSDKと競合しないよう、起動時に全体の集合を1回登録してください。ios_categoryは配布済みのidentifierと一致させます。サーバーによる動的なボタン配列はありません。
let category = UNNotificationCategory(identifier: "ORDER_V1", actions: [
UNNotificationAction(identifier: "VIEW_ORDER", title: "View order", options: .foreground)
], intentIdentifiers: [], options: [])
let actions = try KitsNotificationActions(categories: [category])
let all = try actions.categoriesForRegistration(preserving: otherHostCategories)
UNUserNotificationCenter.current().setNotificationCategories(all)
// In the real response delegate, after forwarding to kitsPush once:
if let event = actions.action(for: response) {
// Authorize current user and business data before handling event.actionIdentifier.
}最大32 category、各1–3個のplain actionで、IDの衝突はエラーです。未登録ボタン・別のpayload・通常のタップ・閉じる操作はnilになります。通常のタップは別に処理し、completion handlerを必ず1回呼び出してください。古いSDKや未設定アプリでのボタン表示は保証されません。
SDKのログインとログアウト
- 認証モードはプロジェクト環境ごとに1つで、その環境のすべてのアプリに適用されます。プロジェクト環境の最初のアプリはBASICで始まり、後から追加したアプリはその環境のモードに従います。新SDKの簡単初期化はダッシュボードの設定を自動取得し、アプリ側での再指定は不要です。BASICでは顧客認証callback・JWT発行API・署名鍵は不要です。BASICはexternal IDの所有権を検証しないため、同じ環境のクライアントが別のUIDを名乗り、その通知を受け取る可能性があります。この制約を受け入れる環境で使用してください。VERIFIEDの認証失敗からBASICへ自動切替しません。
- VERIFIEDでは信頼できる顧客バックエンドのJWTが必要です。アプリ起動時にSDKの認証callbackを設定します。顧客バックエンドはセッションと有効なアカウントを検証し、要求されたexternal_idがセッションのUIDと一致することを確認してからcreateIdentityTokenSignerでJWTをローカル署名します。Kitsへのトークン発行HTTPリクエストはなく、既定TTLは300秒です。アプリから受け取ったユーザーIDだけを信用しないでください。
- 顧客環境でgenerateIdentityKeyPairを使い、鍵ペアを一度生成します。Dashboardのプロジェクト設定→SDK接続で、プロジェクト環境にkeyIdとpublicKeyPemだけを登録し、privateKeyPemは顧客バックエンドの秘密情報ストアに保管します。登録した公開鍵は、そのプロジェクト環境のすべてのアプリで共通です。秘密鍵をKitsにアップロードしないでください。新しい公開鍵の登録、署名鍵の切替、古い鍵の失効の順にローテーションします。JWTの署名・検証失敗を旧方式の発行や未検証ログインへ自動迂回しないでください。
- 両モードとも自身のインストールのdeviceTokenでGET /v1/installations/{id}/identityを読み、POST /v1/installations/{id}/identityで紐付けを変更します。VERIFIED LOGINはtokenのみ、BASIC LOGINはexternal_idのみを送信します。LOGOUTではtokenとexternal_idの両方を省略します。SDKがexpected_binding_id、operation_id、永続保存、直列化、再試行を管理します。証明や端末トークンをログに残さないでください。
- SDKは操作のモードを保存し、旧journalはVERIFIEDとして読みます。別モードで再起動した場合は完了状態を先に確認し、異なるモードの未完了LOGINをIDENTITY_AUTH_MODE_CHANGEDで停止します。現在のセッションでloginを再実行すると、ログアウトbarrierを経て選択したモードの新しい操作を作成します。
- 新しいAUTOフローはGET /v1/installations/{id}/identity-policyでbindingと設定revisionを同時に読みます。新しいLOGINのexpected_policy_revisionが変わると409 IDENTITY_REBIND_CONFLICTで再確認します。取得失敗や不正な応答をBASICと推測しません。送信済み操作は先に結果を確認し、VERIFIEDの証明を勝手に省略しません。LOGOUTは設定取得なしで既存のidentity経路を使います。明示的モードと旧journalも互換性を維持します。
- JWTはES256、aud=kits:identity、端末が属するアプリ環境のiss、identity.external_id、整数秒のiat/expを使用します。TTLは1–3600秒で、端末・binding・operationには固定されません。盗まれた場合、有効期間中に別の認証済みインストールで再利用される可能性があり、内容は暗号化されません。HTTPSとCache-Control: no-storeを使い、秘密鍵・JWT・リクエスト本文をログに残さないでください。メッセージごとのJWT発行は不要です。
- 旧方式との互換性としてidentity.createTokenとPOST /v1/identity-tokensは既存のkitid1証明用に残りますが、新しい連携では非推奨です。この旧HTTP経路だけがidentity:writeサーバーキーとインストール・binding・operationの情報を必要とします。ローカルJWT署名にKits APIキーは不要で、messages.createは別のmessages:sendサーバーキーを使用します。
- ローカルキューへの保存は、サーバーでログアウトが完了したことを意味しません。identityStateで確認済みの結果を確認してください。このフローと既存のバックエンドidentify/logoutを同時に実行しないでください。
以下の鍵設定と署名例はVERIFIEDのみで必要です。
import { createIdentityTokenSigner } from '@marco-inc/kits-node';
// VERIFIED backend only: load the private key from your secret store.
const signer = createIdentityTokenSigner({
appEnvironmentId, keyId, privateKeyPem, ttlSeconds: 300,
});
// In an authenticated handler, after checking the active account:
if (request.external_id !== verifiedUid) throw new Error('Session changed');
const result = signer.sign(verifiedUid); // {token, expires_at}; no HTTP call
// Return only to the authenticated app with Cache-Control: no-store.以下はログインイベント、ログアウトイベント、状態確認それぞれの例です。まとめて順番に実行しないでください。
// Once, on MainActor (APNs production by default):
try Kits.initialize(publicKey: publicKey)
// On authenticated login:
Kits.login(userID)
// On logout (a separate event):
Kits.logout()
// Local submission state is not remote confirmation:
Kits.identitySubmissionState