Simple observable struct with RxSwift? - swift

I'm trying to come up with a simple observable object in Swift and thought to use RxSwift. I couldn't find a simple example to do something like this:
protocol PropertyObservable {
typealias PropertyType
var propertyChanged: Event<(PropertyType, Any)> { get }
}
class Car: PropertyObservable {
typealias PropertyType = CarProperty
let propertyChanged = Event<(CarProperty, Any)>()
dynamic var miles: Int = 0 {
didSet {
propertyChanged.raise(.Miles, oldValue as Any)
}
}
dynamic var name: String = "Turbo" {
didSet {
propertyChanged.raise(.Name, oldValue as Any)
}
}
}
The above is pure Swift solution for observables from this blog post; I really like how it's a protocol-based solution and not invasive. In my case, I have an object in my project where each property is set asynchronously under the hood (bluetooth device). So I need to observe/subscribe to the changes instead of getting/setting the properties in real-time.
I keep hearing RxSwift will do just that and more. However, I can't find a simple example to match above and beginning to think RxSwift is overkill for my need? Thanks for any help.

Easiest way to quickly make this observable with RxSwift would probably be to use the RxSwift class Variable (all code here is untested off the top of my head):
import RxSwift
class Car {
var miles = Variable<Int>(0)
var name = Variable<String>("Turbo")
}
This enables you to observe the values by subscribing to them:
let disposeBag = DisposeBag()
let car = Car
car.name.asObservable()
.subscribeNext { name in print("Car name changed to \(name)") }
.addToDisposeBag(disposeBag) // Make sure the subscription disappears at some point.
Now you've lost the old value in each event. There are of course numerous ways to solve this, the RxSwifty way would probably be to add a scan operation to your element sequence, which works a lot like reduce does on a normal Array:
car.name.asObservable()
.scan(seed: ("", car.name.value)) { (lastEvent, newElement) in
let (_, oldElement) = lastEvent
return (oldElement, newElement)
}
.subscribeNext { (old, new) in print("Car name changed from \(old) to \(new)") }
.addToDisposeBag(disposeBag)

Related

How to implement a simple event emitter style publisher in Combine?

So I'm getting started with Combine, and I want to have a component which publishes events like a simple event emitter.
So in other words, I want to have a model like this (pseudocode):
class MyModel {
var onNewEvent: EventPublisher
func foo(bar: Bar) {
onNewEvent.publish(Event(bar))
}
}
let model: MyModel...
model.onNewEvent.sink(
receiveValue: { event in print(event) }
)
I can achieve something like this using a notification center publisher, but I want to avoid this unnecessary step if possible.
Is there such a thing as a simple publisher which just publishes values on command, or else what would be the idiomatic way to handle this in combine?
you can use a private(outside world cannot change/emit values) subject inside your model. and then expose it using a lazy publisher to let the outside world to subscribe.
class MyModel {
private var myPassthroughSubject = PassthroughSubject<Bar, Never>() // make this subject `private`, so only inside methods can send values.
lazy var myPublisher = myPassthroughSubject.eraseToAnyPublisher() // expose this publisher to the outside world.
func foo(bar: Bar) {
myPassthroughSubject.send(bar)
}
}
struct Bar {
}
let model = MyModel()
model.myPublisher.sink(
receiveValue: { event in print(event) }
)
model.foo(bar: .init())
model.foo(bar: .init())

Is there an alternative to Combine's #Published that signals a value change after it has taken place instead of before?

I would like to use Combine's #Published attribute to respond to changes in a property, but it seems that it signals before the change to the property has taken place, like a willSet observer. The following code:
import Combine
class A {
#Published var foo = false
}
let a = A()
let fooSink = a.$foo.dropFirst().sink { _ in // `dropFirst()` is to ignore the initial value
print("foo is now \(a.foo)")
}
a.foo = true
outputs:
foo is now false
I'd like the sink to run after the property has changed like a didSet observer so that foo would be true at that point. Is there an alternative publisher that signals then, or a way of making #Published work like that?
There is a thread on the Swift forums for this issue. Reasons of why they made the decision to fire signals on "willSet" and not "didSet" explained by Tony_Parker
We (and SwiftUI) chose willChange because it has some advantages over
didChange:
It enables snapshotting the state of the object (since you
have access to both the old and new value, via the current value of
the property and the value you receive). This is important for
SwiftUI's performance, but has other applications.
"will" notifications are easier to coalesce at a low level, because you can
skip further notifications until some other event (e.g., a run loop
spin). Combine makes this coalescing straightforward with operators
like removeDuplicates, although I do think we need a few more grouping
operators to help with things like run loop integration.
It's easier to make the mistake of getting a half-modified object with did,
because one change is finished but another may not be done yet.
I do not intuitively understand that I'm getting willSend event instead of didSet, when I receive a value. It does not seem like a convenient solution for me. For example, what do you do, when in ViewController you receiving a "new items event" from ViewModel, and should reload your table/collection? In table view's numberOfRowsInSection and cellForRowAt methods you can't access new items with self.viewModel.item[x] because it's not set yet. In this case, you have to create a redundant state variable just for the caching of the new values within receiveValue: block.
Maybe it's good for SwiftUI inner mechanisms, but IMHO, not so obvious and convenient for other usecases.
User clayellis in the thread above proposed solution which I'm using:
Publisher+didSet.swift
extension Published.Publisher {
var didSet: AnyPublisher<Value, Never> {
self.receive(on: RunLoop.main).eraseToAnyPublisher()
}
}
Now I can use it like this and get didSet value:
self.viewModel.$items.didSet.sink { [weak self] (models) in
self?.updateData()
}.store(in: &self.subscriptions)
I'm not sure if it is stable for future Combine updates, though.
UPD: Worth to mention that it can possibly cause bugs (races) if you set value from a different thread than the main.
Original topic link: https://forums.swift.org/t/is-this-a-bug-in-published/31292/37?page=2
You can write your own custom property wrapper:
import Combine
#propertyWrapper
class DidSet<Value> {
private var val: Value
private let subject: CurrentValueSubject<Value, Never>
init(wrappedValue value: Value) {
val = value
subject = CurrentValueSubject(value)
wrappedValue = value
}
var wrappedValue: Value {
set {
val = newValue
subject.send(val)
}
get { val }
}
public var projectedValue: CurrentValueSubject<Value, Never> {
get { subject }
}
}
Further to Eluss's good explanation, I'll add some code that works. You need to create your own PassthroughSubject to make a publisher, and use the property observer didSet to send changes after the change has taken place.
import Combine
class A {
public var fooDidChange = PassthroughSubject<Void, Never>()
var foo = false { didSet { fooDidChange.send() } }
}
let a = A()
let fooSink = a.fooDidChange.sink { _ in
print("foo is now \(a.foo)")
}
a.foo = true
Before the introduction of ObservableObject SwiftUI used to work the way that you specify - it would notify you after the change has been made. The change to willChange was made intentionally and is probably caused by some optimizations, so using ObservableObjsect with #Published will always notify you before the changed by design. Of course you could decide not to use the #Published property wrapper and implement the notifications yourself in a didChange callback and send them via objectWillChange property, but this would be against the convention and might cause issues with updating views. (https://developer.apple.com/documentation/combine/observableobject/3362556-objectwillchange) and it's done automatically when used with #Published.
If you need the sink for something else than ui updates, then I would implement another publisher and not go agains the ObservableObject convention.
Another alternative is to just use a CurrentValueSubject instead of a member variable with the #Published attribute. So for example, the following:
#Published public var foo: Int = 10
would become:
public let foo: CurrentValueSubject<Int, Never> = CurrentValueSubject(10)
This obviously has some disadvantages, not least of which is that you need to access the value as object.foo.value instead of just object.foo. It does give you the behavior you're looking for, however.

Referring to properties of containing class when using internal structs in swift

I'm refactoring a project to use MVVM and using protocols to ensure that my view models have a consistent structure. This works fine for defining public properties relating to input and output (which are based on internal structs) but defining actions in the same way is proving problemmatic as, currently, they are defined as closures which have to refer to view model properties. If I use the same approach as I have to input and output properties, I don't think I can access properties of the containing instance.
Example:
protocol ViewModelType {
associatedtype Input
associatedtype Output
associatedtype Action
}
final class MyViewModel: ViewModelType {
struct Input { var test: String }
struct Output { var result: String }
struct Action {
lazy var createMyAction: Action<String, Void> = { ... closure to generate Action which uses a MyViewModel property }
}
var input: Input
var output: Output
var action: Action
}
It's not a deal breaker if I can't do it, but I was curious as I can't see any way of getting access to the parent's properties.
Answer to your question
Let's begin with a note that createMyAction: Action<String, Void> refers to the type (struct) named Action as if it was a generic, but you have not declared it as such and will thus not work.
And to answer your question of the nested struct Action can refer its outer class MyViewModel - yes you can refer static properties, like this:
struct Foo {
struct Bar {
let biz = Foo.buz
}
static let buz = "buz"
}
let foobar = Foo.Bar()
print(foobar.biz)
But you should probably avoid such circular references. And I will omit any ugly hack that might be able to achive such a circular reference on non static properties (would probably involve mutable optional types). It is a code smell.
Suggestion for MVVM
Sounds like you would like to declare Action as a function? I'm using this protocol myself:
protocol ViewModelType {
associatedtype Input
associatedtype Output
func transform(input: Input) -> Output
}
Originally inspired by SergDort's CleanArchitecture.
You can prepare an instance of input (containing Observables) from the UIViewController and call the transform function and then map the Output of transform (being Observabless) to update the GUI.
So this code assumes you have basic Reactive knowledge. As for Observables you can chose between RxSwift or ReactiveSwift - yes their names are similar.
If you are comfortable with Rx, it is an excellent way of achieving a nice MVVM architecture with simple async updates of the GUI. In the example below, you will find the type Driver which is documented here, but the short explanation is that is what you want to use for input from views and input to views, since it updates the views on the GUI thread and it is guaranteed to not error out.
CleanArchitecture contains e.g. PostsViewModel :
final class PostsViewModel: ViewModelType {
struct Input {
let trigger: Driver<Void>
let createPostTrigger: Driver<Void>
let selection: Driver<IndexPath>
}
struct Output {
let fetching: Driver<Bool>
let posts: Driver<[PostItemViewModel]>
let createPost: Driver<Void>
let selectedPost: Driver<Post>
let error: Driver<Error>
}
private let useCase: PostsUseCase
private let navigator: PostsNavigator
init(useCase: PostsUseCase, navigator: PostsNavigator) {
self.useCase = useCase
self.navigator = navigator
}
func transform(input: Input) -> Output {
let activityIndicator = ActivityIndicator()
let errorTracker = ErrorTracker()
let posts = input.trigger.flatMapLatest {
return self.useCase.posts()
.trackActivity(activityIndicator)
.trackError(errorTracker)
.asDriverOnErrorJustComplete()
.map { $0.map { PostItemViewModel(with: $0) } }
}
let fetching = activityIndicator.asDriver()
let errors = errorTracker.asDriver()
let selectedPost = input.selection
.withLatestFrom(posts) { (indexPath, posts) -> Post in
return posts[indexPath.row].post
}
.do(onNext: navigator.toPost)
let createPost = input.createPostTrigger
.do(onNext: navigator.toCreatePost)
return Output(fetching: fetching,
posts: posts,
createPost: createPost,
selectedPost: selectedPost,
error: errors)
}
}

Iterating over and modifying a collection of structs in Swift

Suppose you have some structs like:
struct Tattoo {
var imageTorso:UIImage?
var imageTorsoURL:URL?
var imageArms:UIImage?
var imageArmsURL:URL?
}
struct Player {
var name:String = ""
var tattoos:[Tattoo] = []
}
struct Team {
var name:String = ""
var players:[Player] = []
}
Now imagine that you have a method that was passed in a Team value with some players. You have to iterate thru the players and their tattoos, then download the images and add them into the images variables on the tattoos.
If you use a for in loop, then it won't work because each part of the loop gets a copy of the members of the array it's iterating over. I.e.:
for player in team.players {
for tattoo in player.tattoos {
if let url = tattoo.imageTorsoURL {
MyNetFramework.requestImage(from: url, completion: { image in
tattoo.imageTorso = image
}
}
}
}
After doing all the iterations and completion blocks, still, the original team variable is identical to what it was prior to doing any of this. Because each tattoo that the inner loop got was a copy of what is in the player's tattoos array.
Now I know you can use & to pass structs by reference in Swift but it's highly discouraged. As well I know you can use inout so they don't get copied when they come into functions, which is also discouraged.
I also know these could be made classes to avoid this behavior.
But supposing I don't have a choice in the matter -- they are structs -- it seems the only way to do this is something like:
for p in 0...team.players.count-1 {
for t in 0...team.players[p].tattoos.count-1 {
if let url = team.players[p].tattoos[t].imageTorsoURL {
MyNetFramework.requestImage(from: url, completion: { image in
team.players[p].tattoos[t].imageTorso = image
}
}
}
}
This feels ugly and awkward, but I don't know how else to get around this thing where for in loops give you a copy of the thing you're iterating through.
Can anyone enlighten me, or is this just how it is in Swift?
I think you already got the point: "When your requirement will be modifying the data, you better to use class instead."
Here is the question reference link for you. Why choose struct over class
struct is fast and you can use them to prevent creating a huge, messy class. struct provided the immutable feature and make us easier to follow the Function Programming
The most significant benefit of immutable data is free of race-condition and deadlocks. That because you only read the data and no worries about the problems caused by changing data.
However, to answer your question, I have few ways to solve it.
1. Renew whole data.
// First, we need to add constructors for creating instances more easier.
struct Tattoo {
var imageTorso:UIImage?
var imageTorsoURL:URL?
var imageArms:UIImage?
var imageArmsURL:URL?
init(imageTorso: UIImage? = nil, imageTorsoURL: URL? = nil, imageArms: UIImage? = nil, imageArmsURL: URL? = nil) {
self.imageTorso = imageTorso
self.imageTorsoURL = imageTorsoURL
self.imageArms = imageArms
self.imageArmsURL = imageArmsURL
}
}
struct Player {
var name:String
var tattoos:[Tattoo]
init() {
self.init(name: "", tattoos: [])
}
init(name: String, tattoos: [Tattoo]) {
self.name = name
self.tattoos = tattoos
}
}
struct Team {
var name:String
var players:[Player]
init() {
self.init(name: "", players: [])
}
init(name: String, players: [Player]) {
self.name = name
self.players = players
}
}
for player in team.players {
for tattoo in player.tattoos {
if let url = tattoo.imageTorsoURL {
// Catch old UIImage for matching which Tattoo need to be updated.
({ (needChangeImage: UIImage?) -> Void in
MyNetFramework.requestImage(from: url, completion: { image in
// Reconstruct whole team data structure.
let newPlayers = team.players.map { (player) -> Player in
let newTattos = player.tattoos.map { (tattoo) -> Tattoo in
if needChangeImage == tattoo.imageTorso {
return Tattoo(imageTorso: image)
} else {
return tattoo
}
}
return Player(name: player.name, tattoos: newTattos)
}
team = Team(name: team.name, players: newPlayers)
})
})(tattoo.imageTorso)
}
}
}
These codes are ugly, right? And there will not only be awful performance issue caused by going through whole data every network response; another problem is that might causes the retain cycle.
2. Don't hold UIImage in the data array.
Redesign your data structure, and use Kingfisher to help us download image synchronously.
Kingfisher is useful third party library. It provides clean and simple methods to use, and it's highly flexible.
let url = URL(string: "url_of_your_image")
imageView.kf.setImage(with: url)
However, I think the best way for you if you don't want to use Kingfisher is to change your declaration from struct to class.
Unfortunately that's the nature of struct and Swift doesn't offer a way for you modify the collection in-place while iterating over it. But can user enumerated() to get both the index and the element when iterating:
for (p, player) in team.players.enumerated() {
for (t, tattoo) in player.tattoos.enumerated() {
if let url = tattoo.imageTorsoURL {
MyNetFramework.requestImage(from: url, completion: { image in
team.players[p].tattoos[t].imageTorso = image
}
}
}
}

Generic parameter constraint on class crashes the compiler

This is a simplified form of some swift3 code:
class GenericListViewModel<CellViewModel> {
let cells: [CellViewModel]
required init(cells: [CellViewModel]) {
self.cells = cells
}
}
class ViewController<CellViewModel, ListViewModel: GenericListViewModel<CellViewModel>> {
var viewModel: ListViewModel
init(cellViewModels: [CellViewModel]) {
viewModel = ListViewModel(cells: cellViewModels)
}
}
The compiler crashes with the following error:
While emitting IR SIL function #_TFC4Xxxx14ViewControllercfT14cellViewModelsGSax__GS0_xq__ for 'init'
at /.../GenericStuff.swift:22:5
Am is missing something, or is this a Swift compiler bug?
Edit:
I reported this here https://bugs.swift.org/browse/SR-3315 and it looks like it's fixed in current swift master branch.
You're pushing the system too hard with inheritance. Generics based on subclasses of other generics tends to break the compiler's brain, and is usually not really what you meant anyway. (That said: there is never an excuse for the compiler crashing, so you should absolutely open a bugreport.)
Do you really mean to subclass GenericListViewModel and then parameterize ViewController on that precise subclass? This seems very over-complicated and I'm not seeing how you would get any actual value out of it (since you can't rely on any additional methods added to your subclasses, and you already have dynamic dispatch). You're using both subclasses and generics to solve the same problem.
What you likely mean is that there's a CellViewModel and you want GenericListViewModel<CellViewModel> to wrap that, and aren't thinking about subclasses at all.
So, assuming you don't really mean to parameterize this specifically, let inheritance do its job. ListViewModel should be typealias, not a type parameter:
class ViewController<CellViewModel> {
typealias ListViewModel = GenericListViewModel<CellViewModel>
var viewModel: ListViewModel
init(cellViewModels: [CellViewModel]) {
viewModel = ListViewModel(cells: cellViewModels)
}
}
Now it's fine. That said, do you really need the view model to be a reference type? View models often don't need identity themselves (unless you're observing them with KVO). They may wrap a reference type, but as an adapter, a value type is often fine. Assuming this is true for you, then this can and should be simplified to a struct:
struct GenericListViewModel<CellViewModel> {
let cells: [CellViewModel]
}
class ViewController<CellViewModel> {
typealias ListViewModel = GenericListViewModel<CellViewModel>
var viewModel: ListViewModel
init(cellViewModels: [CellViewModel]) {
viewModel = ListViewModel(cells: cellViewModels)
}
}
To your goals of "custom logic like filtering the model, or keeping some other state specific to each controller," I would be very careful of using subclasses for this. It sounds like you're tempted to mix too much functionality into a single type. First, think about how you'd call your code the way you're thinking about it. ListViewModel isn't constrained by the init call, so you can't use type-inference. You'll have to initialize it like:
let vc: ViewController<SomeCellModel, GenericListViewModelSubclass<SomeCellModel>> = ViewController(cells: cells)
That's pretty hideous and is fighting all the things Swift wants to help you with. Since you want to be able to pass in the ListViewModel type, let's just pass it in. This is what protocols are for, not classes.
protocol CellViewModelProviding {
associatedtype CellViewModel
var cells: [CellViewModel] { get }
}
class ViewController<ListViewModel: CellViewModelProviding> {
var viewModel: ListViewModel
init(listViewModel: ListViewModel) {
viewModel = listViewModel
}
}
Now we can create different providers.
// A more standard name for your GenericListViewModel
struct AnyListViewModel<CellViewModel>: CellViewModelProviding {
let cells: [CellViewModel]
}
struct FilteredListViewModel<CellViewModel>: CellViewModelProviding {
var cells: [CellViewModel] {
return unfilteredCells.filter(predicate)
}
var unfilteredCells: [CellViewModel]
var predicate: (CellViewModel) -> Bool
}
Now we can use it with:
let vc = ViewController(listViewModel: AnyListViewModel(cells: [1,2,3]))
let vc2 = ViewController(listViewModel: FilteredListViewModel(unfilteredCells: [1,2,3],
predicate: { $0 % 2 == 0 }))
So that's pretty nice, but we could do better. It's kind of annoying to have to wrap our cells up in an AnyListViewModel in the normal case. We could probably create a factory method to get around this, but yuck. The better answer is to make use of the power of AnyListViewModel to be a type eraser. This is going to get a little more advanced, so if you're happy with the above solution, you can stop, but let's walk through it because it's really powerful and flexible if you need it.
First, we convert AnyListViewModel into a full type eraser that can accept either another view list model, or just an array.
struct AnyListViewModel<CellViewModel>: CellViewModelProviding {
private let _cells: () -> [CellViewModel]
var cells: [CellViewModel] { return _cells() }
init(cells: [CellViewModel]) {
_cells = { cells }
}
init<ListViewModel: CellViewModelProviding>(_ listViewModel: ListViewModel)
where ListViewModel.CellViewModel == CellViewModel {
_cells = { listViewModel.cells }
}
}
Now ViewController doesn't have to care what kind of ListViewModel is passed. It can turn anything into an AnyListViewModel and work with that.
class ViewController<CellViewModel> {
var viewModel: AnyListViewModel<CellViewModel>
init<ListViewModel: CellViewModelProviding>(listViewModel: ListViewModel)
where ListViewModel.CellViewModel == CellViewModel {
viewModel = AnyListViewModel(listViewModel)
}
init(cells: [CellViewModel]) {
viewModel = AnyListViewModel(cells: cells)
}
}
OK, that's cool, but it's not a huge improvement. Well, let's rebuild FilteredListViewModel and see what that gets us.
struct FilteredListViewModel<CellViewModel>: CellViewModelProviding {
var cells: [CellViewModel] {
return listViewModel.cells.filter(predicate)
}
private var listViewModel: AnyListViewModel<CellViewModel>
var predicate: (CellViewModel) -> Bool
// We can lift any other listViewModel
init<ListViewModel: CellViewModelProviding>(filtering listViewModel: ListViewModel,
withPredicate predicate: #escaping (CellViewModel) -> Bool)
where ListViewModel.CellViewModel == CellViewModel {
self.listViewModel = AnyListViewModel(listViewModel)
self.predicate = predicate
}
// Or, just for convenience, we can handle the simple [cell] case
init(filtering cells: [CellViewModel], withPredicate predicate: #escaping (CellViewModel) -> Bool) {
self.init(filtering: AnyListViewModel(cells: cells), withPredicate: predicate)
}
}
This is where things get powerful. We've said FilteredListViewModel can take some cells and filter them, sure. But it can also filter any other view list model.
let someList = AnyListViewModel(cells: [1,2,3])
let evenList = FilteredListViewModel(filtering: someList, withPredicate: { $0 % 2 == 0 })
So now you can chain things together. You could glue together filtering with sorting or something that modified the cells or whatever. You don't need one super-specialized subclass that does everything you need. You can click together simpler pieces to build complex solutions.