Migration Guide: 2.x → 3.x

Migrate from deprecated Flows & Views APIs to the new Components API, and from OWTheme to the new elements customization system

Version 3.0.0 introduces:

  • OWUIComponents — a unified API that replaces both OWUIFlows and OWUIViews
  • OWCustomizationElements — granular element customization that replaces OWTheme and OWCommentActionsCustomizations
  • SwiftUI ViewsOpenWebPreConversation, OpenWebConversation, OpenWebConversationCount

SwiftUI Views (Recommended)

Version 3.0.0 adds native SwiftUI Views: OpenWebPreConversation, OpenWebConversation, and OpenWebConversationCount. If your app uses SwiftUI, this is the simplest migration path — especially if you were using OWUIViews to embed views in SwiftUI.

import OpenWebSDK

struct ArticleView: View {
    var body: some View {
        ScrollView {
            articleContent
            OpenWebPreConversation(postId: "my-post-id")
                .conversationNavigation(.fullScreen)
        }
    }
}

For full documentation, see SwiftUI Views.


UIKit: Accessing the New Components API

// Before
let flows: OWUIFlows = OpenWeb.manager.ui.flows
let views: OWUIViews = OpenWeb.manager.ui.views

// After
let components: OWUIComponents = OpenWeb.manager.ui.components

UIKit Flows → Components

Quick Reference

Before (Deprecated)After (OWUIComponents)
flows.preConversation(
presentationalMode:…
)
components.getPreConversation(
conversationNavigation:…
)
flows.conversation(
presentationalMode:…,
completion: OWDefaultCompletion
)
components.openConversation(…)
flows.conversation(
completion: OWViewControllerCompletion
)
components.getConversation(…)
flows.commentCreation(…)
components.openConversation(
route: .commentCreation(type: .comment)
)
flows.commentThread(…)
components.openConversation(
route: .commentThread(commentId:…)
)

PreConversation Flow

The presentationalMode parameter is replaced by conversationNavigation, which does not require a view controller reference — the SDK resolves it internally.

let flows = OpenWeb.manager.ui.flows

let view = try await flows.preConversation(
    postId: "somePostId",
    article: article,
    presentationalMode: .present(viewController: self, style: .fullScreen)
)
// Add view as subview
let components = OpenWeb.manager.ui.components

let view = try await components.getPreConversation(
    postId: "somePostId",
    article: article,
    conversationNavigation: .fullScreen
)
// Add view as subview

Navigation Mode Mapping

Before (OWPresentationalMode)After (OWConversationNavigationMode)
.present(viewController: vc, style: .fullScreen).fullScreen
.present(viewController: vc, style: .pageSheet).sheet
.push(navigationController: navController).push

Full Conversation Flow (Full Screen)

let flows = OpenWeb.manager.ui.flows

try await flows.conversation(
    postId: "somePostId",
    article: article,
    presentationalMode: .present(viewController: self, style: .fullScreen)
)
let components = OpenWeb.manager.ui.components

try await components.openConversation(
    postId: "somePostId",
    article: article,
    presentationalMode: .present(viewController: self, style: .fullScreen)
)

Full Conversation Flow (Partial Screen)

let flows = OpenWeb.manager.ui.flows

let conversationVC = try await flows.conversation(
    postId: "somePostId",
    article: article
)
let components = OpenWeb.manager.ui.components

let conversationVC = try await components.getConversation(
    postId: "somePostId",
    article: article
)

Comment Creation Flow

let flows = OpenWeb.manager.ui.flows

try await flows.commentCreation(
    postId: "somePostId",
    article: article,
    presentationalMode: .present(viewController: self, style: .pageSheet)
)
let components = OpenWeb.manager.ui.components

try await components.openConversation(
    postId: "somePostId",
    article: article,
    route: .commentCreation(type: .comment),
    presentationalMode: .present(viewController: self, style: .pageSheet)
)

Comment Thread Flow

let flows = OpenWeb.manager.ui.flows

try await flows.commentThread(
    postId: "somePostId",
    article: article,
    commentId: "someCommentId",
    presentationalMode: .push(navigationController: navController)
)
let components = OpenWeb.manager.ui.components

try await components.openConversation(
    postId: "somePostId",
    article: article,
    route: .commentThread(commentId: "someCommentId"),
    presentationalMode: .push(navigationController: navController)
)

UIKit Views → Components

📘

SwiftUI apps

If you were using OWUIViews to embed views in SwiftUI, consider migrating directly to the new SwiftUI Views (OpenWebPreConversation, OpenWebConversation) instead of going through OWUIComponents.

⚠️

These migrations are not simple renames. Key differences:

  • getConversation() returns a UIViewController, not a UIView. Embed it as a child view controller.
  • OWViewActionsCallbacks is removed — the SDK handles navigation internally. If you do need callbacks, use OWActionsCallbacks.
  • views.reportReason(), views.clarityDetails(), views.webTab(), views.commenterAppeal(), and views.notifications() no longer need to be called — the SDK handles them internally.

Quick Reference

Before (Deprecated)After (OWUIComponents)
views.preConversation(…,
callbacks: OWPreConversationActionsCallbacks
)
components.getPreConversation(…,
conversationNavigation: .custom(…)
)
views.preConversation(…,
callbacks: OWViewActionsCallbacks
)
components.getPreConversation(…,
conversationNavigation: .custom(…)
)
views.conversation(…)
-> UIView
components.getConversation(…)
-> UIViewController
views.commentCreation(…)
-> UIView
components.getConversation(…,
route: .commentCreation(type: .comment)
)
-> UIViewController
views.commentThread(…)
-> UIView
components.getConversation(…,
route: .commentThread(…)
)
-> UIViewController
views.reportReason(…)
views.clarityDetails(…)
views.webTab(…)
views.commenterAppeal(…)
views.notifications(…)
(removed — handled internally by the SDK)

PreConversation View with Custom Navigation

let views = OpenWeb.manager.ui.views

let view = try await views.preConversation(
    postId: "somePostId",
    article: article,
    additionalSettings: additionalSettings,
    callbacks: { callbackType, postId in
        switch callbackType {
        case .openConversationFlow(let route):
            // Navigate to the full conversation manually
        }
    }
)
// Add view as subview
let components = OpenWeb.manager.ui.components

let view = try await components.getPreConversation(
    postId: "somePostId",
    article: article,
    conversationNavigation: .custom(callbacks: { callbackType, postId in
        switch callbackType {
        case .openConversationFlow(let route):
            // Navigate to the full conversation manually
        }
    }),
    additionalSettings: additionalSettings
)
// Add view as subview

Conversation View (Embedding Change)

The old API returned a UIView you could add as a subview. The new API returns a UIViewController that must be embedded as a child view controller.

let views = OpenWeb.manager.ui.views

let conversationView = try await views.conversation(
    postId: "somePostId",
    article: article,
    additionalSettings: additionalSettings,
    callbacks: viewActionsCallbacks
)
// Add as subview
containerView.addSubview(conversationView)
let components = OpenWeb.manager.ui.components

let conversationVC = try await components.getConversation(
    postId: "somePostId",
    article: article,
    additionalSettings: additionalSettings,
    callbacks: actionsCallbacks
)
// Embed as child view controller
addChild(conversationVC)
containerView.addSubview(conversationVC.view)
conversationVC.didMove(toParent: self)

Comment Creation / Thread Views

Same pattern — replace views.commentCreation(…) and views.commentThread(…) with getConversation(route:…) and embed the returned view controller.

let conversationVC = try await components.getConversation(
    postId: "somePostId",
    article: article,
    route: .commentCreation(type: .comment)
)
// Embed as child view controller
let conversationVC = try await components.getConversation(
    postId: "somePostId",
    article: article,
    route: .commentThread(commentId: "someCommentId")
)
// Embed as child view controller

Callbacks

OWFlowActionsCallbacks → OWActionsCallbacks

OWFlowActionsCallbacks is now a typealias for OWActionsCallbacks. Your existing code compiles without changes.

public typealias OWActionsCallbacks = (OWActionCallbackType, OWViewSourceType, OWPostId) -> Void
public typealias OWFlowActionsCallbacks = OWActionsCallbacks

Similarly, OWFlowActionCallbackType is a typealias for OWActionCallbackType:

public enum OWActionCallbackType {
    case openPublisherProfile(ssoPublisherId: String, type: OWUserProfileType, presentationalMode: OWPresentationalMode)
    case conversationDismissed
}

OWViewActionsCallbacks — Removed

OWViewActionsCallbacks and its fine-grained OWViewActionCallbackType cases (.articleHeaderPressed, .closeConversationPressed, .communityGuidelinesPressed(url:), .openReportReason(…), etc.) have no direct replacement in OWUIComponents.

Use OWActionsCallbacks for the supported events (.openPublisherProfile, .conversationDismissed), and .custom(callbacks:) on OWConversationNavigationMode for pre-conversation navigation events.


OWConversationNavigationMode

New enum that replaces OWPresentationalMode in the pre-conversation context. The SDK resolves the parent view controller internally — you only specify the presentation style.

public enum OWConversationNavigationMode {
    case present(OWModalPresentationStyle)
    case push
    case custom(callbacks: OWPreConversationActionsCallbacks)

    public static let fullScreen = OWConversationNavigationMode.present(.fullScreen)
    public static let sheet = OWConversationNavigationMode.present(.pageSheet)
}

Customizations: OWTheme → elements

OWTheme is deprecated. Use OWCustomizationElements via OpenWeb.manager.ui.customizations.elements.

Property Mapping

Deprecated OWTheme propertyReplacement
skeletonColorcustomizations.elements.skeletonGradientEdge.color
skeletonShimmeringColorcustomizations.elements.skeletonGradientCenter.color
primarySeparatorColorcustomizations.elements.sectionDivider.color
secondarySeparatorColorcustomizations.elements.contentDivider.color
tertiarySeparatorColorcustomizations.elements.divider.color
primaryTextColorcustomizations.elements.commentBody.color
secondaryTextColorcustomizations.elements.subtitle.color
tertiaryTextColorcustomizations.elements.detail.color
primaryBackgroundColorcustomizations.elements.background.color
secondaryBackgroundColorcustomizations.elements.overlayBackground.color
tertiaryBackgroundColorcustomizations.elements.cardBackground.color
surfaceColor(removed, no replacement)
primaryBorderColorcustomizations.elements.border.color
secondaryBorderColor(removed, no replacement)
loaderColorcustomizations.elements.loader.color
brandColorcustomizations.elements.brand.color
voteUpUnselectedColorcustomizations.elements.voteUpUnselected.color
voteDownUnselectedColorcustomizations.elements.voteDownUnselected.color
voteUpSelectedColorcustomizations.elements.voteUpSelected.color
voteDownSelectedColorcustomizations.elements.voteDownSelected.color

Example

let customizations = OpenWeb.manager.ui.customizations

customizations.customizedTheme.primaryTextColor = OWColor(lightColor: .black, darkColor: .white)
customizations.customizedTheme.brandColor = OWColor(lightColor: .systemBlue, darkColor: .systemCyan)
let customizations = OpenWeb.manager.ui.customizations

customizations.elements.commentBody.color = UIColor(lightColor: .black, darkColor: .white)
customizations.elements.brand.color = UIColor(lightColor: .systemBlue, darkColor: .systemCyan)

Customizations: OWCommentActionsCustomizations → elements

DeprecatedReplacement
customizations.commentActions.fontStylecustomizations.elements.commentActions.fontWeight
customizations.commentActions.colorcustomizations.elements.commentActions.color

OWCommentActionsFontStyle → UIFont.Weight

fontStyle took an OWCommentActionsFontStyle enum. fontWeight takes UIFont.Weight?:

OWCommentActionsFontStyleUIFont.Weight?
.regularnil (default)
.semiBold.semibold

OWCommentActionsColor → UIColor?

color took an OWCommentActionsColor enum. The new color property takes UIColor?:

OWCommentActionsColorUIColor?
.defaultnil (default)
.brandColorAssign the same color you set on elements.brand.color

OWColor

OWColor is now a typealias for UIColor. Code using OWColor(lightColor:darkColor:) continues to work — UIColor has the same convenience initializer.

// Both work identically
OWColor(lightColor: .black, darkColor: .white)
UIColor(lightColor: .black, darkColor: .white)