This commit is contained in:
Andrey Blinov
2020-02-11 17:06:11 +07:00
parent f5f6d57c0b
commit be08fe209d
11 changed files with 498 additions and 29 deletions

View File

@@ -15,8 +15,10 @@ class ViewController: UIViewController {
// Do any additional setup after loading the view, typically from a nib.
}
@IBAction func openChat(_ sender: Any) {
let chatController = storyboard?.instantiateViewController(withIdentifier: "DemoChatController") as! DemoController
navigationController?.pushViewController(chatController, animated: true)
// let chatController = storyboard?.instantiateViewController(withIdentifier: "DemoChatController") as! DemoController
let chatController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "DemoChatController") as! DemoController
// navigationController?.pushViewController(chatController, animated: true)
navigationController?.present(chatController, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {

View File

@@ -1,7 +1,8 @@
use_frameworks!
target 'OnlineChatSdk_Example' do
pod 'OnlineChatSdk', :path => '../'
pod 'OnlineChatSdk'
# pod 'OnlineChatSdk', :path => '../'
target 'OnlineChatSdk_Tests' do
inherit! :search_paths

View File

@@ -1,5 +1,5 @@
PODS:
- OnlineChatSdk (0.0.1)
- OnlineChatSdk (0.0.2)
DEPENDENCIES:
- OnlineChatSdk
@@ -9,7 +9,7 @@ SPEC REPOS:
- OnlineChatSdk
SPEC CHECKSUMS:
OnlineChatSdk: fdc87678fced5df5f4ad3f4304d3eec4de6dad4c
OnlineChatSdk: 421b17a804f22c193f81f02a891e7d91128c525b
PODFILE CHECKSUM: adee2f574d16e8ed8f504f977cf28d2797543903

View File

@@ -1,5 +1,5 @@
PODS:
- OnlineChatSdk (0.0.1)
- OnlineChatSdk (0.0.2)
DEPENDENCIES:
- OnlineChatSdk
@@ -9,7 +9,7 @@ SPEC REPOS:
- OnlineChatSdk
SPEC CHECKSUMS:
OnlineChatSdk: fdc87678fced5df5f4ad3f4304d3eec4de6dad4c
OnlineChatSdk: 421b17a804f22c193f81f02a891e7d91128c525b
PODFILE CHECKSUM: adee2f574d16e8ed8f504f977cf28d2797543903

View File

@@ -0,0 +1,55 @@
//
// ChatApi.swift
// OnlineChatSdk
//
// Created by Andrew Blinov on 29/03/2019.
// Copyright © 2019 Andrew Blinov. All rights reserved.
//
import Foundation
open class ChatApi {
private func post(_ url: String, _ params: Dictionary<String, Any>, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) {
guard let url = URL(string: url) else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject: params, options: []) else { return }
request.httpBody = httpBody
let session = URLSession.shared
session.dataTask(with: request, completionHandler: completionHandler).resume()
}
public func send(_ token: String, _ method: String, _ params: Dictionary<String, Any>, callback: @escaping (NSDictionary?) -> Void) {
let url = "https://admin.verbox.ru/api/chat/\(token)/\(method)"
post(url, params) { (data, response, error) in
guard let data = data else { return }
do {
let json = try JSONSerialization.jsonObject(with: data, options: []) as? NSDictionary
callback(json)
} catch {}
}
}
public func messages(_ token: String, params: Dictionary<String, Any>, callback: @escaping (NSDictionary?) -> Void) {
send(token, "message", params, callback: callback)
}
public static func getNewMessages(_ token: String, _ clientId: String, callback: @escaping (NSDictionary?) -> Void) {
let dtFormatter = DateFormatter()
dtFormatter.calendar = Calendar(identifier: Calendar.Identifier.iso8601)
dtFormatter.locale = Locale(identifier: "en")
dtFormatter.dateFormat = "yyyy'-'MM'-'dd'"
dtFormatter.timeZone = TimeZone(secondsFromGMT: 10800)
let params = [
"client": ["clientId": clientId],
"sender": "operator",
"status": "unreaded",
"dateRange": ["start": dtFormatter.string(from: Date(timeIntervalSince1970: Date().timeIntervalSince1970 - 86400 * 14)), "stop": dtFormatter.string(from: Date())]
] as [String : Any]
(ChatApi()).messages(token, params: params, callback: callback)
}
}

View File

@@ -0,0 +1,253 @@
//
// ChatController.swift
// OnlineChatSdk
//
// Created by Andrew Blinov on 22/03/2019.
// Copyright © 2019 Andrew Blinov. All rights reserved.
//
import UIKit
import WebKit
import AVFoundation
open class ChatController: UIViewController, WKNavigationDelegate, WKScriptMessageHandler {
public static let event_operatorSendMessage = "operatorSendMessage";
public static let event_clientSendMessage = "clientSendMessage";
public static let event_clientMakeSubscribe = "clientMakeSubscribe";
public static let event_contactsUpdated = "contactsUpdated";
public static let event_sendRate = "sendRate";
public static let event_clientId = "clientId";
public static let method_setClientInfo = "setClientInfo";
public static let method_setTarget = "setTarget";
public static let method_openReviewsTab = "openReviewsTab";
public static let method_openTab = "openTab";
public static let method_sendMessage = "sendMessage";
public static let method_receiveMessage = "receiveMessage";
public static let method_setOperator = "setOperator";
public static let method_getContacts = "getContacts";
public var chatView: WKWebView!
private var callJs: Array<String>!
private var didFinish: Bool = false
override public func loadView() {
let contentController = WKUserContentController()
contentController.add(self, name: "chatInterface")
let preferences = WKPreferences()
preferences.javaScriptEnabled = true
preferences.javaScriptCanOpenWindowsAutomatically = true
let config = WKWebViewConfiguration()
config.userContentController = contentController
config.preferences = preferences
var frame = UIScreen.main.bounds
if self.parent != nil && self.parent?.view != nil && self.parent?.view.bounds != nil {
frame = (self.parent?.view.bounds)!
}
self.chatView = WKWebView(frame: frame, configuration: config)
self.chatView.navigationDelegate = self
self.view = self.chatView
}
public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
self.didFinish = true
if self.callJs != nil && !self.callJs.isEmpty {
for script in self.callJs {
callJs(script)
}
self.callJs = nil
}
}
private func getCallJsMethod(_ name: String, params: Array<Any>) -> String {
var res: String = "window.MeTalk('"
res.append(name)
res.append("'")
if params.count > 0 {
for p in params {
res.append(",")
if p is Int {
res.append(String(describing: p))
} else if p is Command {
res.append((p as! Command).command)
} else {
res.append("'")
res.append(String(describing: p))
res.append("'")
}
}
}
res.append(")")
return res
}
private func callJs(_ script: String) {
self.chatView.evaluateJavaScript(script)
}
private func toJson(_ jsonObj: AnyObject) -> String {
var data:Data? = nil
do {
data = try JSONSerialization.data(withJSONObject: jsonObj, options: JSONSerialization.WritingOptions());
} catch {}
if data != nil {
return NSString(data: data!, encoding: String.Encoding.utf8.rawValue)! as String
}
return "{}"
}
public func load(_ id: String, _ domain: String, _ language: String = "", _ clientId: String = "") {
var setup: Dictionary<String, Any> = [:]
if !language.isEmpty {
setup["language"] = language
}
if !clientId.isEmpty {
setup["clientId"] = clientId
}
let surl = "https://admin.verbox.ru/support/chat/\(id)/\(domain)"
var url = URL(string: surl)
if !setup.isEmpty {
var urlComponents = URLComponents(url: url!, resolvingAgainstBaseURL: false)
urlComponents?.queryItems = [URLQueryItem(name: "setup", value: toJson(setup as AnyObject))]
url = urlComponents!.url!
}
if url == nil {
url = URL(string: surl)
}
self.chatView.load(URLRequest(url: url!))
self.chatView.allowsBackForwardNavigationGestures = true
}
public func callJsMethod(_ name: String, params: Array<Any>) {
if self.didFinish {
callJs(getCallJsMethod(name, params: params))
} else {
if self.callJs == nil {
self.callJs = []
}
self.callJs.append(getCallJsMethod(name, params: params))
}
}
public func callJsSetClientInfo(_ jsonInfo: String) {
callJsMethod(ChatController.method_setClientInfo, params: [jsonInfo])
}
public func callJsSetTarget(_ reason: String) {
callJsMethod(ChatController.method_setTarget, params: [reason])
}
public func callJsOpenReviewsTab() {
callJsMethod(ChatController.method_openReviewsTab, params: [])
}
public func callJsOpenTab(_ index: Int) {
callJsMethod(ChatController.method_openTab, params: [index])
}
public func callJsSendMessage(_ text: String) {
callJsMethod(ChatController.method_sendMessage, params: [text])
}
public func callJsReceiveMessage(_ text: String, _ oper: String, _ simulateTyping: Int) {
callJsMethod(ChatController.method_receiveMessage, params: [text, oper, simulateTyping])
}
public func callJsSetOperator(_ login: String) {
callJsMethod(ChatController.method_setOperator, params: [login])
}
public func callJsGetContacts() {
callJsMethod(ChatController.method_getContacts, params: [Command("window.getContactsCallback")])
}
public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if message.name != "chatInterface" {
return
}
let jsonBody = (message.body as! String).data(using: .utf8)!
let body = try? (JSONSerialization.jsonObject(with: jsonBody, options: .mutableLeaves) as! NSDictionary)
if body == nil {
return
}
if body!["name"] == nil {
return
}
var data: NSDictionary?
if body!["data"] != nil {
data = (body!["data"] as! NSDictionary)
} else {
data = [:]
}
let name = body!["name"] as! String
switch name {
case ChatController.event_clientId:
onClientId(data!["clientId"] != nil ? data!["clientId"] as! String : "")
break
case ChatController.event_sendRate:
onSendRate(data!)
break
case ChatController.event_contactsUpdated:
onContactsUpdated(data!)
break
case ChatController.event_clientSendMessage:
onClientSendMessage(data!)
break
case ChatController.event_clientMakeSubscribe:
onClientMakeSubscribe(data!)
break
case ChatController.event_operatorSendMessage:
onOperatorSendMessage(data!)
break
case ChatController.method_getContacts:
getContactsCallback(data!)
break
default:
break
}
onEvent(name, data!)
}
open func playSound(_ systemSoundId: SystemSoundID) {
AudioServicesPlaySystemSound(systemSoundId)
}
open func onClientId(_ clientId: String) {
}
open func onSendRate(_ data: NSDictionary) {
}
open func onContactsUpdated(_ data: NSDictionary) {
}
open func onClientSendMessage(_ data: NSDictionary) {
}
open func onClientMakeSubscribe(_ data: NSDictionary) {
}
open func onOperatorSendMessage(_ data: NSDictionary) {
self.playSound(1315)
}
open func onEvent(_ name: String, _ data: NSDictionary) {
}
open func getContactsCallback(_ data: NSDictionary) {
}
}

View File

@@ -0,0 +1,17 @@
//
// Command.swift
// OnlineChatSdk
//
// Created by Andrew Blinov on 26/03/2019.
// Copyright © 2019 Andrew Blinov. All rights reserved.
//
import Foundation
public struct Command {
let command: String
init(_ command: String) {
self.command = command
}
}

141
Example/Pods/OnlineChatSdk/README.md generated Normal file
View File

@@ -0,0 +1,141 @@
# OnlineChatSdk-Swift
[![Version](https://img.shields.io/cocoapods/v/OnlineChatSdk.svg?style=flat)](https://cocoapods.org/pods/OnlineChatSdk)
[![License](https://img.shields.io/cocoapods/l/OnlineChatSdk.svg?style=flat)](https://cocoapods.org/pods/OnlineChatSdk)
![Platform](https://img.shields.io/cocoapods/p/SwiftMessages.svg?style=flat)
## Добавление в проект
pod 'OnlineChatSdk'
## Получение id
Перейдите в раздел «Настройки - Установка» и скопируйте значение переменной id.
![](https://github.com/bekannax/OnlineChatSdk-Android/blob/master/images/2019-03-21_16-53-28.png)
## Пример использования
Добавьте свой **ViewController** с суперклассом `ChatController`.
```swift
class MyController: ChatController {
override func viewDidLoad() {
super.viewDidLoad()
load("<Ваш id>", "<Домен вашего сайта>")
}
}
```
Так же при загрузке можно указать **language** и **clientId**.
```swift
load("<Ваш id>", "<Домен вашего сайта>", "en", "newClientId")
```
## События
* **operatorSendMessage** - оператор отправил сообщение посетителю.
* **clientSendMessage** - посетитель отправил сообщение оператору.
* **clientMakeSubscribe** - посетитель заполнил форму.
* **contactsUpdated** - посетитель обновил информацию о себе.
* **sendRate** - посетитель отправил новый отзыв.
* **clientId** - уникальный идентификатор посетителя.
Для каждого события есть персональный обработчик.
```swift
override func onOperatorSendMessage(_ data: NSDictionary) {
}
override func onClientSendMessage(_ data: NSDictionary) {
}
override func onClientMakeSubscribe(_ data: NSDictionary) {
}
override func onContactsUpdated(_ data: NSDictionary) {
}
override func onSendRate(_ data: NSDictionary) {
}
override func onClientId(_ clientId: String) {
}
```
Или можно задать один обработчик на все события.
```swift
override func onEvent(_ name: String, _ data: NSDictionary) {
switch name {
case ChatController.event_operatorSendMessage:
break
case ChatController.event_clientSendMessage:
break
case ChatController.event_clientMakeSubscribe:
break
case ChatController.event_contactsUpdated:
break
case ChatController.event_sendRate:
break
case ChatController.event_clientId:
break
case ChatController.method_getContacts:
break
default:
break
}
}
```
## Методы
* **setClientInfo** - изменение информации о посетителе.
* **setTarget** - пометить посетителя целевым.
* **openReviewsTab** - отобразить форму для отзыва.
* **openTab** - отобразить необходимую вкладку.
* **sendMessage** - отправка сообщения от имени клиента.
* **receiveMessage** - отправка сообщения от имени оператора.
* **setOperator** - выбор любого оператора.
* **getContacts** - получение контактных данных.
```swift
callJsSetClientInfo("{name: \"Имя\", email: \"test@mail.ru\"")
callJsSetTarget("reason")
callJsOpenReviewsTab()
callJsOpenTab(1)
callJsSendMessage("Здравствуйте! У меня серьёзная проблема!")
callJsReceiveMessage("Мы уже спешим на помощь ;)", "", 2000)
callJsSetOperator("Логин оператора")
callJsGetContacts() // результат прилетает в getContactsCallback
override func getContactsCallback(_ data: NSDictionary) {
}
```
Подробное описание методов можно прочесть в разделе «Интеграция и API - Javascript API».
## Получение token
Перейдите в раздел «Интеграция и API - REST API», скопируйте существующий token или добавьте новый.
![](https://github.com/bekannax/OnlineChatSdk-Android/blob/master/images/2019-04-01_18-32-22.png)
## ChatApi
**getNewMessages** - получение новых сообщений от оператора.
```swift
ChatApi.getNewMessages("<TOKEN>", "<clientId>")
{(result) in
if result?["error"] != nil {
print("error : \(String(describing: result?["error"]))")
} else {
print("result : \(result.debugDescription)")
}
}
```
Подробное описание можно прочесть в разделе «Интеграция и API - REST API - Инструкции по подключению».
## License
OnlineChatSdk is available under the MIT license. See the LICENSE file for more info.

View File

@@ -2,25 +2,25 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.0.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<key>NSPrincipalClass</key>
<string></string>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>0.0.3</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>

View File

@@ -8,7 +8,7 @@
Pod::Spec.new do |s|
s.name = 'OnlineChatSdk'
s.version = '0.0.2'
s.version = '0.0.3'
s.summary = 'A small library containing a wrapper for the WKWebView.'
s.swift_versions = '5.0'