Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
Hassan Shahbazi committed May 8, 2020
2 parents 35a8f5d + 5fb50e4 commit 7ec47d9
Show file tree
Hide file tree
Showing 15 changed files with 156 additions and 107 deletions.
9 changes: 2 additions & 7 deletions .github/workflows/swift-sdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,14 @@ jobs:
- name: Force Xcode 11
run: sudo xcode-select -switch /Applications/Xcode_11.4.app

- name: Install Tools
run: |
gem install ocunit2junit
gem install xcpretty
- name: Install Cocoapods
run: pod update

- name: Clean-up workspace
run: xcodebuild clean

- name: Build SDK
run: xcodebuild -workspace NinchatSDKSwift.xcworkspace -scheme NinchatSDKSwift -configuration "Release" -destination "platform=iOS Simulator,name=iPhone 8" CODE_SIGNING_ALLOWED=NO | tee raw.txt | xcpretty -c cat raw.txt | ocunit2junit
run: xcodebuild -workspace NinchatSDKSwift.xcworkspace -scheme NinchatSDKSwift -configuration "Release" -destination "platform=iOS Simulator,name=iPhone 8" CODE_SIGNING_ALLOWED=NO

- name: Run SDK Tests
run: xcodebuild test -workspace NinchatSDKSwift.xcworkspace -scheme NinchatSDKSwift -destination "platform=iOS Simulator,name=iPhone 8" | tee rawTest.txt | xcpretty -c cat rawTest.txt | ocunit2junit
run: xcodebuild test -workspace NinchatSDKSwift.xcworkspace -scheme NinchatSDKSwift -destination "platform=iOS Simulator,name=iPhone 8"
8 changes: 4 additions & 4 deletions NinchatSDKSwift.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
85CC096423DC66D300482956 /* Encodable+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85CC096323DC66D300482956 /* Encodable+Extension.swift */; };
85D245352408F3470081F575 /* ChatTypingCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 85D245342408F3470081F575 /* ChatTypingCell.xib */; };
85DC59CC239E4C42007ABAE3 /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85DC59CB239E4C42007ABAE3 /* Button.swift */; };
85DC59D0239E6A38007ABAE3 /* NINChatSessionClosure.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85DC59CF239E6A38007ABAE3 /* NINChatSessionClosure.swift */; };
85DC59D0239E6A38007ABAE3 /* NINSiteConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85DC59CF239E6A38007ABAE3 /* NINSiteConfiguration.swift */; };
85DC59D2239E7850007ABAE3 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 85DC59D1239E7850007ABAE3 /* Media.xcassets */; };
85DC59D5239E7C3B007ABAE3 /* NINQueueViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85DC59D4239E7C3B007ABAE3 /* NINQueueViewModel.swift */; };
85DD6C5523AE0CBC0023FFEF /* SourceSansPro-BlackItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 85DD6C4923AE0CBC0023FFEF /* SourceSansPro-BlackItalic.ttf */; };
Expand Down Expand Up @@ -288,7 +288,7 @@
85CC096323DC66D300482956 /* Encodable+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Encodable+Extension.swift"; sourceTree = "<group>"; };
85D245342408F3470081F575 /* ChatTypingCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ChatTypingCell.xib; sourceTree = "<group>"; };
85DC59CB239E4C42007ABAE3 /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; };
85DC59CF239E6A38007ABAE3 /* NINChatSessionClosure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NINChatSessionClosure.swift; sourceTree = "<group>"; };
85DC59CF239E6A38007ABAE3 /* NINSiteConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NINSiteConfiguration.swift; sourceTree = "<group>"; };
85DC59D1239E7850007ABAE3 /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = "<group>"; };
85DC59D4239E7C3B007ABAE3 /* NINQueueViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NINQueueViewModel.swift; sourceTree = "<group>"; };
85DD6C4923AE0CBC0023FFEF /* SourceSansPro-BlackItalic.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "SourceSansPro-BlackItalic.ttf"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -515,7 +515,7 @@
children = (
855B9F2D238ECE650081A9C6 /* NINChatSession.swift */,
91A16FA13B1F62E386352B88 /* NINSessionCredentials.swift */,
85DC59CF239E6A38007ABAE3 /* NINChatSessionClosure.swift */,
85DC59CF239E6A38007ABAE3 /* NINSiteConfiguration.swift */,
855B9F2E238ECE650081A9C6 /* NINChatSessionDelegate.swift */,
8543414B239655200078D11A /* Implementations */,
85DD6C4723AE0C8A0023FFEF /* Resources */,
Expand Down Expand Up @@ -1191,7 +1191,7 @@
8561EAD623C2805900943C72 /* NINChatSessionManager.swift in Sources */,
85DC59CC239E4C42007ABAE3 /* Button.swift in Sources */,
855FA2A123C735CD00A2FB41 /* MetaCell.swift in Sources */,
85DC59D0239E6A38007ABAE3 /* NINChatSessionClosure.swift in Sources */,
85DC59D0239E6A38007ABAE3 /* NINSiteConfiguration.swift in Sources */,
8563004923B286360098A7B0 /* NINRatingViewController.swift in Sources */,
8561EAD923C2805900943C72 /* NINChatSessionManagerEventHandlers.swift in Sources */,
8561EADE23C37C3000943C72 /* UITableView+Extension.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
import Foundation
import NinchatLowLevelClient

typealias CompletionWithError = ((Error?) -> Void)
typealias CompletionWithCredentials = ((NINSessionCredentials?, _ willResume: Bool, Error?) -> Void)
typealias Completion = (() -> Void)
typealias CompletionWithError = (Error?) -> Void
typealias CompletionWithCredentials = (NINSessionCredentials?, _ willResume: Bool, Error?) -> Void
typealias Completion = () -> Void

/* Available ratings and assigned status codes for finishing the chat from our end */
enum ChatStatus: Int {
Expand Down Expand Up @@ -38,13 +38,13 @@ protocol NINChatSessionConnectionManager {
func list(queues ID: [String]?, completion: @escaping CompletionWithError) throws

/** Joins a chat queue. */
func join(queue ID: String, progress: @escaping ((Queue?, Error?, Int) -> Void), completion: @escaping Completion) throws
func join(queue ID: String, progress: @escaping (Queue?, Error?, Int) -> Void, completion: @escaping Completion) throws

/** Deallocate a session by resetting local variables */
func deallocateSession()

/** Runs ICE (Interactive Connectivity Establishment) for WebRTC connection negotiations. */
func beginICE(completion: @escaping ((Error?, [WebRTCServerInfo]?, [WebRTCServerInfo]?) -> Void)) throws
func beginICE(completion: @escaping (Error?, [WebRTCServerInfo]?, [WebRTCServerInfo]?) -> Void) throws

/** Closes the chat by shutting down the session. Triggers the API delegate method -ninchatDidEndChatSession:. */
func closeChat() throws
Expand Down Expand Up @@ -85,7 +85,7 @@ protocol NINChatSessionMessenger {

protocol NINChatSessionAttachment {
/** Describe a file by its ID. */
func describe(file id: String, completion: @escaping ((Error?, [String:Any]?) -> Void)) throws
func describe(file id: String, completion: @escaping (Error?, [String:Any]?) -> Void) throws
}

protocol NINChatSessionTranslation {
Expand All @@ -109,7 +109,7 @@ protocol NINChatSessionManagerDelegate {
var onRTCSignal: ((MessageType, ChannelUser?, _ signal: RTCSignal?) -> Void)? { get set }
var onRTCClientSignal: ((MessageType, ChannelUser?, _ signal: RTCSignal?) -> Void)? { get set }

func bindQueueUpdate<T: QueueUpdateCapture>(closure: @escaping ((Events, Queue, Error?) -> Void), to receiver: T)
func bindQueueUpdate<T: QueueUpdateCapture>(closure: @escaping (Events, Queue, Error?) -> Void, to receiver: T)
func unbindQueueUpdateClosure<T: QueueUpdateCapture>(from receiver: T)
}

Expand All @@ -132,5 +132,5 @@ protocol NINChatSessionManager: class, NINChatSessionConnectionManager, NINChatS
var appDetails: String? { get set }

/** Default initializer for NinchatSessionManager. */
init(session: NINChatSessionInternalDelegate?, serverAddress: String, audienceMetadata: NINLowLevelClientProps?)
init(session: NINChatSessionInternalDelegate?, serverAddress: String, audienceMetadata: NINLowLevelClientProps?, configuration: NINSiteConfiguration?)
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ final class NINChatSessionManagerImpl: NSObject, NINChatSessionManager, NINChatD

// MARK: - NINChatSessionManagerClosureHandler

internal var actionBoundClosures: [Int:((Error?) -> Void)] = [:]
internal var actionFileBoundClosures: [Int:((Error?, [String:Any]?) -> Void)] = [:]
internal var actionChannelBoundClosures: [Int:((Error?) -> Void)] = [:]
internal var actionICEServersBoundClosures: [Int:((Error?, [WebRTCServerInfo]?, [WebRTCServerInfo]?) -> Void)] = [:]
internal var queueUpdateBoundClosures: [String:((Events, Queue, Error?) -> Void)] = [:]
internal var actionBoundClosures: [Int: (Error?) -> Void] = [:]
internal var actionFileBoundClosures: [Int: (Error?, [String:Any]?) -> Void] = [:]
internal var actionChannelBoundClosures: [Int: (Error?) -> Void] = [:]
internal var actionICEServersBoundClosures: [Int: (Error?, [WebRTCServerInfo]?, [WebRTCServerInfo]?) -> Void] = [:]
internal var queueUpdateBoundClosures: [String: (Events, Queue, Error?) -> Void] = [:]

// MARK: - NINChatSessionConnectionManager variables

Expand All @@ -71,7 +71,7 @@ final class NINChatSessionManagerImpl: NSObject, NINChatSessionManager, NINChatD
var onRTCSignal: ((MessageType, ChannelUser?, _ signal: RTCSignal?) -> Void)?
var onRTCClientSignal: ((MessageType, ChannelUser?, _ signal: RTCSignal?) -> Void)?

func bindQueueUpdate<T: QueueUpdateCapture>(closure: @escaping ((Events, Queue, Error?) -> Void), to receiver: T) {
func bindQueueUpdate<T: QueueUpdateCapture>(closure: @escaping (Events, Queue, Error?) -> Void, to receiver: T) {
guard queueUpdateBoundClosures.keys.filter({ $0 == receiver.desc }).count == 0 else { return }
queueUpdateBoundClosures[receiver.desc] = closure
}
Expand All @@ -90,22 +90,24 @@ final class NINChatSessionManagerImpl: NSObject, NINChatSessionManager, NINChatD
}
var audienceQueues: [Queue]! = []
var siteConfiguration: SiteConfiguration!
var givenConfiguration: NINSiteConfiguration?
var appDetails: String?

// MARK: - NINChatSessionManagerDevTools

var serverAddress: String!
var siteSecret: String?

init(session: NINChatSessionInternalDelegate?, serverAddress: String, audienceMetadata: NINLowLevelClientProps? = nil) {
init(session: NINChatSessionInternalDelegate?, serverAddress: String, audienceMetadata: NINLowLevelClientProps? = nil, configuration: NINSiteConfiguration?) {
self.delegate = session
self.serverAddress = serverAddress
self.audienceMetadata = audienceMetadata
self.givenConfiguration = configuration
}

/** Designed for test and internal purposes. */
convenience init(session: NINChatSessionInternalDelegate?, serverAddress: String, siteSecret: String?, audienceMetadata: NINLowLevelClientProps? = nil) {
self.init(session: session, serverAddress: serverAddress, audienceMetadata: audienceMetadata)
convenience init(session: NINChatSessionInternalDelegate?, serverAddress: String, siteSecret: String?, audienceMetadata: NINLowLevelClientProps? = nil, configuration: NINSiteConfiguration? = nil) {
self.init(session: session, serverAddress: serverAddress, audienceMetadata: audienceMetadata, configuration: configuration)
self.siteSecret = siteSecret
}

Expand Down Expand Up @@ -147,6 +149,7 @@ extension NINChatSessionManagerImpl {
case .success(let config):
debugger("Got site config: \(String(describing: config.toDictionary))")
self.siteConfiguration = SiteConfigurationImpl(configuration: config.toDictionary, environments: environments)
self.siteConfiguration.override(configuration: self.givenConfiguration)
completion(nil)
case .failure(let error):
completion(error)
Expand Down Expand Up @@ -210,7 +213,7 @@ extension NINChatSessionManagerImpl {
try self.describe(realm: realmID, queuesID: ID, completion: completion)
}

func join(queue ID: String, progress: @escaping ((Queue?, Error?, Int) -> Void), completion: @escaping Completion) throws {
func join(queue ID: String, progress: @escaping (Queue?, Error?, Int) -> Void, completion: @escaping Completion) throws {

func performJoin() throws {
delegate?.log(value: "Joining queue \(ID)..")
Expand Down Expand Up @@ -283,11 +286,12 @@ extension NINChatSessionManagerImpl {
self.currentQueueID = nil
self.myUserID = nil

self.disconnect()
self.onSessionDeallocated?()
}

/// Retrieves the WebRTC ICE STUN/TURN server details
func beginICE(completion: @escaping ((Error?, [WebRTCServerInfo]?, [WebRTCServerInfo]?) -> Void)) throws {
func beginICE(completion: @escaping (Error?, [WebRTCServerInfo]?, [WebRTCServerInfo]?) -> Void) throws {
guard let session = self.session else { throw NINSessionExceptions.noActiveSession }
let param = NINLowLevelClientProps.initiate(action: .beginICE)

Expand All @@ -302,13 +306,24 @@ extension NINChatSessionManagerImpl {
/// Low-level shutdown of the chat's session; invalidates session resource.
func closeChat() throws {
delegate?.log(value: "Shutting down chat Session..")
try self.deleteCurrentUser { [unowned self] error in

func endSession() {
self.disconnect()

/// Signal the delegate that our session has ended
self.delegate?.onDidEnd()
self.didEndSession?()
}

if self.myUserID == nil {
endSession()
} else if let userID = self.myUserID, let user = self.channelUsers[userID], user.guest {
endSession()
} else {
try self.deleteCurrentUser { error in
endSession()
}
}
}

/// High-level chat ending; sends channel metadata and then closes session.
Expand Down
60 changes: 38 additions & 22 deletions NinchatSDKSwift/Implementations/Models/SiteConfiguration.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,79 +20,95 @@ protocol SiteConfiguration {
var userAvatar: AnyHashable? { get }
var userName: String? { get }
var noQueueText: String? { get }
var audienceAutoQueue: String? { get }

init(configuration: [AnyHashable : Any]?, environments: [String]?)
mutating func override(configuration: NINSiteConfiguration?)
}

struct SiteConfigurationImpl: SiteConfiguration {
private let configuration: [AnyHashable : Any]?
private let environments: [String]

init(configuration: [AnyHashable : Any]?, environments: [String]?) {
self.configuration = configuration ?? [:]
self.environments = environments ?? []
// MARK: - NINSiteConfiguration

private var _userName: String?
var userName: String? {
get {
if _userName != nil {
return _userName
}
return self.value(for: "userName")
}
set {
_userName = newValue
}
}


// MARK: - SiteConfiguration

var welcome: String? {
self.value(for: "welcome")
}

var motd: String? {
self.value(for: "motd")
}

var inQueue: String? {
self.value(for: "inQueueText")
}

var sendButtonTitle: String? {
self.value(for: "sendButtonText")
}

var confirmDialogTitle: String? {
self.value(for: "closeConfirmText")
}

var audienceRealm: String? {
self.value(for: "audienceRealmId")
}

var audienceQueues: [String]? {
self.value(for: "audienceQueues")
}

var translation: [String:String]? {
self.value(for: "translations")
}

var agentAvatar: AnyHashable? {
self.value(for: "agentAvatar")
}

var agentName: String? {
self.value(for: "agentName")
}

var userAvatar: AnyHashable? {
self.value(for: "userAvatar")
}

var userName: String? {
self.value(for: "userName")
}

var noQueueText: String? {
self.value(for: "noQueuesText")
}

var audienceAutoQueue: String? {
self.value(for: "audienceAutoQueue")
}

init(configuration: [AnyHashable : Any]?, environments: [String]?) {
self.configuration = configuration ?? [:]
self.environments = environments ?? []
}

mutating func override(configuration: NINSiteConfiguration?) {
guard let configuration = configuration else { return }
self.userName = configuration.userName
}
}

extension SiteConfigurationImpl {
private func value<T>(for key: String) -> T? {
guard let configuration = configuration as? [String : Any] else { return nil }

if let value = self.environments.compactMap({ (configuration[$0] as? [String : Any])?[key] }).first as? T {
guard let configuration = configuration as? [String:Any] else { return nil }

/// Use given environments first, if any
if let value = self.environments.compactMap({ (configuration[$0] as? [String:Any])?[key] }).first as? T {
return value
}

/// Return value from default environment
return (configuration["default"] as? [String : Any])?[key] as? T
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ final class NINChatViewController: UIViewController, ViewController, KeyboardHan
}

private func deallocViewModel() {
debugger("** ** - deallocate view model")
debugger("** ** deallocate view model")

self.viewModel.onChannelClosed = nil
self.viewModel.onQueueUpdated = nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,11 +70,18 @@ final class NINQueueViewController: UIViewController, ViewController {
super.viewDidLoad()
self.setupViewModel()
self.overrideAssets()

NotificationCenter.default.addObserver(self, selector: #selector(spin(notification:)), name: UIApplication.willEnterForegroundNotification, object: nil)
}

override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.spin()
self.spin(notification: nil)
}

deinit {
debugger("`NINQueueViewController` deallocated")
NotificationCenter.default.removeObserver(self, name: UIApplication.willEnterForegroundNotification, object: nil)
}

private func setupViewModel() {
Expand All @@ -96,7 +103,8 @@ final class NINQueueViewController: UIViewController, ViewController {
// MARK: - Helper methods

extension NINQueueViewController {
private func spin() {
@objc
private func spin(notification: Notification?) {
guard spinnerImageView.layer.animation(forKey: "SpinAnimation") == nil else { return }

let animation = CABasicAnimation(keyPath: "transform.rotation.z")
Expand Down
Loading

0 comments on commit 7ec47d9

Please sign in to comment.