Skip to main content
Version: 1.0

No Supplier

When your supplier key is in No Supplier mode (encoded in the key Mealz provides), Mealz owns the basket. Extra WebView helpers cover retailer/store picking, pricing, and transfer CTAs.

Host Setup

In No Supplier mode, do not call setUser("…") and do not select a store yourself (setSelectedStoreWithExternalId / setSelectedStoreWithMealzId). Leave authless defaults; retailer and store picking go through Supplier Selector.

Basket Transfer​

From the Mealz basket preview (No Supplier), the shopper confirms transfer toward an affiliated retailer. That CTA triggers the handoff: MealzUIKit opens the retailer URL outside your app with transferred_basket_token. There is no separate transfer API — host Add to Cart / Supplier Selector and allow that handoff.

No Supplier basket preview — transfer starts from the action under this screen

Full flow (No Supplier emit + Retailer receive): Basket Transfer.

Supplier Selector​

Lets the shopper pick an affiliated grocery retailer (and then a store). Mealz opens this automatically when a store is required, or other No Supplier components request it via redirectToSupplierSelector. You should not set the Mealz store id manually in the normal flow.

Supplier selector — pick retailer Supplier selector — pick store

See Initialisation → Store.

Background: Non-retailer introduction (steps 3–4 of the transfer journey).

Parameters​

ParameterTypeRequiredDefaultWhat it does
contextSupplierSelectorContextyes—Why the selector opened. Today: OpenedForAddToCartByExternalId(externalRecipeId).
navigationCallbackSupplierSelectorNavigationCallbackyes—Only exit matters (cancel or store selected).
modifierCompose Modifiernofills max sizeLayout.
stateContentstate overlaysnolibrary defaultOptional loading UI.

SupplierSelectorNavigationCallback​

CallbackRequiredWhat you must do
exityesDismiss the selector when the user cancels or after a store is selected.

Recipe Pricing​

Shows the price for a recipe in the No Supplier context.

Recipe pricing — no store selected Recipe pricing — store selected

Parameters​

ParameterTypeRequiredDefaultWhat it does
displayRecipeDisplayRecipeyes—Mealz or external recipe id (FromMealzRecipeId / FromExternalRecipeId).
servesInt?nonullPortion / guest count for pricing.
navigationCallbackRecipePricingNavigationCallbackyes—Opens supplier selector when a store is missing.
modifierCompose Modifierno—Layout (fixed height).
stateContentstate overlaysnolibrary defaultOptional loading UI.
Serves

Pass the guest count from your No Supplier serves counter — the value your UI currently shows. Do not pick an arbitrary unrelated number. If the recipe is already in the Mealz basket, serves must stay aligned with that basket guest count (the same value you manage in your counter). Opening pricing, add-to-cart, or details with a mismatched serves can desync portions/ingredients.

RecipePricingNavigationCallback​

CallbackRequiredWhat you must do
redirectToSupplierSelectoryes(SupplierSelectorContext) -> Unit — present SupplierSelectorScreen with that context.

See also Fetching style and Non-retailer setup.

Add to Cart CTA​

Primary CTA next to a recipe to add ingredients to the Mealz basket and start transfer toward an affiliated retailer.

Full web behaviour: no-supplier-add-to-cart-cta (SSR).

Add to Cart CTA — recipe not yet added Add to Cart CTA — recipe added

Parameters​

ParameterTypeRequiredDefaultWhat it does
displayRecipeDisplayRecipe.FromExternalRecipeIdyes—External recipe id (your content id).
servesInt?nonullGuest count for the add-to-cart flow.
navigationCallbackAddToCartCtaNavigationCallbackyes—Details, supplier selector, catalog.
modifierCompose Modifierno—Layout (fixed height).
stateContentstate overlaysnolibrary defaultOptional loading UI.
Serves

Pass the guest count from your No Supplier serves counter — the value your UI currently shows. Do not pick an arbitrary unrelated number. If the recipe is already in the Mealz basket, serves must stay aligned with that basket guest count (the same value you manage in your counter). Opening pricing, add-to-cart, or details with a mismatched serves can desync portions/ingredients.

AddToCartCtaNavigationCallback​

CallbackRequiredWhat you must do
goToRecipeDetailsyes(DisplayRecipe, serves: Int?) -> Unit — open details for that recipe.
redirectToSupplierSelectoryesPresent supplier selector with the given SupplierSelectorContext.
goToCatalogyes(FeatureRoute.Catalog) -> Unit — open catalog when requested.

Base login / store / cart / exit redirects are no-ops on this callback — wire the three callbacks above.

Serves

In No Supplier mode, you own the guest-count counter UI and logic. Pass that same serves into Mealz components (RecipePricing, Add to Cart CTA, RecipeDetails). When goToRecipeDetails gives you a serves argument, forward it as-is into RecipeDetailsScreen / RecipeDetailsViewController — it should match your counter (and the Mealz basket once the recipe is added). Do not substitute a different default.

Basket Count Badges​

In No Supplier mode, Mealz owns the basket. Use BasketCountNotifier to drive recipe / product count badges in your app chrome.

Register after start. Multiple listeners are supported — keep a reference to each listener if you need to unregister it later:

BasketCountNotifier.shared.registerRecipeCountListener { count in /* badge */ }
BasketCountNotifier.shared.registerProductCountListener { count in /* badge */ }
// Unregister with the same listener instance:
// BasketCountNotifier.shared.unregisterRecipeCountListener(listener:)

Counts are persisted locally and refreshed from WebView observers. They can be stale until a Mealz WebView opens again after process death, auth changes, or store changes without reopening Mealz UI.

Inject Examples​

// Pricing — nested Kotlin types flatten in Swift (DisplayRecipeFromExternalRecipeId, …)
let serves = KotlinInt(int: Int32(4))

RecipePricingViewController.companion.get(
    displayRecipe: DisplayRecipeFromExternalRecipeId(id: "ext-recipe-1"),
    serves: serves,
    stateContent: nil,
    navigationCallback: RecipePricingNavigationCallback(
        redirectToSupplierSelector: { context in /* present SupplierSelectorViewController */ }
    )
)

// Add to Cart CTA
AddToCartCTAViewController.companion.get(
    displayRecipe: DisplayRecipeFromExternalRecipeId(id: "ext-recipe-1"),
    serves: serves,
    stateContent: nil,
    navigationCallback: AddToCartCtaNavigationCallback(
        goToRecipeDetails: { displayRecipe, serves in
            // Forward serves as-is (should match your No Supplier counter) into RecipeDetailsViewController
            // serves is KotlinInt? — e.g. serves.map { Int(truncating: $0) }
        },
        redirectToSupplierSelector: { context in },
        goToCatalog: { route in }
    )
)

// Supplier Selector
SupplierSelectorViewController.companion.get(
    context: SupplierSelectorContextOpenedForAddToCartByExternalId(
        externalRecipeId: "ext-recipe-1"
    ),
    stateContent: nil,
    navigationCallback: SupplierSelectorNavigationCallback(
        exit: { /* dismiss */ }
    )
)