I am trying to work with ReSwift in my ios project and had a question regarding how to deal with changes in my view. I am finding that I need to know what the old state was before I can apply changes proposed by the new state coming in. I never needed to know what my old state was while working with redux in my react pojects.
My particular use case is, I am bulding a CameraView with an Overlay screen. From anywhere in the app say a ViewController I can create a CameraView and trigger it to open an UIImagePickerController from within it by firing an action. Here's some code:
//ViewController:
class MainViewController: UIViewController {
var cameraView: CameraView?
@IBOutlet weak var launchCameraButton: UIButton!
init() {
cameraView = CameraView(self)
}
@IBAction func launchCameraButtonClicked(_ sender: Any) {
store.dispatch(OpenCameraAction())
}
}
//CameraActions
struct OpenCameraAction: Action {}
struct CloseCameraAction: Action {}
//CameraState
struct CameraState {
var cameraIsVisible: Bool
}
func cameraReducer(state: CameraState?, action: Action) -> CameraState {
let initialState = state ?? CameraState()
switch action {
case _ as OpenCameraAction:
return CameraState(cameraIsVisible: true)
default:
return initialState
}
}
//CameraView
class CameraView: StoreSubscriber {
private var imagePicker: UIImagePickerController?
weak private var viewController: UIViewController?
init(viewController: UIViewController) {
self.viewController = viewController
super.init()
imagePicker = UIImagePickerController()
imagePicker?.allowsEditing = true
imagePicker?.sourceType = .camera
imagePicker?.cameraCaptureMode = .photo
imagePicker?.cameraDevice = .rear
imagePicker?.modalPresentationStyle = .fullScreen
imagePicker?.delegate = self
imagePicker?.showsCameraControls = false
store.subscribe(self) { subscription in
subscription.select { state in
state.camera
}
}
}
func newState(state: CameraState?) {
guard let state = state else {
return
}
if state.cameraIsVisible {
self.open()
} else if !state.cameraIsVisible {
self.close()
}
}
func open() {
if let imagePicker = self.imagePicker {
self.viewController?.present(
imagePicker,
animated: true
)
}
}
func close(){
self.imagePicker?.dismiss(animated: true)
}
}
The above is all the code to open and close the camera. My confusion starts, when we add more actions, such as disable or enable flash. I need to tack on additional state transitions in my view.
My actions now grow to:
struct OpenCameraAction: Action {}
struct CloseCameraAction: Action {}
struct FlashToggleAction: Action {}
My state now looks like this:
struct CameraState {
var cameraIsVisible: Bool
var flashOn: Bool
}
// not showing reducer changes as it self explanatory
// what the state changes will be for my actions.
My View is where the complications start. If the user enabled flash and I am responding to a FlashToggleAction state change, how do I work the state change in my View?
func newState(state: CameraState?) {
guard let state = state else {
return
}
// this will get triggered regardless of whether
// a change in the flash happened or not.
self.toggleFlash(state.flashOn)
// now the below lines will be executed even though
// the only change was a flash on action.
//I don't want my camera to opened again or closed.
if state.cameraIsVisible {
self.open()
} else if !state.cameraIsVisible {
self.close()
}
}
How do I respond to changes now? The only way I can think of handling this is by storing a reference to the old state and comparing the difference myself.