Player API reference
- Web
- Swift
- Kotlin
After setting up your onBambuserLiveShoppingReady
handler (example below) you can start customizing the behavior of your player.
<head>
<script>
window.onBambuserLiveShoppingReady = player => {
// player = Bambuser Player API
};
</script>
</head>
Note that the player
object and its properties (constants and methods below) are only accessible within the onBambuserLiveShoppingReady
method body.
You should define the onBambuserLiveShoppingReady
method before you call initBambuserLiveShopping
to initialize any instances of the player when embedding the code snippet.
Once you setup BambuserCommerceSDK in SPM, you can initialize the SDK with environment and create a player view instance.
// Import the SDK
import BambuserCommerceSDK
// Initialize the Bambuser video player with the server region
// You can choose between .US or .EU based on your region
let videoPlayer = BambuserVideoPlayer(server: .US) // or .EU
let playerView = videoPlayer.createPlayerView(
videoConfiguration: .init(
type: .live(id: "xxx"),
// List of events to listen to; use ["*"] for all events (currently only option available)
events: ["*"],
// Configuration settings for the player
// More options can be found later:
configuration: ["key": "value"] // pass configuration based on requirement. It can be [String: Any]
)
playerView.delegate = self
)
First, add a new maven repository to your dependency resolution management, and then add the dependency into your app/build.gradle:
repositories {
google()
mavenCentral()
// other repositories you might have...
maven {
url "https://repo.repsy.io/mvn/bambuser/bambuser-commerce-sdk"
}
}
// In gradle
implementation("com.bambuser:commerce-sdk:$insert the latest version")
You need to initialize the SDK before using it. In your Application class, add the following code, and then use the composable function LiveView. This function would require two mandatory parameters:
-
This is the configuration for the video player.videoConfiguration
-
This is the delegate to receive events and errors.videoPlayerDelegate
globalBambuserSDK = BambuserSDK(
applicationContext = this,
organizationServer = OrganizationServer.US,
)
euBambuserSDK = BambuserSDK(
applicationContext = this,
organizationServer = OrganizationServer.EU,
)
Create liveview from either of the reference
application.globalBambuserSDK.GetLiveView(
modifier = Modifier.padding(innerPadding),
// This is the configuration for the player
videoConfiguration = BambuserVideoConfiguration(
// Pass list of events you want to receive
// Or "*" to receive all events
events = listOf("*"),
// Pass the configuration for the video player
// You can find more useful configurations in the documentation
// https://bambuser.com/docs/live/player-api-reference/#constants
configuration = mapOf(
"buttons" to mapOf(
"dismiss" to "none",
),
"currency" to "USD", // Mandatory for product hydration
"autoplay" to false,
),
// Pass the asset you want to play, we support only Live for now
videoType = BambuserVideoAsset.Live(id),
)
)
Constants
A list of available constants to be used throughout the player integration.
player.BUTTON
Used to dictate the functionality of certain buttons inside the player. It includes the below behavior options.
For player configurations in web/JavaScript, the constants (e.g., player.BUTTON.CLOSE
) are used, while in Swift and Kotlin the string values (e.g., "close"
) are used.
Button behavior options
Web | SDK | Behavior |
---|---|---|
player.BUTTON.AUTO | "auto" | The default behavior (actual behavior depending on context). |
player.BUTTON.CLOSE | "close" | Close the player overlay. |
player.BUTTON.LINK | "link" | (Web only) The button should behave like a link, relative to the current context. Opens the product URL/ checkout URL in a new browser tab. |
player.BUTTON.MINIMIZE | "minimize" | Minimize the player and act accordingly for minimized context. |
player.BUTTON.NONE | "none" | Does nothing, and the button may be hidden depending on the context. |
player.BUTTON.EVENT | "event" | Triggers an event that can be handled by your custom event handler. |
Usage example:
- Web
- Swift
- Kotlin
player.configure({
buttons: {
// Set checkout button behavior using a constant
checkout: player.BUTTON.MINIMIZE,
},
});
let videoConfiguration = BambuserVideoPlayerConfiguration(
type: .live(id: "xxx"),
// List of events to listen to; use ["*"] for all events (currently only option available)
events: ["*"],
configuration: [
"buttons": [
"dismiss": "none", // Hides the close button on the player. we can use any string values from the above options.
]
]
)
configuration = mapOf(
"buttons" to mapOf(
"dismiss" to "none",
)
),
player.MINIMIZED_POSITION
Contains the available initial positions of the mini-player.
For configurations in web/JavaScript, the constants (e.g., player.MINIMIZED_POSITION.BOTTOM_RIGHT
) are used, while in Swift and Kotlin player configurations, the string values (e.g., "bottom_right"
) are used.
The default position is bottom right.
Web | SDK |
---|---|
player.MINIMIZED_POSITION.BOTTOM_RIGHT | "bottom_right" |
player.MINIMIZED_POSITION.BOTTOM_LEFT | "bottom_left" |
player.MINIMIZED_POSITION.TOP_LEFT | "top_left" |
player.MINIMIZED_POSITION.TOP_RIGHT | "top_right" |
Usage example:
- Web
- Swift
- Kotlin
player.configure({
// Set the Miniplayer initial position using a position constant
minimizedPosition: player.MINIMIZED_POSITION.BOTTOM_RIGHT,
});
configuration: [
"minimizedPosition": "bottom_right" // Hides the close button on the player. we can use any string values from the above options.
] ;
configuration = mapOf(
"minimizedPosition" to "bottom_right", // Hides the close button on the player. we can use any string values from the above options.
),
player.EVENT
Contains all events that can be emitted from the player.
Web / SDK | Trigger |
---|---|
player.EVENT.ADD_TO_CART | An item was added to users' in-player cart. Consider adding the same item to your on-site cart. The event contains property sku describing the added product. |
player.EVENT.CHECKOUT | The user pressed the checkout button from the player cart view. |
player.EVENT.CLOSE | The user closed the player. |
player.EVENT.PROVIDE_PRODUCT_DATA | The player will (at the time of initiating the player and sometime in the future depending on the context) display a product. The player requests you to provide all necessary product data using player.updateProduct() . |
player.EVENT.PLAYER_SWIPE_DOWN | User made a swipe down gesture. |
player.EVENT.PLAYER_SWIPE_LEFT | User made a swipe left gesture. |
player.EVENT.PLAYER_SWIPE_RIGHT | User made a swipe right gesture. |
player.EVENT.PLAYER_SWIPE_UP | User made a swipe up gesture. |
player.EVENT.READY | The player GUI has been loaded and is ready for interactions by the user. |
player.EVENT.SHOW_ADD_TO_CALENDAR | Add-to-calendar dialog should open. |
player.EVENT.SHOW_PRODUCT_VIEW | A product is clicked (whether from the product list or highlighted product). |
player.EVENT.SHOW_PRODUCT_LIST | Product list should open. |
player.EVENT.SHOW_SHARE | Share dialog should open. |
player.EVENT.SYNC_CART_STATE | Whenever the viewer navigates back to the player, the player requests an update regarding which items should be displayed in the user's in-player cart. |
player.EVENT.UPDATE_ITEM_IN_CART | User has changed the quantity of a product in the in-player cart, consider updating the on-site cart. |
player.EVENT.NOTIFY_URL_CHANGE | This is called when an action in the player component triggers your webpage to navigate to a specific page. |
Usage example:
- Web
- Swift
- Kotlin
On Web, use player.on() and the constant variable (e.g., player.EVENT.READY
)
// Register a event handler for `close` event using event constants
player.on(player.EVENT.CLOSE, () => {
console.log("Player was closed");
});
For Swift, use the constant name as string (e.g., "ready"
).
Host app can use ["*"]
to receive all the events.
BambuserVideoConfiguration(
type: .live(d: "xxx"), // e.g. "b89tWk7M8cCHKmUajyTd"
events: ["*"],
configuration: // player configs
)
Host app can set specific events to be received.
BambuserVideoConfiguration(
type: .live(d: "xxx"), // e.g. "b89tWk7M8cCHKmUajyTd"
events: ["ready", "goto-checkout"],
configuration: // player configs
)
Once this events are set, app can use the BambuserVideoPlayerDelegate
method to get notified when the event is triggered and take respective action.
func onNewEventReceived(playerId: String, _ event: BambuserEventPayload) {
if event.type == "ready" {
// doAnyAction()
}
}
For Kotlin, use the constant name as string (e.g., "player.EVENT.READY"
).
Here is how to set listener for specific player events.
application.globalBambuserSDK.GetLiveView(
modifier = Modifier.padding(innerPadding),
// This is the configuration for the player
videoConfiguration = BambuserVideoConfiguration(
//Pass list of events you want to receive
// Or "*" to receive all events
events = listOf("ready", "checkout"),
configuration = // Configuration
videoType = BambuserVideoAsset.Live(id),
)
)
Host app can set events to [*]
to listen to all the events.
// Host app can send [*] to accecpt all the events in the configuration.
events = listOf("*"),
Once this events are set, app can use the BambuserVideoPlayerDelegate
method to get notified when the event is triggered and take respective action.
// Create a video player delegate to receive events and errors
videoPlayerDelegate = object : BambuserVideoPlayerDelegate {
override fun onNewEventReceived(playerId
: String, event
: BambuserEventPayload, viewActions
: ViewActions, ) {
// Add your logic here for receiving events
Log.d(tag, "onNewEventReceived: $event, playerId: $playerId")
when(event.event) {
"ready"->{
// Action()
}
}
}
}
Methods
Methods available in the player API. Below you can find each method in detail including examples for each one.
player.close()
Usage example:
// Close the current player and remove it from the DOM
player.close();
player.configure(configuration)
The player can be configured to meet your needs using the configure method.
If implementing cart integration, some configurations like currency and locale, are required for the player to correctly display products and translations, whilst others are optional.
Usage example:
- Web
- Swift
- Kotlin
player.configure({
currency: "USD",
locale: "en-US",
buttons: {
dismiss: player.BUTTON.MINIMIZE,
checkout: player.BUTTON.MINIMIZE,
},
});
configuration: [
"buttons": [
"dismiss": "none",
"checkout": "minimize",
],
"currency": "USD",
"locale": "en-US"
]
configuration = mapOf
(
"buttons" to
mapOf
(
"dismiss" to "none",
"checkout" to "minimize",
),
"currency" to "USD",
"locale" to "en-US"
)
configuration.locale
Sets the global locale of the player, defining the language and region for products, cart, and checkout integration. The value must be a
string
in the formatlanguageCode-countryCode
(e.g.,en-US
for English in the United States). If not set, the player uses the default locale from your Bambuser workspace.To support a locale, it must first be added in Bam Hub under Settings > Translations. If the specified locale is not available, the player falls back to the default locale defined in your Bam Hub.
Usage example:
player.configure({
locale: "en-US",
});Dynamic Locale Example:
const userLocale = yourMethodToGetUserLocale(); // e.g., "sv-SE" for Swedish
player.configure({
locale: userLocale,
});Note that translation updates may take up to 30 minutes to reflect due to caching.
configuration.currency
Sets the global currency of the player. The value should be of type
string
and contain the three-letter currency code, e.g.USD
. If none other is specified, this value is used for both products, cart, and checkout integration.Usage example:
player.configure({
currency: "USD",
});
configuration.buttons
Configures how buttons behave in the player.
Each button can be set to follow a behavior specified in the BUTTON constants section.
List of configurable buttons
Content Description Available behaviors dismiss The button in the upper right corner of the player player.BUTTON.CLOSE
player.BUTTON.MINIMIZE
player.BUTTON.NONE
checkout The checkout button player.BUTTON.LINK
player.BUTTON.MINIMIZE
product Click on a product reference, in the highlight or the product list player.BUTTON.LINK
player.BUTTON.MINIMIZE
player.BUTTON.NONE
Usage example:
- Disable the Miniplayer
player.configure({
buttons: {
dismiss: player.BUTTON.CLOSE,
},
});More examples of button configuration
Enable the Miniplayer
player.configure({
buttons: {
dismiss: player.BUTTON.MINIMIZE,
},
});
Enable the Miniplayer & Minimize when checkout button is clicked
player.configure({
buttons: {
dismiss: player.BUTTON.MINIMIZE,
checkout: player.BUTTON.MINIMIZE,
},
});
Enable Miniplayer & Minimize the player when a product is clicked
player.configure({
buttons: {
product: player.BUTTON.MINIMIZE,
}
});
configuration.audioTrackLocale
Sets the default audio track language when dubbed audio is enabled. This enables the player to automatically select and play the appropriate dubbed language when multiple audio options are available, eliminating the need for users to manually choose a language.
Usage example:
- Web
- Swift
- Kotlin
player.configure({
audioTrackLocale: "de-DE", // Sets German dubbed audio as the default where available
});configuration: ["audioTrackLocale": "de-DE"]
configuration = mapOf(
"audioTrackLocale" to "de-DE"
)
configuration.shareTargets
This parameter overrides all the underlying default share targets:
Available Share Targets
instapaper
line
liveJournal
mailRu
odnoklassniki
telegram
tumblr
viber
vk
workplace
Usage example
player.configure({
shareTargets: ['reddit', 'telegram'],
});
configuration.minimizedPosition
Configures the initial position of the mini-player.
List of available positions
player.MINIMIZED_POSITION.BOTTOM_RIGHT
- the default positionplayer.MINIMIZED_POSITION.BOTTOM_LEFT
player.MINIMIZED_POSITION.TOP_LEFT
player.MINIMIZED_POSITION.TOP_RIGHT
Usage example
player.configure({
minimizedPosition: player.MINIMIZED_POSITION.BOTTOM_LEFT,
});
configuration.checkoutOnCartClick
Configures the player to emit the player.EVENT.CHECKOUT event when clicking any of the buttons which would present the cart in the player, instead of opening the player's internal cart.
Usage example
- Web
- Swift
- Kotlin
player.configure({
checkoutOnCartClick: true,
});configuration: ["checkoutOnCartClick": true]
configuration = mapOf(
"checkoutOnCartClick" to true
)
configuration.shareBaseUrl
Overrides the base URL for creating a link to access the show for both Share and AddToCalendar features.
The URL must be an absolute path and can include protocol (
http://
orhttps://
) and query parameters. IfshareBaseUrl
isundefined
,null
, or''
the share URL will default towindow.location.href
.Usage example
- Web
- Swift
- Kotlin
player.configure({
shareBaseUrl: 'https://example.com/live-shopping?referer=joe',
});configuration: [
"shareBaseUrl": "https://example.com/live-shopping?referer=joe"
]configuration = mapOf(
"shareBaseUrl" to "https://example.com/live-shopping?referer=joe"
)
configuration.trackingTags
Add a list of custom tracking tags which should be included to all tracking events dispatched by the player.
Usage example
- Web
- Swift
- Kotlin
player.configure({
trackingTags: [{ key: 'memberId', value: '123-abc' }],
});configuration: [
"trackingTags": [
["memberId" : "123-abc"]
]
]configuration = mapOf(
"shareBaseUrl" to listOf(
mapOf(
"memberId" to "123-abc")
)
)
configuration.allowShareAutoplay
Allow or disable autoplay when opening shared URLs.
If
allowShareAutoplay
is set tofalse
the share URL presented in the player will not include the?autoplayLiveshopping=[showId]
query parameter. (Learn more about Autoplay)Default value:
true
Usage example
- Web
- Swift
- Kotlin
player.configure({
allowShareAutoplay: false,
});configuration: [
"allowShareAutoplay": false
]configuration = mapOf(
"allowShareAutoplay" to
)
configuration.enableFirstPartyCookies
Enables/disables setting of first-party cookies by the player.
If a user declined cookies through your cookie consent, you may need to prevent our player from setting the cookies using this configuration.
Default value:
true
player.configure({
enableFirstPartyCookies: false,
});caution** This flag should be unset only for visitors who declined the cookies. **
The Conversion Tracker can not collect purchase data from users who do not have the cookies set.
Usage example
Here we used an imaginary method (
hasDeclinedCookies
) to check if the cookies have been declined before disabling cookies. You need to replace your own logic.player.configure({
enableFirstPartyCookies: !hasDeclinedCookies()
});
configuration.cookie
An object including settings for first-party cookies set by the player.
Available properties:
domain
Sets the domain attribute of the first-party cookies.activityCookieTTLDays
Changes the expiration time for Conversion Tracking cookies (_bamls_shid
and_bamls_lits
) and consequently the duration of the time that purchases are being trackedUsage example
player.configure({
cookie: {
domain: '.example.com', // Cookies can also be accessed by all subdomains ex. checkout.example.com
activityCookieTTLDays: 1, // Will only track conversions for 1 day after interaction with the show
}
});
configuration.trimPriceTrailingZeros
Allow to remove the price decimals when they are equal to zero, disabled by default.
Usage example
- Web
- Swift
- Kotlin
player.configure({
trimPriceTrailingZeros: true,
});configuration: ["trimPriceTrailingZeros": true]
configuration = mapOf(
"trimPriceTrailingZeros" to true
)
configuration.deeplink
Make the player seek to a specific product appearance reference in the show. The
deeplink
value to a product appearance can be found in the product appearances REST API endpoints. Thisdeeplink
value will become invalid if the video content in specific show is changed, then you should fetch a newplayer.configuration
value to get a valid value.If you have a unique
player.configuration
for a specific show you can use this configuration to specifically tell the player which product appearance the player should start playing from. If you do not have a unique configuration then you can pass thisdeeplink
key/value in the JSON object which is used as an argument forinitBambuserLiveShopping
function call.Usage example
- Web
- Swift
- Kotlin
player.configure({
deeplink: "f00ba5@100",
});
// or
window.initBambuserLiveShopping({
showId: 'show-id',
deeplink: "f00ba5@100",
});configuration: [
"deeplink": "f00ba5@100"
]configuration = mapOf(
"deeplink" to "f00ba5@100"
)
configuration.ui
Hide UI elements when necessary. All hidable elements are listed below:
hideActionBar
hideAddToCalendar
hideCartButton
hideCartView
hideChatOverlay
hideEmojiOverlay
hideProductList
hideProductView
hideShareButton
hideShareView
Usage example
- Web
- Swift
- Kotlin
player.configure({
ui: {
hideAddToCalendar: false,
hideShareView: false,
},
});configuration: [
"ui": [
"hideAddToCalendar": false,
"hideShareView": true
]
]configuration = mapOf(
"ui" to mapOf(
"hideAddToCalendar" to false,
"hideShareView" to true
)
)
configuration.experimental
Configures behavior and functionality of the player using flags. Value is of type
object
and may contain multiple different flags as properties.noteAll flags available under experimental are subject to change and should be used with caution. When a feature is considered "production ready" we will include it in the general configuration.
Available Experimental Properties
- chatName
string
- Sets the chat alias for the user instead of prompting when entering chat- hasAcceptedTerms
boolean
- Disables terms & conditions prompt if set to trueUsage example:
player.configure({
experimental: {
chatName: "John D",
hasAcceptedTerms: true,
},
});
player.minimize()
Minimizes the player when invoked.
player.unminimize()
When the player is minimized, invoking this method maximizes the player.
player.on(eventName, eventHandler)
Registers an event listener for any event from the EVENT constants above.
Usage example:
player.on(player.EVENT.CLOSE, () => {
console.log("Player was closed");
});
player.off(eventName, eventHandler)
Removes a previously registered event listener from receiving any more events.
The second parameter needs to be the same handler method provided at the time of registering the event listener.
Usage example:
// Define an event listener
const someEventHandler = () => {
console.log("Close event was triggered");
};
// Register the listener from this point. Any close event
// will trigger the console.log above
player.on(player.EVENT.CLOSE, someEventHandler);
// Removes the registered listener from this point. Close events
// will no longer trigger the console.log
player.off(player.EVENT.CLOSE, someEventHandler);
player.removeAllListeners()
Removes all event listeners defined by player.on()
. Use this sparsely and with caution.
Usage example:
player.on(player.EVENT.CLOSE, () => {
console.log("Player was closed");
});
player.on(player.EVENT.CHECKOUT, () => {
console.log("User pressed checkout");
});
// After this point, the above listeners will not be triggered again
player.removeAllListeners();
player.showCheckout(checkoutPageUrl)
Navigate to a checkout page (or any other pages) when invoked.
This method uses the checkout button configuration to determine how to navigate to the target page. If no configuration is provided, it opens the URL in a new tab.
-
checkout: player.BUTTON.MINIMIZE
Minimizes the player and opens the link on the same window -
checkout: player.BUTTON.LINK
Opens the link in a new tab
Usage example:
player.on(player.EVENT.CHECKOUT, () => {
player.showCheckout("https://example.com/checkout");
});
player.updateCart(cartData)
Update the player cart state.
Currently, updateCart
only supports emptying the cart. This action is useful when the shopper completes the checkout/empties the cart and comes back to the player.
Usage example:
- Web
- Swift
- Kotlin
player.on(player.EVENT.SYNC_CART_STATE, () => {
// Use your method to check if the user has checkout
if (isOnSiteCartEmpty()) {
// Emptying the in-player cart
player.updateCart({
items: [],
});
}
});
func onNewEventReceived(playerId: String, _ event: BambuserEventPayload) {
if event.type == "player.EVENT.SYNC_CART_STATE" {
try await self.playerView.invoke(
function: "updateCart",
arguments:
""" {
"items": []
}"""
)
}
}
viewActions.invoke(
function = "updateCart",
arguments =
)
player.updateProduct(productId, productFactory)
Updates all necessary product details that allow the player to properly display a product.
List of arguments
Arguments | Description | Type |
---|---|---|
productId | The Bambuser generated ID for each product of a show. Found in the PROVIDE_PRODUCT_DATA event payload (event.products ). | string |
productFactory | Method that should return a finished product. | callback |
Update product details
Here is a full example of updating products using the player.updateProduct()
method.
- Dynamic implementation
- Sample product data
- Static implementation
- Swift implementation
- Kotlin implementation
Host app received the event through delegate and use the event data to get the available product's ids/skus Call the hydrate function for all the available products for the show
func onNewEventReceived(playerId: String, _ event: BambuserEventPayload) {
if event.type == "provide-product-data" {
Task {
try await self.hydrate(data: event.data)
}
}
}
The Hydrate method should call the SDKs invoke() method for each product. We can hydrate the product by sending the raw json string via invoke method.
func hydrate(data: [String: Any]) async throws {
guard let event = data["event"] as? [String: Any],
let products = event["products"] as? [[String: Any]] else { return }
for product in products {
guard let id = product["id"] as? String else { return }
try await self.playerView.invoke(
function: "updateProductWithData",
arguments: """
'\(id)', {
sku: '7777',
name: 'Bambuser Hoodie',
brandName: 'Bambuser',
introduction: 'A nice hoodie that keeps you warm',
description: "<div><h2>World's best hoodie</h2><p>Comes in all sizes and forms!</p></div>",
variations: [
{
sku: '1111-black',
name: 'Black Bambuser Hoodie',
colorName: 'black',
imageUrls: [
'https://demo.bambuser.shop/wp-content/uploads/2021/07/black-hoodie-front.png',
'https://demo.bambuser.shop/wp-content/uploads/2021/07/black-hoodie-right.jpeg',
'https://demo.bambuser.shop/wp-content/uploads/2021/07/black-hoodie-back.jpeg',
'https://demo.bambuser.shop/wp-content/uploads/2021/07/black-hoodie-left.jpeg',
],
sizes: [
{
sku: '1111-black-small',
currency: 'USD',
current: 120,
original: 120,
name: 'Small',
inStock: 9,
},
{
sku: '1111-black-xlarge',
currency: 'USD',
current: 100,
original: 120,
name: 'X-Large',
inStock: 3,
},
],
},
{
sku: '1111-white',
name: 'White Bambuser Hoodie',
colorName: 'white',
images: [
'https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-front.png',
'https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-right.jpeg',
'https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-back.jpeg',
'https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-left.jpeg',
],
sizes: [
{
sku: '1111-white-small',
currency: 'USD',
current: 100,
original: 120,
name: 'Small',
inStock: 8,
},
{
sku: '1111-white-xlarge',
currency: 'USD',
current: 100,
original: 120,
name: 'X-Large',
inStock: 0,
},
],
},
{
sku: '1111-white-2',
name: '2x White Bambuser Hoodie',
colorName: 'white',
imagesUrl: [
'https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-front.png',
'https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-right.jpeg',
'https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-back.jpeg',
'https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-left.jpeg',
],
sizes: [
{
sku: '1111-white-small-2',
currency: 'USD',
current: 180,
original: 239,
name: 'Small',
inStock: 8,
perUnit: 90,
unitAmount: 1,
unitDisplayName: 'st',
},
{
sku: '1111-white-xlarge-2',
currency: 'USD',
current: 180,
original: 239,
name: 'X-Large',
inStock: 0,
perUnit: 90,
unitAmount: 1,
unitDisplayName: 'st',
},
],
},
],
}
"""
)
}
}
Using Builder function
Or you can use our Builder function to create the right format of data from your own data source. To do so, you need copy our Build functions from our demo app. Github This funtion will throw error on if any mandatory product property is not set or set incorrectly. You can update or modify according to your need, this is just a sample Builder function.
func hydrateUsingProductBuilder(data: [String: Any]) async throws {
guard let event = data["event"] as? [String: Any],
let products = event["products"] as? [[String: Any]] else { return }
for product in products {
guard let sku = product["ref"] as? String,
let id = product["id"] as? String else { continue }
// Fetch your own product details
// Dummy function to fetch the details
// - More information: [Bambuser Player API Reference](https://bambuser.com/docs/live/player-api-reference/)
guard let productDetails = ProductHydrationDataSource.mockClientProduct(for: sku)
else { continue}
let hydratedProduct = try HydratedProduct(sku: productDetails.sku)
.withName(productDetails.productName)
.withBrandName(productDetails.brand)
.withVariations(
productDetails.variations.map { variation in
try Variation()
.withSku(variation.sku)
.withColorName(variation.colorName)
.withName(variation.name)
.withImageUrls(variation.imageUrls)
.withSizes(
variation.sizes.map { size in
try ProductSize()
.withSku(size.sku)
.withCurrentPrice(size.current)
.withInStock(size.inStock)
.withName(size.name)
.build()
}
)
.build()
}
)
.build()
//
let hydrationString = "'\(id)', \(try hydratedProduct.toJSON())"
/// This is how to invoke **player functions**.
/// - For example, to send **product hydration data** to the player, the `"updateProductWithData"` function is used.
/// - This method requires:
/// - A **player function name** (e.g., `"updateProductWithData"`).
/// - **Arguments** required by the function.
/// - More information: [Bambuser Player API Reference](https://bambuser.com/docs/live/player-api-reference/)
///
/// **For product hydration, please refer to the format used in this function and `hydrationString`.**
try await playerView?.invoke(
function: "updateProductWithData",
arguments: hydrationString
)
}
}
Host app received the event through delegate and use the event data to get the available product's ids/skus Call the hydrate function for all the available products for the show
override fun onNewEventReceived(playerId: String, event: BambuserEventPayload, viewActions: ViewActions, ) {
// Add your logic here for receiving events
Log.d(tag, "onNewEventReceived: $event, playerId: $playerId")
when(event.event) {
// If you want to hydrate product
"provide-product-data" - > {
(event.data["products"] as ? List < Map < String, Any >> )?.let {
products - > products.forEach {
product - > product["id"]?.toString()?.let {
productId - > lifecycleScope.launch {
// hydrate the product
viewActions.invoke(
function = "updateProductWithData",
arguments = getArguments(productId)
)
}
}
}
}
}
}
}
Example of the sample product hydration
// An example for what to pass to product hydration function
fun getArguments(productId: String) = """
'$productId', {
sku: '7777',
name: 'Bambuser Hoodie',
brandName: 'Bambuser',
introduction: 'A nice hoodie that keeps you warm',
description: "<div><h2>World's best hoodie</h2><p>Comes in all sizes and forms!</p></div>",
variations: [
{
sku: '1111-black',
name: 'Black Bambuser Hoodie',
colorName: 'black',
imageUrls: [
'https://demo.bambuser.shop/wp-content/uploads/2021/07/black-hoodie-front.png',
'https://demo.bambuser.shop/wp-content/uploads/2021/07/black-hoodie-right.jpeg',
'https://demo.bambuser.shop/wp-content/uploads/2021/07/black-hoodie-back.jpeg',
'https://demo.bambuser.shop/wp-content/uploads/2021/07/black-hoodie-left.jpeg',
],
sizes: [
{
sku: '1111-black-small',
currency: 'USD',
current: 120,
original: 120,
name: 'Small',
inStock: 9,
},
{
sku: '1111-black-xlarge',
currency: 'USD',
current: 100,
original: 120,
name: 'X-Large',
inStock: 3,
},
],
},
{
sku: '1111-white',
name: 'White Bambuser Hoodie',
colorName: 'white',
images: [
'https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-front.png',
'https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-right.jpeg',
'https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-back.jpeg',
'https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-left.jpeg',
],
sizes: [
{
sku: '1111-white-small',
currency: 'USD',
current: 100,
original: 120,
name: 'Small',
inStock: 8,
},
{
sku: '1111-white-xlarge',
currency: 'USD',
current: 100,
original: 120,
name: 'X-Large',
inStock: 0,
},
],
},
{
sku: '1111-white-2',
name: '2x White Bambuser Hoodie',
colorName: 'white',
imagesUrl: [
'https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-front.png',
'https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-right.jpeg',
'https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-back.jpeg',
'https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-left.jpeg',
],
sizes: [
{
sku: '1111-white-small-2',
currency: 'USD',
current: 180,
original: 239,
name: 'Small',
inStock: 8,
perUnit: 90,
unitAmount: 1,
unitDisplayName: 'st',
},
{
sku: '1111-white-xlarge-2',
currency: 'USD',
current: 180,
original: 239,
name: 'X-Large',
inStock: 0,
perUnit: 90,
unitAmount: 1,
unitDisplayName: 'st',
},
],
},
],
}
"""
NOTE: Our Builder functions are currently not available in Android demo project.
player.on(player.EVENT.PROVIDE_PRODUCT_DATA, (event) => {
event.products.forEach(async ({ ref: sku, url, id: bambuserId }) => {
const yourProduct = await yourGetProductMethod(sku);
player.updateProduct(bambuserId, (productFactory) =>
productFactory
// -> The .product() method takes a function
// returning a product detail object
.product((productDetailFactory) =>
productDetailFactory
// Name of the product
.name(yourProduct.name)
// Brand name to display for the product
.brandName(yourProduct.brand)
// (Optional) Short product introductory text
.introduction(yourProduct.shortDescription)
// (Optional) Description for the product
// Supports text as well as HTML string
.description(yourProduct.description)
// sku (or any other identifier for your product)
// NOTE: Should be the same as your product reference
// defined in Bambuser Workspace
.sku(yourProduct.productId)
// Describes which index in the variations list below
// contains the default variation
// (e.g. if variations contain colors, and you want to display
// the white version of a shirt, send the index for the white variation)
.defaultVariationIndex(0)
// -> The .variations() method takes a function
// returning an array of variations
.variations((variationFactory) =>
yourProduct.colors.map((variation) =>
// VariationFactory contains a factory returning a new instance of a variation
variationFactory()
// -> (Optional) The .attributes() method takes a function
// defines color attribute for the variation
.attributes((attributeFactory) =>
attributeFactory
// Color name in the variation dropdown selector,
.colorName(variation.colorName)
// (Optional) Color Hex code e.g. '#7d58ee'
.colorHexCode(variation.colorHexCode)
)
// List of image urls for the variation
// ordered the same as you want it displayed
.imageUrls(variation.images)
// Name of the variation
// Shown if colorName is not provided as attribute.
.name(variation.name)
// sku (or any other identifier for your product)
// specific down to this variation
.sku(variation.variationId)
// -> The .sizes() method takes a function that
// returns an array of sizes
.sizes((sizeFactory) =>
variation.sizes.map((size) =>
// s contains a factory returning a new instance of a variation
sizeFactory()
// Name of the size
// (used in dropdowns)
.name(size.name)
// Set whether this combination of
// variation and size is in stock
.inStock(size.quantityInStock > 0)
// sku (or any other identifier for your product)
// specific down to this size (used for add-to-cart)
.sku(size.sizeId)
// -> The price method contains a new chain
// defines price for the variation and size combo
.price((priceFactory) =>
priceFactory
// currency to show price in
// Optional - overrides the default currency
.currency(size.currency)
// current price as a number
.current(size.current)
// (Optional) original price
// Used in case the current is a sale price
.original(size.original)
// (Optional) This set of fields allows showing price per unit, e.g. $77 / 100ml
.perUnit(size.perUnit)
.unitAmount(size.unitAmount)
.unitDisplayName(size.unitDisplayName),
)
)
)
)
)
)
);
});
});
{
productId: "1111",
name: "Bambuser Hoodie",
brand: "Bambuser",
shortDescription: "World's best hoodie",
description:
`<p>Jacket in sweatshirt fabric with a jersey-lined drawstring hood, zip down the front, side pockets and ribbing at the cuffs and hem. Soft brushed inside. Regular Fit</p><b>Benefits:</b><ul><li>Awesome hoodie</li><li>Set approves</li></ul>`,
price: 100,
price_discount: true,
colors: [
{
variationId: "1111-black",
name: "Black Bambuser Hoodie",
colorName: "black",
colorHexCode: "#000000",
images: [
"https://demo.bambuser.shop/wp-content/uploads/2021/07/black-hoodie-front.png",
"https://demo.bambuser.shop/wp-content/uploads/2021/07/black-hoodie-right.jpeg",
"https://demo.bambuser.shop/wp-content/uploads/2021/07/black-hoodie-back.jpeg",
"https://demo.bambuser.shop/wp-content/uploads/2021/07/black-hoodie-left.jpeg"
],
sizes: [
{
sizeId: "1111-black-small",
currency: "SEK",
current: 100,
original: 120,
name: "Small",
quantityInStock: 9
},
{
sizeId: "1111-black-xlarge",
currency: "SEK",
current: 100,
original: 120,
name: "X-Large",
quantityInStock: 3,
perUnit: 100,
unitAmount: 1,
unitDisplayName: "piece",
}
]
},
{
variationId: "1111-white",
name: "White Bambuser Hoodie",
colorName: "white",
colorHexCode: "#FFFFFF",
images: [
"https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-front.png",
"https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-right.jpeg",
"https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-back.jpeg",
"https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-left.jpeg"
],
sizes: [
{
sizeId: "1111-white-small",
currency: "SEK",
current: 100,
original: 120,
name: "Small",
quantityInStock: 8
},
{
sizeId: "1111-white-xlarge",
currency: "SEK",
current: 100,
original: 120,
name: "X-Large",
quantityInStock: 0
}
]
}
]
}
// This is only for demonstration purposes
// Hardcoding products data is not recommended
player.on(player.EVENT.PROVIDE_PRODUCT_DATA, (event) => {
event.products.forEach(async ({ ref: sku, url, id: bambuserId }) => {
player.updateProduct(bambuserId, (productFactory) =>
productFactory.product((productDetailFactory) =>
productDetailFactory
.brandName("Bambuser")
.defaultVariationIndex(0)
.introduction("World's best hoodie")
.description(
`<p>Jacket in sweatshirt fabric with a jersey-lined drawstring hood, zip down thefront, side pockets and ribbing at the cuffs and hem. Soft brushed inside. Regular Fit</p><b>Benefits:</b><ul><li>Awesome hoodie</li><li>Set approves</li></ul>`,
)
.name("Bambuser Hoodie")
.sku("1111")
.variations((variationFactory) => [
variationFactory()
.attributes((attributeFactory) =>
attributeFactory.colorName("black").colorHexCode("#000000")
)
.imageUrls([
"https://demo.bambuser.shop/wp-content/uploads/2021/07/black-hoodie-front.png",
"https://demo.bambuser.shop/wp-content/uploads/2021/07/black-hoodie-right.jpeg",
"https://demo.bambuser.shop/wp-content/uploads/2021/07/black-hoodie-back.jpeg",
"https://demo.bambuser.shop/wp-content/uploads/2021/07/black-hoodie-left.jpeg",
])
.name("Black Bambuser Hoodie")
.sku("1111-1")
.sizes((sizeFactory) => [
sizeFactory()
.name("Small")
.inStock(9 > 0)
.sku("1111-black-small")
.price((priceFactory) =>
priceFactory.currency("SEK").current(100).original(120)
),
sizeFactory()
.name("X-Large")
.inStock(3 > 0)
.sku("1111-black-xlarge")
.price((priceFactory) =>
priceFactory.currency("SEK").current(100).original(120).perUnit(100).unitAmount(1).unitDisplayName("piece"),
),
]),
variationFactory()
.attributes((attributeFactory) =>
attributeFactory.colorName("white").colorHexCode("#FFFFF")
)
.imageUrls([
"https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-front.png",
"https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-right.jpeg",
"https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-back.jpeg",
"https://demo.bambuser.shop/wp-content/uploads/2021/07/white-hoodie-left.jpeg",
])
.name("White Bambuser Hoodie")
.sku("1111-2")
.sizes((sizeFactory) => [
sizeFactory()
.name("Small")
.inStock(8 > 0)
.sku("1111-white-small")
.price((priceFactory) =>
priceFactory.currency("SEK").current(100).original(120)
),
sizeFactory()
.name("X-Large")
.inStock(0 > 0)
.sku("1111-2-2")
.price((priceFactory) =>
priceFactory.currency("SEK").current(100).original(120)
),
]),
])
)
);
});
});
Inherit from scraped product
By default when you hydrate a product using the
updateProduct()
method, it will create a new empty product. Therefore, it requires you to provide all needed details since the product information previously scraped (or manually inserted) through the workspace will get overridden.However, Bambuser offers an option to inherit those product information from the scraped product (E.g. name, brand) after creating the new empty product.
Usage Example
- Web
- Swift
- Kotlin
To do so, you need to chain
inheritFromPlaceholder()
toproductFactory
.player.on(player.EVENT.PROVIDE_PRODUCT_DATA, (event) =>
event.products.forEach(({ ref: sku, id: productId, url: publicUrl }) => {
// Your method to fetch product data
yourGetProductDataMethod(sku).then((currentProduct) =>
player.updateProduct(productId, (productFactory) =>
productFactory
.inheritFromPlaceholder()
.product((productDetailFactory) =>
detailsFactory
.name(item.title)
.sku(item.id)
.brandName(item.vendor)
.variations(
(variationFactory) =>
// ....
)
)
)
);
})
);Using
inheritFromPlaceholder()
you will get more functionalities such as overriding product URLs and hiding product. You can read more about that below.Inheriting from scraped product data is already handled in mobile SDK
updateProductWithData
function. If the new data is passed, that will override the existing data set in the dashboard, otherwise use the pre-set dataInheriting from scraped product data is already handled in mobile SDK
updateProductWithData
function. If the new data is passed, that will override the existing data set in the dashboard, otherwise use the pre-set data
Override products URL
If you use different domains across different markets, you may sometimes (when you link the player to PDPs) need to update product details with market-specific PDP URLs. It is quite simple to do so if the products share the same SKUs across the markets.
To override the PDP's URL, pass the current market PDP URL to
publicUrl(String)
method for each product; as highlighted in the example below.Usage Example
player.on(player.EVENT.PROVIDE_PRODUCT_DATA, (event) =>
event.products.forEach(({ ref: sku, id: productId, url: publicUrl }) => {
// Your method to fetch localized product data
yourMethodThatGetsLocalizedProductData(sku).then((currentProduct) =>
player.updateProduct(productId, (productFactory) =>
productFactory
.inheritFromPlaceholder()
.publicUrl(currentProduct.url)
)
);
})
);
Hide a product
You can hide a product from the viewer through the Player API. This feature hides the product from the player
player.on(player.EVENT.PROVIDE_PRODUCT_DATA, function (event) {
event.products.forEach(({ ref: sku, id: productId, url: publicUrl }) => {
getLocalizedProductBySku(sku).then((currentProduct) =>
player.updateProduct(productId, (productFactory) => {
return productFactory
.inheritFromPlaceholder()
.hidden(!currentProduct.isAvailable);
})
);
});
});Additional Example: Hide product if not available
player.on(player.EVENT.PROVIDE_PRODUCT_DATA, function (event) {
event.products.forEach(({ ref: sku, id, url }) => {
yourGetProductMethod(sku).then(item => {
if (!item.available) {
// Product is unavailable - Hide the product
player.updateProduct(id, productFactory => {
return productFactory
.inheritFromPlaceholder()
.hidden(true)
})
}
else {
// Product is available - Hydrate the product as usual
player.updateProduct(id, productFactory =>
productFactory.product(detailsFactory =>
detailsFactory
.name(item.title)
.sku(item.id)
.brandName(item.vendor)
.variations(variationFactory =>
// ....
),
),
);
}
}
);
});
});
callback()
When handling ADD_TO_CART
and UPDATE_ITEM_IN_CART
events, callback method is provided as the second argument to the event handler method. You need to use this method in order to let the player know if the operation (add to cart/update cart) was successful or unsuccessful and show a proper message to the viewer.
Success
- Web
- Swift
- Kotlin
callback(true);
// Default message: Added to cart!
if event.type == "should-add-item-to-cart" || event.type == "should-update-item-in-cart" {
// Logic to determine if the item should be added to cart or not
// Here we are checking the quantity should not be more that 3
guard let callbackKey = event.data["callbackKey"] as? String else {
return
}
if let event = event.data["event"] as? [String: Any], let value = event["quantity"] as? Int {
// call any async function to check your inventory
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
if quantity <= 3 {
self.playerView.notify(callbackKey: callbackKey, info: true)
}
}
}
}
lifecycleScope.launch {
// event.data will contain a map of the product
// delay is used to simulate adding item to your cart
delay(2000)
// notify the player that you have added the item to cart successfully
event.callbackKey?.let {
viewActions.notifyView(callbackKey = it, info = true, )
}
}
Failure
- Web
- Swift
- Kotlin
callback(false);
// Default message: Problem adding product to basket, please try again!
if event.type == "should-add-item-to-cart" || event.type == "should-update-item-in-cart" {
// Logic to determine if the item should be added to cart or not
// Here we are checking the quantity should not be more that 3
guard let callbackKey = event.data["callbackKey"] as? String else {
return
}
if let event = event.data["event"] as? [String: Any], let value = event["quantity"] as? Int {
// call any async function to check your inventory
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
if quantity > 3 {
self.playerView.notify(callbackKey: callbackKey, info: false)
}
}
}
lifecycleScope.launch {
// event.data will contain a map of the product
// delay is used to simulate adding item to your cart
delay(2000)
// notify the player that you have added the item to cart successfully
event.callbackKey?.let {
viewActions.notifyView(callbackKey = it, info = false)
}
}
out-of-stock
- Web
- Swift
- Kotlin
callback({
success: false,
reason: 'out-of-stock',
});
// Default message: The selected size has been sold out!
self.playerView.notify(
callbackKey: callbackKey,
info: "{
success: false,
reason: 'out-of-stock'
}"
)
viewActions.notifyView(
callbackKey = it,
info = "{"+ "success: false,"
+ "reason: 'out-of-stock'"
+ "}",
)
Custom error message
- Web
- Swift
- Kotlin
callback({
success: false,
reason: "custom-error",
message: "This is my custom error message", //edit this by your choice
});
self.playerView.notify(
callbackKey: callbackKey,
info: "{
success: false,
reason: 'custom-error',
message: 'This is my custom error message'
}"
)
viewActions.notifyView(
callbackKey = it,
info = "{"+ "success: false,"
+ "reason: 'custom-error'",
+ "message: 'This is my custom error message'"
+ "}",
)
setTrackingTags(trackingTags)
Adds a list of custom tracking tags to all Bambuser tracking events.
Usage example:
player.setTrackingTags([
{
key: 'customerId',
value: '123-abc',
},
{
key: 'membership',
value: 'premium',
}
]);