Skip to main content
Version: 5.7

Custom Navigation

If you don't want to use the provided Mealz navigation, you can recreate the navigation yourself. We have documented a simple walkthrough, but feel free to implement your practice of Coordinators.

Steps

1. Add Basket Tag to your product

Presumably, you have already created a component in your application for the products in your basket. You will be adding additional code to your Product in Basket Component.

The BasketTag expects a retailerProductId & BasketTagParameters. The BasketTagParameters expects navigation functions & has optional viewOptions for customizing views.

Additionally, the last parameter is scrollAlignment, which defaults to .vertical. For our the image in the ingredients section above, we pass in .horizontal.

BasketTagParameters

BasketTagParameters(
actions: BasketTagActions(
onShowRecipeDetails: { [weak self] recipeId in
// this should navigate to the RecipeDetailsViewController, passing in the recipeId
}
)
)

Putting it all together

Now we have all the parameters we need for the BasketTag.

private func configureBasketTags() {
guard let id = productId else { return }
let tags = BasketTag.init(
params: BasketTagParameters(
actions: BasketTagParameters(
onShowRecipeDetails: { [weak self] recipeId in
guard let strongSelf = self else { return }
strongSelf.delegate?.didSelectRecipeDetail(with: recipeId)
}
)
),
retailerProductId: id,
scrollAlignment: .horizontal)
let hostingController = UIHostingController(rootView: tags) // its a SwiftUI view so we need to wrap
basketTags.subviews.forEach { $0.removeFromSuperview() } // Clear any previous tags or views in basketTags
guard let tagView = hostingController.view else { return }
basketTags.addSubview(tagView)
tagView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([ // Add constraints for the tags view to fit in basketTags
tagView.leadingAnchor.constraint(equalTo: basketTags.leadingAnchor),
tagView.trailingAnchor.constraint(equalTo: basketTags.trailingAnchor),
tagView.topAnchor.constraint(equalTo: basketTags.topAnchor),
tagView.bottomAnchor.constraint(equalTo: basketTags.bottomAnchor)
])
}

func didSelectRecipeDetail(with recipeId: String) {
let detailsVC = RecipeDetailsViewController(recipeId)
navigationController?.pushViewController(detailsVC, animated: true)
}

If there is no BasketTags related to the product, nothing will appear.

2. Create Recipe Details page

Again, if you've already completed the Catalog Feature, you'll already have the ViewController or page set up. You have nothing more to do for this.

The first thing to be done is to create a RecipeDetailsViewController or RecipeDetailsView standalone page. The only parameters this page may expect are those related to navigating back to the MyMeals page. The navigation to the basket can be ignored as that Call To Action will not be shown as the product is already in the basket.

The RecipeDetails Page expects a recipeId string. RecipeDetails also expects params: RecipeDetailParameters.

Additionally, RecipeDetails has an optional parameter isForMealPlanner that defaults to false. If you are not implementing Meal Planner now or in the future, you can ignore this.

RecipeDetailParameters

RecipeDetailParameters(
actions: RecipeDetailsActions(
onClosed: { [weak self] in
guard let strongSelf = self else { return }
strongSelf.navigationController?.popViewController(animated: true)
},
onSponsorDetailsTapped: { [weak self] sponsor in
guard let strongSelf = self else { return }
strongSelf.navigationController?.pushViewController(SponsorDetailsViewController(sponsor: sponsor), animated: true)
},
onContinueToBasket: { [weak self] in // this is ignored if the Recipe is already in the basket
guard let strongSelf = self else { return }
strongSelf.navigationController?.pushViewController(MyMealsViewController(), animated: true)
}
)
),

Putting it all together

Now we have all the parameters we need for the RecipeDetails.

RecipeDetails(
params: /* the RecipeDetailParameters we just made */,
recipeId: recipeId /* the callback navigating to this view will provide this */,
isForMealPlanner: isForMealPlanner // defaults to false
)

The default Miam Neutral Recipe Details Footer will NOT show the Call To Action if the item is already in the basket. If you customize your footer, make sure you implement this same functionality.

3. Create Sponsor Details page

Again, if you've already completed the Catalog Feature, you'll already have the ViewController or page set up. You have nothing more to do for this.

The first thing to be done is to create a SponsorDetailsViewController or SponsorDetailsView standalone page. The only parameters this page may expect are those related to navigating back to the MyMeals page.

After the RecipeDetails, implement the SponsorDetails Page. SponsorDetails expects a sponsor object. SponsorDetails also expects params: SponsorDetailsParameters.

SponsorDetailsParameters

SponsorDetailsParameters does not expect navigation functions, so you don't need to implement anything unless you add custom views.

Putting it all together

Now we have all the parameters we need for the SponsorDetails.

SponsorDetails(
params: SponsorDetailsParameters() /* default */,
sponsor: sponsor /* the callback navigating to this view will provide this */
)