Integrate Apple Push Notifications into your iOS app to deliver instant updates about messages, mentions, and community activities, keeping users engaged even when your app isn’t active.
Push notifications require proper APNs certificate setup and valid entitlements. Production certificates work with App Store and TestFlight builds only.
Prerequisites
Before implementing push notifications, ensure you have:
Apple Developer Account Active Apple Developer Program membership
Xcode Project iOS project with proper Bundle ID configured
social.plus SDK social.plus iOS SDK properly integrated
Push Capability Push Notifications capability enabled in Xcode
Step 1: Generate APNs Certificate
Create Certificate in Apple Developer Console
Navigate to Certificates
Select Certificate Type
Choose Apple Push Notification service SSL (Sandbox & Production)
This universal certificate works for both development and production
Universal Certificate : The Sandbox & Production certificate eliminates the need for separate development and production certificates, simplifying your workflow.
Configure Certificate
Select your app’s Bundle ID from the dropdown
Follow Apple’s certificate creation wizard
Download the generated .cer file
Install Certificate
Double-click the downloaded .cer file
Keychain Access will open automatically
The certificate will be installed in your Login keychain
Export as .p12
In Keychain Access, navigate to Login → My Certificates
Locate your Apple Push Services certificate
Right-click and select Export “Apple Push Services…”
Choose .p12 format and set a secure password
Save the file (you’ll need both the file and password for console upload)
Certificate Security : Store your .p12 file and password securely. When you create a new certificate, the previous one is automatically revoked.
Step 2: Upload to social.plus Console
Access Console
Upload Certificate
Click Upload Certificate in the iOS section
Select your .p12 file
Enter the password you set during export
Click Save to complete the setup
Certificate Validation : The console will validate your certificate upon upload. If there are issues, check that the Bundle ID matches your app configuration.
Step 3: iOS App Configuration
Enable Push Notifications Capability
In Xcode, enable the Push Notifications capability:
Select your app target
Go to Signing & Capabilities
Click + Capability
Add Push Notifications
Request Permission and Handle Registration
import UIKit
import UserNotifications
import AmitySDK
@main
class AppDelegate : UIResponder , UIApplicationDelegate {
func application ( _ application : UIApplication, didFinishLaunchingWithOptions launchOptions : [UIApplication.LaunchOptionsKey: Any ] ? ) -> Bool {
// Configure notification center
UNUserNotificationCenter. current (). delegate = self
// Request notification permissions
requestNotificationPermissions ()
return true
}
private func requestNotificationPermissions () {
UNUserNotificationCenter. current (). requestAuthorization (
options : [. alert , . badge , . sound , . provisional ]
) { granted, error in
if let error = error {
print ( "Push notification permission error: \( error. localizedDescription ) " )
return
}
if granted {
print ( "Push notifications authorized" )
DispatchQueue. main . async {
UIApplication. shared . registerForRemoteNotifications ()
}
} else {
print ( "Push notifications denied" )
}
}
}
}
Handle Device Token Registration
extension AppDelegate {
func application ( _ application : UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken : Data) {
// Convert token to string
let tokenString = deviceToken. map { String ( format : "%02.2hhx" , $0 ) }. joined ()
print ( "Device token: \( tokenString ) " )
// Register with social.plus SDK
registerDeviceWithAmitySDK ( token : tokenString)
}
func application ( _ application : UIApplication,
didFailToRegisterForRemoteNotificationsWithError error : Error ) {
print ( "Failed to register for remote notifications: \( error. localizedDescription ) " )
// Handle registration failure
handleRegistrationFailure ( error : error)
}
private func registerDeviceWithAmitySDK ( token : String ) {
AmityManager. shared . client ? . registerDevice (
withToken : token,
completion : { [ weak self ] success, error in
if success {
print ( "Successfully registered device with Amity SDK" )
self ? . onDeviceRegistrationSuccess ( token : token)
} else {
print ( "Failed to register device: \( error ? . localizedDescription ?? "Unknown error" ) " )
self ? . handleRegistrationFailure ( error : error)
}
}
)
}
private func onDeviceRegistrationSuccess ( token : String ) {
// Store token locally for reference
UserDefaults. standard . set (token, forKey : "device_token" )
// Update UI or perform additional setup
NotificationCenter. default . post (
name : . deviceTokenRegistered ,
object : nil ,
userInfo : [ "token" : token]
)
}
private func handleRegistrationFailure ( error : Error ? ) {
// Implement retry logic
DispatchQueue. main . asyncAfter ( deadline : . now () + 30 ) {
UIApplication. shared . registerForRemoteNotifications ()
}
}
}
// MARK: - Notification Names
extension Notification .Name {
static let deviceTokenRegistered = Notification. Name ( "deviceTokenRegistered" )
}
Best Practices
Strategic Permission Requests : Don’t ask for notification permissions immediately on app launch. Request them at meaningful moments.// Good: Request after user joins a channel
func didJoinChannel () {
NotificationPermissionManager. shared . requestPermissionIfNeeded { granted in
if granted {
print ( "User enabled notifications for channel updates" )
}
}
}
// Bad: Request immediately on app launch
func application ( _ application : UIApplication, didFinishLaunchingWithOptions...) -> Bool {
// Don't do this immediately
// UNUserNotificationCenter.current().requestAuthorization(...)
return true
}
Robust Token Management : Implement comprehensive error handling for token registration failures.class TokenRetryManager {
private var retryCount = 0
private let maxRetries = 3
private let retryDelay: TimeInterval = 30
func registerWithRetry ( token : String ) {
AmityManager. shared . client ? . registerDevice ( withToken : token) { [ weak self ] success, error in
if success {
self ? . retryCount = 0
print ( "Token registered successfully" )
} else if let self = self , self .retryCount < self .maxRetries {
self . retryCount += 1
DispatchQueue. main . asyncAfter ( deadline : . now () + self . retryDelay ) {
self . registerWithRetry ( token : token)
}
} else {
print ( "Failed to register token after \( self ? . maxRetries ?? 0 ) attempts" )
}
}
}
}
Notification State Tracking : Keep track of notification-related state across app lifecycle.class NotificationStateManager {
static let shared = NotificationStateManager ()
private ( set ) var isRegistered = false
private ( set ) var deviceToken: String ?
private ( set ) var authorizationStatus: UNAuthorizationStatus = . notDetermined
func updateRegistrationState ( isRegistered : Bool , token : String ? ) {
self . isRegistered = isRegistered
self . deviceToken = token
// Notify observers
NotificationCenter. default . post (
name : . notificationStateChanged ,
object : self
)
}
func updateAuthorizationStatus ( _ status : UNAuthorizationStatus) {
self . authorizationStatus = status
}
}
extension Notification .Name {
static let notificationStateChanged = Notification. Name ( "notificationStateChanged" )
}
Comprehensive Testing : Test push notifications across different scenarios and device states.# if DEBUG
class NotificationTestingHelper {
static func logNotificationStatus () {
UNUserNotificationCenter. current (). getNotificationSettings { settings in
print ( "Authorization Status: \( settings. authorizationStatus . rawValue ) " )
print ( "Alert Setting: \( settings. alertSetting . rawValue ) " )
print ( "Badge Setting: \( settings. badgeSetting . rawValue ) " )
print ( "Sound Setting: \( settings. soundSetting . rawValue ) " )
}
}
static func simulateNotification () {
let content = UNMutableNotificationContent ()
content. title = "Test Notification"
content. body = "This is a test notification"
content. sound = . default
let request = UNNotificationRequest (
identifier : "test" ,
content : content,
trigger : UNTimeIntervalNotificationTrigger ( timeInterval : 1 , repeats : false )
)
UNUserNotificationCenter. current (). add (request)
}
}
# endif
Testing & Debugging
Debug Notification Status
class NotificationDebugger {
static func printDebugInfo () {
print ( "=== Notification Debug Info ===" )
// Check authorization status
UNUserNotificationCenter. current (). getNotificationSettings { settings in
print ( "Authorization Status: \( settings. authorizationStatus ) " )
print ( "Alert Setting: \( settings. alertSetting ) " )
print ( "Badge Setting: \( settings. badgeSetting ) " )
print ( "Sound Setting: \( settings. soundSetting ) " )
}
// Check device token
if let token = UserDefaults.standard. string ( forKey : "device_token" ) {
print ( "Stored Device Token: \( token ) " )
} else {
print ( "No device token stored" )
}
// Check badge count
print ( "Current Badge Count: \( UIApplication. shared . applicationIconBadgeNumber ) " )
}
static func testLocalNotification () {
let content = UNMutableNotificationContent ()
content. title = "Test Notification"
content. body = "This is a test notification from your app"
content. sound = . default
content. badge = 1
let request = UNNotificationRequest (
identifier : UUID (). uuidString ,
content : content,
trigger : UNTimeIntervalNotificationTrigger ( timeInterval : 2 , repeats : false )
)
UNUserNotificationCenter. current (). add (request) { error in
if let error = error {
print ( "Failed to schedule test notification: \( error ) " )
} else {
print ( "Test notification scheduled" )
}
}
}
}
Troubleshooting
Notifications Not Received :
Certificate Issues : Verify .p12 certificate is valid and uploaded correctly
Build Configuration : Use TestFlight or App Store builds (not debug builds)
Permissions : Check notification permissions in iOS Settings
Token Registration : Ensure device token is successfully registered with social.plus SDK
Bundle ID Mismatch : Verify certificate Bundle ID matches your app
// Debug token registration
func debugTokenRegistration () {
print ( "App Bundle ID: \( Bundle. main . bundleIdentifier ?? "Unknown" ) " )
if let token = UserDefaults.standard. string ( forKey : "device_token" ) {
print ( "Current Token: \( token ) " )
}
// Re-register token
UIApplication. shared . registerForRemoteNotifications ()
}
Certificate Validation Issues :
Expired Certificate : Check certificate expiration date
Wrong Environment : Ensure using Sandbox & Production certificate
Password Incorrect : Verify .p12 password in console
Bundle ID Mismatch : Certificate must match exact Bundle ID
// Validate certificate setup
func validateCertificateSetup () {
print ( "Bundle ID: \( Bundle. main . bundleIdentifier ?? "Not found" ) " )
print ( "Push capability enabled: \( Bundle. main . object ( forInfoDictionaryKey : "UIBackgroundModes" ) != nil ) " )
}
Platform-Specific Problems :
Simulator Limitations : Push notifications don’t work in iOS Simulator
Debug Build Restrictions : Debug builds can’t receive production push notifications
Background App Refresh : Check if disabled in iOS Settings
Do Not Disturb Mode : May suppress notification display
// Check background refresh status
func checkBackgroundRefreshStatus () {
let status = UIApplication. shared . backgroundRefreshStatus
switch status {
case . available :
print ( "Background refresh available" )
case . denied :
print ( "Background refresh denied" )
case . restricted :
print ( "Background refresh restricted" )
@unknown default :
print ( "Unknown background refresh status" )
}
}
Production Requirements : Push notifications only work with production builds distributed through App Store or TestFlight. Debug builds from Xcode cannot receive push notifications.