Anda dapat menyiapkan aplikasi seluler agar berfungsi dengan Contact Center AI Platform (CCAI Platform) dengan berbagai cara, termasuk dengan Flutter. Halaman ini menunjukkan cara mengintegrasikan iOS SDK ke aplikasi iOS menggunakan Flutter.
Sebelum memulai
Sebelum mengikuti petunjuk di halaman ini, Anda harus mengikuti petunjuk di Mengintegrasikan menggunakan Flutter terlebih dahulu.
Mengintegrasikan SDK menggunakan CocoaPods
Untuk mengintegrasikan SDK menggunakan CocoaPods, ikuti langkah-langkah berikut:
Buka
Podfiledan tambahkan dependensi ke target, seperti dalam contoh kode berikut:UJETPodspec = { :podspec => 'https://sdk.ujet.co/ios/UJET.podspec' } pod 'UJET', UJETPodspec pod 'UJET/Cobrowse', UJETPodspecDi terminal, buka direktori
example/iosdan jalankan perintahpod installuntuk menginstal dependensi.
Menyiapkan notifikasi push iOS
Untuk menyiapkan notifikasi push iOS, ikuti langkah-langkah berikut:
Di XCode, buka
example/ios/Runner.xcodeproj.Tambahkan kode berikut ke file
AppDelegate.swiftAnda: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?() } }Pilih
.xcodeprojAnda, lalu target aplikasi Anda. TambahkanPushKit.frameworkdi bagianFrameworks, Libraries, and Embedded Content.
Menyiapkan deep linking
Untuk menyiapkan deep linking, tambahkan kode berikut:
// 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
}
}