2 min read

Add promotions to cart items

This guide aims to present promotions driven by customData on cart items.

Most backend contracts have an optional customData property which allows custom backends to send additional data to the frontend. The PoqSDK is built to work with these backend contracts and easily allows you to map customData to strongly typed objects using mappers.

For this guide we will use Containers to customise all instances of the Cart. However, it is possible to inject these customisations using a CartBuilder for a specific instance of the Cart.

Before you begin

Make sure you know the format / contract that the customData property will take as sent from your backend.

Decode your promotions

  1. Create a new file named CartItemCustomData in an appropriate location such as Sources/Cart/Models.
  2. Implement the CartItemCustomData structure conforming to Decodable and Hashable to match the agreed backend contract for the customData.
struct CartItemCustomData: Hashable, Decodable {
var promotion: String
}
  1. Open your AppModule and navigate to the setUpCart function.
  2. Inject and customise the PoqCartItemDataMapper by providing a customDataMapper closure.
Container.shared.mappers.cartItemDataMapper = {
PoqCartItemDataMapper() {
let custom: CartItemCustomData? = $0?.decode()
return custom.flatMap(AnyHashable.init)
}
}
  1. Validate that your CartItemDomainModel objects contain your strongly typed customData.

Check the console for decoding errors to validate your struct matches the backend agreed customData contract.

Create the cart item view

  1. Create a new file named ClientCartItemView in an appropriate location such as Sources/Cart/Views.
  2. Implement the ClientCartItemView conforming to CartItemView (or by subclassing the PoqClientItemView). Make sure to include a UILabel for promotion text.
  3. Implement the setup(with viewData: CartItemViewData) as much as you can but leave the promotion label unset. We will come back to this later.

If you are subclassing a Poq view, there are defined functions for addSubviews, addConstraints and style. Use these to call super (or not) and modify the default views / constraints.

  1. Open your AppModule and navigate to the setUpCart function.
  2. Inject your ClientCartItemView to replace the default.
Container.shared.views.cartItemView = { ClientCartItemView() }

Create the cart item view data

  1. Create a new file named ClientCartItemViewData in an appropriate location such as Sources/Cart/ViewData.
  2. Implement the ClientCartItemViewData structure conforming to and satisfying the CartItemViewData protocol.
  3. Add your promotion string to the view data.
  4. Conform to Equatable. You will need to manually implement it due to price and total where you will need to use the following compare function.
&& ...
&& lhs.quantity == rhs.quantity
&& lhs.price.isEqual(to: rhs.price)
&& lhs.total.isEqual(to: rhs.total)
&& ...
  1. Head back to the setup(with viewData: CartItemViewData) function of your ClientCartItemView and cast the viewData to your new ClientCartItemViewData. Use this to set your promotion label.
func setup(with viewData: CartItemViewData?) {
// Set up view without casting...
let clientData = viewData as? ClientCartItemViewData
promotionLabel.text = clientData?.promotion
}

Create the view data mapper to finish

  1. Create a new file named ClientCartItemViewDataMapper in an appropriate location such as Sources/Cart/Mappers
  2. Implement the ClientCartItemViewDataMapper conforming to CartItemViewDataMapper. You will need to create a property for a priceMapper.
lazy var priceMapper = Container.shared.mappers.priceViewDataMapper()
  1. Create and return an instance of your ClientCartItemViewData in the map(from domain:) function.
  2. Map your customData by casting the value.
func map(from domain: CartItemDomainModel) -> CartItemViewData {
let customData = domain.customData?.base as? CartItemCustomData
return ClientCartItemViewData(
...
quantity: domain.quantity,
price: priceMapper.map(from: domain.price),
total: priceMapper.map(from: domain.total),
imageUrl: domain.thumbnailUrl,
...
promotion: customData?.promotion
)
}
  1. Open your AppModule and navigate to the setUpCart function.
  2. Inject your ClientCartItemViewDataMapper to replace the default.
Container.shared.mappers.cartItemViewDataMapper = { ClientCartItemViewDataMapper() }

You have completed this guide! Run your app and check out your new promotions!