PromiseKit to act as RxSwift on variable observing - ios

I use RxSwift at work (ont a big fan, too complex, but really powerful indeed), and I want to try to use PromiseKit as a "smaller" tool.
I try to create sort of an Observable with PromiseKit, but I can't figure it out. I tried to use the NSObject's extension observe(key path: String) -> Promise, but it does not seems to work.
I noted that I have a struct (hence does not inherit from NSObject) and I tried to observe an NSObject, but it does not seems to work.
Here's what I've done :
in my controller that has the value I want to observe, I declared a variable as follows :
lazy var homeworkPromise: Promise<Homework> = self.observe(keyPath: "homework")
Somewhere in my code, I update the value of the homework variable.
And in my presentingVC, here's what I do :
create.homeworkPromise
.then(execute: { h -> Void in
print(h)
})
It seems pretty straightforward to me, but I guess I'm missing something ;-)
Thanks for your help.

Related

Mutable observable object in RxSwift or Combine

What I'm trying to achieve is to have a mutable Array in viewModel that can also be observed by Controller and reload tableView when its value changes.
I tried to use BehaviorRelay but looks like it's immutable. I can't delete and insert data inside it's value.
The best thing I managed to write is this custom ObservableObject class:
final class CustomObservableObject<T> {
var value: T {
didSet {
listener?(value)
}
}
init(value: T) {
self.value = value
}
var listener: ((T) -> Void)?
func bind(listener: #escaping ((T) -> Void)) {
self.listener = listener
}
}
which works exactly how I want. Is there anything I can use in the Combine or RxSwift framework like this class?
Thanks in advance
A BehaviorRelay is built for the specific purpose of storing state and allowing you to imperatively update it which is what you are asking for.
The BehaviorRelay type is a wrapper around the BehaviorSubject type and provides a different interface, one that does not allow the internal subject to emit an error or completed event. The Element contained in the BehaviorSubject is mutable. However, in order to mutate it you must first extract it into a var with value and then push the updated value back in with accept(_:). This will help ensure that no mutations are missed and allows you to batch up multiple mutations into a single update. These are things missing in the class you wrote.
Please always remember though:
Subjects [and Relays] provide a convenient way to poke around Rx, however they are not recommended for day to day use.
-- Introduction to Rx
As you learn more about, and grow more comfortable with, the reactive paradigm you will find yourself reaching for this particular crutch less often.

Swift instance members & functions calls, could someone please clarify?

I'm practicing functions in an Xcode project (not playground, if that matters) and when I set the constant's function name to the above function's name, Xcode won't let me call it.
In other functions I've done the same thing I haven't had this error, I don't know why it's being triggered. I've tried putting the "let fun" constant into it's own struct but that just throws up a different error.
Your let fun = playstation(... is code. It is an executable command. Code consisting of an executable command cannot just live anywhere. It cannot exist just free-floating the way you have it. It must live in a method. For example you could put it inside your viewDidLoad method.
I am sure you know this, but I would like to say that learning the basics/fundamentals of the Swift language is really good if you use playgrounds for that or any other online IDEs which support Swift language.
Swift Playgrounds Experience is very, very different than what an iOS dev has to do later, i.e. when it actually comes to building an app.
Of course in app making, the Swift language is used, and one uses the knowledge he practiced using playgrounds. BUT!
When it comes to storyboards, ViewControllers, etc., one has to understand that this time, it is all about OOP (classes and structs).
It is about managing views, loading them, displaying UIView objects, implementing logic, saving/loading data...
So as people mentioned above, here you are creating an instance method and trying to use it as a regular function inside that Class.
To be able to use this function as you expect, you have to create an object/instance of this class, then you can call this function.
In this picture, you may see that using a static keyword also might be a solution. static func(){} means that this function belongs to the class itself. You access that func by using the full name of the type/class.
You cannot call an instance method to initialize this same exact instance property, so you will need to convert to a lazy var like so:
lazy var fun = playStation(game: "Monsters")
This way, the playStation(game: "Monsters") call will be triggered lazily, in the exact moment when the fun property will be called for the first time. This can be very useful, especially when performing more intensive tasks. The quirk with using lazy keyword is that it can only be used with var, which means the property can be mutated or set again - we lose immutability of let constants.
But maybe you do not want to store the fun value as an instance property and only want to get it once? You could then move it into the viewDidLoad implementation, so the fun is no longer an instance property but a local one, available only within the viewDidLoad method scope.
override func viewDidLoad() {
super.viewDidLoad()
let fun = playStation(game: "Monsters")
// do what you want with the `fun` value.
// maybe set it as text to some label?
myLabel.text = fun
}
You can read more about Lazy Initialization in this blogpost, also make sure to read the official documentation.

How to use Struct as Observer in NotificationCenter

In NotificationCenter Class , Why apple has created Observer of type Any?
func addObserver(Any, selector: Selector, name: NSNotification.Name?, object: Any?)
My Reasoning.
If observer is struct then on assigning inside as function parameter, It will be copied then how my observer can receive the notification.
I can't write any function which uses #objc prefix in Struct.
Selector is always be type of #objc.
So What is the use of Any in addObserver.....
It should always be of type AnyObject.
Secondly we already known that NotificationCenter keep the weak copy of observer, And we can't use weak modifier for type Any. Then how apple is managing all this?
Any help in understanding this concept is highly appreciated.
No one chose to make this parameter Any. It's just what they got by default. It's automatically bridged from ObjC:
- (void)addObserver:(id)observer
selector:(SEL)aSelector
name:(nullable NSNotificationName)aName
object:(nullable id)anObject;
The default way that id is bridged is Any. It hasn't been specially refined for Swift. In practice, you can't really use structs meaningfully here. The fact that the compiler won't stop you from calling it in an unhelpful way doesn't imply that it's intended to be used that way.
Why type Any? - because in Objective C it is type id.
Why you can't mark your function as #obj - #obc is the keyword for Swift code which indicates what compiler should add this method to a header file for this Class, yes you can make headers only for Classes.
Selector also is the objective-c term, it just says which function to invoke, similar to msg_send
In NotificationCenter Class , Why apple has created Observer of type Any.
Because all Objective-C id declarations are translated into Swift as Any.
You might object that this really should be AnyObject, because only a class will work here. And indeed, that's the way id used to be translated into Swift. But nowadays you can pass anything where an id is expected, because if it's something Objective-C can't understand, it will be boxed up as a class instance (e.g. as a _SwiftValue) so that it can make the round-trip into Objective-C and back again to Swift. Therefore id is translated as Any.
However, just because you can pass a struct here doesn't mean you should. It won't work, as you've discovered. Objective-C cannot introspect a Swift struct.
There are lots of situations like this, where Cocoa gives you enough room to hang yourself by passing the wrong thing. The contents of a CALayer is typed as Any, but if you pass anything other than a CGImage, nothing will happen. The layerClass if a UIView is typed as AnyClass, but you'd better pass a CALayer subclass. I could go on and on.

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.

What should I do about frequently repeated constants?

This seems like a very simple question, but I can't find a clear answer. Also it's not specifically about swift or iOs, but I'm new to programming and swift is the only language I know anything about, so I don't know how to phrase it for a more general context.
I'm trying to write an iOs app and I found myself defining the same constants many times throughout my code.
I must have written this line about a hundred times, for instance:
let calendar = NSCalendar.currentCalendar()
Another example is getting my only User object from its persistent store:
let realm = try! Realm()
let user = realm.objects(User).first!
I define those calendar and user constants over and over throughout my whole code in classes and subclasses and extensions and computed properties.
That seems kind of stupid, though. I think I should be able to define calendar once and for all and just use it when I need it. So my first thought was to declare a global constant, but apparently everybody thinks anything with the word "global" in it should be avoided at all costs.
So what should I do? Is there another solution for this? Should I just keep writing the same stuff over and over again?
Thanks in advance,
Daniel
There are many different situations in which the best use of different approaches.
For example in your case:
let calendar = NSCalendar.currentCalendar()
currentCalendar is a static method that already returns a pointer to the object that you will use. And you don't need to set it to some constant for using with simple case:
print(NSCalendar.currentCalendar().calendarIdentifier)
Another thing that is most often better to use a shorter name for the object in your code when you need to refer to it often and this code looks much more readable:
print(calendar.calendarIdentifier)
If you have the functionality that you will often use in application from different places, you can just make it to the static method and does not create an object of this class every time you call it:
class NetworkConnection {
class func getDataFromServet(completion block: (data: SomeType) -> Void) {
...
}
}
And use it without object creation like:
NetworkConnection.getDataFromServer(completion: {(data: SomeType) -> Void in
...
})
If you need to use created object in many places, the best solution is not to make it global or singleton instance, but pass a pointer to it to the objects where you need to use it. This makes the code more readable, for example by looking at the input parameters of the init method, anyone can immediately understand which objects use this class for their work. And this class is easier to take from the project in a separate module and connect to another project. At that time, if you use the singleton instance, the class's interface is not clear what it can be used and this leads to code obfuscation. This applies and to the global objects.
If you're constantly changing it, why aren't you just using var instead of let?

Resources