You can set up mobile apps to work with Contact Center AI Platform (CCAI Platform) in a number of ways, including with Flutter. This page shows you how to integrate the iOS SDK into an iOS app using Flutter.
Before you begin
Before you following the instructions on this page, you must first follow the instruction in Integrate using Flutter .
Integrate the SDK using CocoaPods
To integrate the SDK using CocoaPods, follow these steps:
-
Open
Podfileand add dependencies to the target, as in the following code sample:UJETPodspec = { :podspec = > 'https://sdk.ujet.co/ios/UJET.podspec' } pod 'UJET' , UJETPodspec pod 'UJET/Cobrowse' , UJETPodspec -
In the terminal, go to the
example/iosdirectory and run thepod installcommand to install dependencies.
Set up iOS push notifications
To set up iOS push notifications, follow these steps:
-
In XCode, open
example/ios/Runner.xcodeproj. -
Add the following code to your
AppDelegate.swiftfile:import PushKit import UJETKit @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { override func application ( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [ UIApplication.LaunchOptionsKey: Any ] ? ) -> Bool { // Register Flutter Plugins GeneratedPluginRegistrant.register ( with: self ) UJETModule.register ( with: self.registrar ( forPlugin: "UjetModule" ) ! ) UJETModule.onInitDone = { // setup push notification let voipRegistry = PKPushRegistry ( queue: DispatchQueue.main ) voipRegistry.desiredPushTypes = Set ([ PKPushType.voIP ]) voipRegistry.delegate = self UNUserNotificationCenter.current () .delegate = self } return super.application ( application, didFinishLaunchingWithOptions: launchOptions ) } } // Extension for Push Notification extension AppDelegate { func tokenFromData ( data: Data ) -> String { return data.map { String ( format: "%02x" , $0 ) } .joined () } override func application ( _ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data ) { print ( "apns token: " , tokenFromData ( data: deviceToken )) UJET.updatePushToken ( deviceToken, type: .APN ) } override func application ( _ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error ) { UJET.updatePushToken ( nil, type: .APN ) } override func application ( _ application: UIApplication, didReceiveRemoteNotification userInfo: [ AnyHashable: Any ] , fetchCompletionHandler completionHandler: @escaping ( UIBackgroundFetchResult ) -> Void ) { handleNotification ( userInfo: userInfo, completion: nil ) } } // MARK: PushKit extension AppDelegate: PKPushRegistryDelegate { func pushRegistry ( _ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType ) { print ( "voip token: " , tokenFromData ( data: credentials.token )) if type == .voIP { UJET.updatePushToken ( credentials.token, type: .voIP ) } } func pushRegistry ( _ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType ) { if type == .voIP { UJET.updatePushToken ( nil, type: .voIP ) } } func pushRegistry ( _ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void ) { if type == .voIP { handleNotification ( userInfo: payload.dictionaryPayload, completion: completion ) } } } extension AppDelegate { // handle push received in foreground state override func userNotificationCenter ( _ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping ( UNNotificationPresentationOptions ) -> Void ) { let userInfo = notification.request.content.userInfo handleNotification ( userInfo: userInfo, completion: nil ) } // handle push received and tapped in background state override func userNotificationCenter ( _ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void ) { let userInfo = response.notification.request.content.userInfo handleNotification ( userInfo: userInfo, completion: nil ) } } private func handleNotification ( userInfo: [ AnyHashable: Any ] , completion: (() -> Void ) ? ) { if userInfo [ "ujet" ] ! = nil { UJET.receivedNotification ( userInfo, completion: completion ) } else { // Handle your notification here completion? () } } -
Select your
.xcodeprojand then your app target. AddPushKit.frameworkin theFrameworks, Libraries, and Embedded Contentsection.
Set up deep linking
To set up deep linking, add the following code:
//
Extension
for
deep
linking
extension
AppDelegate
{
override
func
application (
_
app:
UIApplication,
open
url:
URL,
options:
[
UIApplication.OpenURLOptionsKey:
Any ]
=
[
: ])
->
Bool
{
print (
"Open app with url: \(url.absoluteString)"
)
return
self.handleRouting (
url )
}
override
func
application (
_
application:
UIApplication,
continue
userActivity:
NSUserActivity,
restorationHandler:
@escaping
([
UIUserActivityRestoring ]
? )
->
Void )
->
Bool
{
//
Universal
links
if
NSUserActivityTypeBrowsingWeb
==
userActivity.activityType
{
return
self.handleRouting (
userActivity.webpageURL! )
}
else
if
userActivity.activityType
==
"INStartAudioCallIntent"
{
//
Open
app
from
Call
history
UJET.start (
with:
UJETStartOptions ())
return
true
}
return
false
}
func
handleRouting (
_
url:
URL )
->
Bool
{
let
availableSchema
=
[
"ujetrn"
,
//
TODO:
Change
to
your
custom
URL
scheme.
Config
from
Portal >
Developer
Settings >
Mobile
App >
Enable
Send
SMS
to
Download
App >
iOS
App >
URL
"https"
//
universal
link
]
let
availableHostAndPath
=
[
"call"
,
//
custom
URL
scheme
"ujet.cx/app"
//
universal
link,
]
if
! (
availableSchema.contains (
url.scheme
??
""
))
{
return
false
}
let
hostAndPath
=
String.init (
format:
"%@%@"
,
url.host
??
""
,
url.path )
if
! (
availableHostAndPath.contains (
hostAndPath ))
{
return
false
}
//
ujet://call?call_id ={
call_id }
& nonce
={
nonce }
//
https://ujet.co/app?call_id ={
call_id }
& nonce
={
nonce }
let
urlComponents:
URLComponents?
=
URLComponents (
url:
url,
resolvingAgainstBaseURL:
false
)
let
queryItems
=
urlComponents?.queryItems
let
callId
=
value (
forKey:
"call_id"
,
fromQueryItems:
queryItems )
//
validate
call
ID
if
!isValidCallId (
callId )
{
return
false
}
guard
let
nonce
=
value (
forKey:
"nonce"
,
fromQueryItems:
queryItems )
else
{
return
false
}
let
options
=
UJETStartOptions.init (
callId:
callId!,
nonce:
nonce )
UJET.start (
with:
options )
return
true
}
func
value (
forKey
key:
String?,
fromQueryItems
queryItems:
[
URLQueryItem ]
? )
->
String?
{
let
predicate
=
NSPredicate (
format:
"name=%@"
,
key
??
""
)
let
filtered
=
(
queryItems
as
NSArray? )
?.filtered (
using:
predicate )
as?
[
URLQueryItem ]
let
queryItem:
URLQueryItem?
=
filtered?.first
return
queryItem?.value
}
func
isValidCallId (
_
callId:
String? )
->
Bool
{
if
(
callId
??
""
)
.isEmpty
{
return
false
}
let
nonNumbers
=
CharacterSet.decimalDigits.inverted
let
r
=
callId?.rangeOfCharacter (
from:
nonNumbers )
return
r
==
nil
}
}

