1"use strict";(globalThis.webpackChunkbambuser_docs||=[]).push([[3236],{30245(e,r,t){t.r(r),t.d(r,{assets:()=>a,contentTitle:()=>o,default:()=>h,frontMatter:()=>d,metadata:()=>n,toc:()=>c});const n=JSON.parse('{"id":"mobileSDK/ios/vc-ios-provide-product-data","title":"iOS SDK \u2014 Provide Product Data","description":"Return product details when the agent adds a product to the shared view. Implement the provideProductData handler and build a BambuserFactorySpec response.","source":"@site/video-consultation/mobileSDK/ios/provide-product-data.mdx","sourceDirName":"mobileSDK/ios","slug":"/mobileSDK/ios/mobile-sdk/ios/provide-product-data","permalink":"/docs/video-consultation/mobileSDK/ios/mobile-sdk/ios/provide-product-data","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"id":"vc-ios-provide-product-data","title":"iOS SDK \u2014 Provide Product Data","description":"Return product details when the agent adds a product to the shared view. Implement the provideProductData handler and build a BambuserFactorySpec response.","sidebar_label":"Provide Product Data","slug":"mobile-sdk/ios/provide-product-data"},"sidebar":"someSidebars","previous":{"title":"Integration with UIKit","permalink":"/docs/video-consultation/mobileSDK/ios/mobile-sdk/ios/integration-uikit"},"next":{"title":"Provide Search Data","permalink":"/docs/video-consultation/mobileSDK/ios/mobile-sdk/ios/provide-search-data"}}');var s=t(74848),i=t(28453);const d={id:"vc-ios-provide-product-data",title:"iOS SDK \u2014 Provide Product Data",description:"Return product details when the agent adds a product to the shared view. Implement the provideProductData handler and build a BambuserFactorySpec response.",sidebar_label:"Provide Product Data",slug:"mobile-sdk/ios/provide-product-data"},o="Provide Product Data",a={},c=[{value:"When to implement",id:"when-to-implement",level:2},{value:"Handler signature",id:"handler-signature",level:2},{value:"Wire the handler",id:"wire-the-handler",level:2},{value:"Build the factory spec",id:"build-the-factory-spec",level:2},{value:"Error path \u2014 the "verify function" pattern",id:"error-path--the-verify-function-pattern",level:2},{value:"See also",id:"see-also",level:2}];function l(e){const r={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",header:"header",li:"li",mdxAdmonitionTitle:"mdxAdmonitionTitle",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,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(r.header,{children:(0,s.jsx)(r.h1,{id:"provide-product-data",children:"Provide Product Data"})}),"\n",(0,s.jsxs)(r.p,{children:[(0,s.jsx)(r.code,{children:"provideProductData"})," fires when the agent adds a product to the shared view during a call \u2014 either by pasting a URL, scanning a barcode, or selecting from their integrated product tool. The SDK hands you a reference (SKU / URL / scanned code), you look it up in your catalogue, and return a ",(0,s.jsx)(r.code,{children:"BambuserFactorySpec"})," that the widget renders as a product card."]}),"\n",(0,s.jsx)(r.h2,{id:"when-to-implement",children:"When to implement"}),"\n",(0,s.jsxs)(r.p,{children:["You need this only when the widget ",(0,s.jsx)(r.strong,{children:"cannot see your catalogue directly"})," \u2014 e.g. the catalogue is region-locked, personalised per shopper, driven by live stock, or otherwise not exposable via BamHub feed. If your catalogue is in BamHub, product search and product detail inside the call are handled automatically and this handler is optional."]}),"\n",(0,s.jsx)(r.h2,{id:"handler-signature",children:"Handler signature"}),"\n",(0,s.jsx)(r.pre,{children:(0,s.jsx)(r.code,{className:"language-swift",children:"public struct BambuserCallHandlers {\n public var provideProductData: (\n (BambuserProductRef) async -> BambuserReply<BambuserFactorySpec>\n )?\n}\n"})}),"\n",(0,s.jsxs)(r.p,{children:[(0,s.jsx)(r.code,{children:"BambuserProductRef"})," fields:"]}),"\n",(0,s.jsxs)(r.table,{children:[(0,s.jsx)(r.thead,{children:(0,s.jsxs)(r.tr,{children:[(0,s.jsx)(r.th,{children:"Field"}),(0,s.jsx)(r.th,{children:"Type"}),(0,s.jsx)(r.th,{children:"Notes"})]})}),(0,s.jsxs)(r.tbody,{children:[(0,s.jsxs)(r.tr,{children:[(0,s.jsx)(r.td,{children:(0,s.jsx)(r.code,{children:"ref"})}),(0,s.jsx)(r.td,{children:(0,s.jsx)(r.code,{children:"String"})}),(0,s.jsxs)(r.td,{children:["The raw reference \u2014 SKU, URL, or barcode depending on ",(0,s.jsx)(r.code,{children:"kind"}),"."]})]}),(0,s.jsxs)(r.tr,{children:[(0,s.jsx)(r.td,{children:(0,s.jsx)(r.code,{children:"kind"})}),(0,s.jsx)(r.td,{children:(0,s.jsx)(r.code,{children:"BambuserProductRef.Kind"})}),(0,s.jsxs)(r.td,{children:[(0,s.jsx)(r.code,{children:".productReference"}),", ",(0,s.jsx)(r.code,{children:".url"}),", ",(0,s.jsx)(r.code,{children:".scannedCode"}),", or ",(0,s.jsx)(r.code,{children:".unknown(String)"}),"."]})]}),(0,s.jsxs)(r.tr,{children:[(0,s.jsx)(r.td,{children:(0,s.jsx)(r.code,{children:"bambuserId"})}),(0,s.jsx)(r.td,{children:(0,s.jsx)(r.code,{children:"String"})}),(0,s.jsx)(r.td,{children:"Widget-generated id you don't need to construct \u2014 the SDK echoes it back automatically."})]}),(0,s.jsxs)(r.tr,{children:[(0,s.jsx)(r.td,{children:(0,s.jsx)(r.code,{children:"raw"})}),(0,s.jsx)(r.td,{children:(0,s.jsx)(r.code,{children:"BambuserJSONValue"})}),(0,s.jsx)(r.td,{children:"Full unmodified JSON entry the embed sent, in case you need a field the SDK hasn't modelled."})]})]})]}),"\n",(0,s.jsx)(r.p,{children:"Return values:"}),"\n",(0,s.jsxs)(r.ul,{children:["\n",(0,s.jsxs)(r.li,{children:[(0,s.jsx)(r.code,{children:".reply(BambuserFactorySpec)"})," \u2014 success."]}),"\n",(0,s.jsxs)(r.li,{children:[(0,s.jsx)(r.code,{children:".error(message:)"}),' \u2014 surface an agent-visible error ("we don\'t sell that", "region locked").']}),"\n",(0,s.jsxs)(r.li,{children:[(0,s.jsx)(r.code,{children:".skip"})," \u2014 send no reply. The widget's agent-tool times out; prefer ",(0,s.jsx)(r.code,{children:".error"})," in production."]}),"\n"]}),"\n",(0,s.jsx)(r.h2,{id:"wire-the-handler",children:"Wire the handler"}),"\n",(0,s.jsx)(r.pre,{children:(0,s.jsx)(r.code,{className:"language-swift",children:'handlers.provideProductData = { ref in\n print("[Bambuser] provideProductData ref=\\(ref.ref) kind=\\(ref.kind.rawValue)")\n\n guard let sku = resolveSKU(from: ref) else {\n return .error(message: "Unrecognised product reference: \'\\(ref.ref)\'")\n }\n guard let product = ProductCatalog.product(for: sku) else {\n return .error(message: "We don\'t sell \'\\(sku)\'")\n }\n return .reply(product.toBambuserProduct().factorySpec)\n}\n'})}),"\n",(0,s.jsxs)(r.p,{children:["The ",(0,s.jsx)(r.code,{children:"resolveSKU(from:)"})," helper is entirely yours \u2014 the exact rules depend on how your URLs are structured and what scanned codes you support:"]}),"\n",(0,s.jsx)(r.pre,{children:(0,s.jsx)(r.code,{className:"language-swift",children:'func resolveSKU(from ref: BambuserProductRef) -> String? {\n switch ref.kind {\n case .productReference, .scannedCode:\n return ref.ref.isEmpty ? nil : ref.ref\n case .url:\n guard let url = URL(string: ref.ref) else { return nil }\n return url.pathComponents.reversed().first(where: { $0 != "/" && !$0.isEmpty })\n case .unknown:\n return ref.ref.isEmpty ? nil : ref.ref\n }\n}\n'})}),"\n",(0,s.jsx)(r.h2,{id:"build-the-factory-spec",children:"Build the factory spec"}),"\n",(0,s.jsxs)(r.p,{children:[(0,s.jsx)(r.code,{children:"BambuserFactorySpec"})," mirrors the JS builder chain the widget expects. The ",(0,s.jsxs)(r.a,{href:"https://github.com/bambuser/BambuserCallsShopperSDK/tree/main/BambuserCallsShopperDemo/BambuserCallsShopperDemo/Bambuser",children:["reference ",(0,s.jsx)(r.code,{children:"BambuserProduct"})," model"]})," in the demo maps a plain product struct to the spec via a ",(0,s.jsx)(r.code,{children:".factorySpec"})," computed property."]}),"\n",(0,s.jsxs)(r.admonition,{type:"warning",children:[(0,s.jsxs)(r.mdxAdmonitionTitle,{children:[(0,s.jsx)(r.code,{children:".url()"})," is mandatory on the product"]}),(0,s.jsxs)(r.p,{children:["Always emit ",(0,s.jsx)(r.code,{children:'.url("https://\u2026/product/{sku}/\u2026")'})," on the ",(0,s.jsx)(r.strong,{children:"product"})," builder (the ",(0,s.jsx)(r.code,{children:"detailFactory"}),'). This URL is what the agent\'s tool uses to render the "View page" link and to fire ',(0,s.jsx)(r.code,{children:"navigate-to"})," / ",(0,s.jsx)(r.code,{children:"surf-behind-to"})," co-browse events into the shopper's app."]}),(0,s.jsxs)(r.p,{children:["Variations may ",(0,s.jsx)(r.strong,{children:"optionally"})," carry their own ",(0,s.jsx)(r.code,{children:".url()"})," too \u2014 pass it when a specific colour/size has a different landing page, otherwise leave it off. The product-level URL is the required one; variation URL is a nice-to-have."]})]}),"\n",(0,s.jsx)(r.p,{children:"A minimal single-variation example:"}),"\n",(0,s.jsx)(r.pre,{children:(0,s.jsx)(r.code,{className:"language-swift",children:'BambuserFactorySpec([\n .method("currency", args: [.string(currency)]),\n .method("locale", args: [.string("en-US")]),\
1n .method("product", factory: BambuserFactorySpec([\n .method("name", args: [.string(name)]),\n .method("sku", args: [.string(sku)]),\n .method("description", args: [.string(description)]),\n // Product-level URL \u2014 mandatory.\n .method("url", args: [.string(url)]),\n .method("variations", items: [\n BambuserFactoryItem(\n factoryArgs: [],\n spec: BambuserFactorySpec([\n .method("name", args: [.string(name)]),\n .method("sku", args: [.string(sku)]),\n .method("subtitle", args: [.string(brand)]),\n .method("inStock", args: [.bool(inStock)]),\n .method("imageUrls", args: [.array(imageUrls.map { .string($0) })]),\n .method("price", factory: BambuserFactorySpec([\n .method("current", args: [.double(price)]),\n ])),\n ])\n )\n ]),\n ])),\n])\n'})}),"\n",(0,s.jsxs)(r.p,{children:["Full field list \u2014 attributes, comparable attributes, related products, ratings, details \u2014 lives in the reference ",(0,s.jsx)(r.code,{children:"BambuserProduct"})," model. Copy the file into your app and adapt."]}),"\n",(0,s.jsx)(r.h2,{id:"error-path--the-verify-function-pattern",children:'Error path \u2014 the "verify function" pattern'}),"\n",(0,s.jsxs)(r.p,{children:['The widget\'s built-in "verify" pattern (',(0,s.jsx)(r.code,{children:"if (!validProduct(...)) throw new Error(...)"}),") is supported natively:"]}),"\n",(0,s.jsx)(r.pre,{children:(0,s.jsx)(r.code,{className:"language-swift",children:'handlers.provideProductData = { ref in\n guard let sku = resolveSKU(from: ref) else {\n return .error(message: "Please paste a valid product URL.")\n }\n guard hasStock(sku) else {\n return .error(message: "Out of stock in your region.")\n }\n // \u2026\n}\n'})}),"\n",(0,s.jsx)(r.p,{children:"The message you return renders as an agent-visible error in the widget's tool \u2014 no crash, no timeout."}),"\n",(0,s.jsx)(r.h2,{id:"see-also",children:"See also"}),"\n",(0,s.jsxs)(r.ul,{children:["\n",(0,s.jsxs)(r.li,{children:[(0,s.jsx)(r.a,{href:"/docs/video-consultation/mobileSDK/ios/mobile-sdk/ios/provide-search-data",children:"Provide Search Data"})," \u2014 the paginated-results twin."]}),"\n",(0,s.jsxs)(r.li,{children:[(0,s.jsx)(r.a,{href:"/docs/video-consultation/mobileSDK/ios/mobile-sdk/ios/cart-integration",children:"Cart Integration"})," \u2014 routing add-to-cart intents."]}),"\n",(0,s.jsxs)(r.li,{children:[(0,s.jsx)(r.a,{href:"/docs/video-consultation/mobileSDK/ios/mobile-sdk/ios/architecture",children:"Architecture"})," \u2014 how handlers gate the JS subscriptions."]}),"\n"]})]})}function h(e={}){const{wrapper:r}={...(0,i.R)(),...e.components};return r?(0,s.jsx)(r,{...e,children:(0,s.jsx)(l,{...e})}):l(e)}},28453(e,r,t){t.d(r,{R:()=>d,x:()=>o});var n=t(96540);const s={},i=n.createContext(s);function d(e){const r=n.useContext(i);return n.useMemo(function(){return"function"==typeof e?e(r):{...r,...e}},[r,e])}function o(e){let r;return r=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:d(e.components),n.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.