PageSourceSearch

https://bambuser.com/docs/assets/js/e0e605b5.4abff1d9.js

js bambuser.com collected 2026-09-24 08:29:01 UTC 17,747 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkbambuser_docs||=[]).push([[2871],{19284(e,r,t){t.r(r),t.d(r,{assets:()=>o,contentTitle:()=>c,default:()=>h,frontMatter:()=>s,metadata:()=>d,toc:()=>a});const d=JSON.parse('{"id":"playerSDKIntegrationIos-product-hydration","title":"Product hydration","description":"Supply product data to the iOS live player at runtime: provide-product-data, updateProductWithData invoke (raw or HydratedProduct builder), currency/locale.","source":"@site/live/playerSDKIntegrationIos-product-hydration.mdx","sourceDirName":".","slug":"/mobile-sdk-integration-ios/product-hydration","permalink":"/docs/live/mobile-sdk-integration-ios/product-hydration","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"id":"playerSDKIntegrationIos-product-hydration","title":"Product hydration","description":"Supply product data to the iOS live player at runtime: provide-product-data, updateProductWithData invoke (raw or HydratedProduct builder), currency/locale.","sidebar_label":"Product hydration","slug":"mobile-sdk-integration-ios/product-hydration"},"sidebar":"someSidebars","previous":{"title":"Cart integration","permalink":"/docs/live/mobile-sdk-integration-ios/cart-integration"},"next":{"title":"Product highlights","permalink":"/docs/live/mobile-sdk-integration-ios/product-highlights"}}');var n=t(74848),i=t(28453);const s={id:"playerSDKIntegrationIos-product-hydration",title:"Product hydration",description:"Supply product data to the iOS live player at runtime: provide-product-data, updateProductWithData invoke (raw or HydratedProduct builder), currency/locale.",sidebar_label:"Product hydration",slug:"mobile-sdk-integration-ios/product-hydration"},c="Product hydration",o={},a=[{value:"Prerequisites",id:"prerequisites",level:2},{value:"Handle the <code>provide-product-data</code> Event",id:"handle-the-provide-product-data-event",level:2},{value:"Send Product Data Back to the Player",id:"send-product-data-back-to-the-player",level:2},{value:"Full Product Data Structure",id:"full-product-data-structure",level:2},{value:"Product",id:"product",level:3},{value:"Variation",id:"variation",level:3},{value:"Size",id:"size",level:3},{value:"Using the Builder Pattern (Recommended)",id:"using-the-builder-pattern-recommended",level:2},{value:"Notes",id:"notes",level:2}];function l(e){const r={a:"a",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,i.R)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(r.header,{children:(0,n.jsx)(r.h1,{id:"product-hydration",children:"Product hydration"})}),"\n",(0,n.jsxs)(r.p,{children:["Product hydration allows your app to supply enriched product data (pricing, images, variants, stock) to the Bambuser live player at runtime. When a live show starts, the player fires a ",(0,n.jsx)(r.code,{children:"provide-product-data"})," event listing all products it needs data for. Your app looks up each product by SKU and pushes the data back into the player using ",(0,n.jsx)(r.code,{children:'invoke("updateProductWithData", ...)'}),"."]}),"\n",(0,n.jsx)(r.h2,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,n.jsxs)(r.p,{children:[(0,n.jsx)(r.code,{children:"currency"})," and ",(0,n.jsx)(r.code,{children:"locale"})," ",(0,n.jsx)(r.strong,{children:"must"})," be set in your player configuration \u2014 product hydration will not work without them."]}),"\n",(0,n.jsx)(r.pre,{children:(0,n.jsx)(r.code,{className:"language-swift",children:'let playerView = bambuserPlayer.createPlayerView(\n    videoConfiguration: .init(\n        type: .live(id: "your-show-id"),\n        events: ["*"],\n        configuration: [\n            "currency": "USD",   // Required for product hydration\n            "locale": "en-US"    // Required for product hydration\n        ]\n    )\n)\nplayerView.delegate = self\n'})}),"\n",(0,n.jsxs)(r.h2,{id:"handle-the-provide-product-data-event",children:["Handle the ",(0,n.jsx)(r.code,{children:"provide-product-data"})," Event"]}),"\n",(0,n.jsxs)(r.p,{children:["The player fires ",(0,n.jsx)(r.code,{children:"provide-product-data"})," at show start, and again whenever a new product needs to be displayed. Handle it in your ",(0,n.jsx)(r.code,{children:"BambuserVideoPlayerDelegate"}),":"]}),"\n",(0,n.jsx)(r.pre,{children:(0,n.jsx)(r.code,{className:"language-swift",children:'func onNewEventReceived(id: String, event: BambuserEventPayload) {\n    if event.type == "provide-product-data" {\n        Task {\n            try await self.hydrate(data: event.data)\n        }\n    }\n}\n'})}),"\n",(0,n.jsxs)(r.p,{children:["The event payload contains an ",(0,n.jsx)(r.code,{children:'"event"'})," dictionary with a ",(0,n.jsx)(r.code,{children:'"products"'})," array. Each entry has:"]}),"\n",(0,n.jsxs)(r.table,{children:[(0,n.jsx)(r.thead,{children:(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.th,{children:"Field"}),(0,n.jsx)(r.th,{children:"Type"}),(0,n.jsx)(r.th,{children:"Description"})]})}),(0,n.jsxs)(r.tbody,{children:[(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"id"})}),(0,n.jsx)(r.td,{children:"String"}),(0,n.jsx)(r.td,{children:"Bambuser-generated product ID \u2014 pass this back to the player"})]}),(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"ref"})}),(0,n.jsx)(r.td,{children:"String"}),(0,n.jsx)(r.td,{children:"Your product SKU \u2014 use this to look up your own data"})]}),(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"url"})}),(0,n.jsx)(r.td,{children:"String"}),(0,n.jsx)(r.td,{children:"Product URL as set in the Bambuser dashboard"})]})]})]}),"\n",(0,n.jsx)(r.h2,{id:"send-product-data-back-to-the-player",children:"Send Product Data Back to the Player"}),"\n",(0,n.jsxs)(r.p,{children:["Call ",(0,n.jsx)(r.code,{children:'invoke("updateProductWithData", ...)'})," for each product. The ",(0,n.jsx)(r.code,{children:"arguments"})," string must start with the Bambuser product ",(0,n.jsx)(r.code,{children:"id"})," in single quotes, followed by a comma and the product object:"]}),"\n",(0,n.jsx)(r.pre,{children:(0,n.jsx)(r.code,{className:"language-swift",children:"func hydrate(data: [String: Any]) async throws {\n    guard let event = data[\"event\"] as? [String: Any],\n          let products = event[\"products\"] as? [[String: Any]] else { return }\n\n    for product in products {\n        guard let id  = product[\"id\"]  as? String,\n              let sku = product[\"ref\"] as? String else { continue }\n\n        // Look up product data from your own catalog using the SKU\n        guard let productData = YourProductService.product(for: sku) else { continue }\n\n        try await self.playerView?.invoke(\n            function: \"updateProductWithData\",\n            arguments: \"\"\"\n            '\\(id)', {\n              sku: '\\(productData.sku)',\n              name: '\\(productData.name)',\n              brandName: '\\(productData.brand)',\n              introduction: '\\(productData.shortDescription)',\n              description: '\\(productData.description)',\n              variations: [\n                {\n                  sku: '\\(productData.variationSku)',\n                  name: '\\(productData.variationName)',\n                  colorName: '\\(productData.colorName)',\n                  imageUrls: ['\\(productData.imageUrl)'],\n                  sizes: [\n                    {\n                      sku: '\\(productData.sizeSku)',\n                      currency: 'USD',\n                      current: \\(productData.price),\n                      original: \\(productData.originalPrice),\n                      name: '\\(productData.sizeName)',\n                      inStock: \\(productData.stock),\n                    }\n                  ]\n                }\n              ]\n            }\n            \"\"\"\n        )\n    }\n}\n"})}),"\n",(0,n.jsx)(r.h2,{id:"full-product-data-structure",children:"Full Product Data Structure"}),"\n",(0,n.jsxs)(r.p,{children:["The product object passed to ",(0,n.jsx)(r.code,{children:"updateProductWithData"})," supports the following fields:"]}),"\n",(0,n.jsx)(r.h3,{id:"product",children:"Product"}),"\n",(0,n.jsxs)(r.table,{children:[(0,n.jsx)(r.thead,{children:(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.th,{children:"Field"}),(0,n.jsx)(r.th,{children:"Type"}),(0,n.jsx)(r.th,{children:"Required"}),(0,n.jsx)(r.th,{children:"Description"})]})}),(0,n.jsxs)(r.tbody,{children:[(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"sku"})}),(0,n.jsx)(r.td,{children:"String"}),(0,n.jsx)(r.td,{children:"Yes"}
1),(0,n.jsx)(r.td,{children:"Your product SKU / identifier"})]}),(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"name"})}),(0,n.jsx)(r.td,{children:"String"}),(0,n.jsx)(r.td,{children:"Yes"}),(0,n.jsx)(r.td,{children:"Product display name"})]}),(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"brandName"})}),(0,n.jsx)(r.td,{children:"String"}),(0,n.jsx)(r.td,{children:"Yes"}),(0,n.jsx)(r.td,{children:"Brand name"})]}),(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"introduction"})}),(0,n.jsx)(r.td,{children:"String"}),(0,n.jsx)(r.td,{children:"No"}),(0,n.jsx)(r.td,{children:"Short introductory text"})]}),(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"description"})}),(0,n.jsx)(r.td,{children:"String"}),(0,n.jsx)(r.td,{children:"No"}),(0,n.jsx)(r.td,{children:"Full description \u2014 supports HTML"})]}),(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"variations"})}),(0,n.jsx)(r.td,{children:"Array"}),(0,n.jsx)(r.td,{children:"Yes"}),(0,n.jsx)(r.td,{children:"List of product variations (colors/styles)"})]})]})]}),"\n",(0,n.jsx)(r.h3,{id:"variation",children:"Variation"}),"\n",(0,n.jsxs)(r.table,{children:[(0,n.jsx)(r.thead,{children:(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.th,{children:"Field"}),(0,n.jsx)(r.th,{children:"Type"}),(0,n.jsx)(r.th,{children:"Required"}),(0,n.jsx)(r.th,{children:"Description"})]})}),(0,n.jsxs)(r.tbody,{children:[(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"sku"})}),(0,n.jsx)(r.td,{children:"String"}),(0,n.jsx)(r.td,{children:"Yes"}),(0,n.jsx)(r.td,{children:"Variation SKU"})]}),(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"name"})}),(0,n.jsx)(r.td,{children:"String"}),(0,n.jsx)(r.td,{children:"Yes"}),(0,n.jsx)(r.td,{children:"Variation display name"})]}),(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"colorName"})}),(0,n.jsx)(r.td,{children:"String"}),(0,n.jsx)(r.td,{children:"Yes"}),(0,n.jsx)(r.td,{children:"Color name shown in the variation selector"})]}),(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"colorHexCode"})}),(0,n.jsx)(r.td,{children:"String"}),(0,n.jsx)(r.td,{children:"No"}),(0,n.jsxs)(r.td,{children:["Hex color code e.g. ",(0,n.jsx)(r.code,{children:'"#000000"'})]})]}),(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"imageUrls"})}),(0,n.jsx)(r.td,{children:"[String]"}),(0,n.jsx)(r.td,{children:"Yes"}),(0,n.jsx)(r.td,{children:"Ordered list of image URLs for this variation"})]}),(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"sizes"})}),(0,n.jsx)(r.td,{children:"Array"}),(0,n.jsx)(r.td,{children:"Yes"}),(0,n.jsx)(r.td,{children:"List of sizes/SKUs for this variation"})]})]})]}),"\n",(0,n.jsx)(r.h3,{id:"size",children:"Size"}),"\n",(0,n.jsxs)(r.table,{children:[(0,n.jsx)(r.thead,{children:(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.th,{children:"Field"}),(0,n.jsx)(r.th,{children:"Type"}),(0,n.jsx)(r.th,{children:"Required"}),(0,n.jsx)(r.th,{children:"Description"})]})}),(0,n.jsxs)(r.tbody,{children:[(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"sku"})}),(0,n.jsx)(r.td,{children:"String"}),(0,n.jsx)(r.td,{children:"Yes"}),(0,n.jsx)(r.td,{children:"Size-level SKU"})]}),(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"name"})}),(0,n.jsx)(r.td,{children:"String"}),(0,n.jsx)(r.td,{children:"Yes"}),(0,n.jsxs)(r.td,{children:["Size name e.g. ",(0,n.jsx)(r.code,{children:'"Small"'}),", ",(0,n.jsx)(r.code,{children:'"XL"'})]})]}),(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"current"})}),(0,n.jsx)(r.td,{children:"Double"}),(0,n.jsx)(r.td,{children:"Yes"}),(0,n.jsx)(r.td,{children:"Current (sale) price"})]}),(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"original"})}),(0,n.jsx)(r.td,{children:"Double"}),(0,n.jsx)(r.td,{children:"No"}),(0,n.jsx)(r.td,{children:"Original price \u2014 shown as strike-through"})]}),(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"currency"})}),(0,n.jsx)(r.td,{children:"String"}),(0,n.jsx)(r.td,{children:"Yes"}),(0,n.jsxs)(r.td,{children:["Three-letter currency code e.g. ",(0,n.jsx)(r.code,{children:'"USD"'})]})]}),(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"inStock"})}),(0,n.jsx)(r.td,{children:"Int"}),(0,n.jsx)(r.td,{children:"Yes"}),(0,n.jsxs)(r.td,{children:["Available stock quantity (",(0,n.jsx)(r.code,{children:"0"})," = out of stock)"]})]}),(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"perUnit"})}),(0,n.jsx)(r.td,{children:"Double"}),(0,n.jsx)(r.td,{children:"No"}),(0,n.jsx)(r.td,{children:"Price per unit (for bundle/multi-pack pricing)"})]}),(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"unitAmou
1nt"})}),(0,n.jsx)(r.td,{children:"Int"}),(0,n.jsx)(r.td,{children:"No"}),(0,n.jsx)(r.td,{children:"Quantity per unit"})]}),(0,n.jsxs)(r.tr,{children:[(0,n.jsx)(r.td,{children:(0,n.jsx)(r.code,{children:"unitDisplayName"})}),(0,n.jsx)(r.td,{children:"String"}),(0,n.jsx)(r.td,{children:"No"}),(0,n.jsxs)(r.td,{children:["Unit label e.g. ",(0,n.jsx)(r.code,{children:'"kg"'}),", ",(0,n.jsx)(r.code,{children:'"L"'}),", ",(0,n.jsx)(r.code,{children:'"st"'})]})]})]})]}),"\n",(0,n.jsx)(r.h2,{id:"using-the-builder-pattern-recommended",children:"Using the Builder Pattern (Recommended)"}),"\n",(0,n.jsxs)(r.p,{children:["Instead of raw string interpolation, use the ",(0,n.jsx)(r.code,{children:"HydratedProduct"})," builder from the ",(0,n.jsx)(r.a,{href:"https://github.com/bambuser/bambuser-commerce-sdk-ios/tree/main/Example",children:"SDK example app"}),". The builder validates required fields and serialises the product to the correct format:"]}),"\n",(0,n.jsx)(r.pre,{children:(0,n.jsx)(r.code,{className:"language-swift",children:'func hydrateUsingBuilder(data: [String: Any]) async throws {\n    guard let event = data["event"] as? [String: Any],\n          let products = event["products"] as? [[String: Any]] else { return }\n\n    for product in products {\n        guard let sku = product["ref"] as? String,\n              let id  = product["id"]  as? String else { continue }\n\n        guard let source = YourProductService.product(for: sku) else { continue }\n\n        let hydratedProduct = try HydratedProduct(sku: source.sku)\n            .withName(source.name)\n            .withBrandName(source.brand)\n            .withVariations(\n                source.variations.map { v in\n                    try Variation()\n                        .withSku(v.sku)\n                        .withName(v.name)\n                        .withColorName(v.colorName)\n                        .withImageUrls(v.imageUrls)\n                        .withSizes(\n                            v.sizes.map { s in\n                                try ProductSize()\n                                    .withSku(s.sku)\n                                    .withName(s.name)\n                                    .withCurrentPrice(s.currentPrice)\n                                    .withInStock(s.inStock)\n                                    .build()\n                            }\n                        )\n                        .build()\n                }\n            )\n            .build()\n\n        let hydrationString = "\'\\(id)\', \\(try hydratedProduct.toJSON())"\n\n        try await playerView?.invoke(\n            function: "updateProductWithData",\n            arguments: hydrationString\n        )\n    }\n}\n'})}),"\n",(0,n.jsx)(r.h2,{id:"notes",children:"Notes"}),"\n",(0,n.jsxs)(r.ul,{children:["\n",(0,n.jsxs)(r.li,{children:["The player may fire ",(0,n.jsx)(r.code,{children:"provide-product-data"})," multiple times during a show as new products are highlighted. Your hydration handler must be idempotent."]}),"\n",(0,n.jsx)(r.li,{children:"Fields not provided in your hydration response fall back to data previously scraped or manually set in the Bambuser dashboard."}),"\n",(0,n.jsxs)(r.li,{children:[(0,n.jsx)(r.code,{children:"invoke"})," is an ",(0,n.jsx)(r.code,{children:"async throws"})," method \u2014 always call it inside a ",(0,n.jsx)(r.code,{children:"Task"})," or ",(0,n.jsx)(r.code,{children:"async"})," context."]}),"\n"]})]})}function h(e={}){const{wrapper:r}={...(0,i.R)(),...e.components};return r?(0,n.jsx)(r,{...e,children:(0,n.jsx)(l,{...e})}):l(e)}},28453(e,r,t){t.d(r,{R:()=>s,x:()=>c});var d=t(96540);const n={},i=d.createContext(n);function s(e){const r=d.useContext(i);return d.useMemo(function(){return"function"==typeof e?e(r):{...r,...e}},[r,e])}function c(e){let r;return r=e.disableParentContext?"function"==typeof e.components?e.components(n):e.components||n:s(e.components),d.createElement(i.Provider,{value:r},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.