Last active
June 30, 2023 13:42
-
-
Save stakancheck/85013d753dc198db1724140155bac889 to your computer and use it in GitHub Desktop.
Binding for moko filed from any place
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ADD THIS CODE | |
struct Binder { | |
private static func binding<T, R>( | |
mutable: CMutableStateFlow<T>, | |
viewModel: ViewModel, | |
equals: @escaping (T?, T?) -> Bool, | |
getMapper: @escaping (T) -> R, | |
setMapper: @escaping (R) -> T | |
) -> Binding<R> { | |
let stateFlow: CMutableStateFlow<T> = mutable | |
var lastValue: T? = stateFlow.value | |
var disposable: DisposableHandle? = nil | |
disposable = stateFlow.subscribe(onCollect: { [weak viewModel] value in | |
if !equals(lastValue, value) { | |
lastValue = value | |
viewModel?.objectWillChange.send() | |
disposable?.dispose() | |
} | |
}) | |
return Binding( | |
get: { getMapper(stateFlow.value!) }, | |
set: { stateFlow.value = setMapper($0) } | |
) | |
} | |
private static func state<T, R>( | |
cstate: CStateFlow<T>, | |
viewModel: ViewModel, | |
equals: @escaping (T?, T?) -> Bool, | |
getMapper: @escaping (T?) -> R? | |
) -> State<R?> { | |
let stateFlow: CStateFlow<T> = cstate | |
var lastValue: T? = stateFlow.value | |
var disposable: DisposableHandle? = nil | |
disposable = stateFlow.subscribe(onCollect: { [weak viewModel] value in | |
if !equals(lastValue, value) { | |
lastValue = value | |
viewModel?.objectWillChange.send() | |
disposable?.dispose() | |
} | |
}) | |
return State(wrappedValue: getMapper(cstate.value)) | |
} | |
private static func bindingTextField( | |
_ field: Fields_flowFormField<NSString, StringDesc>, | |
viewModel: ViewModel, | |
onChange: @escaping (_: String) -> Void | |
) -> Binding<String> { | |
return binding( | |
mutable: field.data, | |
viewModel: viewModel, | |
equals: { $0 == $1 }, | |
getMapper: { $0 as String }, | |
setMapper: { | |
onChange($0) | |
return $0 as NSString | |
} | |
) | |
} | |
private static func stateErrorField( | |
_ field: Fields_flowFormField<NSString, StringDesc>, | |
viewModel: ViewModel | |
) -> State<String?> { | |
return state( | |
cstate: field.error, | |
viewModel: viewModel, | |
equals: { $0 === $1 }, | |
getMapper: { $0?.localized() }) | |
} | |
static func bindingField( | |
_ field: Fields_flowFormField<NSString, StringDesc>, | |
viewModel: ViewModel, | |
onChange: @escaping (_: String) -> Void = {_ in} | |
) -> MokoField { | |
return MokoField( | |
value: bindingTextField(field, viewModel: viewModel, onChange: onChange), | |
error: stateErrorField(field, viewModel: viewModel) | |
) | |
} | |
} | |
struct MokoField { | |
let value: Binding<String> | |
let error: State<String?> | |
} | |
// USAGE | |
let authViewModel: ViewModel = ... | |
let codeField: Fields_flowFormField<NSString, StringDesc> = ... | |
let code = Binder.bindingField(codeField, viewModel: authViewModel) | |
// SIMPLE TEXT FIELD | |
VStack(spacing: 8) { | |
TextField(SharedRes().getLabelVerificationCode().localized(), text: code.value) | |
.keyboardType(.decimalPad) | |
.textContentType(.oneTimeCode) | |
.submitLabel(.done) | |
.multilineTextAlignment(.center) | |
.font(.title.weight(.bold)) | |
.onSubmit { | |
authViewModel.onLoginButtonClicked() | |
} | |
if let error code.error.wrappedValue { | |
Text(error).font(.caption).foregroundColor(.red).bold() | |
} | |
} |
#Update
add error state
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@kramlex this really makes sense, thank you)) In this case, I will be able not to pass the viewmodel to Binding.bind...