Shopping
Shopping Integration
Stories created in JOIN Studio can include shopping CTAs such as Add to Cart, See Cart, and Shopping Redirect. On iOS, these are delivered through the JOINStoriesListenerDelegate
. Implement the callbacks in your app and wire them via JOINStoriesListener.setDelegate
.
Events and callback signatures
- Add to Cart
- Signature:
public protocol JOINStoriesListenerDelegate: AnyObject {
// ...
func joinStoriesAnalyticsCallback(event: JOINTrackingEvent, payload: [String: Any?])
func addToCart(offerId: String, completion: @escaping (Bool) -> Void)
func seeToCart()
func shoppingRedirect(offerId: String?)
}
-
Payload: offerId (may be an empty string when not provided by content)
-
Completion: call completion(true) on success or completion(false) on failure so the SDK can update its UI (e.g., stop loader, show confirmation)
-
Typical use: add the item to your app cart, update UI, optionally dismiss the player
-
See Cart
- Signature:
func seeToCart()
- Payload: none
- Typical use: dismiss the player and navigate to your app’s cart screen
- Signature:
-
Shopping Redirect
- Signature:
func shoppingRedirect(offerId: String?)
- Payload: offerId (may be empty or nil)
- Typical use: open a PDP or handle a deep link inside your app; if external, route with your web/deep-link handler
- Signature:
Notes
- Delivery: callbacks are invoked on the main thread.
- Acknowledgment: for Add to Cart, you must invoke completion(Bool) to acknowledge completion; you do not return a value from the method itself.
- Robustness: handle missing or empty offerId gracefully.
- Analytics: if needed, observe joinStoriesAnalyticsCallback(event:payload:) for additional tracking.
Wiring the delegate
Set your delegate as early as convenient (e.g., AppDelegate or scene start):
public final class JOINStoriesListener {
public static func setDelegate(delegate: JOINStoriesListenerDelegate?) {
Listener.shared.setDelegate(delegate: delegate)
}
}
Example:
import UIKit
import JOINStoriesSDK
@main
class AppDelegate: UIResponder, UIApplicationDelegate, JOINStoriesListenerDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
JOINStories.initSDK(teamId: "YOUR_TEAM_ID")
JOINStoriesListener.setDelegate(delegate: self)
return true
}
// MARK: Shopping callbacks
func addToCart(offerId: String, completion: @escaping (Bool) -> Void) {
Cart.shared.add(offerId: offerId) { success in
completion(success)
if success {
// Optional UX: toast/snackbar
}
}
}
func seeToCart() {
JOINStories.dismissPlayer() // avoid overlapping UIs
Router.shared.navigate(to: .cart)
}
func shoppingRedirect(offerId: String?) {
guard let id = offerId, !id.isEmpty else {
return
}
Router.shared.navigate(to: .product(id: id))
}
func joinStoriesAnalyticsCallback(event: JOINTrackingEvent, payload: [String : Any?]) {
Analytics.log("join_\(event)", payload)
}
}
Best practices
- Keep UI responsive: do not block the main thread; show lightweight toasts/snackbars and offload heavy work to async code.
- Dismiss before navigation: if a story player is visible, call
JOINStories.dismissPlayer()
before pushing a new view controller to avoid overlapping UIs. - Error handling: if cart updates fail, communicate failure to the user and allow retry; always call completion(false) on failure.
- Testing: verify that each CTA set in JOIN Studio triggers the expected callback and that empty or unknown offerId values are handled safely.
Updated 6 days ago