- Установка
- Swift
- Objective-C
- Важная информация о Push уведомлениях
- Дублирование уведомлений и статистика доставленных пушей
- Локализация
- Xcode 15
- Использование ссылок в пушах
- Отлючение дебажных логов
На данный момент Carrot quest для iOS можно установить с помощью CocoaPod и Swift Package Manager.
Добавьте следующую строчку в pod файл:
pod 'CarrotquestSDK'
В Xcode нажмите File->Add Package Dependencies...
Затем в появившемся окне в поле "Search or Enter Package URL" вставте ссылку на SwiftPM репозиторий:
https://github.com/carrotquest/carrotquest-ios-spm.git
Для работы с Carrot quest для iOS вам понадобится API Key и User Auth Key. Вы можете найти эти ключи на вкладке "Настройки > Разработчикам":
Для инициализации Carrot quest вам нужно добавить следующий код в файл AppDelegate вашего приложения:
import CarrotSDK
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey:Any]?) -> Bool {
....
Carrot.shared.setup(
withApiKey: API-KEY,
successHandler: {
print("Carrotquest SDK connected")
},
errorHandler: { error in
print("Carrotquest SDK error: " + error)
})
....
}
Если в вашем приложении присутствует авторизация пользователей, вы можете передать ID пользователя в Carrot quest. Существует два способа авторизации: напрямую передать userAuthKey, передать hash генерируемый у вас на бэке.
- Вход через user auth key:
Carrot.shared.auth(
withUserId: userId,
withUserAuthKey: userAuthKey,
successHandler: { carrotId in
print("Carrotquest SDK user auth successed, CarrotId = \(carrotId)")
},
errorHandler: { error in
print("Carrotquest SDK user auth error: " + error)
})
- Вход через hash:
Carrot.shared.hashedAuth(
withUserId: userId,
withHash: hash,
successHandler: { carrotId in
print("Carrotquest SDK user auth successed, CarrotId = \(carrotId)")
},
errorHandler: { error in
print("Carrotquest SDK user auth error: " + error)
})
Для реализации функции выхода:
Carrot.shared.logout(
successHandler: {
print("Carrotquest SDK user logout successed")
},
errorHandler: { error in
print("Carrotquest SDK user logout error: " + error)
})
Вы можете установить необходимые свойства пользователя с помощью:
Carrot.shared.setUserProperty(userProperties)
Где userProperties
это объект типа [UserProperty]
.
Для описания свойств пользователя используйте класс UserProperty
:
UserProperty(key: key, value: value)
UserProperty(key: key, value: value, operation: .updateOrCreate)
Более подробно про Operations
можно прочитать в разделе «Свойства пользователя».
Внимание!
Поле key
не может начинаться с символа $
.
Для установки системных свойств реализовано 2 класса CarrotUserProperty
и EcommerceUserProperty
.
Для отслеживания событий используйте:
Carrot.shared.trackEvent(withName: name, withParams: params)
где params
— дополнительные параметры для события в виде JSON-строки.
B SDK есть возможность трекинга навигации внутри приложения для того, чтобы при необходимости запускать различные триггерные сообщения на определенных экранах. Для этого используйте метод:
let name: String = "screenName"
Carrot.shared.trackScreen(name)
Вы можете дать пользователю мобильного приложения возможность перейти в чат с оператором из любого места. Это можно реализовать двумя разными путями - через плавающую кнопку, либо напрямую вызвав метод открытия чата в любое нужное время.
Виджет предоставляющий быстрый доступ к чату. Добавить кнопку можно с помощью следующего метода:
Carrot.shared.showButton(in: view)
Для того чтобы скрыть кнопку возпльзуйтесь методом:
Carrot.shared.hideButton()
Открыть чат можно также, вызвав из произвольного места (после инициализации) следующий код:
Carrot.shared.openChat()
Для отслеживания количества непрочтенных диалогов:
Carrot.shared.getUnreadConversationsCount({ count in
print("Carrotquest SDK dialogs count: \(count)")
})
и для количества непрочтенных сообщений:
Carrot.shared.getUnreadMessagesCount({ count in
print("Carrotquest SDK messages count: \(count)")
})
Для того, чтобы при клике на ссылку внутри SDK правильно работали диплинки (universal link) существует специальный метод ручного управления методом открытия ссылок. Его можно вызвать где угодно, но лучше всего где-то в вашем AppDeletage/SceneDelegate рядом с инициализацией SDK:
import CarrotSDK
CustomUrlOpener.shared.set(for: .chat, customLogic: { url in
// Любая кастомная логика по открытию ссылок
})
Как вы можете заметить, первый аргумент, который имеет label for
на 4 доступных варианта:
- push - изменяет логику при клике на ссылку в пуше
- chat - изменяет логику при клике на ссылку в чате
- popup - изменяет логику при клике на ссылку в попапе
- all - изменяет логику при клике на ссылку во всех 3 местах
Таким образом, если вы хотите обработать клики на диплинк (universal link) во всех местах SDK, можно написать какой-то такой код:
import CarrotSDK
CustomUrlOpener.shared.set(for: .all) { url in
if url.host?.contains("ВАШ ДОМЕН") ?? false {
CustomUrlOpener.shared.openUniversalLink(url)
} else {
CustomUrlOpener.shared.openBrowserLink(url)
}
}
Если что, ошибки тут нет. Актуальные версии swift позволяют не указывать label последего замыкания в вызове функции.
Для работы с уведомлениями SDK использует сервис Firebase Cloud Messaging. В связи с этим необходимо получить ключ и отправить его в Carrot. Вы можете найти поле для ввода ключа на вкладке Настройки > Разработчикам. Процесс настройки сервиса Firebase Cloud Messaging описан здесь.
Далее, в делегате MessagingDelegate необходимо установить fcmToken для Carrot SDK:
import FirebaseMessaging
import CarrotSDK
extension AppDelegate: MessagingDelegate {
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
if let fcmToken = fcmToken {
CarrotNotificationService.shared.setToken(fcmToken)
} else {
print("Carrotquest SDK error: fcmToken not found")
}
...
}
}
Для отображения уведомлений, необходимо добавить код в UNUserNotificationCenterDelegate:
import CarrotSDK
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(
_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
let notificationService = CarrotNotificationService.shared
if notificationService.canHandle(notification) {
notificationService.show(notification, completionHandler: completionHandler)
} else {
// Логика для пользовательских уведомлений
}
}
}
Для обработки кликов на уведомления:
import CarrotSDK
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let notificationService = CarrotNotificationService.shared
if notificationService.canHandle(response) {
notificationService.clickNotification(notificationResponse: response)
} else {
// Логика для пользовательских уведомлений
}
completionHandler()
}
}
Для инициализации Carrot quest вам нужно добавить следующий код в файл AppDelegate вашего приложения:
#import "CarrotSDK/CarrotSDK.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
....
Carrot *carrot = [Carrot shared];
[
carrot
setupWithApiKey: API-KEY
successHandler: ^(){
NSLog(@"Carrotquest SDK connected");
}
errorHandler: ^(NSString *error){
NSLog(@"Carrotquest SDK error: %@", error);
}];
....
return YES;
}
Если в вашем приложении присутствует авторизация пользователей, вы можете передать ID пользователя в Carrot quest. Существует два способа авторизации: напрямую передать userAuthKey, передать hash генерируемый у вас на бэке.
- Вход через user auth key:
Carrot *carrot = [Carrot shared];
[
carrot
authWithUserId: userId
withUserAuthKey: userAuthKey
successHandler: ^(NSString *carrotId){
NSLog(@"Carrotquest SDK user auth successed, CarrotId: %@", carrotId);
}
errorHandler: ^(NSString *error){
NSLog(@"Carrotquest SDK user auth error: %@", error);
}];
- Вход через hash:
Carrot *carrot = [Carrot shared];
[
carrot
authWithUserId: userId
withHash: hash
successHandler: ^(NSString *carrotId){
NSLog(@"Carrotquest SDK user auth successed, CarrotId: %@", carrotId);
}
errorHandler: ^(NSString *error){
NSLog(@"Carrotquest SDK user auth error: %@", error);
}];
Для реализации функции выхода:
Carrot *carrot = [Carrot shared];
[
carrot
logoutWithSuccessHandler: ^(){
NSLog(@"Carrotquest SDK user logout successed");
} errorHandler: ^(NSString *error){
NSLog(@"Carrotquest SDK user logout error: %@", error);
}];
Вы можете установить необходимые свойства пользователя с помощью:
Carrot *carrot = [Carrot shared];
[
carrot
setUserProperty:userProperties
]
Где userProperties
это объект типа [UserProperty]
.
Для описания свойств пользователя используйте класс UserProperty
:
Carrot *carrot = [Carrot shared];
UserProperty *userProp = [[UserProperty alloc] initWithKey: key value: value];
UserProperty *userProp = [[UserProperty alloc] initWithKey: key value: value operation: @"updateOrCreate"];
Более подробно про Operations
можно прочитать в разделе «Свойства пользователя».
Внимание!
Поле key
не может начинаться с символа $
.
Для установки системных свойств реализовано 2 класса CarrotUserProperty
и EcommerceUserProperty
.
Для отслеживания событий используйте:
Carrot *carrot = [Carrot shared];
[
carrot
trackEventWithName: name
withParams: params
];
где params
— дополнительные параметры для события в виде JSON-строки.
B SDK есть возможность трекинга навигации внутри приложения для того, чтобы при необходимости запускать различные триггерные сообщения на определенных экранах. Для этого используйте метод:
Carrot *carrot = [Carrot shared];
[carrot trackScreen:@"screenName"];
Вы можете дать пользователю мобильного приложения возможность перейти в чат с оператором из любого места. Это можно реализовать двумя разными путями - через плавающую кнопку, либо напрямую вызвав метод открытия чата в любое нужное время.
Виджет предоставляющий быстрый доступ к чату. Добавить кнопку можно с помощью следующего метода:
Carrot *carrot = [Carrot shared];
[carrot showButtonIn: self.view];
Для того чтобы скрыть кнопку возпльзуйтесь методом:
Carrot *carrot = [Carrot shared];
[carrot hideButton];
Открыть чат можно также, вызвав из произвольного места (после инициализации) следующий код:
Carrot *carrot = [Carrot shared];
[carrot openChat];
Для отслеживания количества непрочтенных диалогов:
Carrot *carrot = [Carrot shared];
[
carrot
getUnreadConversationsCount:^(NSInteger count){
NSLog(@"Carrotquest SDK dialogs count: %ld", (long)count);
}];
и для количества непрочтенных сообщений:
Carrot.shared.getUnreadMessagesCount({ count in
print("Carrotquest SDK messages count: \(count)")
})
Carrot *carrot = [Carrot shared];
[
carrot
getUnreadMessagesCount:^(NSInteger count){
NSLog(@"Carrotquest SDK dialogs count: %ld", (long)count);
}];
Для того, чтобы при клике на ссылку внутри SDK правильно работали диплинки (universal link) существует специальный метод ручного управления методом открытия ссылок. Его можно вызвать где угодно, но лучше всего где-то в вашем AppDeletage/SceneDelegate рядом с инициализацией SDK:
CustomUrlOpener *opener = [CustomUrlOpener shared];
[
opener
for: 1
customLogic: ^(NSURL *url){
// Любая кастомная логика по открытию ссылок
}
];
Как вы можете заметить, первый аргумент, который имеет label for
на 4 доступных варианта:
- push - изменяет логику при клике на ссылку в пуше
- chat - изменяет логику при клике на ссылку в чате
- popup - изменяет логику при клике на ссылку в попапе
- all - изменяет логику при клике на ссылку во всех 3 местах
Таким образом, если вы хотите обработать клики на диплинк (universal link) во всех местах SDK, можно написать какой-то такой код:
CustomUrlOpener *opener = [CustomUrlOpener shared];
[
opener
for: 3
customLogic: ^(NSURL *url){
if ([[url host] containsString:@"ВАШ ДОМЕН"]) {
[[CustomUrlOpener shared] openUniversalLink:url];
} else {
[[CustomUrlOpener shared] openBrowserLink:url];
}
}
];
Для работы с уведомлениями SDK использует сервис Firebase Cloud Messaging. В связи с этим необходимо получить ключ и отправить его в Carrot. Вы можете найти поле для ввода ключа на вкладке Настройки > Разработчикам. Процесс настройки сервиса Firebase Cloud Messaging описан здесь.
Далее, в делегате MessagingDelegate необходимо установить fcmToken для Carrot SDK:
#import "CarrotSDK/CarrotSDK.h"
#import <Firebase.h>
- (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken {
CarrotNotificationService *service = [CarrotNotificationService shared];
[service setToken: fcmToken];
}
Для отображения уведомлений, необходимо добавить код в AppDelegate:
#import <UserNotifications/UserNotifications.h>
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
CarrotNotificationService *service = [CarrotNotificationService shared];
if ([service canHandle:notification]) {
[service show:notification appGroudDomain:nil completionHandler:completionHandler];
} else {
// Логика для пользовательских уведомлений
}
}
Для обработки кликов на уведомления:
#import <UserNotifications/UserNotifications.h>
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void(^)(void))completionHandler {
CarrotNotificationService *service = [CarrotNotificationService shared];
if ([service canHandleWithResponse:response]) {
[service clickNotificationWithNotificationResponse:response appGroudDomain:nil openLink:true];
} else {
// Логика для пользовательских уведомлений
}
}
Подробнее о том, зачем нужен пункт appGroudDomain можно почитать тут.
Подробнее о том, зачем нужен пункт openLink можно почитать тут.
Необходимо добавить в info.plist вашего проекта параметр:
<key>FirebaseAppDelegateProxyEnabled</key>
<string>0</string>
И обязательно, убедиться, что поле имеет значение string. Если кратко, то это переключает управление уведолмениями в ручной режим и позволяет SDK правильно функционировать. Подробнее можете почитать тут.
Мы используем 2 канала доставки сообщений, поэтому в некоторых случаях уведомления могут дублироваться. Например: при выходе из приложения, или при очень быстром удалении уведомления, возможно получение повтороного уведомления. Для предотвращения такого поведения нужно создать Notification Service Extension. В Xcode, в списке файлов выберите свой проект, а затем File/New/Target/Notification Service Extension.
После чего необходимо зарегистрировать AppGroup в Apple Developer Portal. Identifier App Group должен быть уникальным, и начинаться на "group." иначе Xcode его не примет.
Теперь необходимо добавить Identifier в Xcode:
-
В списке файлов выберите свой проект.
-
В списке targets выберете пункт с именем вашего проекта.
-
Во вкладке "Singing & Capabitities" нажмите на "+ Capability".
-
В выпадающем списке найдите найдите и выберите App Group.
-
На вкладке появится пустой список для идентификаторов App Group. Добавте туда Identifier, который зарегистрировали в Apple Developer Portal ранее.
-
Вернитесь к списку Targets. Аналогичным образом добавте App Group к вашему Notification Service Extension.
Внесите изменения в метод инициализирующий библиотеку:
Carrot.shared.setup(
...
withAppGroup: <group_id>,
...
)
Теперь нужно добавить логику в ваш Notification Service Extension. В списке файлов, должна была появиться новая папка с именем вашего Notification Service Extension. Добавте код в файл NotificationService.swift:
import UserNotifications
import CarrotSDK
class NotificationService: CarrotNotificationServiceExtension {
override func setup() {
self.apiKey = <api_key>
self.domainIdentifier = <group_id>
}
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
<ваша логика>
super.didReceive(request, withContentHandler: contentHandler)
}
}
Обновите ваш pod файл, добавьте:
target 'NotificationService' do
inherit! :search_paths
pod 'CarrotquestSDK'
end
И напоследок, нужно передать Identifier зарегистрированный в Apple Developer Portal ранее в метод show в UNUserNotificationCenterDelegate:
let domain = "Identifier зарегистрированный в Apple Developer Portal ранее"
notificationService.show(notification, appGroudDomain: domain, completionHandler: completionHandler)
Для того, чтобы SDK автоматически подтягивал и русскую локализацию, кроме стандартной, английской, необходимо убедиться, что в Xcode проекте такая локализация включена.
Если вы используете Xcode 15 и выше, и CocoaPods 1.12.1 и ниже, то у вас возникнет ошибка директорий, вроде такой:
Чтобы исправить это, добавьте следующий код в конец своего podfile:
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
if config.base_configuration_reference.is_a? Xcodeproj::Project::Object::PBXFileReference
xcconfig_path = config.base_configuration_reference.real_path
IO.write(xcconfig_path, IO.read(xcconfig_path).gsub("DT_TOOLCHAIN_DIR", "TOOLCHAIN_DIR"))
end
end
end
end
Возможно, в будущем, CocoaPods обновится, и этот код придется удалить, но в данный момент, он необходим.
Небольшой словарь терминов, перед тем как мы начнем:
Universal link (еще его называют Deeplink, но это не терминология Apple):
https://example.com/section
URL scheme:
example://section
Итак, вы можете приложить ссылку к пушу.
Однако, не все так просто. Внутри обработчика пуша лежит функция:
if let clickActionUrl = URL(string: "Ваша ссылка") {
UIApplication.shared.open(clickActionUrl, options: [:])
}
Простейшая логика. Однако, по какой-то причине, функция iOS для открытия ссылок, указанная выше, не распознает универсальную ссылку приложения, если она вызывается из этого же приложения. Это отправит пользователя прямо в браузер.
Поэтому, есть два возможных варианта решения проблемы:
- URL Scheme
- Ручная обработка Universal link
- URL Scheme
Если в вашем приложении настроены URL Scheme то все уже готово. Просто приложите нужную схему к пушу.
Далее прикладываю небольшой туториал по настройке URL Scheme.
URL Scheme - это более простой и надежный способ открыть нужную страницу в приложении, в отличии от Universal Link. Однако, они не выглядят как ссылка из интернета:
deeplink://test
Перейдем к настройке. Выберите цель в настройках проекта Xcode и перейдите на вкладку «Информация». Внизу страницы вы найдете раздел «URL Types».
Нажав +
, мы можем создать новый тип. В качестве идентификатора люди часто повторно используют пакет приложения. Для схем URL-адресов мы рекомендуем использовать название приложения (или сокращенное название), чтобы оно было как можно более кратким. В нем не должно быть никаких специальных символов. Мы будем использовать deeplink
в качестве примера.
Ваше приложение готово распознать URL схему, теперь нам нужно обработать его, когда мы его получим.
Приложение фиксирует открытие следующим образом в более ранних приложениях, в которых есть только AppDelegate
.
extension AppDelegate {
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any]) -> Bool {
print(url)
return true
}
}
Для новых приложений, включающих SceneDelegate, необходимо добавить обработчик еще и туда. Важно отметить, что метод AppDelegateне будет вызван, даже если вы его реализуете.
extension SceneDelegate {
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let firstUrl = URLContexts.first?.url else {
return
}
print(firstUrl.absoluteString)
}
}
Если хотите проверить ссылку, введите ее в браузере Safari. Так же, доступен вариант для быстрой проверки на симуляторе. Вот команда для терминала:
xcrun simctl openurl booted "deeplink://test"
- Ручная обработка Universal link
Вернитесь к обработчику кликов на пуши, и передайте аргумент false в параметр openLink:
CarrotNotificationService.shared.clickNotification(
notificationResponse: response,
openLink: false
)
Затем, нужно достать ссылку из объекта response, который пришел в пуше. Мы заранее подготовили для этого функцию:
let link: String? = CarrotNotificationService.shared.getLink(from: response)
Обратите внимание, что функция возвращает опционал, потому что пуш не всегда содержит ссылку.
Таким образом, в методе обработки кликов на пуши, у вас получится что-то такое:
import CarrotSDK
extension AppDelegate: UNUserNotificationCenterDelegate {
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let notificationService = CarrotNotificationService.shared
if notificationService.canHandle(response) {
notificationService.clickNotification(notificationResponse: response, openLink: false)
if let link = CarrotNotificationService.shared.getLink(from: response) {
print(link)
// Обработчик открытия Universal link
}
completionHandler()
}
}
Для отключения дебажных логов от встроенного в SDK moya, и от самого SDK, необходимо добавить специальный ключ в info.plist вашего проекта.
<key>moyaLog</key>
<string>0</string>
0 - логи выключены
1 - логи включены