2 min read
Customise the Wishlist Button to select a Variant and update the Product Details screen
Last Updated - Platform 22.0 - SDK 17.0This guide will walk through how you can customise the WishlistWidget
to present the Variant Selector for Variant level wishlists.
This is a nice customisation for the PDP to improve the user wishlisting experience as seen below.
Selecting a Variant
- Create a new file for your custom
WishlistWidget
somewhere likeSources/WishlistButton/Views
. - Subclass the
WishlistWidget
and override themakeInteractionMiddleware
function.
import PoqCatalogueimport PoqCatalogueClient
class CustomWishlistWidget: WishlistWidget { override func makeInteractionMiddleware() { let middleware = super.makeInteractionMiddleware() return Middleware { store, action in store.continue(action)
// TODO: Add custom code here.
middleware?.execute(store: store, action: action) } }}
- Handle the
WishlistWidgetAction.wishlist
action by presenting the variant selector, only if theinput.id
is not a variant.
We can use the VariantSelectorCoordinator
from Containers to conveniently present the Variant Selector.
When the user completes variant selection we can add the selected variant to the Wishlist.
return Middleware { store, action in store.continue(action) switch action { case WishlistWidgetAction.wishlist: // Ensure the widget is not loading and there is an input. guard let state = store.state, !state.itemState.isLoading, var input = state.input else { fallthrough } // Only handle invalid input by showing the variant selector to select the variant to wishlist. guard !Settings.Wishlist.level.isValid(id: input.id) else { fallthrough } Container.shared.coordinators.variantSelector().presentVariantSelector( state: .init(id: .init(id: input.id)), completion: { variant in input.id.listingId = variant.listingId input.id.variantId = variant.id // Add the variant to the wishlist (add only). store.dispatch(ServiceAction.WishlistEntry.update(.init(id: input.id, isWishlisted: true))) } ) default: middleware?.execute(store: store, action: action) }}
- Lastly, inject your custom widget in your
AppModule
in thedidAddToPlatform
orsetUpWishlist
function.
Container.shared.views.wishlistWidget.replace { CustomWishlistWidget() }
Nice! Now the wishlist button selects a variant and adds it to the Wishlist.
But there are a couple of issues with this implementation on the PDP:
- When the variant selector opens it loads the Product with a call to the API.
- When the user selects a variant the PDP is not updated to match the form selection.
- For products with many forms the user needs to select forms that may have been selected on the PDP.
Let's solve these issues.
Passing a better state to the Widget
For this we are going to pass the entire ProductDetailState
(or any ProductState
) to the widget.
Doing this allows the widget to setup matching the current PDP selection.
- Create a new file for a custom
ProductDetailsViewDataMapper
somewhere likeSources/ProductDetails/Mappers
. - Subclass the
PoqProductDetailsViewDataMapper
and override themapWishlist
function to pass the entire state as custom data.
import PoqCatalogueimport PoqProductDetails
class CustomProductDetailsViewDataMapper: PoqProductDetailsViewDataMapper { override func mapWishlist(from state: ProductDetailsState) -> WishlistWidgetState.Input? { var wishlist = super.mapWishlist(from: state) wishlist?.customData = VariantSelectorState(state) return wishlist }}
- Inject your custom mapper in your
AppModule
in thedidAddToPlatform
orsetUpProductDetails
function.
Container.shared.mappers.productDetailsViewDataMapper.replace { CustomProductDetailsViewDataMapper() }
- Update your
CustomWishlistWidget
code to use the custom data state.
// Use the custom state if one was passed, otherwise default to using the id.let inputState = input.customData(VariantSelectorState.self)Container.shared.coordinators.variantSelector().presentVariantSelector( state: inputState ?? .init(id: .init(id: input.id)), completion: { variant in ... })
Great! Now the variant selector doesn't make an API call and skips user selected PDP forms.
Let's tackle the last issue by updating the PDP as forms are selected.
Update the PDP as forms are selected
If you've followed all the previous steps you'll notice that after wishlisting a variant the PDP stays the same. This feels odd for the user so let's make the PDP select the variant.
- Open your
CustomWishlistWidget
and add a delegate to delegateformSelected
to the owning feature.
class CustomWishlistWidget: WishlistWidget { weak var formSelectionDelegate: FormSelectorDelegate?}
- Update the Variant Selector presentation code to use the
formSelection
parameter.
Container.shared.coordinators.variantSelector().presentVariantSelector( state: inputState ?? .init(id: .init(id: input.id)), formSelection: { [weak self] _, variationId in guard let variationId = variationId else { return } self?.formSelectionDelegate?.formSelected(id: variationId) }, completion: { variant in ... })
- Create a new file for a custom
ProductSummaryView
(or use the existing) somewhere likeSources/ProductDetails/Views
. - Subclass the
PoqProductSummaryView
forward the delegate to yourCustomWishlistWidget
.
class CustomProductSummaryView: PoqProductSummaryView { override func delegate(to delegate: ProductViewDelegate?) { super.delegate(to: delegate) (wishlistWidget as? CustomWishlistWidget)?.formSelectionDelegate = delegate }}
- Finally, inject your
CustomProductSummaryView
in theAppModule
in thedidAddToPlatform
orsetUpProductDetails
function.
Container.shared.views.productSummaryView.replace { CustomProductSummaryView() }
Success! The customisation is complete and you can fully enjoy wishlisting variants.