Commit 27da4aa2 authored by yogevbd's avatar yogevbd

Split specific iOS events, Update docs

parent 6a64314c
---
id: advanced-ios
title: iOS Advanced API
sidebar_label: iOS
---
## PushKit API
The PushKit framework provides the classes for your iOS apps to receive background pushes from remote servers. it has better support for background notifications compared to regular push notifications with `content-available: 1`. More info in [iOS PushKit documentation](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Reference/PushKit_Framework/).
### Register to PushKit
[Prepare your app to receive VoIP push notifications](https://developer.apple.com/library/ios/documentation/Performance/Conceptual/EnergyGuide-iOS/OptimizeVoIP.html)
### Listen to PushKit notifications
On receiving PushKit notification, a `pushKitNotificationReceived` event will be fired with the notification payload.
```js
Notifications.ios.events().registerPushKitNotificationReceived((payload: object) => {
console.log(JSON.stringify(payload));
});
```
In your ReactNative code, add event handler for `pushKitRegistered` event and call to `registerPushKit()`:
```javascript
constructor() {
Notifications.ios.events().registerPushKitRegistered((event: RegisteredPushKit) => {
console.log("PushKit Token Received: " + event.pushKitToken);
});
Notifications.ios.events().registerPushKitNotificationReceived((payload: object) => {
console.log('PushKit notification Received: ' + JSON.stringify(payload));
});
Notifications.ios.registerPushKit();
}
```
> 1. Notice that PushKit device token and regular notifications device token are different, so you must handle two different tokens in the server side in order to support this feature.
> 2. PushKit will not request permissions from the user for push notifications.
---
## Interactive / Actionable Notifications
> This section provides description for iOS. For notifications customization on Android, refer to [our wiki](https://github.com/wix/react-native-notifications/wiki/Android-Customizations#customizing-notifications-layout).
Interactive notifications allow you to reply to a message right from the notification banner or take action right from the lock screen.
On the Lock screen and within Notification Center, you swipe from right to left
to reveal actions. Destructive actions, like trashing an email, are color-coded red. Relatively neutral actions, like dismissing an alert or declining an invitation, are color-coded gray.
For banners, you pull down to reveal actions as buttons. For popups, the actions are immediately visible — the buttons are right there.
You can find more info about interactive notifications [here](http://www.imore.com/interactive-notifications-ios-8-explained).
![Interactive Notifications](http://i.imgur.com/XrVzy9w.gif)
Notification **actions** allow the user to interact with a given notification.
Notification **categories** allow you to group multiple actions together, and to connect the actions with the push notification itself.
Follow the basic workflow of adding interactive notifications to your app:
1. Config the actions.
2. Group actions together into categories.
3. Register to push notifications with the configured categories.
4. Push a notification (or trigger a [local](#triggering-local-notifications) one) with the configured category name.
### Example
#### Config the Actions
We will config two actions: upvote and reply.
```javascript
import { Notifications, NotificationAction, NotificationCategory } from 'react-native-notifications';
let upvoteAction = new NotificationAction({
activationMode: "background",
title: String.fromCodePoint(0x1F44D),
identifier: "UPVOTE_ACTION",
textInput: {
buttonTitle: 'title',
placeholder: 'placeholder text'
}
});
let replyAction = new NotificationAction({
activationMode: "background",
title: "Reply",
authenticationRequired: true,
identifier: "REPLY_ACTION"
});
```
#### Config the Category
We will group `upvote` action and `reply` action into a single category: `EXAMPLE_CATEGORY `. If the notification contains `EXAMPLE_CATEGORY ` under `category` field, those actions will appear.
```javascript
let exampleCategory = new NotificationCategory({
identifier: "EXAMPLE_CATEGORY",
actions: [upvoteAction, replyAction]
});
```
#### Register to Push Notifications
Instead of basic registration like we've done before, we will register the device to push notifications with the category we've just created.
```javascript
Notifications.setCategories([exampleCategory]);
```
#### Push an Interactive Notification
Notification payload should look like this:
```javascript
{
aps: {
// ... (alert, sound, badge, etc)
category: "EXAMPLE_CATEGORY"
}
}
```
The [example app](https://github.com/wix/react-native-notifications/tree/master/example) contains this interactive notification example, you can follow there.
### `NotificationAction` Payload
- `title` - Action button title.
- `identifier` - Action identifier (must be unique).
- `activationMode` - Indicating whether the app should activate to the foreground or background.
- `foreground` (default) - Activate the app and put it in the foreground.
- `background` - Activate the app and put it in the background. If the app is already in the foreground, it remains in the foreground.
- `textInput` - `TextInput` payload, when supplied, the system will present text input in this action.
- `destructive` - A Boolean value indicating whether the action is destructive. When the value of this property is `true`, the system displays the corresponding button differently to indicate that the action is destructive.
- `authenticationRequired` - A Boolean value indicating whether the user must unlock the device before the action is performed.
### `NotificationCategory` Payload
- `identifier` - The name of the action group (must be unique).
- `actions` - An array of `NotificationAction` objects, which related to this category.
### `TextInput` Payload
- `buttonTitle` - Title of the `send` button.
- `placeholder` - Placeholder for the `textInput`.
#### Get and set application icon badges count (iOS only)
Get the current number:
```javascript
Notifications.ios.getBadgesCount((count) => console.log(count));
```
Set to specific number:
```javascript
Notifications.ios.setBadgesCount(2);
```
Clear badges icon:
```javascript
Notifications.ios.setBadgesCount(0);
```
...@@ -13,7 +13,7 @@ Notifications.registerRemoteNotifications(); ...@@ -13,7 +13,7 @@ Notifications.registerRemoteNotifications();
``` ```
## getInitialNotification() ## getInitialNotification()
This method returns a promise. If the app was launched by a push notification, this promise resolves to an object of type Notification. Otherwise, it resolves to undefined. This method returns a promise. If the app was launched by a push notification, this promise resolves to an object of type [Notification](http://localhost:3000/react-native-notifications/docs/notification-object). Otherwise, it resolves to undefined.
```js ```js
const notification: Notification = await Notifications.getInitialNotification(); const notification: Notification = await Notifications.getInitialNotification();
......
...@@ -14,7 +14,7 @@ Notifications.events().registerRemoteNotificationsRegistered((event: Registered) ...@@ -14,7 +14,7 @@ Notifications.events().registerRemoteNotificationsRegistered((event: Registered)
``` ```
## registerNotificationReceived() ## registerNotificationReceived()
Fired when a remote notification is received in foreground state. The handler will be invoked with an instance of `Notification`. Fired when a remote notification is received in foreground state. The handler will be invoked with an instance of [Notification](http://localhost:3000/react-native-notifications/docs/notification-object).
Should call completion function on iOS, will be ignored on Android. Should call completion function on iOS, will be ignored on Android.
```js ```js
...@@ -27,7 +27,7 @@ Notifications.events().registerNotificationReceived((notification: Notification, ...@@ -27,7 +27,7 @@ Notifications.events().registerNotificationReceived((notification: Notification,
``` ```
## registerRemoteNotificationOpened() ## registerRemoteNotificationOpened()
Fired when a remote notification is opened from dead or background state. The handler will be invoked with an instance of `Notification`. Fired when a remote notification is opened from dead or background state. The handler will be invoked with an instance of [Notification](http://localhost:3000/react-native-notifications/docs/notification-object).
Should call completion function on iOS, will be ignored on Android. Should call completion function on iOS, will be ignored on Android.
```js ```js
......
...@@ -8,7 +8,7 @@ sidebar_label: iOS specific ...@@ -8,7 +8,7 @@ sidebar_label: iOS specific
Fired when the user registers for PushKit notifications. The handler will be invoked with an event holding the hex string representing the `pushKitToken` Fired when the user registers for PushKit notifications. The handler will be invoked with an event holding the hex string representing the `pushKitToken`
```js ```js
Notifications.events().registerPushKitRegistered((event: RegisteredPushKit) => { Notifications.ios.events().registerPushKitRegistered((event: RegisteredPushKit) => {
console.log(event.pushKitToken); console.log(event.pushKitToken);
}); });
``` ```
...@@ -17,8 +17,8 @@ Notifications.events().registerPushKitRegistered((event: RegisteredPushKit) => { ...@@ -17,8 +17,8 @@ Notifications.events().registerPushKitRegistered((event: RegisteredPushKit) => {
Fired when a PushKit notification is received. The handler will be invoked with the notification object. Fired when a PushKit notification is received. The handler will be invoked with the notification object.
```js ```js
Notifications.events().registerPushKitNotificationReceived((event: object) => { Notifications.ios.events().registerPushKitNotificationReceived((payload: object) => {
console.log(JSON.stringify(event)); console.log(JSON.stringify(payload));
}); });
``` ```
---
id: notification-object
title: Notification object
sidebar_label: Notification
---
Contains the payload data.
Example:
```js
Notifications.events().registerNotificationReceived((notification: Notification, completion: (response: NotificationCompletion) => void) => {
// Prints the notification payload
console.log(JSON.stringify(notification.data));
completion({alert: false, sound: false, badge: false});
});
```
\ No newline at end of file
...@@ -28,19 +28,17 @@ describe('Notifications', () => { ...@@ -28,19 +28,17 @@ describe('Notifications', () => {
}); });
describe('Dead state', () => { describe('Dead state', () => {
it.only('Should receive notification', async () => { it('Should receive notification', async () => {
await device.launchApp({newInstance: true, userNotification: createNotification({link: 'deadState/notification'})}); await device.launchApp({newInstance: true, userNotification: createNotification({link: 'deadState/notification'})});
await linkShouldBeVisible('deadState/notification'); await linkShouldBeVisible('deadState/notification');
}); });
}); });
async function linkShouldBeVisible(link) { async function linkShouldBeVisible(link) {
return await expect(elementByLabel(`Extra Link Param: ${link}`)).toBeVisible(); return await expect(elementByLabel(`Extra Link Param: ${link}`)).toBeVisible();
} }
}); });
function createNotification({link, showAlert}) { function createNotification({link, showAlert}) {
return { return {
trigger: { trigger: {
......
...@@ -6,7 +6,7 @@ import { ...@@ -6,7 +6,7 @@ import {
Button Button
} from 'react-native'; } from 'react-native';
import React, {Component} from 'react'; import React, {Component} from 'react';
import {Notifications} from 'react-native-notifications'; import {Notifications, NotificationAction, NotificationCategory} from 'react-native-notifications';
class NotificationsExampleApp extends Component { class NotificationsExampleApp extends Component {
constructor() { constructor() {
...@@ -43,13 +43,13 @@ class NotificationsExampleApp extends Component { ...@@ -43,13 +43,13 @@ class NotificationsExampleApp extends Component {
} }
setCategories() { setCategories() {
const upvoteAction = { const upvoteAction = new NotificationAction({
activationMode: 'background', activationMode: 'background',
title: String.fromCodePoint(0x1F44D), title: String.fromCodePoint(0x1F44D),
identifier: 'UPVOTE_ACTION' identifier: 'UPVOTE_ACTION'
}; });
const replyAction = { const replyAction = new NotificationAction({
activationMode: 'background', activationMode: 'background',
title: 'Reply', title: 'Reply',
authenticationRequired: true, authenticationRequired: true,
...@@ -58,12 +58,13 @@ class NotificationsExampleApp extends Component { ...@@ -58,12 +58,13 @@ class NotificationsExampleApp extends Component {
placeholder: 'Insert message' placeholder: 'Insert message'
}, },
identifier: 'REPLY_ACTION' identifier: 'REPLY_ACTION'
}; });
const category = {
const category = new NotificationCategory({
identifier: 'SOME_CATEGORY', identifier: 'SOME_CATEGORY',
actions: [upvoteAction, replyAction] actions: [upvoteAction, replyAction]
}; });
Notifications.setCategories([category]); Notifications.setCategories([category]);
} }
......
...@@ -2,6 +2,7 @@ import { NativeCommandsSender } from './adapters/NativeCommandsSender'; ...@@ -2,6 +2,7 @@ import { NativeCommandsSender } from './adapters/NativeCommandsSender';
import { NativeEventsReceiver } from './adapters/NativeEventsReceiver'; import { NativeEventsReceiver } from './adapters/NativeEventsReceiver';
import { Commands } from './commands/Commands'; import { Commands } from './commands/Commands';
import { EventsRegistry } from './events/EventsRegistry'; import { EventsRegistry } from './events/EventsRegistry';
import { EventsRegistryIOS } from './events/EventsRegistryIOS';
import { Notification } from './DTO/Notification'; import { Notification } from './DTO/Notification';
import { UniqueIdProvider } from './adapters/UniqueIdProvider'; import { UniqueIdProvider } from './adapters/UniqueIdProvider';
import { CompletionCallbackWrapper } from './adapters/CompletionCallbackWrapper'; import { CompletionCallbackWrapper } from './adapters/CompletionCallbackWrapper';
...@@ -17,6 +18,7 @@ export class NotificationsRoot { ...@@ -17,6 +18,7 @@ export class NotificationsRoot {
private readonly nativeCommandsSender: NativeCommandsSender; private readonly nativeCommandsSender: NativeCommandsSender;
private readonly commands: Commands; private readonly commands: Commands;
private readonly eventsRegistry: EventsRegistry; private readonly eventsRegistry: EventsRegistry;
private readonly eventsRegistryIOS: EventsRegistryIOS;
private readonly uniqueIdProvider: UniqueIdProvider; private readonly uniqueIdProvider: UniqueIdProvider;
private readonly completionCallbackWrapper: CompletionCallbackWrapper; private readonly completionCallbackWrapper: CompletionCallbackWrapper;
...@@ -30,8 +32,9 @@ export class NotificationsRoot { ...@@ -30,8 +32,9 @@ export class NotificationsRoot {
this.uniqueIdProvider this.uniqueIdProvider
); );
this.eventsRegistry = new EventsRegistry(this.nativeEventsReceiver, this.completionCallbackWrapper); this.eventsRegistry = new EventsRegistry(this.nativeEventsReceiver, this.completionCallbackWrapper);
this.eventsRegistryIOS = new EventsRegistryIOS(this.nativeEventsReceiver);
this.ios = new NotificationsIOS(this.commands); this.ios = new NotificationsIOS(this.commands, this.eventsRegistryIOS);
this.android = new NotificationsAndroid(this.commands); this.android = new NotificationsAndroid(this.commands);
} }
......
import { Notification } from './DTO/Notification'; import { Notification } from './DTO/Notification';
import { Commands } from './commands/Commands'; import { Commands } from './commands/Commands';
import { Platform } from 'react-native'; import { Platform } from 'react-native';
import { EventsRegistryIOS } from './events/EventsRegistryIOS';
export class NotificationsIOS { export class NotificationsIOS {
constructor(private readonly commands: Commands) { constructor(private readonly commands: Commands, private readonly eventsRegistry: EventsRegistryIOS) {
return new Proxy(this, { return new Proxy(this, {
get(target, name) { get(target, name) {
if (Platform.OS === 'ios') { if (Platform.OS === 'ios') {
...@@ -86,4 +87,11 @@ export class NotificationsIOS { ...@@ -86,4 +87,11 @@ export class NotificationsIOS {
public getDeliveredNotifications(): Array<Notification> { public getDeliveredNotifications(): Array<Notification> {
return this.commands.getDeliveredNotifications(); return this.commands.getDeliveredNotifications();
} }
/**
* Obtain the events registry instance
*/
public events(): EventsRegistryIOS {
return this.eventsRegistry;
}
} }
...@@ -146,20 +146,6 @@ describe('EventsRegistry', () => { ...@@ -146,20 +146,6 @@ describe('EventsRegistry', () => {
expect(mockNativeEventsReceiver.registerRemoteNotificationsRegistered).toHaveBeenCalledWith(cb); expect(mockNativeEventsReceiver.registerRemoteNotificationsRegistered).toHaveBeenCalledWith(cb);
}); });
it('delegates registerPushKitRegistered to nativeEventsReceiver', () => {
const cb = jest.fn();
uut.registerPushKitRegistered(cb);
expect(mockNativeEventsReceiver.registerPushKitRegistered).toHaveBeenCalledTimes(1);
expect(mockNativeEventsReceiver.registerPushKitRegistered).toHaveBeenCalledWith(cb);
});
it('delegates registerPushKitNotificationReceived to nativeEventsReceiver', () => {
const cb = jest.fn();
uut.registerPushKitNotificationReceived(cb);
expect(mockNativeEventsReceiver.registerPushKitNotificationReceived).toHaveBeenCalledTimes(1);
expect(mockNativeEventsReceiver.registerPushKitNotificationReceived).toHaveBeenCalledWith(cb);
});
it('delegates registerRemoteNotificationsRegistrationFailed to nativeEventsReceiver', () => { it('delegates registerRemoteNotificationsRegistrationFailed to nativeEventsReceiver', () => {
const cb = jest.fn(); const cb = jest.fn();
uut.registerRemoteNotificationsRegistrationFailed(cb); uut.registerRemoteNotificationsRegistrationFailed(cb);
......
...@@ -3,7 +3,6 @@ import { NativeEventsReceiver } from '../adapters/NativeEventsReceiver'; ...@@ -3,7 +3,6 @@ import { NativeEventsReceiver } from '../adapters/NativeEventsReceiver';
import { import {
Registered, Registered,
RegistrationError, RegistrationError,
RegisteredPushKit,
NotificationResponse NotificationResponse
} from '../interfaces/NotificationEvents'; } from '../interfaces/NotificationEvents';
import { CompletionCallbackWrapper } from '../adapters/CompletionCallbackWrapper'; import { CompletionCallbackWrapper } from '../adapters/CompletionCallbackWrapper';
...@@ -19,18 +18,10 @@ export class EventsRegistry { ...@@ -19,18 +18,10 @@ export class EventsRegistry {
public registerRemoteNotificationsRegistered(callback: (event: Registered) => void): EmitterSubscription { public registerRemoteNotificationsRegistered(callback: (event: Registered) => void): EmitterSubscription {
return this.nativeEventsReceiver.registerRemoteNotificationsRegistered(callback); return this.nativeEventsReceiver.registerRemoteNotificationsRegistered(callback);
} }
public registerPushKitRegistered(callback: (event: RegisteredPushKit) => void): EmitterSubscription {
return this.nativeEventsReceiver.registerPushKitRegistered(callback);
}
public registerNotificationReceived(callback: (notification: Notification, completion: (response: NotificationCompletion) => void) => void): EmitterSubscription { public registerNotificationReceived(callback: (notification: Notification, completion: (response: NotificationCompletion) => void) => void): EmitterSubscription {
return this.nativeEventsReceiver.registerRemoteNotificationReceived(this.completionCallbackWrapper.wrapReceivedCallback(callback)); return this.nativeEventsReceiver.registerRemoteNotificationReceived(this.completionCallbackWrapper.wrapReceivedCallback(callback));
} }
public registerPushKitNotificationReceived(callback: (event: object) => void): EmitterSubscription {
return this.nativeEventsReceiver.registerPushKitNotificationReceived(callback);
}
public registerRemoteNotificationOpened(callback: (response: NotificationResponse, completion: () => void) => void): EmitterSubscription { public registerRemoteNotificationOpened(callback: (response: NotificationResponse, completion: () => void) => void): EmitterSubscription {
return this.nativeEventsReceiver.registerRemoteNotificationOpened(this.completionCallbackWrapper.wrapOpenedCallback(callback)); return this.nativeEventsReceiver.registerRemoteNotificationOpened(this.completionCallbackWrapper.wrapOpenedCallback(callback));
...@@ -39,5 +30,4 @@ export class EventsRegistry { ...@@ -39,5 +30,4 @@ export class EventsRegistry {
public registerRemoteNotificationsRegistrationFailed(callback: (event: RegistrationError) => void): EmitterSubscription { public registerRemoteNotificationsRegistrationFailed(callback: (event: RegistrationError) => void): EmitterSubscription {
return this.nativeEventsReceiver.registerRemoteNotificationsRegistrationFailed(callback); return this.nativeEventsReceiver.registerRemoteNotificationsRegistrationFailed(callback);
} }
} }
import { EventsRegistryIOS } from './EventsRegistryIOS';
import { NativeEventsReceiver } from '../adapters/NativeEventsReceiver.mock';
describe('EventsRegistryIOS', () => {
let uut: EventsRegistryIOS;
const mockNativeEventsReceiver = new NativeEventsReceiver();
beforeEach(() => {
uut = new EventsRegistryIOS(mockNativeEventsReceiver);
});
it('delegates registerPushKitRegistered to nativeEventsReceiver', () => {
const cb = jest.fn();
uut.registerPushKitRegistered(cb);
expect(mockNativeEventsReceiver.registerPushKitRegistered).toHaveBeenCalledTimes(1);
expect(mockNativeEventsReceiver.registerPushKitRegistered).toHaveBeenCalledWith(cb);
});
it('delegates registerPushKitNotificationReceived to nativeEventsReceiver', () => {
const cb = jest.fn();
uut.registerPushKitNotificationReceived(cb);
expect(mockNativeEventsReceiver.registerPushKitNotificationReceived).toHaveBeenCalledTimes(1);
expect(mockNativeEventsReceiver.registerPushKitNotificationReceived).toHaveBeenCalledWith(cb);
});
});
import { EmitterSubscription } from 'react-native';
import { NativeEventsReceiver } from '../adapters/NativeEventsReceiver';
import {
RegisteredPushKit
} from '../interfaces/NotificationEvents';
export class EventsRegistryIOS {
constructor(
private nativeEventsReceiver: NativeEventsReceiver)
{}
public registerPushKitRegistered(callback: (event: RegisteredPushKit) => void): EmitterSubscription {
return this.nativeEventsReceiver.registerPushKitRegistered(callback);
}
public registerPushKitNotificationReceived(callback: (event: object) => void): EmitterSubscription {
return this.nativeEventsReceiver.registerPushKitNotificationReceived(callback);
}
}
...@@ -6,3 +6,4 @@ export const Notifications = notificationsSingleton; ...@@ -6,3 +6,4 @@ export const Notifications = notificationsSingleton;
export * from './interfaces/EventSubscription'; export * from './interfaces/EventSubscription';
export * from './DTO/Notification'; export * from './DTO/Notification';
export * from './interfaces/NotificationEvents'; export * from './interfaces/NotificationEvents';
export * from './interfaces/NotificationCategory';
export interface NotificationCategory { export class NotificationCategory {
identifier: string identifier: string
actions: [NotificationAction?]; actions: [NotificationAction?];
}
constructor(identifier: string, actions: [NotificationAction?]) {
this.identifier = identifier;
this.actions = actions;
}
}
export interface NotificationTextInput { export interface NotificationTextInput {
buttonTitle: string; buttonTitle: string;
placeholder: string; placeholder: string;
} }
export interface NotificationAction { export class NotificationAction {
identifier: string; identifier: string;
activationMode: 'foreground' | 'authenticationRequired' | 'destructive'; activationMode: 'foreground' | 'authenticationRequired' | 'destructive';
title: string; title: string;
authenticationRequired: boolean; authenticationRequired: boolean;
textInput: NotificationTextInput textInput: NotificationTextInput;
constructor(identifier: string, activationMode: 'foreground' | 'authenticationRequired' | 'destructive', title: string, authenticationRequired: boolean, textInput: NotificationTextInput) {
this.identifier = identifier;
this.activationMode = activationMode;
this.title = title;
this.authenticationRequired = authenticationRequired;
this.textInput = textInput;
}
} }
\ No newline at end of file
...@@ -5,22 +5,14 @@ ...@@ -5,22 +5,14 @@
"previous": "Previous", "previous": "Previous",
"tagline": "Documentation", "tagline": "Documentation",
"docs": { "docs": {
"advanced-ios": {
"title": "iOS Advanced API",
"sidebar_label": "iOS"
},
"android-api": { "android-api": {
"title": "Android Specific Commands", "title": "Android Specific Commands",
"sidebar_label": "Android specific" "sidebar_label": "Android specific"
}, },
"doc2": {
"title": "document number 2"
},
"doc3": {
"title": "This is document number 3"
},
"doc4": {
"title": "Other Document"
},
"doc5": {
"title": "Fifth Document"
},
"general-api": { "general-api": {
"title": "General Commands", "title": "General Commands",
"sidebar_label": "General" "sidebar_label": "General"
...@@ -44,6 +36,10 @@ ...@@ -44,6 +36,10 @@
"ios-events": { "ios-events": {
"title": "iOS", "title": "iOS",
"sidebar_label": "iOS specific" "sidebar_label": "iOS specific"
},
"notification-object": {
"title": "Notification object",
"sidebar_label": "Notification"
} }
}, },
"links": { "links": {
...@@ -52,8 +48,10 @@ ...@@ -52,8 +48,10 @@
}, },
"categories": { "categories": {
"Installation": "Installation", "Installation": "Installation",
"Advanced": "Advanced",
"Commands": "Commands", "Commands": "Commands",
"Events": "Events" "Events": "Events",
"Objects": "Objects"
} }
}, },
"pages-strings": { "pages-strings": {
......
{ {
"docs": { "docs": {
"Installation": ["installation-ios", "installation-android"] "Installation": ["installation-ios", "installation-android"],
"Advanced": ["advanced-ios"]
}, },
"api": { "api": {
"Commands": ["general-api", "ios-api", "android-api"], "Commands": ["general-api", "ios-api", "android-api"],
"Events": ["general-events", "ios-events"] "Events": ["general-events", "ios-events"],
"Objects": ["notification-object"]
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment