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#>
}
}
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.
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))")
}
You can also deactivate analytics on our side and continue to benefit from these notifications by setting this configuration at any time :
Mealz.environment.setAnalyticsTracking(false)
You will still recieve notification from AnalyticsNotifier but they will not be sent to our analytics solution.
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()