はじめに
はじめまして。
今回は気軽にチャットによるカスタマーサポート(cs)機能を追加できるSaaS
Zendesk Chat を導入しiOSアプリでチャットができるようにする手順を記録していきます。
(Chat SDK v2 for iOSを利用しています)
zendeskは便利な反面、ios SDKのドキュメントが少ないのと
情報がいくつかの製品のドキュメントに跨っており、
必要な設定を見落としがちなので記事にまとめることにしました。
zendesk chatの説明
このようなチャットの画面を簡単に構築できるツールです。
詳細な料金表はこちら
無料含めて4プランあり、
push通知が$19/月のTeam以上、
営業時間設定が$35/月のProfessional以上になっています。
ある程度規模のあるプロダクトなら Professional プランが安心ですが、工夫次第では無料プランでも十分に活用できそうです。
大まかな手順
- SDK導入
- 初期化
- チャット画面の呼び出し
- エージェントがオフラインならチャットボタンを消す
- Push通知設定
SDK導入
公式ドキュメントに従って作業します。
SDKの導入はCocoaPods、Carthage、手動の3パターンありますが
私はCocoaPodsを利用しています。
これがPodfileです
target 'MyApp' do
# zendesk chatに必要
pod 'ZendeskChatSDK'
# zendesk chatで細かい挙動のフックをしたいときに必要
pod 'ZendeskChatProvidersSDK'
# push通知を飛ばすために必要だった Support
pod 'ZendeskSupportSDK'
ハマったのが、Push通知を飛ばすために
どうやらZendeskSupportSDKが必要になるということでした。
「SDKが用意してくれる基本のチャット画面を利用するだけ」という必要最低限の利用であれば
pod 'ZendeskChatSDK'
だけで動きます。
初期化
ZendeskSuportとChatを初期化していきます。
import ZendeskCoreSDK
import SupportProvidersSDK
import ChatProvidersSDK
import ChatSDK
Zendesk.initialize(appId: ZENDESK_APP_ID,
clientId: ZENDESK_CLIENT_ID,
zendeskUrl: ZENDESK_URL)
Support.initialize(withZendesk: Zendesk.instance)
Chat.initialize(accountKey: ZENDESK_ACCOUNT_KEY, appId: ZENDESK_CHAT_APP_ID)
Zendesk.initializeに渡すID群は zendeskの設定 > install modele SDK
に
記載されているのをコピペするだけなのですが、
ここで迷うのが Chat.initializeに渡す accountKey
と appId
って
どこに書いてあるんだ!ということです。
accountKey
はこちら、chat > 右上のアイコン > 接続を確認
で確認できます。
appId
はメソッドの定義を見ると appId: String? = nil
と、
appIdを指定し無くても実行できます。
無くても動くので、すっかりその存在を忘れてしまうのですが
これがないと、Push通知が届きません。
appId
は後半の Push通知についての箇所で明記するので
いったん Chat.initialize(accountKey: チャットアカウントキー)
だけで動かして
先に進んでしまっても問題ありません。
ユーザーの指定はjwtの認証を必要としない
匿名ユーザーで問題ありませんでした。
// 匿名ユーザーとして作成
let anonymous = Identity.createAnonymous()
Zendesk.instance?.setIdentity(anonymous)
これらを AppDelegate.swift
の didFinishLaunchingWithOptions
で初期化します。
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Zendesk.initialize(appId: ZENDESK_APP_ID,
clientId: ZENDESK_CLIENT_ID,
zendeskUrl: ZENDESK_URL)
Support.initialize(withZendesk: Zendesk.instance)
Chat.initialize(accountKey: ZENDESK_ACCOUNT_KEY, appId: ZENDESK_CHAT_APP_ID)
let anonymous = Identity.createAnonymous()
Zendesk.instance?.setIdentity(anonymous)
}
わかりやすいようにベタで書いていますが、
当然メソッドに切り出すなりクラスに切り出すなりしてご使用ください。笑
チャット画面の呼び出し
公式ドキュメントだとこの辺りです。
// 「チャット画面を開くボタン」のタップアクション
@IBAction func tapStartChat(_ sender: Any) {
try! startChat()
}
// チャット画面への遷移(ViewContollerを生成してモーダル遷移)
func startChat() throws {
let chatViewController = trybuildChatUI()
let button = UIBarButtonItem(title: "閉じる", style: .plain, target: self, action: #selector(dismissNavigationController))
chatViewController.navigationItem.leftBarButtonItem = button
let modalController = UINavigationController(rootViewController: chatViewController)
present(modalController, animated: true)
}
// チャット画面の生成
private func buildChatUI() throws -> UIViewController {
let messagingConfiguration = getMessagingConfiguration()
let chatConfiguration = getChatConfigure()
// Build view controller
let chatEngine = try ChatEngine.engine()
return try Messaging.instance.buildUI(engines: [chatEngine], configs: [messagingConfiguration, chatConfiguration])
// ちなみにenginesにはChatEngineだけでなく、
// メッセージを残してメールでやり取りをしたりFAQを用意するSupportEngine、
// Botが自動でよしなに返信してくれるAnswerBotEngineがあります。
}
// チャットBotの表示の設定
private func getMessagingConfiguration() -> MessagingConfiguration {
let messagingConfiguration = MessagingConfiguration()
messagingConfiguration.name = "チャット画面に表示されるBotの名前"
return messagingConfiguration
}
// チャットの詳細設定
private func getChatConfigure() -> ChatConfiguration {
// isPreChatFormEnabled = trueだと
// 画面を開いたときに「お問い合わせありがとうございます。」とチャットが自動でメッセージを送信し
// ユーザーに対して、名前やメールアドレス、電話番号などを質問してくれる
let chatConfiguration = ChatConfiguration()
chatConfiguration.isPreChatFormEnabled = true
// isPreChatでどの情報をユーザーに聞くか
let formConfiguration = ChatFormConfiguration(name: .optional, email: .optional, phoneNumber: .hidden, department: .hidden)
chatConfiguration.preChatFormConfiguration = formConfiguration
return chatConfiguration
}
@objc private func dismissNavigationController() {
self.navigationController?.dismiss(animated: true, completion: nil)
}
これでチャット画面を開き、
エージェントとチャットをすることができるようになりました。
エージェントがオフラインならチャットボタンを消す
エージェントがオフラインだと
チャットを開始すると自動で 「オンライン中のエージェントがおりません」
とBotが回答してくれます。
とはいえ、この辺の自動回答メッセージがカスタマイズできない(多分)こともあり、
エージェントがいないときはボタン自体をdisabledにしたい!という場合は
ChatProvidersSDK
の機能を使って実装していきます。
公式ドキュメントだとこの辺り
Chat.accountProvider?.getAccount { (result) in
switch result {
case .success(let account):
switch account.accountStatus {
case .online:
// オンラインの場合、ボタンをenabledに
chatButton.isEnabled = true
default:
// オンライン以外の場合、ボタンをdisabledに
chatButton.isEnabled = false
}
case .failure(let error):
// エラーが返ってきたときもボタンをdisabledに
chatButton.isEnabled = false
}
}
Push通知設定
いよいよ Push通知設定です。
Push通知は、エージェントがユーザーに向けて返信をした場合と
エージェントがチャットを終了させた場合に発火します。
- pemの作成
- zendesk chatの管理画面にpemをアップロード
- pem登録時に作成されるアプリIDをChat.initialize()に渡す
- push通知の実装
この流れでpush通知が利用できるようになります。
pemの作成
参考になるのはこちらの記事です。
ご自身のApple Developerのページから作業します。
Production SSL Certificate をCreate Certificate します
すると.p12の証明書ファイルがDLできるのでこれからpemを作成します。
先程の公式ドキュメントに
Save the .p12 file to your computer. Leave the password empty.
と書いてあるのでpasswordは空で設定します。
ターミナルの作業
> openssl pkcs12 -clcerts -nodes -in ~/Desktop/証明書.p12 -out myapp.pem
Enter Import Password: # 空でEnter
MAC verified OK
pemが正しく生成されたのかテストを実行
> openssl s_client -connect gateway.push.apple.com:2195 -cert myapp.pem -keymyapp.pem
~~~大量の文字列~~~
New, TLSv1/SSLv3, Cipher is DES-CBC3-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : DES-CBC3-SHA
Session-ID:
Session-ID-ctx:
Master-Key:
Start Time: 1602554889
Timeout : 7200 (sec)
Verify return code: 0 (ok)
最後が Verify return code: 0 (ok)
になっていれば成功です。
zendesk chatの管理画面にpemをアップロード
chat管理画面 > 設定 > アカウント > モバイルSDK > アプリを追加
でpemを登録すると「アプリが作成されました」
とアプリIDが生成されます。
pem登録時に作成されるアプリIDをChat.initialize()に渡す
最初の初期化のときのこれ↓です
Chat.initialize(accountKey: ZENDESK_ACCOUNT_KEY, appId: ここ!!)
これに気が付かず半日潰した。。。
push通知の実装
公式ドキュメントはこちら
一般的にiosアプリでpush通知を利用するときと同様の設定も多く含みます。
・push通知の許可依頼を表示させ
import UserNotifications
let notificationCenter = UNUserNotificationCenter.current()
notificationCenter.delegate = self
notificationCenter.requestAuthorization(options: [.alert, .sound, .badge]) { granted, _ in
guard granted else { return }
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
・didRegisterForRemoteNotificationsWithDeviceTokenでトークンを登録
import ChatProvidersSDK
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Chat.registerPushToken(deviceToken)
}
・ push通知をタップしたときの挙動
import ChatProvidersSDK
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
let userInfo = response.notification.request.content.userInfo
let application = UIApplication.shared
Chat.didReceiveRemoteNotification(userInfo, in: application)
completionHandler()
}
・アプリがフォアグラウンドでもpush通知を受信
import UserNotifications
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.alert, .sound, .badge])
}
まとめ
- チャット画面を開いて、エージェントとチャットができた
- エージェントのオンライン状態をアプリ側でフックできた
- チャットが来たときに Push通知を受け取ることができた