From e78d9b9b94ed30a9effc74410c81f575df3c2747 Mon Sep 17 00:00:00 2001 From: Greg Jurzak Date: Tue, 18 Oct 2022 19:25:41 +0200 Subject: [PATCH 1/3] support swift ui views fix crash with dismiss modal --- .../Reducers/DismissModalReducer.swift | 2 + .../Sources/Reducers/ShowOnRootReducer.swift | 1 - ReMVVMExt/Sources/StoreActions.swift | 113 ++++++++++++++---- 3 files changed, 95 insertions(+), 21 deletions(-) diff --git a/ReMVVMExt/Sources/Reducers/DismissModalReducer.swift b/ReMVVMExt/Sources/Reducers/DismissModalReducer.swift index 03a56cb..0ae405f 100644 --- a/ReMVVMExt/Sources/Reducers/DismissModalReducer.swift +++ b/ReMVVMExt/Sources/Reducers/DismissModalReducer.swift @@ -15,6 +15,8 @@ public struct DismissModalReducer: Reducer { public static func reduce(state: Navigation, with action: DismissModal) -> Navigation { var modals = state.modals + + guard !modals.isEmpty else { return Navigation(root: state.root, modals: modals) } if action.dismissAllViews { modals.removeAll() } else { diff --git a/ReMVVMExt/Sources/Reducers/ShowOnRootReducer.swift b/ReMVVMExt/Sources/Reducers/ShowOnRootReducer.swift index 1861139..3d868ba 100644 --- a/ReMVVMExt/Sources/Reducers/ShowOnRootReducer.swift +++ b/ReMVVMExt/Sources/Reducers/ShowOnRootReducer.swift @@ -37,7 +37,6 @@ public struct ShowOnRootMiddleware: Middleware { interceptor.next { _ in // newState - state variable is used below // side effect - uiState.setRoot(controller: action.controllerInfo.loader.load(), animated: action.controllerInfo.animated, navigationBarHidden: action.navigationBarHidden) diff --git a/ReMVVMExt/Sources/StoreActions.swift b/ReMVVMExt/Sources/StoreActions.swift index cf3d939..f34384c 100644 --- a/ReMVVMExt/Sources/StoreActions.swift +++ b/ReMVVMExt/Sources/StoreActions.swift @@ -9,35 +9,49 @@ import Loaders import ReMVVMCore import RxSwift +import SwiftUI import UIKit public struct SynchronizeState: StoreAction { - + public let type: SynchronizeType public init(_ type: SynchronizeType) { self.type = type } - + public enum SynchronizeType { case navigation, modal } } public struct ShowOnRoot: StoreAction { - + public let controllerInfo: LoaderWithFactory public let navigationBarHidden: Bool - + public init(loader: Loader, factory: ViewModelFactory? = nil, animated: Bool = true, navigationBarHidden: Bool = true) { - + self.controllerInfo = LoaderWithFactory(loader: loader, factory: factory, animated: animated) self.navigationBarHidden = navigationBarHidden } + + @available(iOS 13.0, *) + public init(view: V, + factory: ViewModelFactory? = nil, + animated: Bool = true, + navigationBarHidden: Bool = true) where V: View { + + self.controllerInfo = LoaderWithFactory(view: view, + factory: factory, + animated: animated) + self.navigationBarHidden = navigationBarHidden + + } } public struct Show: StoreAction { @@ -45,13 +59,13 @@ public struct Show: StoreAction { public let navigationBarHidden: Bool public let item: AnyNavigationItem let navigationType: NavigationType - + public init(on item: Item, - loader: Loader, - factory: ViewModelFactory? = nil, - animated: Bool = true, - navigationBarHidden: Bool = true) { - + loader: Loader, + factory: ViewModelFactory? = nil, + animated: Bool = true, + navigationBarHidden: Bool = true) { + self.controllerInfo = LoaderWithFactory(loader: loader, factory: factory, animated: animated) @@ -59,12 +73,28 @@ public struct Show: StoreAction { self.item = AnyNavigationItem(item) self.navigationType = Item.navigationType } + + @available(iOS 13.0, *) + public init(on item: Item, + view: V, + factory: ViewModelFactory? = nil, + animated: Bool = true, + navigationBarHidden: Bool = true) where V: View { + + self.controllerInfo = LoaderWithFactory(view: view, + factory: factory, + animated: animated) + self.navigationBarHidden = navigationBarHidden + self.item = AnyNavigationItem(item) + self.navigationType = Item.navigationType + } } public struct Push: StoreAction { - + public let controllerInfo: LoaderWithFactory public let pop: PopMode? + public init(loader: Loader, factory: ViewModelFactory? = nil, pop: PopMode? = nil, @@ -74,6 +104,18 @@ public struct Push: StoreAction { factory: factory, animated: animated) } + + @available(iOS 13.0, *) + public init(view: V, + factory: ViewModelFactory? = nil, + pop: PopMode? = nil, + animated: Bool = true) where V: View { + self.pop = pop + self.controllerInfo = LoaderWithFactory(view: view, + factory: factory, + animated: animated) + } + } public enum PopMode { @@ -90,13 +132,13 @@ public struct Pop: StoreAction { } public struct ShowModal: StoreAction { - + public let controllerInfo: LoaderWithFactory public let withNavigationController: Bool public let showOverSplash: Bool public let showOverSelfType: Bool public let presentationStyle: UIModalPresentationStyle - + public init(loader: Loader, factory: ViewModelFactory? = nil, animated: Bool = true, @@ -104,7 +146,7 @@ public struct ShowModal: StoreAction { showOverSplash: Bool = true, showOverSelfType: Bool = true, presentationStyle: UIModalPresentationStyle = .fullScreen) { - + self.controllerInfo = LoaderWithFactory(loader: loader, factory: factory, animated: animated) @@ -113,13 +155,30 @@ public struct ShowModal: StoreAction { self.showOverSelfType = showOverSelfType self.presentationStyle = presentationStyle } + + @available(iOS 13.0, *) + public init(view: V, + factory: ViewModelFactory? = nil, + animated: Bool = true, + withNavigationController: Bool = true, + showOverSplash: Bool = true, + showOverSelfType: Bool = true, + presentationStyle: UIModalPresentationStyle = .fullScreen) where V: View { + self.controllerInfo = LoaderWithFactory(view: view, + factory: factory, + animated: animated) + self.withNavigationController = withNavigationController + self.showOverSplash = showOverSplash + self.showOverSelfType = showOverSelfType + self.presentationStyle = presentationStyle + } } public struct DismissModal: StoreAction { - + public let dismissAllViews: Bool public let animated: Bool - + public init(dismissAllViews: Bool = false, animated: Bool = true) { self.dismissAllViews = dismissAllViews self.animated = animated @@ -127,14 +186,28 @@ public struct DismissModal: StoreAction { } public struct LoaderWithFactory { - + public let loader: Loader public let factory: ViewModelFactory? public let animated: Bool - - public init(loader: Loader, factory: ViewModelFactory?, animated: Bool = true) { + + public init(loader: Loader, + factory: ViewModelFactory?, + animated: Bool = true) { self.loader = loader self.factory = factory self.animated = animated } + + @available(iOS 13.0, *) + public init(view: V, + factory: ViewModelFactory?, + animated: Bool = true) where V: View { + + let hostLoader: Loader = Loader { + Loader(view).load() + } + + self.init(loader: hostLoader, factory: factory, animated: animated) + } } From 0092eec73f18ea2a83751c4df63e46a692233cbd Mon Sep 17 00:00:00 2001 From: Greg Jurzak Date: Thu, 20 Oct 2022 16:49:55 +0200 Subject: [PATCH 2/3] add reset stack to pop option --- ReMVVMExt/Sources/Reducers/PopReducer.swift | 4 ++-- ReMVVMExt/Sources/Reducers/PushReducer.swift | 6 +++++- ReMVVMExt/Sources/StoreActions.swift | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ReMVVMExt/Sources/Reducers/PopReducer.swift b/ReMVVMExt/Sources/Reducers/PopReducer.swift index 1e794eb..ef39eb4 100644 --- a/ReMVVMExt/Sources/Reducers/PopReducer.swift +++ b/ReMVVMExt/Sources/Reducers/PopReducer.swift @@ -18,7 +18,7 @@ public struct PopReducer: Reducer { private static func updateStateTree(_ stateTree: Navigation, for mode: PopMode) -> Navigation { switch mode { - case .popToRoot: + case .popToRoot, .resetStack: return popStateTree(stateTree, dropCount: stateTree.topStack.count - 1) case .pop(let count): return popStateTree(stateTree, dropCount: count) @@ -69,7 +69,7 @@ public struct PopMiddleware: Middleware { // side effect switch action.mode { - case .popToRoot: + case .popToRoot, .resetStack: self.uiState.navigationController?.popToRootViewController(animated: action.animated) case .pop(let count): if count > 1 { diff --git a/ReMVVMExt/Sources/Reducers/PushReducer.swift b/ReMVVMExt/Sources/Reducers/PushReducer.swift index 47b4a8b..c75095a 100644 --- a/ReMVVMExt/Sources/Reducers/PushReducer.swift +++ b/ReMVVMExt/Sources/Reducers/PushReducer.swift @@ -38,9 +38,11 @@ public struct PushReducer: Reducer { } private static func updateStack(_ stack: [ViewModelFactory], for pop: PopMode?) -> [ViewModelFactory] { - guard let popMode = pop, stack.count > 1 else { return stack } + guard let popMode = pop, stack.count > 0 else { return stack } switch popMode { + case .resetStack: + return [] case .pop(let count): let dropCount = min(count, stack.count) return Array(stack.dropLast(dropCount)) @@ -84,6 +86,8 @@ public struct PushMiddleware: Middleware { if let pop = action.pop { var viewControllers = navigationController.viewControllers switch pop { + case .resetStack: + viewControllers = [] case .popToRoot: viewControllers = viewControllers.dropLast(viewControllers.count - 1) case .pop(let count): diff --git a/ReMVVMExt/Sources/StoreActions.swift b/ReMVVMExt/Sources/StoreActions.swift index f34384c..dbcbcf9 100644 --- a/ReMVVMExt/Sources/StoreActions.swift +++ b/ReMVVMExt/Sources/StoreActions.swift @@ -120,6 +120,7 @@ public struct Push: StoreAction { public enum PopMode { case popToRoot, pop(Int) + case resetStack } public struct Pop: StoreAction { From d81b83be7757c8f8066bd971a022fc7cb0c8f40b Mon Sep 17 00:00:00 2001 From: Greg Jurzak Date: Fri, 21 Oct 2022 23:06:29 +0200 Subject: [PATCH 3/3] Add clear background for modals --- ReMVVMExt/Sources/StoreActions.swift | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/ReMVVMExt/Sources/StoreActions.swift b/ReMVVMExt/Sources/StoreActions.swift index dbcbcf9..972c176 100644 --- a/ReMVVMExt/Sources/StoreActions.swift +++ b/ReMVVMExt/Sources/StoreActions.swift @@ -164,10 +164,12 @@ public struct ShowModal: StoreAction { withNavigationController: Bool = true, showOverSplash: Bool = true, showOverSelfType: Bool = true, - presentationStyle: UIModalPresentationStyle = .fullScreen) where V: View { + presentationStyle: UIModalPresentationStyle = .fullScreen, + clearBackground: Bool = false) where V: View { self.controllerInfo = LoaderWithFactory(view: view, factory: factory, - animated: animated) + animated: animated, + clearBackground: clearBackground) self.withNavigationController = withNavigationController self.showOverSplash = showOverSplash self.showOverSelfType = showOverSelfType @@ -203,10 +205,16 @@ public struct LoaderWithFactory { @available(iOS 13.0, *) public init(view: V, factory: ViewModelFactory?, - animated: Bool = true) where V: View { + animated: Bool = true, + clearBackground: Bool = false) where V: View { let hostLoader: Loader = Loader { - Loader(view).load() + let loader = Loader(view).load() + if clearBackground { + loader.view.backgroundColor = .clear + } + return loader + } self.init(loader: hostLoader, factory: factory, animated: animated)