I am using ModelView-ViewModel in the project I am currently working and using RxSwift, RxBlocking & RxTests. Currently I am attempting to test the ViewModel but having many troubles to get my head around this.
So lets say I have an ExampleViewModel for my ExampleViewController. My ExampleViewModel is expecting an Observable stream which is the combination (combineLatest) of two streams from UITextField, one being if the textField is either focused and the other is the stream of text; so something like Observable<(Bool, String)>. Depending on whether focused and the context of the string my ExampleViewModel will emit an event into its internally exposed property which is an Observable the state of the UITextField's backgroundColor; Observable<UIColor>.
ExampleViewModel.swift :
class ExampleViewModel {
private let disposeBag = DisposeBag()
private let _textFieldColor: PublishSubject<UIColor>
var textFieldColor: Observable<UIColor> { get { return self._textFieldColor.asObservable() } }
init(textFieldObservable: Observable<(Bool, String)>) {
textFieldObservable.subscribeNext { (focus, text) in
self.validateTextField(focus, text: text)
}.addDisposableTo(self.disposeBag)
}
func validateTextField(focus: Bool, text: String) {
if !focus && !text.isEmpty {
self._textFieldColor.onNext(UIColor.whiteColor())
} else {
self._textFieldColor.onNext(UIColor.redColor())
}
}
}
(sorry I don't know how to format it correctly)
Basically I would like to test the ExampleViewModel class and test that it emits the correct UIColor by controlling the focus and text inputs.
Thanks
Thanks to the suggestion of my colleague I found a better way on structuring the ExampleViewModel for testability. By separating out the validation method with ExampleViewModel and setting the textFieldColor Observable by using the map operator in which the validator is used the validation is done outside and does not use Rx simplifying the testing of the logic.
ExampleViewModel
class ExampleViewModel {
var textFieldColor: Observable<UIColor>
init(
textFieldText: Observable<String>,
textFieldFocus: Observable<Bool>,
validator: TextFieldValidator
) {
self. textFieldColor = Observable.combineLatest(textFieldText, textFieldFocus) { ($0, $1) }. map { validator.validate($1, text: $0) }
}
}
class TextFieldValidator {
func validate(focus: Bool, text: String) -> UIColor {
if !focus && !text.isEmpty {
return UIColor.whiteColor()
} else {
return UIColor.redColor()
}
}
}
Related
I am trying to learn Combine. I know the terms and the basic concept theoretically. But when trying to work with it, I am lost.
I am trying to do is map an Input stream of events to Output stream of state. Is there a way to bind the result of the map to outputSubject? I am trying to make it work with sink but is there a better way?
Also is there an operator equivalent of RxSwift's withLatestFrom?
import Combine
class LearnCombine {
typealias Input = PassthroughSubject<Event, Never>
typealias Ouput = AnyPublisher<State, Never>
let input: Input
var output: Ouput
private var outputSubject: CurrentValueSubject<State, Never>
private var cancellables = Set<AnyCancellable>()
init() {
self.input = PassthroughSubject()
self.outputSubject = CurrentValueSubject(.initial)
self.output = outputSubject.eraseToAnyPublisher()
transformPipeline()
}
private func transformPipeline() {
input
.map { event in
mapEventToState(event, with: outputSubject.value)
}
.handleOutput { state in
handleSideEffects(for: state) // Also, how do I access the event here if I needed?
}
.sink {
outputSubject.send($0)
}
.store(in: &cancellables)
}
func mapEventToState(_ event: Event, with state: State) -> State {
// Some code that converts `Event` to `State`
}
}
extension Publisher {
func handleOutput(_ receiveOutput: #escaping ((Self.Output) -> Void)) -> Publishers.HandleEvents<Self> {
handleEvents(receiveOutput: receiveOutput)
}
}
Instead of using sink to assign a value to a CurrentValueSubject, I would use assign.
If you want to do something with the values in the middle of a pipeline you can use the handleEvents operator, though if you look in the documentation you'll see that the operator is listed as a debugging operator because generally your pipeline should not have side effects (building it from pure functions is one of the primary benefits.
Just reading the description of withLatestFrom in the RX documentation, I think the equivalent in combine is combineLatest
Here's your code, put into a Playground, and modified a bit to illustrates the first two points:
import Combine
struct Event {
var placeholder: String
}
enum State {
case initial
}
class LearnCombine {
typealias Input = PassthroughSubject<Event, Never>
typealias Ouput = AnyPublisher<State, Never>
let input: Input
var output: Ouput
private var outputSubject: CurrentValueSubject<State, Never>
private var cancellables = Set<AnyCancellable>()
init() {
self.input = PassthroughSubject()
self.outputSubject = CurrentValueSubject(.initial)
self.output = outputSubject.eraseToAnyPublisher()
transformPipeline()
}
private func transformPipeline() {
input
.map { event in
self.mapEventToState(event, with: self.outputSubject.value)
}
.handleEvents(receiveOutput: { value in
debugPrint("Do something with \(value)")
})
.assign(to: \.outputSubject.value, on: self)
.store(in: &cancellables)
}
func mapEventToState(_ event: Event, with state: State) -> State {
return .initial
// Some code that converts `Event` to `State`
}
}
extension Publisher {
func handleOutput(_ receiveOutput: #escaping ((Self.Output) -> Void)) -> Publishers.HandleEvents<Self> {
handleEvents(receiveOutput: receiveOutput)
}
}
Hey I saw a code for email validation in swift that uses onEditingChanged protocol as follows:-
TextField("Email address", text: $formModel.textEmail, onEditingChanged: { (isChanged) in
if !isChanged {
if formModel.textFieldValidatorEmail(formModel.textEmail) {
formModel.isEmailValid = true
} else {
formModel.isEmailValid = false
formModel.textEmail = ""
}
}
})
if !formModel.isEmailValid {
InvalidEmailView()
}
I want to refactor this code in such a manner that the protocol onEditingChanged can be reused.
Can anyone please help me with it.
If I understand your question correctly, you mean "closure" rather than "protocol". In any case, to reuse the logic, you can just create a function that takes the same parameters as the closure, plus parameters for things the closure captures. In your example, it captures formModel.
Assuming formModel is of type FormModel, and that it is a value type rather than a reference type, it would look something like this:
func handleEditChanged(_ isChanged: Bool, formModel: inout FormModel)
{
if !isChanged {
if formModel.textFieldValidatorEmail(formModel.textEmail) {
formModel.isEmailValid = true
} else {
formModel.isEmailValid = false
formModel.textEmail = ""
}
}
}
Of course, readability is another reason to refactor, and to that end, you could clean up the nested ifs:
func handleEditChanged(_ isChanged: Bool, formModel: inout FormModel)
{
guard !isChanged else { return }
formModel.isEmailValid =
formModel.textFieldValidatorEmail(formModel.textEmail)
if !formModel.isEmailValid {
formModel.textEmail = ""
}
}
If FormModel is code under your control, and textEmail should be an empty string any time isEmailValid is false, you could move setting textEmail to a property observer. In FormModel
var isEmailValid: Bool {
didSet { if !isEmailValid { textEmail = "" } }
}
Then handleEditChanged is just
func handleEditChanged(_ isChanged: Bool, formModel: inout FormModel)
{
guard !isChanged else { return }
formModel.isEmailValid =
formModel.textFieldValidatorEmail(formModel.textEmail)
}
Obviously substitute your own real type for FormModel. If it's actually a reference (ie. class) then you don't need the inout specification in the parameter list, and below where I show how to use it, you don't need the &.
Then to use it, you still provide a closure to TextEdit, but you call your function. It looks like this, passing it using trailing closure syntax rather than as an explicit parameter.
TextField("Email address", text: $formModel.textEmail) {
handleEditChanged($0, formModel: &formModel)
}
if !formModel.isEmailValid {
InvalidEmailView()
}
Let's say there are three components and their respective dynamic dependencies:
struct Component1 {
let dependency1: Dependency1
func convertOwnDependenciesToDependency2() -> Dependency2
}
struct Component2 {
let dependency2: Dependency2
let dependency3: Dependency3
func convertOwnDependenciesToDependency4() -> Dependency4
}
struct Component3 {
let dependency2: Dependency2
let dependency4: Dependency4
func convertOwnDependenciesToDependency5() -> Dependency5
}
Each of those components can generate results which can then be used as dependencies of other components. I want to type-safely inject the generated dependencies into the components which rely on them.
I have several approaches which I already worked out but I feel like I am missing something obvious which would make this whole task way easier.
The naive approach:
let component1 = Component1(dependency1: Dependency1())
let dependency2 = component1.convertOwnDependenciesToDependency2()
let component2 = Component2(dependency2: dependency2, dependency3: Dependency3())
let dependency4 = component2.convertOwnDependenciesToDependency4()
let component3 = Component3(dependency2: dependency2, dependency4: dependency4)
let result = component3.convertOwnDependenciesToDependency5()
Now I know that you could just imperatively call each of the functions and simply use the constructor of each component to inject their dependencies. However this approach does not scale very well. In a real scenario there would be up to ten of those components and a lot of call sites where this approach would be used. Therefore it would be very cumbersome to update each of the call sites if for instance Component3 would require another dependency.
The "SwiftUI" approach:
protocol EnvironmentKey {
associatedtype Value
}
struct EnvironmentValues {
private var storage: [ObjectIdentifier: Any] = [:]
subscript<Key>(_ type: Key.Type) -> Key.Value where Key: EnvironmentKey {
get { return storage[ObjectIdentifier(type)] as! Key.Value }
set { storage[ObjectIdentifier(type)] = newValue as Any }
}
}
struct Component1 {
func convertOwnDependenciesToDependency2(values: EnvironmentValues) {
let dependency1 = values[Dependency1Key.self]
// do some stuff with dependency1
values[Dependency2Key.self] = newDependency
}
}
struct Component2 {
func convertOwnDependenciesToDependency4(values: EnvironmentValues) {
let dependency2 = values[Dependency2Key.self]
let dependency3 = values[Dependency3Key.self]
// do some stuff with dependency2 and dependency3
values[Dependency4Key.self] = newDependency
}
}
struct Component3 {
func convertOwnDependenciesToDependency5(values: EnvironmentValues) {
let dependency2 = values[Dependency2Key.self]
let dependency4 = values[Dependency4Key.self]
// do some stuff with dependency2 and dependency4
values[Dependency5Key.self] = newDependency
}
}
But what I dislike with this approach is that you first of all have no type-safety and have to either optionally unwrap the dependency and give back an optional dependency which feels odd since what should a component do if the dependency is nil? Or force unwrap the dependencies like I did. But then the next point would be that there is no guarantee whatsoever that Dependency3 is already in the environment at the call site of convertOwnDependenciesToDependency4. Therefore this approach somehow weakens the contract between the components and could make up for unnecessary bugs.
Now I know SwiftUI has a defaultValue in its EnvironmentKey protocol but in my scenario this does not make sense since for instance Dependency4 has no way to instantiate itself without data required from Dependency2 or Depedency3 and therefore no default value.
The event bus approach
enum Event {
case dependency1(Dependency1)
case dependency2(Dependency2)
case dependency3(Dependency3)
case dependency4(Dependency4)
case dependency5(Dependency5)
}
protocol EventHandler {
func handleEvent(event: Event)
}
struct EventBus {
func subscribe(handler: EventHandler)
func send(event: Event)
}
struct Component1: EventHandler {
let bus: EventBus
let dependency1: Dependency1?
init(bus: EventBus) {
self.bus = bus
self.bus.subscribe(handler: self)
}
func handleEvent(event: Event) {
switch event {
case let .dependency1(dependency1): self.dependency1 = dependency1
}
if hasAllReceivedAllDependencies { generateDependency2() }
}
func generateDependency2() {
bus.send(newDependency)
}
}
struct Component2: EventHandler {
let dependency2: Dependency2?
let dependency3: Dependency3?
init(bus: EventBus) {
self.bus = bus
self.bus.subscribe(handler: self)
}
func handleEvent(event: Event) {
switch event {
case let .dependency2(dependency2): self.dependency2 = dependency2
case let .dependency3(dependency3): self.dependency3 = dependency3
}
if hasAllReceivedAllDependencies { generateDependency4() }
}
func generateDependency4() {
bus.send(newDependency)
}
}
struct Component3: EventHandler {
let dependency2: Dependency2?
let dependency4: Dependency4?
init(bus: EventBus) {
self.bus = bus
self.bus.subscribe(handler: self)
}
func handleEvent(event: Event) {
switch event {
case let .dependency2(dependency2): self.dependency2 = dependency2
case let .dependency4(dependency4): self.dependency4 = dependency4
}
if hasAllReceivedAllDependencies { generateDependency5() }
}
func generateDependency5() {
bus.send(newDependency)
}
}
I think in terms of type-safety and "dynamism" this approach would be a good fit. However to check if all dependencies were received already to start up the internal processes feels like a hack somehow. It feels like I am misusing this pattern in some form. Furthermore I think this approach may be able to "deadlock" if some dependency event was not sent and is therefore hard to debug where it got stuck. And again I would have to force unwrap the optionals in generateDependencyX but since this function would only get called if all optionals have a real value it seems safe to me.
I also took a look at some other design patterns (like chain-of-responsibility) but I couldn't really figure out how to model this pattern to my use-case.
My dream would be to somehow model a given design pattern as a result builder in the end so it would look something like:
FinalComponent {
Component1()
Component2()
Component3()
}
And in my opinion result builders would be possible with the "SwiftUI" and the event bus approach but they have the already described drawbacks. Again maybe I am missing an obvious design pattern which is already tailored to this situation or I am just modeling the problem in a wrong way. Maybe someone has a suggestion.
I have the following code:
class Note: NSObject {
}
struct Global {
static var notes: Array<Note> = [] {
didSet {
print("hi")
}
}
}
This prints "hi" if I add or remove an item from the array or if I do
Global.notes = []
Is there a way to print("hi") every time when one of the Note objects in the array is modified?
Thanks for your answers
Without changing the class to a struct, I have two basic ways to handle this.
This is the object you asked about
class Note: NSObject {
}
struct Global {
static var notes: Array<Note> = [] {
didSet {
print("hi")
}
}
}
Wrap Notes in a wrapper that is a struct to get the struct behavior.
extension Note {
struct Wrapper { let note: Note }
}
extension Global {
static var wrappedNotes = [Note.Wrapper]() {
didSet {
print("hi")
}
}
}
Global.wrappedNotes.append(Note.Wrapper(note: Note()))
Global.wrappedNotes[0] = Note.Wrapper(note: Note())
Global.wrappedNotes.remove(at: 0)
The other way is to create a note manager to wrap access to the array.
class NoteManager {
subscript(index: Int) -> Note {
get {
return values[index]
}
set {
defer { onUpdate() }
values[index] = newValue
}
}
func append(_ newNote: Note) {
defer { onUpdate() }
values.append(newNote)
}
func remove(at index: Int) -> Note {
defer { onUpdate() }
return values.remove(at: index)
}
private func onUpdate() {
print("hi")
}
private var values = [Note]()
}
extension Global {
static var managedNotes = NoteManager()
}
Global.managedNotes.append(Note())
Global.managedNotes[0] = Note()
Global.managedNotes.remove(at: 0)
As per #staticVoidMan comment , If you make your model , a struct, rather than a class, then the property observer didSet will work for your Note model's own properties as well.
import Foundation
struct Note {
var name: String
}
struct Global {
static var notes: Array<Note> = [] {
didSet {
print("hi")
}
}
}
Global.notes.append(Note(name: "Shubham"))
Global.notes.append(Note(name: "Bakshi"))
Global.notes[0].name = "Boxy"
This will print the following on the console :
hi
hi
hi
Swift Array is a struct, and structs are value-type which means they change completely when elements are added/removed/replaced. Hence when you add/remove/replace a Note, the didSet property observer gets called as the array has been set again.
However, as per you question:
Is there a way to print("hi") every time when one of the Note objects in the array is modified?
By this I am assuming that you want to do something when an element within this array is accessed and an internal property is modified.
This would have been fine if you were dealing with only value-type objects, i.e. had your Note object also been a struct, then changing anything inside one Note would have caused the array to change as well.
But your Note object is a class, i.e. reference-type, and stays as the same object even if it's internal elements change. Hence your array doesn't need to update and didSet does not get called.
Read: Value and Reference Types
KVO Solution:
Now... Since your Note is subclassing NSObject, you can use the KVO concept
As per the following working example, we observe only one property of the Note class.
If you want to observe more properties then you will need to observe those many more keypaths.
Example:
class Note: NSObject {
#objc dynamic var content = ""
init(_ content: String) {
self.content = content
}
}
class NoteList {
var notes: [Note] = [] {
didSet {
print("note list updated")
//register & save observers for each note
self.noteMessageKVOs = notes.map { (note) -> NSKeyValueObservation in
return note.observe(\Note.content, options: [.new, .old]) { (note, value) in
print("note updated: \(value.oldValue) changed to \(value.newValue)")
}
}
}
}
//array of observers
var noteMessageKVOs = [NSKeyValueObservation]()
}
let list = NoteList()
list.notes.append(Note("A")) //note list updated
list.notes.append(Note("B")) //note list updated
list.notes[0].content = "X" //note updated: A changed to X
list.notes[1].content = "Y" //note updated: B changed to Y
Notes:
NSObject is required for KVO
#objc dynamic is required to make a property observable
\Note.message is a keypath
noteMessageKVOs are required to keep the observers alive
I have an object and its properties as following:
class Section {
var cards: [MemberCard]
init(card: [MemberCard]) {
}
}
class MemberCard {
var name: String
var address: String?
init(name: String) {
self.name = name
}
}
I'm subscribing to a RxStream of type Observable<[Section]>. Before I subscribe I would to want flat map this function.
where the flat map would perform the following actions:
let sectionsStream : Observable<[Section]> = Observable.just([sections])
sectionsStream
.flatMap { [weak self] (sections) -> Observable<[Section]> in
for section in sections {
for card in section.cards {
}
}
}.subscribe(onNext: { [weak self] (sections) in
self?.updateUI(memberSections: sections)
}).disposed(by: disposeBag)
func getAddressFromCache(card: MemberCard) -> Observable<MemberCard> {
return Cache(id: card.name).flatMap ({ (card) -> Observable<MemberCard> in
asyncCall{
return Observable.just(card)
}
}
}
How would the flatmap look like when it comes to converting Observable<[Section]> to array of [Observable<MemberCard>] and back to Observable<[Section]>?
Technically, like that -
let o1: Observable<MemberCard> = ...
let o2: Observable<Section> = omc.toList().map { Section($0) }
let o2: Observable<[Section]> = Observable.concat(o2 /* and all others */).toList()
But I do not think it is an optimal solution, at least because there is no error handling for the case when one or more cards cannot be retrieved. I would rather build something around aggregation with .scan() operator as in https://github.com/maxvol/RaspSwift
Here you go:
extension ObservableType where E == [Section] {
func addressedCards() -> Observable<[Section]> {
return flatMap {
Observable.combineLatest($0.map { getAddresses($0.cards) })
}
.map {
$0.map { Section(cards: $0) }
}
}
}
func getAddresses(_ cards: [MemberCard]) -> Observable<[MemberCard]> {
return Observable.combineLatest(cards
.map {
getAddressFromCache(card: $0)
.catchErrorJustReturn($0)
})
}
If one of the caches emits an error, the above will return the MemberCard unchanged.
I have a couple of other tips as well.
In keeping with the functional nature of Rx, your Section and MemberCard types should either be structs or (classes with lets instead of vars).
Don't use String? unless you have a compelling reason why an empty string ("") is different than a missing string (nil). There's no reason why you should have to check existence and isEmpty every time you want to see if the address has been filled in. (The same goes for arrays and Dictionaries.)
For this code, proper use of combineLatest is the key. It can turn an [Observable<T>] into an Observable<[T]>. Learn other interesting ways of combining Observables here: https://medium.com/#danielt1263/recipes-for-combining-observables-in-rxswift-ec4f8157265f