Skip to content

Instantly share code, notes, and snippets.

@pzmudzinski
Created March 26, 2020 11:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save pzmudzinski/155f998861bdee46ee467e824569dbdb to your computer and use it in GitHub Desktop.
Save pzmudzinski/155f998861bdee46ee467e824569dbdb to your computer and use it in GitHub Desktop.
//
// ShoppingCart.swift
// CombineShoppingCart
//
// Created by Piotr on 25/03/2020.
// Copyright © 2020 Piotr. All rights reserved.
//
import Combine
struct Product: Equatable {
var id: Int
var name: String
var price: Double
}
struct ProductOrder: Equatable {
var product: Product
var quantity: UInt
init(product: Product, quantity: UInt = 1) {
self.product = product
self.quantity = quantity
}
var incremented: ProductOrder {
return ProductOrder(product: product, quantity: quantity + 1)
}
var decremented: ProductOrder {
return ProductOrder(product: product, quantity: quantity - 1)
}
}
enum CartAction {
case insert(product: Product)
case incrementProduct(withId: Int)
case decrementProduct(withId: Int)
case clear
}
class ShoppingCart {
let orders: AnyPublisher<[ProductOrder], Never>
let input: AnySubscriber<CartAction, Never>
init() {
let actionInput = PassthroughSubject<CartAction, Never>()
self.orders = actionInput.scan([Int:ProductOrder]()) { (currentOrders, action) -> [Int:ProductOrder] in
var newOrders = currentOrders
switch (action) {
case .insert(let product):
newOrders.updateValue(ProductOrder(product: product), forKey: product.id)
case .incrementProduct(withId: let productId):
if let order = newOrders[productId] {
newOrders.updateValue(order.incremented, forKey: productId)
}
case .decrementProduct(withId: let productId):
if let order = newOrders[productId] {
let decrementedOrder = order.decremented
if (decrementedOrder.quantity == 0) {
newOrders.removeValue(forKey: productId)
} else {
newOrders.updateValue(decrementedOrder, forKey: productId)
}
}
case .clear:
return [:]
}
return newOrders
}
.map(\.values)
.map(Array.init)
.eraseToAnyPublisher()
self.input = AnySubscriber(actionInput)
}
}
extension ProductOrder {
var price: Double {
return product.price * Double(quantity)
}
}
extension ShoppingCart {
var numberOfProducts: AnyPublisher<Int, Never> {
return orders.map(\.count).eraseToAnyPublisher()
}
var totalPrice: AnyPublisher<Double, Never> {
return orders.map { $0.reduce(0) { (acc, order) -> Double in
acc + order.price
}
}.eraseToAnyPublisher()
}
}
extension ShoppingCart {
func discountedTotalPrice(discountRate: Double = 0.1) -> AnyPublisher<Double, Never> {
return totalPrice.map { $0 * (1.0 - discountRate) }.eraseToAnyPublisher()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment