Перейти к содержанию

Интеграция в мобильные приложения iOS (v2)

В данной статье представлена информация об интеграции Webim Mobile SDK версии 2 в ваше iOS-приложение.

Внимание!

Данная версия SDK устарела. Техническая поддержка клиентов, использующих эту версию SDK, более не осуществляется!

Пример приложения: WebimClient

Нами предлагается пример приложения, написанный на Objective-C с использованием библиотеки Webim Mobile SDK для iOS. Это приложение позволяет открыть чат между пользователем телефона и оператором.

Исходный код (GitHub)

Адрес административного раздела https://demo.webim.ru

Приложение работает под учётной записью demo.


Инструкция по добавлению Webim Mobile SDK в проект iOS

WebimClientLibrary доступен с помощью технологии CocoaPods (репозиторий). Чтобы установить SDK необходимо добавить в ваш Podfile:

pod 'WebimClientLibrary', :git => 'https://github.com/webim/webim-client-sdk-ios.git', :branch => 'version2', :tag => '2.8.1'
(Номер версии со временем может меняться.)

Если у вас не установлен CocoaPods, сначала необходимо выполнить команду:

sudo gem install cocoapods

Структура

Webim Mobile SDK предоставляют простой и удобный интерфейс для работы с сервисом Webim. Все API сосредоточены в классе WMSession, который через механизм делегирования оповещает об изменении состояния чата. Для того, чтобы начать работать с чатом, необходимо инициализировать объект сессии и выставить ему делегата. Делегатом может быть любой контроллер, который будет управлять отображаемыми видами и следить за изменением состояния сессии. Делегат должен следовать протоколу WMSessionDelegate, описанному в файле WMSession.h. Кроме выбора делегата необходимо определиться с размещением и учётной записью в сервисе Webim. Объект WMSession работает только с одним выбранным размещением. Сессия инициализируется методом:

- (id)initWithAccountName:(NSString *)accountName
                 location:(NSString *)location
                 delegate:(id)delegate
             visitorFields:(NSDictionary *)visitorFields;

Параметр accountName — это имя аккаунта в системе Webim. В случае использования сервиса на своем домене, вместо аккаунта нужно предать полный URL до API сервиса. Необязательный параметр visitorFields используется для идентификации пользователя. Значения словаря должны задаваться в соответствии с правилами идентификации, в чем могут помочь следующие константы, объявленные в WMSession.h:

extern NSString *const WMVisitorParameterDisplayName;
extern NSString *const WMVisitorParameterPhone;
extern NSString *const WMVisitorParameterEmail;
extern NSString *const WMVisitorParameterICQ;
extern NSString *const WMVisitorParameterProfileURL;
extern NSString *const WMVisitorParameterAvatarURL;
extern NSString *const WMVisitorParameterID;
extern NSString *const WMVisitorParameterLogin;
extern NSString *const WMVisitorParameterCRC

Онлайн сессия использует определенный набор асинхронных методов, имеющих блок завершения, описанный следующим типом:

typedef void (^WMResponseCompletionBlock)(BOOL successful);

Начало работы

Для начала необходимо начать сессию методом:

(void)startSession:(WMResponseCompletionBlock)block;

При успешном старте сессии на делегате вызовется метод

(void)sessionDidReceiveFullUpdate:(WMSession *)session;

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

Чтобы остановить сессию, необходимо вызывать метод

(void)stopSession;

После вызова данного метода отключается обновление данных и вызовы методов делегата. Внутренние данные сессии, такие как состояние, чат, оператор, — могут измениться по ходу работы. Для этого необходимо анализировать изменения, присылаемые на делегата. Например, можно анализировать состояние сессии методами делегата:

(void)sessionDidChangeStatus:(WMSession *)session;
— (void)session:(WMSession *)session didChangeOnlineStatus:(WMSessionOnlineStatus)onlineStatus;
— (void)session:(WMSession *)session didChangeOperatorTyping:(BOOL)typing;

Для получения push-уведомлений необходимо передать push-токен устройства, полученный от APNs, методом класса:

+ (void)setDeviceToken:(NSData *)deviceToken;
— (void)setDeviceToken:(NSData *)deviceToken completion:(WMResponseCompletionBlock)block;

+ (void)setDeviceTokenString:(NSString *)token;

Вызов чата

Для использования чата интересно только одно состояние сессии - WMSessionStateOfflineMessage. Выставление состояния сессии в этот флаг означает, что не осталось операторов в онлайн и чат будет завершён. При первом запуске чат скорее всего не будет инициализирован, то есть session.chat == nil или session.chat.state == WMChatStateUnknown. Однако при повторной инициализации чат уже/ещё может существовать, если это так, то необходимо отобразить этот чат. Он уже будет содержать сообщения. Объект WMChat имеет несколько состояний, которые важны для определения необходимости отображать этот чат пользователю.

typedef enum {
WMChatStateUnknown,
WMChatStateQueue,
WMChatStateChatting,
WMChatStateClosed,
WMChatStateClosedByVisitor,
WMChatStateClosedByOperator,
WMChatStateInvitation, } WMChatState;

Если чат существует и находится в одном из состояний WMChatStateQueue, WMChatStateChatting или WMChatStateClosedByOperator, то его необходимо отобразить пользователю. Если на делегате вызывается один из методов об изменении состояния чата, то при изменении состояния на любое другое — чат должен закрыться. Обращения к такому чату (отправка сообщений, закрытие) будут возвращать ошибку. Перед началом чата необходимо убедиться в наличии операторов онлайн. Это можно узнать, вызвав метод WMSessionOnlineStatus session.onlineStatus. Статус WMSessionOnlineStatusOnline говорит о том, что возможен онлайн-чат. Статус WMSessionOnlineStatusBusyOnline говорит о том, что очередь онлайн-чатов закончена. Аналогично статус WMSessionOnlineStatusOffline — доступны оффлайн-обращения и WMSessionOnlineStatusBusyOffline — оффлайн-очередь переполнена.

Если чата не существует, то его можно инициировать методом сессии:

- (NSString *)startChat:(WMResponseCompletionBlock)block;
- (NSString *)startChatWithClientSideId:(NSString *)clientSideId
                        completionBlock:(WMResponseCompletionBlock)completionBlock;

Для идентификации чата локально, можно использовать так называемые clientSideId — это уникальная строка, с помощью которой клиентская сторона может сопоставить начатый чат и тот, что пришёл на делегате. Оба метода возвращают этот идентификатор. Метод startChatWithClientSideId также принимает clientSideId, сгенерированный внешним алгоритмом.

Если вызов одного из этих методов будет удачным, то на делегате вызовется один из методов, в зависимости от текущего состояния чата:

(void)session:(WMSession *)session didStartChat:(WMChat *)chat;
— (void)sessionDidChangeChatStatus:(WMSession *)session;

Использование чата

Для закрытия чата вызывается метод:

(void)closeChat:(WMResponseCompletionBlock)block;

который аналогичным открытию чата образом влияет на делегата и состояние сессии и чата. Инициированный чат содержит указатель на массив сообщений, принадлежащих этому чату. Каждое из сообщений имеет тип, описанный в WMMessage.h. Условно они делятся на три типа:

  • сообщения от посетителя

  • от оператора

  • системные сообщения

Для отправки сообщения от пользователя необходимо вызвать метод объекта сессии, полная версия которого выглядит следующим образом:

- (NSString *)sendMessage:(NSString *)message
         withClientSideId:(NSString *)clientSideId
             successBlock:(void (^)(NSString *clientSideId))successBlock
             failureBlock:(void (^)(NSString *clientSideId, WMSessionError error))failureBlock;
  • message — текст сообщения

  • clientSideId — уникальный клиентский идентификатор сообщения

  • successBlock — блок, вызываемый при успешном добавлении сообщения в очередь на сервере

  • failureBlock — блок, вызываемый при ошибке отправки. В параметрах будет содержать уникальный идентификатор и ошибку

Метод возвращает в место вызова clientSideId, сгенерированный SDK или алгоритмом пользователя.

Реакцией на успешную доставку сообщения, также как и на появления системного или операторского сообщения, будет вызов на делегате метода:

(void)session:(WMSession *)session didReceiveMessage:(WMMessage *)message;

При работе с сообщениями оператора в чатах реального времени также доступны данные оператора: в экземпляре класса WMMessage доступны поля senderUID — уникальный идентификатора оператора, senderName — имя оператора и senderAvatarURL — ссылка для скачивания фото профиля оператора. Аналогично предыдущему работает метод отправки файлов:

- (NSString *)sendFile:(NSData *)fileData
                  name:(NSString *)fileName
              mimeType:(NSString *)mimeType
      withClientSideId:(NSString *)clientSideId
          successBlock:(void (^)(NSString *clientSideId))succcessBlock
          failureBlock:(void(^)(NSString *clientSideId, WMSessionError error))failureBlock;

При пересылке необходимо указать тип вложения — mime type и соответствующее ему расширение у имени файла. Так, для изображений необходимо указать, например, filename = «image.png» и mimeType = «image/png». В случае файловых сообщений на делегате будет WMMessage, содержащий поле WMFileParams *fileParams. Данное поле имеет описания вложения, такие как размер, имя и тип содержимого (contentType). Для тех сообщений, которые имеют вложение-изображение, будут содержаться данные в fileParams.imageParams. WMImageParams *imageParams описывают размер изображения, доступного к скачиванию.

Для сообщений типа WMMessageKindFileFromOperator и WMMessageKindFileFromVisitor содержимое можно загрузить по соответствующей ссылке, полученной вызовом метода

(NSURL *)attachmentURLForMessage:(WMMessage *)message;

В чате реального времени возможно задание состояния чата «посетитель начал набирать сообщение». Выставить это состояние можно методом

(void)setComposingMessage:(BOOL)isComposing draft:(NSString *)draft;

После вызова метода с параметром isComposing YES, необходимо вызывать его же со значением NO, чтобы в Панели управления отображалось правильное состояние сессии. При этом возможно передать текст, который печатает посетитель, заполнив параметр draft. Пустое значение draft удаляет хранимый черновик.

Если объект чата инициализирован, то он может содержать ссылку на объект оператора. В случае, если оператор меняется или берёт посетителя в обработку, на делегате вызывается метод

(void)session:(WMSession *)session didUpdateOperator:(WMOperator *)chatOperator

По объекту WMOperator можно определить имя оператора и путь для загрузки его фото профиля. Оператор для чата в истории сообщений может быть не определен, так как в процессе работы возможна смена операторов, отвечающих в чате. Операторы чата могут быть найдены по сообщениям в чате — поля WMMessage::SenderUID, SenderName и SenderAvatarURL в полученных сообщениях.

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

(void)refreshSessionWithCompletionBlock:(WMResponseCompletionBlock)block;
который принимает параметром блок, который исполнится после выполнения запроса обновления. Для отслеживания ошибок при работе с чатом необходимо на делегате имплементировать метод протокола:

(void)session:(WMSession *)session didReceiveError:(WMSessionError)errorID;

который возвращает номер ошибки из перечисления WMSessionError. Если на устройстве нет сети, то при вызове методов API на делегата будет возвращаться константа WMSessionErrorNetworkError. При смене режимов работы сети библиотека сама возобновляет соединение с сервером. При этом на делегате будет вызываться опциональный метод:

(void)session:(WMSession *)session didChangeConnectionStatus:(WMSessionConnectionStatus)status;

Ожидаемая реакция приложения на переключение статуса сети — оповещение пользователя и, по необходимости, либо ограничение доступа к чату (вид блокирующей деятельности), либо перехват соответствующих методов для дальнейшего восстановления данных.

Для оценки оператора по пятибалльной шкале нужно использовать метод

(void)rateOperator:(NSString *)authorID withRate:(WMOperatorRate)rate completion:(WMResponseCompletionBlock)block;

Аргументами передается authorID из сообщения оператора и rate, выбранное из одного из перечисления WMOperatorRate.

enum WMOperatorRate {
WMOperatorRateOneStar,
WMOperatorRateTwoStars,
WMOperatorRateThreeStars,
WMOperatorRateFourStars,
WMOperatorRateFiveStars
};

Оффлайн-обращения

Обращения к оператору могут происходить в режиме «оффлайн». В этом режиме задержка ответа не критична, но важно время хранения и способ обращения с чатами на стороне сервера. За работу с таким типом чатов отвечает класс SDK WMOfflineSession. Как и для обычных чатов, необходимо инициализировать его перед вызовом любых других методов работы с классом.

(id)initWithAccountName:(NSString *)accountName location:(NSString *)location token:(NSString *)token platform:(NSString *)platform visitorFields:(NSDictionary *)visitorFields;

Параметр token является обязательным, он однозначно определяет пользователя системы, то есть, является уникальным. Более того, токен не должен меняться от запуска приложения к запуску, иначе произойдет потеря данных пользователя. При использовании iOS push-токена, сервер Webim будет уведомлять приложение через стандартный механизм push-уведомлений.

Параметр platform, если установлен, используется для определения сервером Webim механизма отсылки уведомлений — использование пользовательского сервера или стандартного механизма iOS push-уведомлений. Для использования этого параметра необходимо связаться со службой поддержки Webim.

Данные о чатах хранятся в памяти и доступны через свойство класса:

@property (nonatomic, strong) NSMutableArray *appealsArray; Данный массив хранит объекты типа WMChat. Каждый чат имеет ссылку на относящиеся к нему сообщения. В общем случае чаты и сообщения отсортированы по дате создания.

Для уменьшения объёма трафика объекты SDK сохраняют своё состояние в файле на диске. Предполагается, что клиентское приложение будет периодически опрашивать сервер на предмет появления новых сообщений в чатах. Это осуществляется методом класса WMOfflineMessage:

(void)getHistoryForced:(BOOL)forced completion:(void (^)(BOOL successful, id changes, NSError *error))block;

Флаг forced заставляет SDK заново запросить всю историю. Не рекомендуется злоупотреблять этим флагом из-за увеличения объёма трафика. Параметр block вызывается при завершении запроса. Здесь и далее используется блок, вызываемый при завершении запроса. Он имеет следующую структуру:

  • флаг, сигнализирующий успех запроса

  • один или несколько объектов, значения которых имеют значение при успешном завершении метода

  • объект ошибки, действительный только при неуспешном завершении метода

Ошибка может быть нескольких типов — ошибки домена Webim определяются константой WMWebimErrorDomain. В случае такой ошибки имеет место её код — он будет одним из значений перечисления WMSessionError из заголовочного файла WMBaseSession.h. Остальные ошибки могут быть сетевыми NSURLConnection или критическими, наподобие ошибки сервера - 500 Internal Server Error. Возвращаясь к методу запроса истории: параметр changes блока содержит в себе информацию о модификациях с предыдущего запроса. По сути - это словарь, значения которого достаются ключами, описанными в заголовочном файле WMOfflineSession:

extern NSString *const WMOfflineChatChangesNewChatsKey; extern NSString *const WMOfflineChatChangesModifiedChatsKey; extern NSString *const WMOfflineChatChangesMessagesKey;

С помощью данных ключей можно определить, какие чаты были добавлены, изменены и какие сообщения были добавлены в эти чаты. Для связи объектов WMMessage и WMChat можно использовать метод класса WMOfflineSession:

(WMChat *)chatForMessage:(WMMessage *)message;

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

- (void)sendMessage:(NSString *)text
             inChat:(WMChat *)chat
      departmentKey:(NSString *)departmentKey
        onDataBlock:(void (^)(BOOL successful, WMChat *chat, WMMessage *message, NSError *error))block
         completion:(void (^)(BOOL successful))completion;

Параметр text является обязательным. Если параметр chat является пустым (передается nil), то метод создаёт новый чат с одним новым сообщением. Если параметр chat передан, то создастся новое сообщение в этом чате. Параметр departmentKey назначает отдел новому чату, может быть пустым. Блок onDataBlock вызывается сразу после ответа сервера и содержит объекты WMChat и WMMessage — новые объекты сообщения и чата, если было запрошено создание нового чата. Блок completion вызывается после записи новых данных в хранилище. Аналогично работает метод отправки изображения:

- (void)sendImage:(NSData *)imageData
             type:(WMChatAttachmentImageType)type
           inChat:(WMChat *)chat
    departmentKey:(NSString *)departmentKey
      onDataBlock:(void (^)(BOOL successful, WMChat *chat, WMMessage *message, NSError *error))block
       completion:(void (^)(BOOL successful))completion;

В параметр imageData должно передаваться изображение в формате png или jpeg. Тип изображения при этом предается в параметр type.

- (void)sendFile:(NSData *)fileData
            name:(NSString *)fileName
        mimeType:(NSString *)mimeType
          inChat:(WMChat *)chat
   departmentKey:(NSString *)departmentKey
     onDataBlock:(void (^)(BOOL successful, WMChat *chat, WMMessage *message, NSError *error))block
      completion:(void (^)(BOOL successful))completion;

Сообщения WMMessage, созданные данным методом будут иметь тип WMMessageKindFileFromVisitor. SDK предоставляет способ загрузки одного изображения через объект сессии:

(void)dowloadImageForMessage:(WMMessage *)message completion:(void (^)(BOOL successful, UIImage *image, NSError *error))block;
Однако получить URL файла можно вызвав метод

(NSURL *)attachmentURLForMessage:(WMMessage *)message;

SDK не хранит прикрепленные к чату файлы. Управление загрузкой и отображением целиком ложится на пользовательское приложение. Для избежания регулярных скачиваний рекомендуется использовать кэш изображений — в памяти и/или на диске. При появлении в истории ответа от оператора, соответствующий чат помечается как непрочитанный. Поэтому, после отображения чата пользователю, необходимо пометить его прочитанным методом класса сессии:

(void)markChatAsRead:(WMChat *)chat completion:(void (^)(BOOL successful, NSError *error))block;

Удаление чата осуществляется с помощью:

(void)deleteChat:(WMChat *)chat completion:(void (^)(BOOL successful, NSError *error))block;

Смена пользователя

В текущей реализации активной может быть только одна оффлайн-сессия, поэтому для того чтобы получить данные другого пользователя необходимо:

  1. Перестать запрашивать историю для сессии А

  2. Отключить слежение за сессией А. Например, для данных в appealsArray.

  3. Cделать очистку данных (WMSession clearCachedUserData) и освободить сессию А.

  4. Создать сессию B с другими желаемыми параметрами.

  5. Запросить историю сессии В.