Engagement starter on iOS
The Engagement Starter is a compact, self-contained widget that invites readers into the
conversation for a piece of content. It picks its own layout based on what the conversation
actually has to show:
| Variant | When it renders | What it shows |
|---|---|---|
| Default | No poll and no featured comment | Header, title, description and a call-to-action button |
| Editor's pick | The conversation has a pinned or picked comment | The featured comment with its author, like/dislike/reply row, and the call to action |
| Poll | The conversation has a poll and polls are enabled for the site | The poll question, its options, and (after voting) results |
A pinned comment takes precedence over an editor's pick, and a poll takes precedence over
both.
In the iOS SDK the widget is named Conversation Starter
(VFConversationStarterViewController), which is the same widget as the web Engagement
Starter.
Requires ViafouraCore 1.3.1 or newer.
Behaviour worth knowing before you integrate
- It never creates a conversation. The widget reads the comment container without
creating it, so rendering the Engagement Starter on an article that has never been
commented on does not bring a conversation into existence. When the container does not
exist yet, the default variant is shown. - It hides itself when there is nothing to say. If the conversation is hidden, or closed
with zero comments, and there is no poll, the widget hides its content and reports a
height of0. Size the host view off the height delegate rather than a fixed height. - The call to action adapts to the comment count. With at least
minimumCommentCount
comments the button shows the count; with zero comments it invites the first comment;
otherwise it drops the count. The default is
VFConversationStarterDefaults.minimumCommentCount(5). - On the poll variant the call to action only appears once results are visible — that is,
after the reader votes or once the poll is closed. - The dislike button on the featured comment is only rendered when dislikes are enabled
for the site, and voting on a poll prompts login only when the site requires
authentication to vote. - Analytics are on by default. The widget logs its own load, view and click events with a
subtype ofdefault,editor_pickorpoll. The view event fires the first time the
widget is actually on screen — when it is embedded in a scroll view, the SDK tracks that
scroll view to decide when the widget becomes visible. To honour a reader's consent
choice, callViafouraSDK.setDataTrackingEnabled(false); nothing else needs to change.
UIKit
import ViafouraSDK
let starter = VFConversationStarterViewController.new(
containerId: "YOUR_CONTAINER_ID",
articleMetadata: VFArticleMetadata(
url: articleURL,
title: "Article title",
subtitle: "Article subtitle",
thumbnailUrl: thumbnailURL
),
loginDelegate: self,
settings: VFSettings(colors: VFColors(colorPrimary: .systemBlue)),
title: nil, // optional override for the default variant's title
description: nil, // optional override for its description
minimumCommentCount: 5, // VFConversationStarterDefaults.minimumCommentCount
syndicationKey: nil
)
starter.setTheme(theme: .light)
starter.setLayoutDelegate(layoutDelegate: self)
starter.setCustomUIDelegate(customUIDelegate: self)
addChild(starter)
view.addSubview(starter.view)
starter.didMove(toParent: self)Pass a sectionUUID to the second new(...) overload if the content lives in a section
other than your site's default; otherwise the site UUID is used.
Actions
starter.setActionCallbacks { action in
switch action {
case .seeMoreCommentsPressed:
// Reader tapped the call to action — open your comments screen
break
case .writeNewCommentPressed(let actionType):
// Reader tapped "Reply" on the featured comment; actionType is .reply(content:)
break
case .openProfilePressed(let userUUID, let presentationType):
// Reader tapped the featured comment's author
break
case .commentLiked(let contentUUID), .commentDisliked(let contentUUID):
// Reader reacted to the featured comment
break
case .authPressed(let promptType):
// Reader reacted or voted while logged out
break
default:
break
}
}VFLoginDelegate.startLogin() is also called when a logged-out reader likes, dislikes or
votes, so present your login flow there.
Height
The widget reports its own height through VFLayoutDelegate, in points:
func containerHeightUpdated(viewController: VFUIViewController, height: CGFloat) {
starterHeightConstraint.constant = height
}The height is 0 whenever the widget hides itself, so a constraint driven by this callback
collapses the widget without leaving a gap.
SwiftUI
import ViafouraSDK
VFConversationStarterView(
containerId: "YOUR_CONTAINER_ID",
articleMetadata: articleMetadata,
settings: settings,
title: nil,
description: nil,
minimumCommentCount: 5,
onLogin: { presentLogin() },
onAction: { action in
if case .seeMoreCommentsPressed = action {
openComments()
}
},
onHeightChange: { height in
// Optional — the view already sizes itself when autoSize is true
}
)VFConversationStarterView sizes itself by default (autoSize: true) and follows the
environment colour scheme unless you pass an explicit theme:.
Refreshing
starter.reload() // re-fetch the container, featured comment and poll
starter.reloadPoll() // re-fetch only the poll, e.g. after a poll changes elsewhereStyling
Colours, fonts and theme come from the VFSettings you pass in, and
setTheme(theme:) switches the theme at runtime.
For finer control, VFCustomUIDelegate hands you these views:
VFCustomizableView case | View |
|---|---|
conversationStarterBackgroundView | The widget's root view |
conversationStarterHeaderLabel | The small header above the title |
conversationStarterTitleLabel | The default variant's title |
conversationStarterDescriptionLabel | The default variant's description |
conversationStarterFeaturedCommentLabel | The featured comment's text |
conversationStarterActionButton | The call-to-action button |
func customizeView(theme: VFTheme, view: VFCustomizableView) {
if case .conversationStarterActionButton(let button) = view {
button.layer.cornerRadius = 20
}
}Updated about 7 hours ago
