11import Foundation
22import Combine
3+ import Observation
34
45import protocol Yosemite. POSOrderableItem
56import protocol WooFoundation. Analytics
@@ -38,24 +39,24 @@ protocol PointOfSaleAggregateModelProtocol {
3839}
3940
4041@available ( iOS 17 . 0 , * )
41- class PointOfSaleAggregateModel : ObservableObject , PointOfSaleAggregateModelProtocol {
42- @ Published private( set) var orderStage : PointOfSaleOrderStage = . building
43-
44- @ Published private( set) var cardReaderConnectionStatus : CardPresentPaymentReaderConnectionStatus = . disconnected
45- @ Published private( set) var paymentState : PointOfSalePaymentState
46- @ Published var cardPresentPaymentAlertViewModel : PointOfSaleCardPresentPaymentAlertType ?
47- @ Published private( set) var cardPresentPaymentInlineMessage : PointOfSaleCardPresentPaymentMessageType ?
48- @ Published var cardPresentPaymentOnboardingViewModel : CardPresentPaymentsOnboardingViewModel ?
42+ @ Observable final class PointOfSaleAggregateModel : PointOfSaleAggregateModelProtocol {
43+ private( set) var orderStage : PointOfSaleOrderStage = . building
44+
45+ private( set) var cardReaderConnectionStatus : CardPresentPaymentReaderConnectionStatus = . disconnected
46+ private( set) var paymentState : PointOfSalePaymentState
47+ var cardPresentPaymentAlertViewModel : PointOfSaleCardPresentPaymentAlertType ?
48+ private( set) var cardPresentPaymentInlineMessage : PointOfSaleCardPresentPaymentMessageType ?
49+ var cardPresentPaymentOnboardingViewModel : CardPresentPaymentsOnboardingViewModel ?
4950 private var onOnboardingCancellation : ( ( ) -> Void ) ?
5051
51- @ Published private( set) var itemsViewState : ItemsViewState = ItemsViewState ( containerState: . loading,
52- itemsStack: ItemsStackState ( root: . loading( [ ] ) ,
53- itemStates: [ : ] ) )
52+ private( set) var itemsViewState : ItemsViewState = ItemsViewState ( containerState: . loading,
53+ itemsStack: ItemsStackState ( root: . loading( [ ] ) ,
54+ itemStates: [ : ] ) )
5455
55- @ Published private( set) var cart : [ CartItem ] = [ ]
56+ private( set) var cart : [ CartItem ] = [ ]
5657
57- @ Published private( set) var orderState : PointOfSaleOrderState = . idle
58- @ Published private var internalOrderState : PointOfSaleInternalOrderState = . idle
58+ private( set) var orderState : PointOfSaleOrderState = . idle
59+ private var internalOrderState : PointOfSaleInternalOrderState = . idle
5960
6061 private let itemsController : PointOfSaleItemsControllerProtocol
6162
@@ -90,7 +91,10 @@ class PointOfSaleAggregateModel: ObservableObject, PointOfSaleAggregateModelProt
9091@available ( iOS 17 . 0 , * )
9192extension PointOfSaleAggregateModel {
9293 private func publishItemsViewState( ) {
93- itemsController. itemsViewStatePublisher. assign ( to: & $itemsViewState)
94+ itemsController. itemsViewStatePublisher. sink { [ weak self] state in
95+ self ? . itemsViewState = state
96+ }
97+ . store ( in: & cancellables)
9498 }
9599
96100 @MainActor
@@ -155,8 +159,11 @@ extension PointOfSaleAggregateModel {
155159@available ( iOS 17 . 0 , * )
156160extension PointOfSaleAggregateModel {
157161 private func publishCardReaderConnectionStatus( ) {
158- // When adopting Observable, we can use `assign(to: on:)` here instead
159- cardPresentPaymentService. readerConnectionStatusPublisher. assign ( to: & $cardReaderConnectionStatus)
162+ cardPresentPaymentService. readerConnectionStatusPublisher
163+ . sink ( receiveValue: { [ weak self] connectionStatus in
164+ self ? . cardReaderConnectionStatus = connectionStatus
165+ } )
166+ . store ( in: & cancellables)
160167 }
161168
162169 func connectCardReader( ) {
@@ -177,7 +184,7 @@ extension PointOfSaleAggregateModel {
177184 /// e.g. when the TotalsView goes offscreen.
178185 private func startPaymentWhenCardReaderConnected( ) async {
179186 guard case . connected = cardReaderConnectionStatus else {
180- return startPaymentOnCardReaderConnection = $cardReaderConnectionStatus
187+ return startPaymentOnCardReaderConnection = cardPresentPaymentService . readerConnectionStatusPublisher
181188 . filter { status in
182189 switch status {
183190 case . connected:
@@ -259,17 +266,19 @@ extension PointOfSaleAggregateModel {
259266 await collectCardPayment ( )
260267 }
261268
262- private func setupReaderReconnectionObservation( ) {
263- $orderStage . sink ( receiveValue : { [ weak self] stage in
269+ @ Sendable private func setupReaderReconnectionObservation( ) {
270+ withObservationTracking { [ weak self] in
264271 guard let self else { return }
265- switch stage {
266- case . building:
267- cancelCardReaderPreparation ( )
268- case . finalizing:
269- observeReaderReconnection ( )
272+ switch orderStage {
273+ case . building:
274+ cancelCardReaderPreparation ( )
275+ case . finalizing:
276+ observeReaderReconnection ( )
270277 }
271- } )
272- . store ( in: & cancellables)
278+ } onChange: { [ weak self] in
279+ guard let self else { return }
280+ DispatchQueue . main. async ( execute: setupReaderReconnectionObservation)
281+ }
273282 }
274283
275284 private func cancelCardReaderPreparation( ) {
@@ -279,7 +288,7 @@ extension PointOfSaleAggregateModel {
279288 }
280289
281290 private func observeReaderReconnection( ) {
282- cardReaderDisconnection = $cardReaderConnectionStatus
291+ cardReaderDisconnection = cardPresentPaymentService . readerConnectionStatusPublisher
283292 . filter ( { $0 == . disconnected } )
284293 . sink { [ weak self] _ in
285294 Task { @MainActor [ weak self] in
@@ -321,13 +330,19 @@ private extension PointOfSaleAggregateModel {
321330 }
322331 return alertType
323332 }
324- . assign ( to: & $cardPresentPaymentAlertViewModel)
333+ . sink ( receiveValue: { [ weak self] alertType in
334+ self ? . cardPresentPaymentAlertViewModel = alertType
335+ } )
336+ . store ( in: & cancellables)
325337
326338 cardPresentPaymentService. paymentEventPublisher
327339 . map { [ weak self] event -> PointOfSaleCardPresentPaymentMessageType ? in
328340 self ? . mapCardPresentPaymentEventToMessageType ( event)
329341 }
330- . assign ( to: & $cardPresentPaymentInlineMessage)
342+ . sink ( receiveValue: { [ weak self] message in
343+ self ? . cardPresentPaymentInlineMessage = message
344+ } )
345+ . store ( in: & cancellables)
331346
332347 cardPresentPaymentService. paymentEventPublisher
333348 . compactMap { [ weak self] paymentEvent -> PointOfSalePaymentState ? in
@@ -338,7 +353,10 @@ private extension PointOfSaleAggregateModel {
338353
339354 return newPaymentState
340355 }
341- . assign ( to: & $paymentState)
356+ . sink ( receiveValue: { [ weak self] paymentState in
357+ self ? . paymentState = paymentState
358+ } )
359+ . store ( in: & cancellables)
342360
343361 cardPresentPaymentService. paymentEventPublisher
344362 . map { [ weak self] event -> CardPresentPaymentsOnboardingViewModel ? in
@@ -349,7 +367,10 @@ private extension PointOfSaleAggregateModel {
349367 onOnboardingCancellation = onCancel
350368 return viewModel
351369 }
352- . assign ( to: & $cardPresentPaymentOnboardingViewModel)
370+ . sink ( receiveValue: { [ weak self] onboardingViewModel in
371+ self ? . cardPresentPaymentOnboardingViewModel = onboardingViewModel
372+ } )
373+ . store ( in: & cancellables)
353374 }
354375
355376 /// Maps PaymentEvent to POSMessageType and annonates additional information if necessary
@@ -413,9 +434,16 @@ extension PointOfSaleAggregateModel {
413434 func publishOrderState( ) {
414435 orderController. orderStatePublisher
415436 . map { $0. externalState }
416- . assign ( to: & $orderState)
437+ . sink ( receiveValue: { [ weak self] orderState in
438+ self ? . orderState = orderState
439+ } )
440+ . store ( in: & cancellables)
417441
418- orderController. orderStatePublisher. assign ( to: & $internalOrderState)
442+ orderController. orderStatePublisher
443+ . sink ( receiveValue: { [ weak self] internalOrderState in
444+ self ? . internalOrderState = internalOrderState
445+ } )
446+ . store ( in: & cancellables)
419447 }
420448}
421449
0 commit comments