Skip to content

Conversational platform for business. iOS sdk.

Notifications You must be signed in to change notification settings

carrotquest/ios-sdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Carrot quest для iOS

VersionSwiftPM compatible

Содержание

Установка

На данный момент Carrot quest для iOS можно установить с помощью CocoaPod и Swift Package Manager.

CocoaPods

Добавьте следующую строчку в pod файл:

pod 'CarrotquestSDK'

Swift Package Manager

В 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. Вы можете найти эти ключи на вкладке "Настройки > Разработчикам": Разработчикам

Swift

Инициализация

Для инициализации 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 генерируемый у вас на бэке.

  1. Вход через 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)
        })
  1. Вход через 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)

Чат с оператором

Вы можете дать пользователю мобильного приложения возможность перейти в чат с оператором из любого места. Это можно реализовать двумя разными путями - через плавающую кнопку, либо напрямую вызвав метод открытия чата в любое нужное время.

Плавающая кнопка (Floating Button)

Виджет предоставляющий быстрый доступ к чату. Добавить кнопку можно с помощью следующего метода:

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()
    }
}

Objective-C

Инициализация

Для инициализации 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 генерируемый у вас на бэке.

  1. Вход через 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);
}];
  1. Вход через 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"];

Чат с оператором

Вы можете дать пользователю мобильного приложения возможность перейти в чат с оператором из любого места. Это можно реализовать двумя разными путями - через плавающую кнопку, либо напрямую вызвав метод открытия чата в любое нужное время.

Плавающая кнопка (Floating Button)

Виджет предоставляющий быстрый доступ к чату. Добавить кнопку можно с помощью следующего метода:

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 можно почитать тут.

Важная информация о Push уведомлениях

Необходимо добавить в 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:

AppGroup

  1. В списке файлов выберите свой проект.

  2. В списке targets выберете пункт с именем вашего проекта.

  3. Во вкладке "Singing & Capabitities" нажмите на "+ Capability".

  4. В выпадающем списке найдите найдите и выберите App Group.

  5. На вкладке появится пустой список для идентификаторов App Group. Добавте туда Identifier, который зарегистрировали в Apple Developer Portal ранее.

  6. Вернитесь к списку 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

Если вы используете 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

Итак, вы можете приложить ссылку к пушу.

PushLink

Однако, не все так просто. Внутри обработчика пуша лежит функция:

if let clickActionUrl = URL(string: "Ваша ссылка") {
		UIApplication.shared.open(clickActionUrl, options: [:])
}

Простейшая логика. Однако, по какой-то причине, функция iOS для открытия ссылок, указанная выше, не распознает универсальную ссылку приложения, если она вызывается из этого же приложения. Это отправит пользователя прямо в браузер.

Поэтому, есть два возможных варианта решения проблемы:

  1. URL Scheme
  2. Ручная обработка Universal link

  1. URL Scheme

Если в вашем приложении настроены URL Scheme то все уже готово. Просто приложите нужную схему к пушу.

Далее прикладываю небольшой туториал по настройке URL Scheme.

URL Scheme - это более простой и надежный способ открыть нужную страницу в приложении, в отличии от Universal Link. Однако, они не выглядят как ссылка из интернета:

deeplink://test

Перейдем к настройке. Выберите цель в настройках проекта Xcode и перейдите на вкладку «Информация». Внизу страницы вы найдете раздел «URL Types».

URL_scheme

Нажав +, мы можем создать новый тип. В качестве идентификатора люди часто повторно используют пакет приложения. Для схем 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"
  1. Ручная обработка 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 - логи включены