PageSourceSearch

https://bambuser.com/docs/assets/js/6e56a1d5.8080ba12.js

js bambuser.com collected 2026-09-24 08:31:09 UTC 11,270 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkbambuser_docs||=[]).push([[4874],{14533(e,t,n){n.r(t),n.d(t,{assets:()=>d,contentTitle:()=>o,default:()=>h,frontMatter:()=>a,metadata:()=>s,toc:()=>l});const s=JSON.parse('{"id":"mobileSDK/ios/vc-ios-cart-integration","title":"iOS SDK \u2014 Cart Integration","description":"Route the agent\'s add-to-cart and update-cart intents to your app\'s own cart via shouldAddToCart and shouldUpdateCart handlers on BambuserCallHandlers.","source":"@site/video-consultation/mobileSDK/ios/cart-integration.mdx","sourceDirName":"mobileSDK/ios","slug":"/mobileSDK/ios/mobile-sdk/ios/cart-integration","permalink":"/docs/video-consultation/mobileSDK/ios/mobile-sdk/ios/cart-integration","draft":false,"unlisted":false,"tags":[],"version":"current","frontMatter":{"id":"vc-ios-cart-integration","title":"iOS SDK \u2014 Cart Integration","description":"Route the agent\'s add-to-cart and update-cart intents to your app\'s own cart via shouldAddToCart and shouldUpdateCart handlers on BambuserCallHandlers.","sidebar_label":"Cart Integration","slug":"mobile-sdk/ios/cart-integration"},"sidebar":"someSidebars","previous":{"title":"Provide Search Data","permalink":"/docs/video-consultation/mobileSDK/ios/mobile-sdk/ios/provide-search-data"},"next":{"title":"Customer Events","permalink":"/docs/video-consultation/mobileSDK/ios/mobile-sdk/ios/customer-events"}}');var r=n(74848),i=n(28453);const a={id:"vc-ios-cart-integration",title:"iOS SDK \u2014 Cart Integration",description:"Route the agent's add-to-cart and update-cart intents to your app's own cart via shouldAddToCart and shouldUpdateCart handlers on BambuserCallHandlers.",sidebar_label:"Cart Integration",slug:"mobile-sdk/ios/cart-integration"},o="Cart Integration",d={},l=[{value:"Two handlers",id:"two-handlers",level:2},{value:"Reply values",id:"reply-values",level:2},{value:"Wire the handlers",id:"wire-the-handlers",level:2},{value:"Reject a cart intent",id:"reject-a-cart-intent",level:2},{value:"Reacting to checkout",id:"reacting-to-checkout",level:2},{value:"Skip vs error vs failure",id:"skip-vs-error-vs-failure",level:2},{value:"See also",id:"see-also",level:2}];function c(e){const t={a:"a",code:"code",h1:"h1",h2:"h2",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,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(t.header,{children:(0,r.jsx)(t.h1,{id:"cart-integration",children:"Cart Integration"})}),"\n",(0,r.jsxs)(t.p,{children:['Cart integration lets the "add to cart" and "update quantity" actions the agent triggers during a call flow into ',(0,r.jsx)(t.strong,{children:"your app's own cart"})," \u2014 so a shopper who ends the call and taps to their cart tab sees the products the agent just added, with the tax / shipping / promo logic already in place."]}),"\n",(0,r.jsx)(t.p,{children:"If you don't wire this up, the widget's built-in cart is used and the shopper checks out through Bambuser's flow. Both are valid \u2014 the integration is opt-in."}),"\n",(0,r.jsx)(t.h2,{id:"two-handlers",children:"Two handlers"}),"\n",(0,r.jsxs)(t.table,{children:[(0,r.jsx)(t.thead,{children:(0,r.jsxs)(t.tr,{children:[(0,r.jsx)(t.th,{children:"Handler"}),(0,r.jsx)(t.th,{children:"When it fires"})]})}),(0,r.jsxs)(t.tbody,{children:[(0,r.jsxs)(t.tr,{children:[(0,r.jsx)(t.td,{children:(0,r.jsx)(t.code,{children:"shouldAddToCart"})}),(0,r.jsx)(t.td,{children:'Agent taps "add" on a product in their tool.'})]}),(0,r.jsxs)(t.tr,{children:[(0,r.jsx)(t.td,{children:(0,r.jsx)(t.code,{children:"shouldUpdateCart"})}),(0,r.jsx)(t.td,{children:"Agent changes the quantity of a line already in the cart (0 removes it)."})]})]})]}),"\n",(0,r.jsxs)(t.p,{children:["Both are on ",(0,r.jsx)(t.code,{children:"BambuserCallHandlers"}),":"]}),"\n",(0,r.jsx)(t.pre,{children:(0,r.jsx)(t.code,{className:"language-swift",children:"public struct BambuserCallHandlers {\n    public var shouldAddToCart: (\n        (BambuserCartIntent) async -> BambuserReply<BambuserJSONValue>\n    )?\n    public var shouldUpdateCart: (\n        (BambuserCartIntent) async -> BambuserReply<BambuserJSONValue>\n    )?\n}\n"})}),"\n",(0,r.jsxs)(t.p,{children:[(0,r.jsx)(t.code,{children:"BambuserCartIntent"})," fields:"]}),"\n",(0,r.jsxs)(t.table,{children:[(0,r.jsx)(t.thead,{children:(0,r.jsxs)(t.tr,{children:[(0,r.jsx)(t.th,{children:"Field"}),(0,r.jsx)(t.th,{children:"Type"}),(0,r.jsx)(t.th,{children:"Notes"})]})}),(0,r.jsxs)(t.tbody,{children:[(0,r.jsxs)(t.tr,{children:[(0,r.jsx)(t.td,{children:(0,r.jsx)(t.code,{children:"sku"})}),(0,r.jsx)(t.td,{children:(0,r.jsx)(t.code,{children:"String"})}),(0,r.jsx)(t.td,{children:"Which product to add / update."})]}),(0,r.jsxs)(t.tr,{children:[(0,r.jsx)(t.td,{children:(0,r.jsx)(t.code,{children:"quantity"})}),(0,r.jsx)(t.td,{children:(0,r.jsx)(t.code,{children:"Int"})}),(0,r.jsxs)(t.td,{children:["The ",(0,r.jsx)(t.strong,{children:"target"})," quantity after the intent applies (not a delta). ",(0,r.jsx)(t.code,{children:"0"})," on ",(0,r.jsx)(t.code,{children:"shouldUpdateCart"})," removes the line."]})]}),(0,r.jsxs)(t.tr,{children:[(0,r.jsx)(t.td,{children:(0,r.jsx)(t.code,{children:"previousQuantity"})}),(0,r.jsx)(t.td,{children:(0,r.jsx)(t.code,{children:"Int?"})}),(0,r.jsx)(t.td,{children:"On updates, the quantity before this change."})]}),(0,r.jsxs)(t.tr,{children:[(0,r.jsx)(t.td,{children:(0,r.jsx)(t.code,{children:"raw"})}),(0,r.jsx)(t.td,{children:(0,r.jsx)(t.code,{children:"BambuserJSONValue"})}),(0,r.jsx)(t.td,{children:"Full JSON entry, in case you need a field the SDK hasn't modelled."})]})]})]}),"\n",(0,r.jsx)(t.h2,{id:"reply-values",children:"Reply values"}),"\n",(0,r.jsx)(t.p,{children:"The reply drives what the agent's tool shows:"}),"\n",(0,r.jsx)(t.pre,{children:(0,r.jsx)(t.code,{className:"language-swift",children:"extension BambuserReply where Payload == BambuserJSONValue {\n    // Success \u2014 agent sees a confirmation.\n    public static var success: BambuserReply { \u2026 }\n\n    // Failure \u2014 agent sees the reason string.\n    public static func failure(reason: String? = nil) -> BambuserReply { \u2026 }\n}\n"})}),"\n",(0,r.jsxs)(t.p,{children:["Plus the generic ",(0,r.jsx)(t.code,{children:".reply(customJson)"}),", ",(0,r.jsx)(t.code,{children:".error(message:)"}),", and ",(0,r.jsx)(t.code,{children:".skip"})," from ",(0,r.jsx)(t.code,{children:"BambuserReply<T>"}),". For most integrations, ",(0,r.jsx)(t.code,{children:".success"})," / ",(0,r.jsx)(t.code,{children:".failure(reason:)"})," are what you'll return."]}),"\n",(0,r.jsx)(t.h2,{id:"wire-the-handlers",children:"Wire the handlers"}),"\n",(0,r.jsx)(t.pre,{children:(0,r.jsx)(t.code,{className:"language-swift",children:'handlers.shouldAddToCart = { intent in\n    guard !intent.sku.isEmpty else {\n        return .failure(reason: "missing-sku")\n    }\n    cart.setQuantity(intent.quantity, for: intent.sku)\n    return .success\n}\n\nhandlers.shouldUpdateCart = { intent in\n    guard !intent.sku.isEmpty else {\n        return .failure(reason: "missing-sku")\n    }\n    cart.setQuantity(intent.quantity, for: intent.sku)\n    return .success\n}\n'})}),"\n",(0,r.jsxs)(t.p,{children:[(0,r.jsx)(t.code,{children:"intent.quantity"})," is the ",(0,r.jsx)(t.strong,{children:"absolute target"}),", not a delta. ",(0,r.jsx)(t.code,{children:"setQuantity(0, for: sku)"})," typically removes the line \u2014 decide the semantics of ",(0,r.jsx)(t.code,{children:"0"})," in your own cart layer."]}
1),"\n",(0,r.jsx)(t.h2,{id:"reject-a-cart-intent",children:"Reject a cart intent"}),"\n",(0,r.jsxs)(t.p,{children:["Return ",(0,r.jsx)(t.code,{children:".failure(reason:)"})," and the agent sees a toast with the reason. Typical rejects:"]}),"\n",(0,r.jsxs)(t.ul,{children:["\n",(0,r.jsx)(t.li,{children:'Stock ran out between the agent looking at the product and clicking "add".'}),"\n",(0,r.jsx)(t.li,{children:"The product is region-locked for the shopper."}),"\n",(0,r.jsx)(t.li,{children:"The shopper's session expired and needs to log back in."}),"\n"]}),"\n",(0,r.jsx)(t.pre,{children:(0,r.jsx)(t.code,{className:"language-swift",children:'handlers.shouldAddToCart = { intent in\n    guard let product = productCatalog.product(for: intent.sku) else {\n        return .failure(reason: "sku-not-found")\n    }\n    guard product.hasStock(quantity: intent.quantity) else {\n        return .failure(reason: "out-of-stock")\n    }\n    cart.setQuantity(intent.quantity, for: intent.sku)\n    return .success\n}\n'})}),"\n",(0,r.jsx)(t.h2,{id:"reacting-to-checkout",children:"Reacting to checkout"}),"\n",(0,r.jsxs)(t.p,{children:["The ",(0,r.jsx)(t.code,{children:".checkout"})," delegate event fires when the shopper taps checkout inside the widget. Switch to your cart tab so they land on your normal checkout flow:"]}),"\n",(0,r.jsx)(t.pre,{children:(0,r.jsx)(t.code,{className:"language-swift",children:"func bambuserCall(_ controller: BambuserCallController, didEmit event: BambuserCallEvent) {\n    if case .checkout = event {\n        navigation.switchTo(.cart)\n    }\n}\n"})}),"\n",(0,r.jsx)(t.p,{children:"The widget then closes itself (or drops to PiP if configured) and the shopper is inside your cart / checkout stack."}),"\n",(0,r.jsx)(t.h2,{id:"skip-vs-error-vs-failure",children:"Skip vs error vs failure"}),"\n",(0,r.jsxs)(t.ul,{children:["\n",(0,r.jsxs)(t.li,{children:[(0,r.jsx)(t.code,{children:".success"})," / ",(0,r.jsx)(t.code,{children:".failure(reason:)"})," \u2014 the two you'll use for almost every cart integration."]}),"\n",(0,r.jsxs)(t.li,{children:[(0,r.jsx)(t.code,{children:".error(message:)"})," \u2014 for ",(0,r.jsx)(t.strong,{children:"verify-function"}),'-style errors ("this action isn\'t allowed at all"). Rare for cart intents.']}),"\n",(0,r.jsxs)(t.li,{children:[(0,r.jsx)(t.code,{children:".skip"})," \u2014 fire-and-forget. Agent sees no confirmation and no error. The widget times out on the agent side. Use only when you genuinely can't answer (e.g. cart service is down and you'd rather stall than confuse)."]}),"\n"]}),"\n",(0,r.jsx)(t.h2,{id:"see-also",children:"See also"}),"\n",(0,r.jsxs)(t.ul,{children:["\n",(0,r.jsxs)(t.li,{children:[(0,r.jsx)(t.a,{href:"/docs/video-consultation/mobileSDK/ios/mobile-sdk/ios/provide-product-data",children:"Provide Product Data"}),", ",(0,r.jsx)(t.a,{href:"/docs/video-consultation/mobileSDK/ios/mobile-sdk/ios/provide-search-data",children:"Provide Search Data"}),"."]}),"\n",(0,r.jsxs)(t.li,{children:[(0,r.jsx)(t.a,{href:"/docs/video-consultation/mobileSDK/ios/mobile-sdk/ios/co-browse",children:"Co-browse"})," \u2014 routing navigate-to events so the shopper reaches their real cart page on tap."]}),"\n"]})]})}function h(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,r.jsx)(t,{...e,children:(0,r.jsx)(c,{...e})}):c(e)}},28453(e,t,n){n.d(t,{R:()=>a,x:()=>o});var s=n(96540);const r={},i=s.createContext(r);function a(e){const t=s.useContext(i);return s.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function o(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:a(e.components),s.createElement(i.Provider,{value:t},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.