iOS Setup
Hub
Requires SDK 4.13.0 or later
The Hub (previously known as the Communication Hub) is an embeddable app area that includes an inbox of posts and other features. It is intended to be embedded within a tab bar item in the app, and includes support for badging the tab bar item when new content is available.
Hub
This feature is distinct from the legacy "Inbox" feature. The legacy Inbox (InboxViewController et al) is deprecated and will be removed in a future release.
Configuration Change
The title and accentColor parameters were previously documented here. These are now configured server-side, so they are now ignored and can be removed from your code.
Prerequisites
Ensure that you have adopted silent push support, as described in the Background Content-Available Push section of the Push Notifications guide. This is needed for properly badging the app in the background when a new Inbox post becomes available.
App Icon Badging
On iOS, the Rover SDK automatically badges the app icon with the post unread count when posts arrive via silent push notifications. This happens in the background without requiring the app to be actively running, providing users with a visual indicator of new content even when the app is closed.
SwiftUI
Add the HubView to your SwiftUI TabView, define a tab item for it, and observe the Rover badge count to update the tab bar badge:
import RoverNotifications
/// An example of how to embed the Rover Hub in a SwiftUI tab.
struct ContentView: View {
@ObservedObject var roverBadge: RoverBadge = Rover.shared.roverBadge
var body: some View {
TabView {
// --- app's other tabs are here ---
HubView()
.tabItem {
Label("For you", systemImage: "person.circle.fill")
}
// Observe the Rover badge count and update the tab bar badge.
.badge(roverBadge.newBadge)
}
}
}
UIKit
Add the HubHostingController to your UITabBarController, set up the tab bar item, and observe the Rover badge count publisher using Combine to update the tab bar badge:
import UIKit
import Combine
import RoverNotifications
class TabBarController: UITabBarController {
private var cancellables = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
// Create the Hub hosting controller
let hubController = HubHostingController()
// Set up the tab bar item
hubController.tabBarItem = UITabBarItem(
title: "For You",
image: UIImage(systemName: "person.circle.fill"),
tag: 0
)
// Add other view controllers as needed
viewControllers = [
// --- app's other tab view controllers are here ---
hubController
]
// Observe badge changes using Combine
Rover.shared.roverBadge.$newBadge
.receive(on: DispatchQueue.main)
.sink { [weak self] badgeValue in
hubController.tabBarItem.badgeValue = badgeValue
}
.store(in: &cancellables)
}
}
Revealing the Hub from a Push Notification or Deep Link
When a user taps a push notification for a post or a conversation, or opens a Rover deep link such as rv-myapp://posts/{id}, the SDK needs to get the Hub on screen and then navigate to that post. The SDK cannot switch your app's tabs or present your screens for you, so it asks your app to do that half by opening a deep link that your app owns. This is the Hub deep link, and it is configured in the Rover dashboard under Settings → Channels → Mobile → App Hub → Deep link. The same value is used for both iOS and Android. The SDK only uses it while Enable Inbox is also switched on for the App Hub.
Once the Hub deep link is set, the flow is:
- The SDK records the destination (the post or conversation) in the Hub's own navigation state.
- The SDK opens the Hub deep link with
UIApplication.shared.open(_:). - Your app receives the URL and navigates to wherever it shows the Hub.
- As soon as the
HubView(orHubHostingController) is on screen, it reads the destination back and pushes the post or conversation.
If the Hub deep link is not set, the SDK skips this handoff and presents the post or conversation modally in its own standalone screen, on top of whatever is currently visible. That works, but the user is not left in the Hub afterwards.
To support the Hub deep link, your app has to do two things.
1. Choose and register a URL your app owns
Pick a URL with your app's own custom scheme, for example myapp://hub. Do not use your Rover rv- scheme for this: that scheme is for links the SDK handles, and this one is handled by you.
Register the scheme under "URL types" in your Info.plist, exactly as described in Prepare Your App on the Deep Links page, but with your own scheme. Then give the URL to whoever administers your Rover dashboard so it can be entered in the App Hub Deep link field.
2. Handle the URL by navigating to the Hub
When the URL arrives, navigate to the screen that contains the Hub. Keep your existing router-first handler from the Deep Links page: Rover.shared.router.handle(url) returns false for this URL because it is not a Rover link, and your own code then takes over. You do not need to tell the SDK anything, the Hub picks up the destination on its own when it appears.
Note that your URL handler runs twice for a single notification tap. The first call carries the Rover link from the notification (rv-myapp://posts/…), which you hand to Rover.shared.router as usual. Handling it is what makes the SDK open the Hub deep link, so the second call carries myapp://hub, which the router declines and you handle by navigating to the Hub.
SwiftUI
Track the selected tab in state and select the Hub tab when the URL arrives, alongside your existing Rover router handling:
import RoverNotifications
@main
struct MyApp: App {
@State private var selectedTab: Tab = .home
enum Tab { case home, hub }
var body: some Scene {
WindowGroup {
TabView(selection: $selectedTab) {
HomeView()
.tabItem { Label("Home", systemImage: "house") }
.tag(Tab.home)
HubView()
.tabItem { Label("For you", systemImage: "person.circle.fill") }
.tag(Tab.hub)
}
.onOpenURL { url in
// First call: the Rover link from the notification (rv-myapp://posts/…).
// Handling it makes the SDK open the Hub deep link below.
if Rover.shared.router.handle(url) {
return
}
// Second call: the Hub deep link configured on the dashboard is ours to handle.
if url.scheme == "myapp" && url.host == "hub" {
selectedTab = .hub
}
}
}
}
}
UIKit App Delegate
Select the Hub's tab from application(_:open:options:):
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
// First call: the Rover link from the notification (rv-myapp://posts/…).
// Handling it makes the SDK open the Hub deep link below.
if Rover.shared.router.handle(url) {
return true
}
// Second call: the Hub deep link configured on the dashboard is ours to handle.
if url.scheme == "myapp" && url.host == "hub" {
// `tabBarController` is your app's root UITabBarController and
// `hubController` is the HubHostingController you added to it.
tabBarController.selectedViewController = hubController
return true
}
return false
}
If your app presents the Hub modally rather than embedding it in a tab, your handler for the Hub deep link must present the HubHostingController (or a sheet containing HubView) itself, dismissing anything already presented on top of it first. The SDK never presents the whole Hub on its own, so a Hub deep link whose handler does not put the Hub on screen leaves the post waiting in a Hub the user cannot see.
Testing the Hub deep link
On a simulator with the app installed, fire the URL and check that your app brings the Hub forward (on a device, type the URL into Safari's address bar instead):
xcrun simctl openurl booted "myapp://hub"
Then, with the Hub deep link entered in the dashboard, send yourself a test post from the Rover dashboard, tap the notification, and confirm the app lands inside the Hub on that post rather than on a standalone modal screen.
Test both halves before shipping
The SDK relies on your app being able to open the Hub deep link and navigate to the Hub when it arrives, and it does not report a problem with either. If the scheme is not registered in your Info.plist, the open fails and the screen the user was on stays visible. If the link opens your app but your handler does not navigate to the Hub, the post stays queued until the user next opens the Hub on their own. Check both with the test above.