0
# iOS Features
1
2
Apple Push Notification service (APNs) integration and iOS-specific messaging features. These capabilities provide deep integration with iOS notification system and platform-specific functionality.
3
4
## Capabilities
5
6
### APNs Token Management
7
8
Apple Push Notification service token management for direct APNs integration alongside FCM.
9
10
```typescript { .api }
11
/**
12
* Get the APNs token for the device (iOS only)
13
* @returns Promise resolving to APNs token string or null if unavailable
14
*/
15
function getAPNSToken(): Promise<string | null>;
16
17
/**
18
* Set the APNs token manually (iOS only)
19
* @param token - Hexadecimal string representing APNs token
20
* @param type - Token type: 'prod', 'sandbox', or 'unknown' (optional)
21
* @returns Promise that resolves when token is set
22
*/
23
function setAPNSToken(token: string, type?: 'prod' | 'sandbox' | 'unknown'): Promise<void>;
24
```
25
26
**Usage Examples:**
27
28
```typescript
29
import messaging from '@react-native-firebase/messaging';
30
import { Platform } from 'react-native';
31
32
if (Platform.OS === 'ios') {
33
// Get current APNs token
34
const apnsToken = await messaging().getAPNSToken();
35
36
if (apnsToken) {
37
console.log('APNs Token:', apnsToken);
38
// Send token to your server for direct APNs messaging
39
await sendApnsTokenToServer(apnsToken);
40
} else {
41
console.log('APNs token not available');
42
}
43
44
// Set APNs token manually (if method swizzling is disabled)
45
const deviceToken = await getDeviceTokenFromDelegate();
46
await messaging().setAPNSToken(deviceToken, 'prod');
47
}
48
49
// Modular API
50
import { getMessaging, getAPNSToken, setAPNSToken } from '@react-native-firebase/messaging';
51
const messagingInstance = getMessaging();
52
53
if (Platform.OS === 'ios') {
54
const apnsToken = await getAPNSToken(messagingInstance);
55
await setAPNSToken(messagingInstance, apnsToken, 'prod');
56
}
57
```
58
59
### Notification Settings Handler
60
61
Handle when users tap the notification settings link in iOS Settings app.
62
63
```typescript { .api }
64
/**
65
* Set handler for when notification settings link is opened (iOS 12+)
66
* @param handler - Function called when settings link is tapped
67
* @returns void
68
*/
69
function setOpenSettingsForNotificationsHandler(handler: (message: RemoteMessage) => any): void;
70
71
/**
72
* Check if app was opened from notification settings (iOS 12+)
73
* @returns Promise resolving to boolean indicating if opened from settings
74
*/
75
function getDidOpenSettingsForNotification(): Promise<boolean>;
76
```
77
78
**Usage Examples:**
79
80
```typescript
81
import messaging from '@react-native-firebase/messaging';
82
import { Platform } from 'react-native';
83
84
if (Platform.OS === 'ios') {
85
// Set handler for settings link (call outside React components)
86
messaging().setOpenSettingsForNotificationsHandler(remoteMessage => {
87
console.log('User opened notification settings:', remoteMessage);
88
89
// Navigate to in-app notification settings
90
navigateToNotificationSettings();
91
});
92
93
// Check if app was opened from settings
94
const openedFromSettings = await messaging().getDidOpenSettingsForNotification();
95
96
if (openedFromSettings) {
97
console.log('App opened from notification settings');
98
// Show notification settings screen
99
showNotificationSettingsModal();
100
}
101
}
102
103
// Modular API
104
import {
105
getMessaging,
106
setOpenSettingsForNotificationsHandler,
107
getDidOpenSettingsForNotification
108
} from '@react-native-firebase/messaging';
109
110
if (Platform.OS === 'ios') {
111
const messagingInstance = getMessaging();
112
113
setOpenSettingsForNotificationsHandler(messagingInstance, remoteMessage => {
114
console.log('Settings opened:', remoteMessage);
115
});
116
117
const openedFromSettings = await getDidOpenSettingsForNotification(messagingInstance);
118
}
119
```
120
121
### iOS Notification Configuration
122
123
Configuration options specific to iOS notification behavior and presentation.
124
125
```typescript { .api }
126
/**
127
* Enable/disable notification delegation to Firebase (iOS only)
128
* @param enabled - Whether to enable notification delegation
129
* @returns Promise that resolves when setting is updated
130
*/
131
function setNotificationDelegationEnabled(enabled: boolean): Promise<void>;
132
133
/**
134
* Check if notification delegation is enabled (iOS only)
135
* @returns Boolean indicating delegation status
136
*/
137
readonly isNotificationDelegationEnabled: boolean;
138
```
139
140
**Usage Examples:**
141
142
```typescript
143
import messaging from '@react-native-firebase/messaging';
144
import { Platform } from 'react-native';
145
146
if (Platform.OS === 'ios') {
147
// Check current delegation status
148
const delegationEnabled = messaging().isNotificationDelegationEnabled;
149
console.log('Delegation enabled:', delegationEnabled);
150
151
// Enable notification delegation
152
await messaging().setNotificationDelegationEnabled(true);
153
console.log('Notification delegation enabled');
154
155
// Disable for custom notification handling
156
await messaging().setNotificationDelegationEnabled(false);
157
}
158
159
// Modular API
160
import {
161
getMessaging,
162
isNotificationDelegationEnabled,
163
setNotificationDelegationEnabled
164
} from '@react-native-firebase/messaging';
165
166
if (Platform.OS === 'ios') {
167
const messagingInstance = getMessaging();
168
const enabled = isNotificationDelegationEnabled(messagingInstance);
169
await setNotificationDelegationEnabled(messagingInstance, false);
170
}
171
```
172
173
## iOS-Specific Message Properties
174
175
When handling messages on iOS, additional properties are available:
176
177
```typescript { .api }
178
interface RemoteMessage {
179
/** Whether APNs message was configured as background update (iOS) */
180
contentAvailable?: boolean;
181
182
/** Whether APNs mutable-content property was set (iOS) */
183
mutableContent?: boolean;
184
185
/** iOS category for notification actions (iOS) */
186
category?: string;
187
188
/** iOS thread identifier for notification grouping (iOS) */
189
threadId?: string;
190
}
191
192
interface Notification {
193
ios?: {
194
/** Notification subtitle (iOS) */
195
subtitle?: string;
196
/** Localization key for subtitle (iOS) */
197
subtitleLocKey?: string;
198
/** Localization arguments for subtitle (iOS) */
199
subtitleLocArgs?: string[];
200
/** Badge number to display on app icon (iOS) */
201
badge?: string;
202
/** Sound file name or critical sound configuration (iOS) */
203
sound?: string | NotificationIOSCriticalSound;
204
};
205
}
206
207
interface NotificationIOSCriticalSound {
208
/** Enable critical alert flag */
209
critical?: boolean;
210
/** Sound file name */
211
name: string;
212
/** Volume between 0.0 and 1.0 */
213
volume?: number;
214
}
215
```
216
217
## iOS Permission Types
218
219
Detailed iOS notification permission options:
220
221
```typescript { .api }
222
interface IOSPermissions {
223
/** Display alerts (default: true) */
224
alert?: boolean;
225
/** Siri announcement over AirPods (iOS 13+, default: false) */
226
announcement?: boolean;
227
/** Update app badge (default: true) */
228
badge?: boolean;
229
/** Critical alerts (default: false) */
230
criticalAlert?: boolean;
231
/** CarPlay notifications (default: true) */
232
carPlay?: boolean;
233
/** Provisional authorization (iOS 12+, default: false) */
234
provisional?: boolean;
235
/** Play notification sounds (default: true) */
236
sound?: boolean;
237
/** Show notification settings button (iOS 12+, default: false) */
238
providesAppNotificationSettings?: boolean;
239
}
240
```
241
242
## iOS Integration Patterns
243
244
### Complete iOS Setup
245
246
```typescript
247
import messaging from '@react-native-firebase/messaging';
248
import { Platform } from 'react-native';
249
250
async function setupiOSMessaging() {
251
if (Platform.OS !== 'ios') return;
252
253
const messagingInstance = messaging();
254
255
// Request permissions with iOS-specific options
256
const authStatus = await messagingInstance.requestPermission({
257
alert: true,
258
badge: true,
259
sound: true,
260
provisional: true, // iOS 12+
261
providesAppNotificationSettings: true, // iOS 12+
262
criticalAlert: false, // Requires special entitlement
263
announcement: false, // iOS 13+
264
carPlay: true,
265
});
266
267
if (authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
268
authStatus === messaging.AuthorizationStatus.PROVISIONAL) {
269
270
// Register for remote notifications
271
await messagingInstance.registerDeviceForRemoteMessages();
272
273
// Get tokens
274
const fcmToken = await messagingInstance.getToken();
275
const apnsToken = await messagingInstance.getAPNSToken();
276
277
console.log('FCM Token:', fcmToken);
278
console.log('APNs Token:', apnsToken);
279
280
// Send tokens to server
281
await updateServerTokens({ fcmToken, apnsToken });
282
283
return { fcmToken, apnsToken };
284
}
285
286
return null;
287
}
288
289
// Settings handler setup (outside components)
290
messaging().setOpenSettingsForNotificationsHandler(remoteMessage => {
291
console.log('User opened notification settings');
292
// Handle settings navigation
293
});
294
```
295
296
### iOS Background Processing
297
298
```typescript
299
import messaging from '@react-native-firebase/messaging';
300
import { Platform } from 'react-native';
301
302
// Background message handler (iOS specific considerations)
303
messaging().setBackgroundMessageHandler(async remoteMessage => {
304
console.log('Background message on iOS:', remoteMessage);
305
306
if (Platform.OS === 'ios') {
307
// iOS has strict background processing limits
308
try {
309
// Perform only essential background tasks
310
await quickBackgroundTask(remoteMessage);
311
312
// Signal completion for iOS background processing
313
// This is handled automatically by the library
314
} catch (error) {
315
console.error('iOS background processing error:', error);
316
}
317
}
318
});
319
```
320
321
### iOS Notification Categories
322
323
```typescript
324
// iOS notification categories for interactive notifications
325
import messaging from '@react-native-firebase/messaging';
326
import { Platform } from 'react-native';
327
328
if (Platform.OS === 'ios') {
329
// Handle category-based notifications
330
messaging().onMessage(async remoteMessage => {
331
if (remoteMessage.category) {
332
switch (remoteMessage.category) {
333
case 'MESSAGE_CATEGORY':
334
await handleMessageNotification(remoteMessage);
335
break;
336
case 'REMINDER_CATEGORY':
337
await handleReminderNotification(remoteMessage);
338
break;
339
default:
340
await handleDefaultNotification(remoteMessage);
341
}
342
}
343
});
344
}
345
```
346
347
## iOS Best Practices
348
349
1. **Permission Timing**: Request permissions at appropriate moments, not immediately on app launch
350
2. **Provisional Authorization**: Use provisional permissions for less intrusive introduction
351
3. **Settings Integration**: Implement `providesAppNotificationSettings` for better UX
352
4. **Background Limits**: Keep background processing minimal due to iOS restrictions
353
5. **APNs Integration**: Use APNs tokens for direct Apple Push Notification service if needed
354
6. **Thread Grouping**: Use `threadId` for notification organization
355
7. **Critical Alerts**: Only use with proper entitlements and user understanding
356
8. **Sound Management**: Provide appropriate sound files for different notification types
357
358
## iOS Debugging
359
360
Common iOS-specific debugging approaches:
361
362
```typescript
363
import messaging from '@react-native-firebase/messaging';
364
import { Platform } from 'react-native';
365
366
if (__DEV__ && Platform.OS === 'ios') {
367
// Debug iOS messaging setup
368
console.log('iOS Messaging Debug Info:');
369
console.log('Auto Init Enabled:', messaging().isAutoInitEnabled);
370
console.log('Device Registered:', messaging().isDeviceRegisteredForRemoteMessages);
371
console.log('Delegation Enabled:', messaging().isNotificationDelegationEnabled);
372
373
// Check initial state
374
messaging().getDidOpenSettingsForNotification().then(opened => {
375
console.log('Opened from settings:', opened);
376
});
377
378
messaging().getIsHeadless().then(headless => {
379
console.log('Is headless:', headless);
380
});
381
}
382
```