Skip to main content
Version: 4.1

Updating MealzManager

This section is upgrading miam-sdk from Version 3 to Version 4.

Initialization

First & foremost, you'll need to update how you initialize your miam-sdk inside your MealzManager. We have made it more straightforward & readable to initialize miam-sdk, which comes with changes to existing implementations.

SupplierKey - base64 key to set information

This will be provided by Mealz during implementation. This will include information such as PROD vs UAT & your unique company identifier.

If you just want to play around, you can use the sample Mealz Store:

let supplierKey = "ewoJInN1cHBsaWVyX2lkIjogIjE0IiwKCSJwbGF1c2libGVfZG9tYWluZSI6ICJtaWFtLnRlc3QiLAoJIm1pYW1fb3JpZ2luIjogIm1pYW0iLAoJIm9yaWdpbiI6ICJtaWFtIiwKCSJtaWFtX2Vudmlyb25tZW50IjogIlVBVCIKfQ=="

which you'll see decodes from base 64 to this:

{
"supplier_id": "14",
"plausible_domaine": "miam.test",
"miam_origin": "miam",
"origin": "miam",
"miam_environment": "UAT"
}

BasketHandler - Comunicating with the Mealz Baskets

Previous, implementations would the BasketHandler to communicate with the two baskets. The BasketHandler has been replaced in version 4 by protocols BasketSubscriber & BasketPublisher.

Your previous code would look something like this inside your MealzManager:

BasketHandlerInstance.shared.instance.pushProductsToMiamBasket(retailerBasket: [])
BasketHandlerInstance.shared.instance.setListenToRetailerBasket(func: initBasketListener)
BasketHandlerInstance.shared.instance.setPushProductsToSupplierBasket(func: pushProductToBasket)

We'll go through the differences on these calls one by one.

We think it's best to start to create a new class that implements our new protocols. You can copy this & change the name:

class YourNewBasketService: BasketSubscriber, BasketPublisher {
var initialValue: [SupplierProduct]

func receive(event: [SupplierProduct]) {
<#code#>
}

func onBasketUpdate(sendUpdateToSDK: @escaping ([SupplierProduct]) -> Void) {
<#code#>
}
}
info

IMPORTANT: For all upcoming code examples, we will use PretendProduct to replicate YOUR product. In your implementation, use your actual Product class.

onBasketUpdate - setListenToRetailerBasket

This code is very similiar to before, but instead we put the code inside our onBasketUpdate function. For example, your old code should be streaming your basket to the Mealz Basket, something like this:

private func initBasketListener() {
cancelable = PretendBasket.shared.$items.sink { receiveValue in
BasketHandlerInstance.shared.instance.pushProductsToMiamBasket(
retailerBasket: self.pretendProductsToRetailerProducts(products: receiveValue)
)
}
}

where

private func pretendProductsToRetailerProducts(
products: [PretendProduct]
) -> [SupplierProduct] {
return products.map {
return SupplierProduct(
id: $0.id,
quantity: Int32($0.quantity),
name: $0.name,
productIdentifier: nil,
imageURL: $0.imageUrl
)
}
}

Now, you can move the content from initBasketListener directly into the onBasketUpdate.

import Combine

private var cancellable: AnyCancellable? // used to create stream between mealz basket & our own

func onBasketUpdate(sendUpdateToSDK: @escaping ([SupplierProduct]) -> Void) {
cancellable = PretendBasket.shared.$items.sink { receiveValue in
`sendUpdateToSDK`(
self.pretendProductsToRetailerProducts(products: receiveValue)
)
}
}

initialValue - pushProductsToMiamBasket

The pushProductsToMiamBasket creates the initial basket state. Instead, we set this inside our new BasketService.

In our implementation, we create an initializer to pass our DemoProducts into before translating them. Here is our code:

init(initialBasketList: [PretendProduct]) {
self.initialValue = [] // First, initialize all properties as [] so we can use pretendProductsToRetailerProducts

if initialBasketList.count > 0 {
// Now convert (safely) if we have products
self.initialValue = pretendProductsToRetailerProducts(products: initialBasketList)
}
}

So now in our MealzManager, we can call:

let demoBasketService = DemoBasketService(initialBasketList: PretendBasket.shared.items)

receive - setPushProductsToSupplierBasket

The last step of the basketHandler is to implement the receive. This will update make sure both baskets are connected & up to date.

You'll need to create a new function, ours is named updateBasketFromExternalSource that accepts a list of SupplierProducts & updates your basket. The function will iterate through the new products, checking if they are in your basket. If yes, they will either delete them (if the quantity is 0), or update their quantity & info. If no, they will be added to your basket.

info

IMPORTANT: you'll want to create a local temporary list inside this function so that you can edit your basket all at once. If you update your basket with each product iteration, you will have unexpected behavior (& increase time complexity as you'll be iterating through a growing list).

Here is our example:

private func updateBasketFromExternalSource(products: [SupplierProduct]) {
// we need to update the basket all at once, otherwise we will have issues with Mealz updating too frequently
var basketCopy = PretendBasket.shared.items

for product in products {
// check if we already have the product to remove or update info
if let productToUpdateIndex = PretendBasket.shared.items.firstIndex(where: { $0.id == product.id }) {
if product.quantity == 0 { // we know an item is deleted if the qty is 0
if basketCopy.indices.contains(productToUpdateIndex) {
basketCopy.remove(at: productToUpdateIndex)
}
} else {
let item = PretendBasket.shared.items[productToUpdateIndex]
basketCopy[productToUpdateIndex] = PretendProduct( // your product
id: product.id,
name: product.name ?? item.name,
quantity: product.quantity,
imageUrl: product.imageUrl ?? item.imageUrl)
}
} else { // otherwise add it to the client basket
let newProduct = PretendProduct( // your product
id: product.id,
name: product.name ?? "product",
quantity: Int(product.quantity),
imageUrl: product.imageURL
)
basketCopy.append(newProduct)
}
}
// update your basket after all operations
PretendBasket.shared.items = basketCopy
}

and then simply:

func receive(event: [SupplierProduct]) {
updateBasketFromExternalSource(products: event)
}

Putting it all together

You can now remove the previous BasketHandler & add this code:

let demoBasketService = DemoBasketService(initialBasketList: PretendBasket.shared.items)

Mealz.shared.Core(init: { coreBuilder in
// set supplier key
coreBuilder.sdkRequirement(init: { requirementBuilder in
requirementBuilder.key = supplierKey
})
// set listeners & notifiers
coreBuilder.subscriptions(init: { subscriptionBuilder in
subscriptionBuilder.basket(init: { basketSubscriptionBuilder in
// subscribe
basketSubscriptionBuilder.subscribe(subscriber: demoBasketService)
// push updates
basketSubscriptionBuilder.register(publisher: demoBasketService)
})
})
})

User

ContextHandlerInstance

If you've implemented the above code, you can delete this line:

ContextHandlerInstance.shared.instance.doInitMiam(base64_key: supplierKey)

PointOfSaleHandler

Previously, to set the store, you'd call the PointOfSaleHandler like this:

PointOfSaleHandler.shared.updateStoreId(storeId: "25910")

Now, you use user inside the Mealz SDK like this:

// set store
Mealz.shared.user.setStoreId(storeId: "25910")

UserHandler

Previously, to set the userId, you'd call the UserHandler like this:

UserHandler.shared.updateUserId(userId: existingUserId)

Now, you use user inside the Mealz SDK like this:

// set userID
Mealz.shared.user.updateUserId(userId: existingUserId, authorization: Authorization.userId)

Additionally

You can also add this code to be explicit about enabled Favoriting Recipes & Personalized Recipes.

// allow profiling -> can we use your personal data to provide custom recipes?
Mealz.shared.user.setProfilingAllowance(allowance: true)
// allow users to heart recipes
Mealz.shared.user.setEnableLike(isEnable: true)

Clear Basket

To clear the basket, you can remove this code:

BasketHandlerInstance.shared.instance.clear()

& replace it with this:

Mealz.shared.basket.clear()

Set Price Book

To set the price book, you can remove this code:

BasketHandlerInstance.shared.instance.setPriceBookKey(priceBookKey: pricebook.rawValue)

& replace it with this:

Mealz.basket.setPriceBook(priceBookId: pricebook.rawValue)

Handle Payment

To inform the SDK that recipes will be purchased, you can remove this code:

BasketHandlerInstance.shared.instance.handlePayment(totalAmount: totalAmount)

& replace it with this:

Mealz.shared.basket.handlePayment(totalAmount: totalAmount)

Analytics & Notifications

Analytics

Previously, you would use the AnalyticsHandler to follow the Mealz updates:

AnalyticsInstance.shared.instance.setOnEventEmitted( 
onEventEmittedCallBack: { event in
print("event Miam \(event.eventType) \(event.path) \(event.props)")
}
)

Now, you can call:

// listen to analytics events
Mealz.shared.notifications.analytics.listen { event in
print("Mealz.Notifications.analytics \(String(describing: event))")
}

Notifications

Previously, you could use Mealz Toast like this:

ToasterHandler.shared.setOnAddRecipeText("Well done !")
ToasterHandler.shared.setOnLikeRecipeText("Good taste !")

We have added notifications like Toaster, so you can add this to listen:

// show toast to users on certain events
Mealz.shared.notifications.toaster.listen(callBack: { event in
switch event as? ToasterNotification {
case ToasterNotification.RecipeAdded():
print("MealzNotification: Recipe Added")
case ToasterNotification.RecipeLiked():
print("MealzNotification: Recipe Liked")
default:
break
}
})

Localization

If you have overwritten our localization, you can KEEP this line:

I18nResolver.shared.registerAppBundle(bundle: .main) // or the bundle/package where you store the localized files

Is Ready

Previously, you would use one of these functions to know if Mealz was ready

ContextHandlerInstance.shared.instance.isReady()

or

 ContextHandlerInstance.shared.instance.onReadyEvent(callback: {
// do your call back here
})

Now, you can use our AvailabilityStore like this:

 Mealz.environment.isAvailable()

Recipe Count

Previously you could monitor the recipe count this way:

GroceriesListHandler.shared.onRecipeCountChanges { count in
// do stuff
print(count)
}

Now you can access it with this:

Mealz.basket.getRecipeCountInBasket()

or listen to it with a subscriber like this:

Mealz.notifications.recipeCount()