Flutter for iOS

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:

  1. Open Podfile and 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 
  2. In the terminal, go to the example/ios directory and run the pod install command to install dependencies.

Set up iOS push notifications

To set up iOS push notifications, follow these steps:

  1. In XCode, open example/ios/Runner.xcodeproj.

  2. Add the following code to your AppDelegate.swift file:

    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?()  } } 
  3. Select your .xcodeproj and then your app target. Add PushKit.framework in the Frameworks, Libraries, and Embedded Content section.

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  } }