BoostAI 1.1.20

BoostAI 1.1.20

Bjørnar Tollaksen 维护。



BoostAI 1.1.20

  • boost.ai

BoostAI SDK

目录

授权协议

任何希望使用 SDK 的 Boost AI 客户将获得商业授权。

安装

CocoaPods

CocoaPods是一个适用于Cocoa项目的依赖管理器。有关使用和安装说明,请访问他们的网站。要使用CocoaPods将BoostAI集成到您的Xcode项目中,请在Podfile中指定它

pod 'BoostAI', '~> 1.1.8'

Carthage

Carthage是一个分散式依赖管理器,构建您的依赖项并提供二进制框架。要使用Carthage将BoostAI集成到您的Xcode项目中,请在Cartfile中指定它

github "BoostAI/mobile-sdk-ios" ~> 1.1.8

前端/UI

该UI库开发有两个主要目标

  1. 使其适用于常规用例的“即插即用”
  2. 通过公开可配置的变量并允许子类化来使其易于扩展和定制

查看 iOS 示例项目,了解如何通过浮动头像或位于标签栏标签下的聊天视图设置聊天视图。

ChatBackend

首先,我们需要一个ChatBackend类的实例。如果您只想在应用程序中有一个会话,可以使用ChatBackend.shared静态变量;如果您想在应用程序的多个/不同位置进行多个/独立的会话,可以创建多个ChatBackend实例。

let backend = ChatBackend.shared
backend.domain = "sdk.boost.ai"
backend.languageCode = "en-US" // Default value – will potentially be overriden by the backend config

配置

几乎所有颜色、字符串和其他自定义项都可以在从服务器获取的Config对象中找到。可以通过ChatBackend.config属性在任何时间点访问配置对象。在我们显示聊天视图之前,我们应该等待配置准备好。这可以通过调用带有回调的ChatBackend.onReady(completion: @escaping (ChatConfig?, Error?) -> Void)来完成。

如果想要在本地覆盖一些配置变量,可以将自定义的ChatConfig对象传递给ChatViewControllerAgentAvatarView

let customConfig = ChatConfig(
    chatPanel: ChatPanel(
        header: Header(title: "Testing..."),
        styling: Styling(
            primaryColor: .red,
            contrastColor: .blue,
            chatBubbles: ChatBubbles(
                userBackgroundColor: .brown,
                userTextColor: .purple,
                vaBackgroundColor: .green,
                vaTextColor: .yellow),
            buttons: Buttons(
                multiline: true,
                backgroundColor: .cyan,
                textColor: .magenta
            )
        ),
        settings: Settings(
            showLinkClickAsChatBubble: true
        )
    )
)

let vc = ChatViewController(backend: backend, customConfig: customConfig)
let navController = UINavigationController(rootViewController: vc)

present(navController, animated: true, completion: nil)

有关可用覆盖选项的全面概述,请参阅聊天面板JavaScript文档的“配置聊天窗口”>“选项”章节。

字体

默认情况下,UI使用UIFont.preferredFont(forTextStyle: ...)方法获取不同用途的字体。传递一个自定义字体来自定义字体和/或字体大小。支持的字体(带默认值)

/// Font used for body text
public var bodyFont: UIFont = UIFont.preferredFont(forTextStyle: .body)

/// Font used for headlines
public var headlineFont: UIFont = UIFont.preferredFont(forTextStyle: .headline)

/// Font used for menu titles
public var menuItemFont: UIFont = UIFont.preferredFont(forTextStyle: .title3)

/// Font used for footnote sized strings (status messages, character count text etc.)
public var footnoteFont: UIFont = UIFont.preferredFont(forTextStyle: .footnote)

请注意,这些不属于JavaScript规范的一部分。

在自定义聊天配置上设置它们,并将其传递给ChatViewController

let customConfig = ChatConfig(
    chatPanel: ChatPanel(
        styling: Styling(
            fonts: Fonts(
                bodyFont: // My custom body font here
            )
        )
    )
)

配置概述

以下是对可用的 ChatConfig 对象属性及其对应类型的全面概述(请注意,这不是可运行的代码)

ChatConfig(
    messages: Languages?,
    chatPanel: ChatPanel(
        styling: Styling(
            pace: ConversationPace?,
            avatarShape: AvatarShape?,
            primaryColor: UIColor?,
            contrastColor: UIColor?,
            panelBackgroundColor: UIColor?,
            panelScrollbarStyle: UIScrollView.IndicatorStyle?,
            chatBubbles: ChatBubbles(
                userBackgroundColor: UIColor?,
                userTextColor: UIColor?,
                vaBackgroundColor: UIColor?,
                vaTextColor: UIColor?,
                typingDotColor: UIColor?,
                typingBackgroundColor: UIColor?
            ),
            buttons: Buttons(
                backgroundColor: UIColor?,
                textColor: UIColor?,
                focusBackgroundColor: UIColor?,
                focusOutlineColor: UIColor?,
                variant: ButtonType?,
                multiline: Bool?
            ),
            composer: Composer(
                hide: Bool?,
                composeLengthColor: UIColor?,
                frameBackgroundColor: UIColor?,
                sendButtonColor: UIColor?,
                sendButtonDisabledColor: UIColor?,
                textareaBackgroundColor: UIColor?,
                textareaBorderColor: UIColor?,
                textareaFocusBorderColor: UIColor?,
                textareaFocusOutlineColor: UIColor?,
                textareaPlaceholderTextColor: UIColor?,
                textareaTextColor: UIColor?,
                topBorderColor: UIColor?,
                topBorderFocusColor: UIColor?
            ),
            messageFeedback: MessageFeedback(
                hide: Bool?,
                outlineColor: UIColor?,
                selectedColor: UIColor?
            ),
            fonts: Fonts(
                bodyFont: UIFont?,
                headlineFont: UIFont?,
                footnoteFont: UIFont?,
                menuItemFont: UIFont?
            )
        ),
        settings: Settings(
            authStartTriggerActionId: Int?,
            contextTopicIntentId: Int?,
            conversationId: String?,
            customPayload: String?,
            fileUploadServiceEndpointUrl: String?,
            messageFeedbackOnFirstAction: Bool?,
            rememberConversation: Bool?,
            requestFeedback: Bool?,
            showLinkClickAsChatBubble: Bool?,
            skill: String?,
            startLanguage: String?,
            startNewConversationOnResumeFailure: Bool?,
            startTriggerActionId: Int?,
            userToken: String?
        )
    )
)

显示聊天

浮动头像

要设置一个浮动头像,你可以执行类似下面的操作

backend.onReady { [weak self] (_, _) in
    // The backend has received a config and is now ready
    DispatchQueue.main.async {
        guard let self = self else { return }

        let avatarView = AgentAvatarView(backend: self.backend)
        avatarView.translatesAutoresizingMaskIntoConstraints = false

        self.view.addSubview(avatarView)

        let constraints = [
            self.view.trailingAnchor.constraint(equalTo: avatarView.trailingAnchor, constant: 20),
            self.view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: avatarView.bottomAnchor, constant: 20)
        ]

        NSLayoutConstraint.activate(constraints)
    }
}

AgentAvatarView 类处理图标上的点击并在默认情况下显示 ChatViewController。你可以通过传递一个 UIImageavatarImage 属性来配置 AgentAvatarView,并且可以选择性地覆盖默认的 avatarSize(默认为60点)。

在标签栏中的固定聊天视图

要将 ChatViewController 显示在标签栏中

let chatViewController = ChatViewController(backend: backend)
let chatDialogNavController = UINavigationController(rootViewController: chatViewController)
tabBarController.viewControllers = [chatDialogNavController]

模态聊天视图

以模态方式呈现聊天

let backend = ChatBackend.shared
backend.onReady { [weak self] (_, _) in
    DispatchQueue.main.async {
        let chatViewController = ChatViewController(backend: backend)
        let chatDialogNavController = UINavigationController(rootViewController: chatViewController)
        self?.present(chatDialogNavController, animated: true)
    }
}

ChatViewController

ChatViewController 是聊天视图的主要入口。它可以被继承以进行精细控制,或者您可以设置和重写属性,将自己分配为代理以配置大多数常见用例。

自定义响应(即处理自定义的 JSON 响应)

如果想要覆盖来自服务器的响应显示,可以为 ChatViewControllerDelegate 分配自己

let chatViewController = ChatViewController(backend: backend)
chatViewController.chatResponseViewDataSource = self

为了显示自定义 JSON 对象的视图,为 json 类型返回一个视图(返回 nil 将导致 ChatResponseView 处理它)。

使用 JSONDecoder 用描述 JSON 对象的自定义类来解析 JSON,例如,对于 "genericCard" 默认模板,对象看起来像

{
  "body": {
    "text": "This is the logo for the worlds best football club."
  },
  "heading": {
    "text": "UNITED"
  },
  "image": {
    "alt": "Photo of product",
    "position": "top",
    "url": "https://cdn.united.no/uploads/2020/09/kenilworthroad220720.jpg"
  },
  "link": {
    "text": "More information",
    "url": "https://united.no"
  },
  "template": "genericCard"
}

定义一个与数据匹配的 Decodable 结构体

public struct GenericCard: Decodable {
    
    public struct TextContent: Decodable {
        public let text: String
    }

    public struct Image: Decodable {
        public let alt: String?
        public let position: String?
        public let url: String?
    }

    public struct Link: Decodable {
        public let text: String?
        public let url: String
    }

    public let body: TextContent?
    public let heading: TextContent?
    public let image: Image?
    public let link: Link?
    public let template: String?

    enum CodingKeys: String, CodingKey {
        case body = "body"
        case heading = "heading"
        case image = "image"
        case link = "link"
        case template = "template"
    }

    public init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)

        body = try container.decodeIfPresent(TextContent.self, forKey: .body)
        heading = try container.decodeIfPresent(TextContent.self, forKey: .heading)
        image = try container.decodeIfPresent(Image.self, forKey: .image)
        link = try container.decodeIfPresent(Link.self, forKey: .link)
        template = try container.decodeIfPresent(String.self, forKey: .template)
    }
}

返回显示数据的视图

class MyClass: ChatResponseViewDataSource {
    func messageViewFor(element: Element) -> UIView? {
        switch element.type {
        case .json:
            guard let json = element.payload.json else { return nil }
            
            let decoder = JSONDecoder()
            do {
                let content = try decoder.decode(GenericCard.self, from: json)
                
                // Create a view tree that displays the data
                let view = ...
                return view
            } catch {
                // Handle JSON parsing error
            }
        default:
            return nil
    }
}

如果需要更多控制,可以从 chatResponseView(backend: ChatBackend) 方法返回 ChatResponseView 子类,该方法将为来自服务器或用户的每条消息调用其 configureWith(response: Response, conversation: ConversationResult? = nil, animateElements: Bool = true, sender: ChatResponseViewDelegate?) 方法。

class CustomChatResponseView: ChatResponseView {
    override open func configureWith(response: Response, conversation: ConversationResult? = nil, animateElements: Bool = true, sender: ChatResponseViewDelegate?) {
          ...
    }
}

class MyClass: ChatViewControllerDelegate {

    /// Return a fully custom response view (must be a subclass of ChatResponseView
    func chatResponseView(backend: ChatBackend) -> ChatResponseView? {
        return CustomChatResponseView(backend: backend)
    }
    
    /// Return a custom subclass of a menu view controller if needed
    func menuViewController(backend: ChatBackend) -> MenuViewController? {
        return nil
    }

    /// Return a custom subclass of the conversation feedback view controller if needed
    func conversationFeedbackViewController(backend: ChatBackend) -> ConversationFeedbackViewController? {
        return nil
    }
}

如果需要,返回 MenuViewControllerConversationFeedbackViewController 子类,否则返回 nil。

订阅UI事件

您可以通过向BoostUIEvents添加观察者来订阅UI事件。event(定义为名为Event的枚举)和detail分别指代所述事件和细节,详情请参考JS聊天面板文档中的“事件”章节下的“addEventListener”。

BoostUIEvents.shared.addEventObserver(self) { event, detail in
   switch event {
   case .menuOpened:
       // Do something when the menu has opened
   default:
       break
   }
}

后端

ChatBackend类是所有后端/API相关功能的主要入口点。至少需要提供SDK域名进行指向。

let backend = ChatBackend()
backend.domain = "sdk.boost.ai" // Replace with a possible custom domain

如果您在提供的UI类之外使用ChatBackend,请始终首先调用getConfig(completion: @escaping (ChatConfig?, Error?) -> Void)来获取服务器配置对象,它包含颜色和字符串等用于UI的所需内容。

订阅消息

要为您的前端需求使用后端,最简单的方法是订阅消息

backend.addMessageObserver(self) { [weak self] (message, error) in
    DispatchQueue.main.async {
        // Handle the ApiMessage (Response.swift) received
    }
}

订阅配置更改

服务器配置可能会根据用户聊天进行更新/更改。如果用户被转移到VAN(虚拟代理网络)中的另一个虚拟代理,即用户在询问有关保险的问题后,被转移到专门处理保险案例的虚拟代理,虚拟代理头像可能会改变,对话框颜色等也会改变。

订阅配置更改的提醒,并相应地更新UI样式

backend.addConfigObserver(self) { (config, error) in
    DispatchQueue.main.async {
        if let config = config {
            // Update styling based on the new config
        }
    }
}

订阅后端 emitEvent JSON

如果您从服务器端操作流发送自定义事件,您可以通过将观察器添加到聊天后端在应用中订阅这些事件。 typedetail 指的是事件和详细描述,这在 JS Chat Panel 文档的“事件”章节(addEventListener)中进行了说明,相关于 emitEvent JSON 类型。

backend.addEventObserver(self) { type, detail in
   switch type {
   case "myEventKey":
       // Handle emitted event with the key "myEventKey"
   default:
       break
   }
}

命令

API 中所有可用的命令在 Commands.swift 文件中可用,Command 枚举描述了所有命令。

public enum Command: String, Codable {
    case
    START = "START",
    POST = "POST",
    DOWNLOAD = "DOWNLOAD",
    RESUME = "RESUME",
    DELETE = "DELETE",
    FEEDBACK = "FEEDBACK",
    TYPING = "TYPING",
    POLL = "POLL",
    POLLSTOP = "POLLSTOP",
    STOP = "STOP",
    LOGINEVENT = "LOGINEVENT",
    SMARTREPLY = "SMARTREPLY",
    HUMANCHATPOST = "HUMANCHATPOST",
    CONFIG = "CONFIG"
}

您可以在 ChatBackend 对象上找到所有命令。其中几乎所有命令都可以不带参数调用,这将使用默认值;或者,您也可以从 Commands.swift 中添加消息定义。

backend.start()
backend.start(CommandStart())
backend.start(CommandStart(filterValues: ['test']))

backend.stop()
backend.resume()
backend.delete()
backend.poll()
backend.pollStop()
backend.smartReply()
backend.humanChatPost()
backend.typing()
backend.conversationFeedback(CommandFeedback())
backend.download()
backend.loginEvent(CommandLoginEvent())

发布

发布有些不同。您应尽量避免使用内部的 backend.post(parameters: Data, completion: @escaping (APIMessage?, Error?) -> Void) 命令,而改用预定义命令。

backend.actionButton(id: String)
backend.message(value: String)
backend.feedback(id: String, value: FeedbackValue)
backend.urlButton(id: String)
backend.sendFiles(files: [File])
backend.triggerAction(id: String)
backend.smartReply(value: String)
backend.humanChatPost(value: String)
backend.clientTyping(value: String) -> ClientTyping
backend.conversationFeedback(rating: Int, text: String?)
backend.loginEvent(userToken: String)

发送

您还可以使用 send() 来发送命令。您可以使用预定义命令,这应包括服务器支持的所有命令,或者如果您使用的服务器支持更多命令,您也可以定义自己的命令。该命令必须符合 CommandProtocolJSONEncoder()

public protocol CommandProtocol: Encodable {
    var command: Command {get set}
}

backend.send(CommandStart())

结果将通过上面描述的常规发布/订阅方法接收。

或者,如果您想直接控制结果,请使用回调。

backend.send(CommandStart()) { (apiMessage, error) in
    if let error = error {
        // Handle the error
        return
    }
    
    // Handle the ApiMessage
}

证书固定

如果您想使用Boost API后端通信固定的SSL证书,可以将isCertificatePinningEnabled设置为true以启用此功能。

backend.isCertificatePinningEnabled = true

请注意,证书固定针对Amazon Root CAs,具体请参考此处说明:将运行在AWS上的应用程序固定到AWS证书管理器(ACM)颁发的证书上是否可以?

固定根CA的列表可以在Amazon Trust Repository中找到。