Help us understand the problem. What is going on with this article?

Zendesk Chat SDK を使ってiOSアプリにカスタマーサポートチャットを1時間で実装する

はじめに

はじめまして。
今回は気軽にチャットによるカスタマーサポート(cs)機能を追加できるSaaS
Zendesk Chat を導入しiOSアプリでチャットができるようにする手順を記録していきます。
Chat SDK v2 for iOSを利用しています)

zendeskは便利な反面、ios SDKのドキュメントが少ないのと
情報がいくつかの製品のドキュメントに跨っており、
必要な設定を見落としがちなので記事にまとめることにしました。

zendesk chatの説明

このようなチャットの画面を簡単に構築できるツールです。

料金形態は↓こんな感じ
image.png

詳細な料金表はこちら

無料含めて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に渡す accountKeyappId って
どこに書いてあるんだ!ということです。

accountKeyはこちら、chat > 右上のアイコン > 接続を確認 で確認できます。

appId はメソッドの定義を見ると appId: String? = nil と、
appIdを指定し無くても実行できます。

無くても動くので、すっかりその存在を忘れてしまうのですが
これがないと、Push通知が届きません。

appIdは後半の Push通知についての箇所で明記するので
いったん Chat.initialize(accountKey: チャットアカウントキー) だけで動かして
先に進んでしまっても問題ありません。

ユーザーの指定はjwtの認証を必要としない
匿名ユーザーで問題ありませんでした。

// 匿名ユーザーとして作成
let anonymous = Identity.createAnonymous()
Zendesk.instance?.setIdentity(anonymous)

これらを AppDelegate.swiftdidFinishLaunchingWithOptions で初期化します。

AppDelegate.swift
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のページから作業します。

push通知の設定のEditをクリックし、

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が生成されます。

スクリーンショット 2020-10-21 16.57.15.jpg

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でトークンを登録

AppDelegate.swift
import ChatProvidersSDK

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    Chat.registerPushToken(deviceToken)
}

・ push通知をタップしたときの挙動

AppDelegate.swift
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通知を受信

AppDelegate.swift
import UserNotifications

func userNotificationCenter(_ center: UNUserNotificationCenter,
                           willPresent notification: UNNotification,
                           withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
   completionHandler([.alert, .sound, .badge])
}

これでエージェントが返信をするとpush通知が届きます!

まとめ

  • チャット画面を開いて、エージェントとチャットができた
  • エージェントのオンライン状態をアプリ側でフックできた
  • チャットが来たときに Push通知を受け取ることができた
s_higeru
eversense
家族を幸せにすることで、笑顔溢れる社会をつくる。
https://eversense.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away