Swift print(PublishSubject) - ios

var schoolData = PublishSubject<[SchoolInfo]>()
I would like to print a variable array like this. Please tell me how.
Printing the variable itself prints like this.
RxSwift.PublishSubject<Swift.Array<DDUBIDUBAB.SchoolInfo>>

The variable does not hold an array, it is an RxSwift PublishSubject that provides an array of SchoolInfo data objects when they are published on it.
To actually get the array value you need to subscribe to the subject as observable, like
schoolData.asObservable().subscribe(onNext: { schoolInfoArray in
print("School infos: \(schoolInfoArray.joined("|")")
}).disposed(by: disposeBag)
Please read up on RxSwift and how to use it before going on.

Related

What is PassthroughSubject & CurrentValueSubject

I happen to look into Apple's new Combine framework, where I see two things
PassthroughSubject<String, Failure>
CurrentValueSubject<String, Failure>
Can someone explain to me what is meaning & use of them?
I think we can make analogies with real world cases.
PassthroughSubject = A doorbell push button
When someone rings the door, you are notified only if you are at home (you are the subscriber)
PassthroughSubject doesn't have a state, it emits whatever it receives to its subscribers.
CurrentValueSubject = A light switch
Someone turns on the lights in your home when you are outside. You get back home and you know someone has turned them on.
CurrentValueSubject has an initial state, it retains the data you put in as its state.
Both PassthroughSubject and CurrentValueSubject are publishers that conform to the Subject protocol which means you can call send on them to push new values downstream at will.
The main difference is that CurrentValueSubject has a sense of state (current value) and PassthroughSubject simply relays values directly to its subscribers without remembering the "current" value:
var current = CurrentValueSubject<Int, Never>(10)
var passthrough = PassthroughSubject<Int, Never>()
current.send(1)
passthrough.send(1)
current.sink(receiveValue: { print($0) })
passthrough.sink(receiveValue: { print($0) })
You'd see that the current.sink is called immediately with 1. The passthrough.sink is not called because it has no current value. The sink will only be called for values that are emitted after you subscribe.
Note that you can also get and set the current value of a CurrentValueSubject using its value property:
current.value // 1
current.value = 5 // equivalent to current.send(5)
This isn't possible for a passthrough subject.
PassthroughSubject is used for representing events. Use it for events like button tap.
CurrentValueSubject is used representing state. Use it for storing any value, say state of switch as off and on.
Note: #Published is kind of CurrentValueSubject.
PassthroughSubject and CurrentValueSubject are both Publishers — a type introduced by Combine — that you can subscribe to (performing operations on values when values are available).
They both are designed to make it easy to transfer to using the Combine paradigm. They both have a value and an error type, and you can "send" values to them (making the values available to all subscribers)
The main difference between the two that I've seen is that CurrentValueSubject starts with a value, while PassthroughSubject does not. PassthroughSubject seems easier to grasp conceptually, at least for me.
PassthroughSubject can easily be used in place of a delegate pattern, or to convert an existing delegate pattern to Combine.
//Replacing the delegate pattern
class MyType {
let publisher: PassthroughSubject<String, Never> = PassthroughSubject()
func doSomething() {
//do whatever this class does
//instead of this:
//self.delegate?.handleValue(value)
//do this:
publisher.send(value)
}
}
//Converting delegate pattern to Combine
class MyDel: SomeTypeDelegate {
let publisher: PassthroughSubject<String, Never> = PassthroughSubject()
func handle(_ value: String) {
publisher.send(value)
}
}
Both of these examples use String as the type of the value, while it could be anything.
Hope this helps!
PassthroughSubject is suitable for event like tap action
CurrentValueSubject is suitable for state
Already a lot of good answers posted in this thread, just thought of adding this answer for someone who is coming from using RxSwift.
PassThroughSubject is like PublishSubject where it broadcasts an event to its subscribers, probably with some value passed along.
CurrentValueSubject is similar to BehaviorRelay where a single value is persisted with the subject instance and passed along during the event broadcast.

Swift bind AnyObserver to Observable

So, for example I have this observer in var myObserver: AnyObserver<Bool>
Can I somehow subscribe to it like for an observable, so I would be able to do some operations with it like .subscribe(onNext:) ?
An Observer is what you actually pass to subscribe(...)
In this example, you could do
var myObserver: AnyObserver<Bool> = //...
myObservable
.subscribe(myObserver)
.disposed(by: disposeBag)
The syntax subscribe(onNext:onError:onCompleted:) is just syntactic sugar to not have to actually create an Observer object every time.
Observable is a read-only entity. You can only subscribe to "Read" things off of it and manipulate/transform them.
The "Writable" portion of Rx is called an Observer and is usually abstracted with either a Subject, a Relay or a manual creation using Observable.create.
You can read more about Subjects here:
https://speakerdeck.com/freak4pc/rxswift-debunking-the-myth-of-hard?slide=34

Creating an observable with RxSwift

I'm using RxSwift and I'm trying to extend another library I'm using to make something observable.
The library basically calls a delegate method every time a value changes, and I want to hook into this and whenever it calls the delegate, also add the new value to an observable sequence I want to create in my subclass.
I've seen how you can create observable sequences, but in each example next events are sent to the observer within the block given to the Observable.create method. I have no idea how I can add things to the observable sequence from "outside" this block passed to create.
I just want to create something that I can observe or can drive things with RxSwift, and manually add to the sequence at certain points.
I'd be very grateful if someone could point me in the right direction, as I'm very new to this.
Observable is readonly interface. Sequences created by Observable.create can only produce the value(s) given at construction time and nothing more. You can't "add things" to it, to use your words. Speaking in RxSwift terms, you can't do away with just Observable interface, you need also ObserverType - it must also observe your mutating value. There is more than one way to do it in RxSwift, but i think that you need PublishSubject:
let value = PublishSubject<YourType>()
let disposer = DisposeBag()
init() {
value.subscribe(onNext: { (newValue) in
// use newValue ...
}).addDisposableTo(disposer)
}
func yourDelegateHandler(newValue: YourType)
{
value.onNext(newValue)
}

RxSwift - Custom class as observable

I'm creating a custom generic TableView model to work with MVVM and RxSwift. I'm aware of the RxTableViewSectionedReloadDataSource class but not willing to use it now in my project.
The model is created and working
typealias TableViewModel = TableModel<CellDescriptor>
class TableModel<T> {
var sections = [SectionModel<T>]()
func add(item: SectionModel<T>) {
sections.append(item)
}
// More funcs there...
}
I created an instance inside my ViewModel as such :
var tableViewModel = Variable<TableViewModel>(TableViewModel())
And then listen to event in my viewController
viewModel.tableViewModel.asObservable().subscribe({ [weak self] value in
self?.tableView.reloadData()
}).addDisposableTo(dispose)
Several questions here (I'm fairly new to FRP)
:
How can I "emit" an event to trigger my subscription in the viewController from my custom class?
I know that a solution would be to create my sections array as a RxSwift Variable() and then listen to it directly, but wonder if there's a way to make a class (or struct) itself Observable.
Is there a better approach to this problem ? I went through the RxSwift playground and example project but it sounds like there's a thousand ways to do the same thing. Maybe an Observable is not what i would be looking for. :)
Don't make the view model itself an Observable. It's unnecessary complexity and it's just not intuitive. If you really wanted to, then take a look at how Variable is implemented, which should give you an idea of how to do it.
Instead, just use a subject (such as Variable) to hold onto your data within your view model.

Limiting Array extension to a particular Array type?

Is it possible when writing an extension to Array to limit the extension just to a particular Array type? What I am trying to do is add an extension to Array that only works on Arrays of type [SKTexture].
I don't want the extension to do anything with Arrays containing other types (i.e. [Int], [Double], [String], etc.)
I agree with the comments above that it is not possible to write an extension like this, but wanted to suggest using a global function instead, with the option of currying it for greater flexibility.
For some background information, see this blog post by Ole Begemann where he notes that in Swift,
Instance methods are just curried functions that take the instance as the first argument.
So from Swift's point of view there is no difference between instance.function() and function(instance)().
I don't know what you are trying to do, but a global function that looks like this...
func skTextureFunc(textures: [SKTexture])(arg: SomeArgType) { }
...is, for all intents and purposes, exactly the same as an instance method that can be bound to a particular instance of [SKTexture].
And if you want, you can partially apply the function to a specific instance of [SKTexture] that you will be using repeatedly and then use it as if it were an instance method bound to that instance. So, for example:
class Example {
var localSKTextureFunc: SomeArgType -> () {
return skTextureFunc(textureArray)
}
let textureArray = [SKTexture]()
}
So globally you can always use the curried function, and locally (inside a class) you can bind the curried function to a specific instance of a class.
I know that's not exactly what you asked, but I think it is the best work around for the fact that we can't write extensions like the one you describe.

Resources