Stay organized with collections
Save and categorize content based on your preferences.
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 Podfile and add dependencies to the target, as in the following
code sample:
[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Hard to understand","hardToUnderstand","thumb-down"],["Incorrect information or sample code","incorrectInformationOrSampleCode","thumb-down"],["Missing the information/samples I need","missingTheInformationSamplesINeed","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2025-09-04 UTC."],[[["\u003cp\u003eThis guide outlines the steps to integrate the iOS SDK for Contact Center AI Platform (CCAI Platform) into a Flutter-based iOS application.\u003c/p\u003e\n"],["\u003cp\u003eIntegration of the SDK is achieved using CocoaPods, which requires modifying the \u003ccode\u003ePodfile\u003c/code\u003e and running the \u003ccode\u003epod install\u003c/code\u003e command.\u003c/p\u003e\n"],["\u003cp\u003eSetting up iOS push notifications involves adding specific code to the \u003ccode\u003eAppDelegate.swift\u003c/code\u003e file and adding the \u003ccode\u003ePushKit.framework\u003c/code\u003e to the app's target.\u003c/p\u003e\n"],["\u003cp\u003eDeep linking is implemented by adding code to the \u003ccode\u003eAppDelegate.swift\u003c/code\u003e file, managing both custom URL schemes and universal links to handle different methods of opening the application.\u003c/p\u003e\n"],["\u003cp\u003eThe deep linking setup also includes the implementation of functions to handle and validate the \u003ccode\u003ecall_id\u003c/code\u003e and \u003ccode\u003enonce\u003c/code\u003e parameters passed through the deep link URLs.\u003c/p\u003e\n"]]],[],null,["# Flutter for iOS\n\nYou can set up mobile apps to work with Contact Center AI Platform (CCAI Platform) in a number of\nways, including with Flutter. This page shows you how to integrate the iOS\nSDK into an iOS app using Flutter.\n\nBefore you begin\n----------------\n\nBefore you following the instructions on this page, you must first follow the\ninstruction in [Integrate using Flutter](/contact-center/ccai-platform/docs/mobileSDK-overview#integrate-using-flutter).\n\nIntegrate the SDK using CocoaPods\n---------------------------------\n\nTo integrate the SDK using CocoaPods, follow these steps:\n\n1. Open `Podfile` and add dependencies to the target, as in the following\n code sample:\n\n UJETPodspec = { :podspec =\u003e 'https://sdk.ujet.co/ios/UJET.podspec' }\n\n pod 'UJET', UJETPodspec\n pod 'UJET/Cobrowse', UJETPodspec\n\n2. In the terminal, go to the `example/ios` directory and run the `pod install`\n command to install dependencies.\n\nSet up iOS push notifications\n-----------------------------\n\nTo set up iOS push notifications, follow these steps:\n\n1. In XCode, open `example/ios/Runner.xcodeproj`.\n\n2. Add the following code to your `AppDelegate.swift` file:\n\n import PushKit\n import UJETKit\n\n @UIApplicationMain\n @objc class AppDelegate: FlutterAppDelegate {\n override func application(\n _ application: UIApplication,\n didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n ) -\u003e Bool {\n // Register Flutter Plugins\n GeneratedPluginRegistrant.register(with: self)\n UJETModule.register(with: self.registrar(forPlugin: \"UjetModule\")!)\n UJETModule.onInitDone = {\n // setup push notification\n let voipRegistry = PKPushRegistry(queue: DispatchQueue.main)\n voipRegistry.desiredPushTypes = Set([PKPushType.voIP])\n voipRegistry.delegate = self\n UNUserNotificationCenter.current().delegate = self\n }\n return super.application(application, didFinishLaunchingWithOptions: launchOptions)\n }\n }\n\n // Extension for Push Notification\n\n extension AppDelegate {\n func tokenFromData(data: Data) -\u003e String {\n return data.map { String(format: \"%02x\", $0) }.joined()\n }\n\n override func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {\n print(\"apns token: \", tokenFromData(data: deviceToken))\n\n UJET.updatePushToken(deviceToken, type: .APN)\n }\n\n override func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {\n UJET.updatePushToken(nil, type: .APN)\n }\n\n override func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -\u003e Void) {\n handleNotification(userInfo: userInfo, completion: nil)\n }\n }\n\n // MARK: PushKit\n\n extension AppDelegate: PKPushRegistryDelegate {\n func pushRegistry(_ registry: PKPushRegistry, didUpdate credentials: PKPushCredentials, for type: PKPushType) {\n print(\"voip token: \", tokenFromData(data: credentials.token))\n\n if type == .voIP {\n UJET.updatePushToken(credentials.token, type: .voIP)\n }\n }\n\n func pushRegistry(_ registry: PKPushRegistry, didInvalidatePushTokenFor type: PKPushType) {\n if type == .voIP {\n UJET.updatePushToken(nil, type: .voIP)\n }\n }\n\n func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -\u003e Void) {\n if type == .voIP {\n handleNotification(userInfo: payload.dictionaryPayload, completion: completion)\n }\n }\n }\n\n extension AppDelegate {\n // handle push received in foreground state\n override func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -\u003e Void) {\n let userInfo = notification.request.content.userInfo\n handleNotification(userInfo: userInfo, completion: nil)\n }\n\n // handle push received and tapped in background state\n override func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -\u003e Void) {\n let userInfo = response.notification.request.content.userInfo\n handleNotification(userInfo: userInfo, completion: nil)\n }\n }\n\n private func handleNotification(userInfo: [AnyHashable: Any], completion: (() -\u003e Void)?) {\n if userInfo[\"ujet\"] != nil {\n UJET.receivedNotification(userInfo, completion: completion)\n } else {\n // Handle your notification here\n completion?()\n }\n }\n\n3. Select your `.xcodeproj` and then your app target. Add `PushKit.framework`\n in the `Frameworks, Libraries, and Embedded Content` section.\n\nSet up deep linking\n-------------------\n\nTo set up deep linking, add the following code: \n\n // Extension for deep linking\n\n extension AppDelegate {\n override func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -\u003e Bool {\n print(\"Open app with url: \\(url.absoluteString)\")\n\n return self.handleRouting(url)\n }\n\n override func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -\u003e Void) -\u003e Bool {\n // Universal links\n if NSUserActivityTypeBrowsingWeb == userActivity.activityType {\n return self.handleRouting(userActivity.webpageURL!)\n } else if userActivity.activityType == \"INStartAudioCallIntent\" {\n // Open app from Call history\n UJET.start(with: UJETStartOptions())\n\n return true\n }\n return false\n }\n\n func handleRouting(_ url: URL) -\u003e Bool {\n let availableSchema = [\n \"ujetrn\", // TODO: Change to your custom URL scheme. Config from Portal \u003e Developer Settings \u003e Mobile App \u003e Enable Send SMS to Download App \u003e iOS App \u003e URL\n \"https\" // universal link\n ]\n let availableHostAndPath = [\n \"call\", // custom URL scheme\n \"ujet.cx/app\" // universal link,\n ]\n\n if !(availableSchema.contains(url.scheme ?? \"\")) {\n return false\n }\n\n let hostAndPath = String.init(format: \"%@%@\", url.host ?? \"\", url.path)\n if !(availableHostAndPath.contains(hostAndPath)) {\n return false\n }\n\n // ujet://call?call_id={call_id}&nonce={nonce}\n // https://ujet.co/app?call_id={call_id}&nonce={nonce}\n let urlComponents: URLComponents? = URLComponents(url: url, resolvingAgainstBaseURL: false)\n let queryItems = urlComponents?.queryItems\n let callId = value(forKey: \"call_id\", fromQueryItems: queryItems)\n\n // validate call ID\n if !isValidCallId(callId) {\n return false\n }\n\n guard let nonce = value(forKey: \"nonce\", fromQueryItems: queryItems) else {\n return false\n }\n\n let options = UJETStartOptions.init(callId: callId!, nonce: nonce)\n\n UJET.start(with: options)\n\n return true\n }\n\n func value(forKey key: String?, fromQueryItems queryItems: [URLQueryItem]?) -\u003e String? {\n let predicate = NSPredicate(format: \"name=%@\", key ?? \"\")\n let filtered = (queryItems as NSArray?)?.filtered(using: predicate) as? [URLQueryItem]\n let queryItem: URLQueryItem? = filtered?.first\n\n return queryItem?.value\n }\n\n func isValidCallId(_ callId: String?) -\u003e Bool {\n if (callId ?? \"\").isEmpty {\n return false\n }\n\n let nonNumbers = CharacterSet.decimalDigits.inverted\n let r = callId?.rangeOfCharacter(from: nonNumbers)\n\n return r == nil\n }\n }"]]