iOS: Integrating Live Chat as an Embedded View

Updated 

The Live Chat SDK provides flexibility to integrate Messenger directly into your app’s UI. Instead of launching Messenger as a standalone activity, you can embed it as a fragment within your host application. This approach allows the chat experience to coexist seamlessly with your app’s navigation, layouts, and lifecycle management.

1. Add Messenger view controller in your app layout

Add SPRMessengerViewController as a child and pin it using your custom layout constraints.

let vc = SPRMessengerViewController(launchOptions: launchOptions)addChild(vc)let messengerView = vc.view!messengerView.translatesAutoresizingMaskIntoConstraints = falsecontainerView.addSubview(messengerView)// Add your own constraints based on parent app layout.NSLayoutConstraint.activate([  messengerView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),  messengerView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),  messengerView.topAnchor.constraint(equalTo: containerView.topAnchor),  messengerView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),])vc.didMove(toParent: self)

SPRMessengerViewController *vc =    [[SPRMessengerViewController alloc] initWithLaunchOptions:launchOptions];[self addChildViewController:vc];UIView *messengerView = vc.view;messengerView.translatesAutoresizingMaskIntoConstraints = NO;[self.containerView addSubview:messengerView];// Add your own constraints based on parent app layout.[NSLayoutConstraint activateConstraints:@[  [messengerView.leadingAnchor constraintEqualToAnchor:self.containerView.leadingAnchor],  [messengerView.trailingAnchor constraintEqualToAnchor:self.containerView.trailingAnchor],  [messengerView.topAnchor constraintEqualToAnchor:self.containerView.topAnchor],  [messengerView.bottomAnchor constraintEqualToAnchor:self.containerView.bottomAnchor],]];[vc didMoveToParentViewController:self];

2. Handle close in parent app

Set a close handler and restore parent app UI when Messenger is closed.

SPRMessenger.shared().setSPRMessengerCloseHandler {
// Restore parent app state/screen after Messenger close.
self.restoreParentAppAfterMessengerClose()
}

[[SPRMessenger shared] setSPRMessengerCloseHandler:^{  // Restore parent app state/screen after Messenger close.  [self restoreParentAppAfterMessengerClose];}];

Clear handler when needed:

SPRMessenger.shared().setSPRMessengerCloseHandler(nil)

[[SPRMessenger shared] setSPRMessengerCloseHandler:nil];

3. Notify layout changes

If parent app layout changes Messenger container size without recreating the view controller, call the updateMessengerWindowLayout method.

Example scenario: Suppose your app supports device rotation or split-screen multitasking, and the size of the container view holding the Messenger changes. In such cases, after updating your layout constraints, call the Messenger’s layout update method to ensure the chat UI resizes correctly.

SPRMessenger.shared().updateMessengerWindowLayout()

[[SPRMessenger shared] updateMessengerWindowLayout];

4. Handle open-from-notification in embedded flow

This section explains how to manage the flow when Live Chat is opened from a notification in an embedded setup.

Components and Methods:

  • setOpenEmbeddedSPRMessengerHandler: A callback that must be registered in your app. It is triggered when Live Chat is opened from notifications, and the SDK needs your app to show the embedded Live Chat UI first.

  • LiveChatViewController: Your app’s custom view controller responsible for displaying the embedded Live Chat UI. It manages all chat-related interactions and should be reused if already present in the navigation stack, or created when needed.

  • handleNotificationIfNeeded: A method on LiveChatViewController that processes any pending notification data. When a notification (such as an in‑app or push notification) is clicked, it opens the Live Chat view inside LiveChatViewController.

    If LiveChatViewController manages multiple child view controllers, this method ensures the correct chat screen or context (such as a specific thread or message) is displayed. Always call this method after the Live Chat UI is visible, so the notification’s context is handled appropriately.

Note: Call onComplete only after the Live Chat controller is visible to the user.

SPRMessenger.shared().setOpenEmbeddedSPRMessengerHandler { onComplete in    // Navigate/switch to your embedded Live Chat screen.    self.openEmbeddedLiveChatScreen(onComplete)}// Example: Opening Embedded Live Chat Screenfunc openEmbeddedLiveChatScreen(onComplete: (() -> Void)?) {    // 1. Get navigation controller    guard let root = window?.rootViewController,          let nav = resolveNavigationController(from: root) else {        return    }    // 2. Reuse existing Live Chat if present    if let existing = findExistingLiveChat(in: nav) {        nav.popToViewController(existing, animated: true)        DispatchQueue.main.async {            existing.handleNotificationIfNeeded()            onComplete?()        }        return    }    // 3. Create new Live Chat screen    let launchOpts = cachedLaunchOptions    let liveChat = LiveChatViewController(launchOptions: launchOpts)    nav.pushViewController(liveChat, animated: true)    // 4. Call onComplete AFTER UI is visible    DispatchQueue.main.async {        liveChat.handleNotificationIfNeeded()        onComplete?()    }}

[[SPRMessenger shared] setOpenEmbeddedSPRMessengerHandler:^(void (^onComplete)(void)) {    // Navigate/switch to your embedded Live Chat screen.    [self openEmbeddedLiveChatScreenWithCompletion:onComplete];}];// Example: Opening Embedded Live Chat Screen- (void)openEmbeddedLiveChatScreenWithCompletion:(void (^ _Nullable)(void))onComplete {    // 1. Get root / navigation controller    UIViewController *root = self.window.rootViewController;    UINavigationController *nav = [self resolveNavigationControllerFrom:root];    if (!nav) { return; }    // 2. If Live Chat already exists → reuse it    LiveChatViewController *existing = [self findExistingLiveChatIn:nav];    if (existing) {        [nav popToViewController:existing animated:YES];        // Ensure UI is visible before calling onComplete        dispatch_async(dispatch_get_main_queue(), ^{            [existing handleNotificationIfNeeded];            if (onComplete) { onComplete(); }        });        return;    }    // 3. Create new Live Chat screen    NSDictionary *launchOpts = self.cachedLaunchOptions;    LiveChatViewController *liveChat =        [[LiveChatViewController alloc] initWithLaunchOptions:launchOpts];    [nav pushViewController:liveChat animated:YES];    // 4. Call onComplete AFTER screen is mounted/visible    dispatch_async(dispatch_get_main_queue(), ^{        [liveChat handleNotificationIfNeeded];        if (onComplete) { onComplete(); }    });}