Is it possible to use Observable<Double>.interval in RxSwift? - ios

Hi I have a question here, all the docs I found on RxSwift so far are using observable interval as below:
let subscription = Observable<Int>.interval(0.3, scheduler: scheduler)
.subscribe { event in
print(event)
}
NSThread.sleepForTimeInterval(2)
subscription.dispose()
Now I have the need to implement a Observable<Double>.interval timer, I would like to a Double value in my subscribeNext calls from Observable.
After change above code to Double as a testing, I have an error saying Type Observable<Double> has no member IntegerLiteralType , anyone knows how to implement this in RxSwift?

extension Observable where Element : SignedIntegerType {
/**
Returns an observable sequence that produces a value after each period, using the specified scheduler to run timers and to send out observer messages.
- seealso: [interval operator on reactivex.io](http://reactivex.io/documentation/operators/interval.html)
- parameter period: Period for producing the values in the resulting sequence.
- parameter scheduler: Scheduler to run the timer on.
- returns: An observable sequence that produces a value after each period.
*/
#warn_unused_result(message="http://git.io/rxs.uo")
public static func interval(period: RxTimeInterval, scheduler: SchedulerType)
-> Observable<E> {
return Timer(dueTime: period,
period: period,
scheduler: scheduler
)
}
}
The Element Type is SignedIntegerType. So you can't use Double.If you want to use double , you can rewrite Timer and associated classes.But I don't suggest to rewrite it.You can think another way to do it.

I am not quite sure why you would want a sequence of Double values, because the value just tells you how many times the interval has passed (it does not tell you the amount of time that has passed). So it makes sense that the value is an Int.
But if you really want a sequence of Double values you could easily map the Int sequence to a Double sequence:
_ = Observable<Int>.interval(0.4, scheduler: MainScheduler.instance)
.map { Double($0) }
.subscribeNext { value in
print(value)
}
.addDisposableTo(disposeBag)
Which prints:
1.0
2.0
3.0
4.0
...

Related

With Combine, how can you flatMap with nil-coalescing

I have a publisher which emits an optional output type. I need to flatMap to a new publisher if the output isn't nil, or fallback to an empty publisher if it is nil.
For example, something like:
[1, nil, 5].publisher // Generic parameter 'T' could not be inferred
.flatMap {
$0?.someNewPublisher ?? Empty(completeImmediately: false)
}
[1, nil, 5].publisher
.map {
$0?.someNewPublisher
}
.replaceNil(with: Empty(completeImmediately: false)) // Generic parameter 'Failure' could not be inferred
.flatMap { $0 }
I'm wondering if maybe I'm trying to solve this in the wrong way. Just to be clear, filtering nil before mapping would not solve my issue, as that would not replace a current publisher with an empty publisher (I would continue to receive elements which I should no longer be receiving).
There is an Optional.Publisher type. If you call .publisher on an optional value you’ll get a publisher that either produces the wrapped type or completes immediately.
optionalValue.publisher.flatMap(\.someNewPublisher)
Edit:
Since this functionality is gatekept to iOS14+, here is an extension that creates the same functionality but as a function so that it doesn't collide with the naming of the publisher var.
Note that I'm not setting completeImmediately to false, it’s automatically set to true, and setting it to false means the publisher will never complete.
extension Optional {
func publisher() -> AnyPublisher<Wrapped, Never> {
switch self {
case let .some(wrapped):
return Just(wrapped).eraseToAnyPublisher()
case .none:
return Empty().eraseToAnyPublisher()
}
}
}
Your original approach will work, it just looks like you need to specify the types because the compiler is having trouble with they type inference.
Specifying the return type in the flat map does the trick
Note in combine each publisher has it's own type and each time you use an operator it returns a different (chained) type, so use the type eraser .eraseToAnyPublisher() liberally.

Call two Swift Combine calls where the second call depends on the result of the first call

I'm new to Swift Combine, so having a small issue
Im trying to call two api calls, and merge the results as the second API call depends on the result from the first call.
Here is the code
return self.gameRepository.fetchGames(forUser: user.id)
.map { games -> AnyPublisher<[Game], ApiError> in
let result = games.map { gameDto -> Game in
let location = self.venueRepository.fetch(by: gameDto.siteId)
.map { $0.mapToModel() }
let game = gameDto.mapToModel(location: location)
return game
}
My error is on line 4 "let location" the compiler complains when I try and pass this through to
line 5
game.mapToModel(location: location)
Cannot convert value of type 'AnyPublisher' to expected argument type Location
The fetch call signature from the repository looks like this
func fetch(by id: String) -> AnyPublisher<LocationDto, ApiError>
So it is correct, but the .map call I use on the result allows the
$0.mapToModel()
to occur, so I have locationDto object that allows me to cast to my Domain model.
Any help on how I can call these two apis together would be much appreciated.
Instead of the first map operator, try to use flatMap and return from this .flatmap AnyPublisher with your model, that needs to be passed to another request. Pseudocode:
fetchGames -> AnyPublisher<[Game]> // First request
fetchGame(by id: String) -> AnyPublisher<LocationDto> // Second request that depends on model from first one
fetchGames.flatMap { fetchGame(by: $0.id) }

How to edit function input in Swift?

I am building an idle clicker app in Swift and I am adding in auto clicks through a function. I want to be able to enter the price through the input for the function, but because the input is a constant, I can't add to the cost price by 9% like I want every time the user clicks the upgrade.
For example if I type 50.0 into the initialPrice how would I make it so that it increases by 10% every time the user clicks it.
func upgrade(intialPrice : Float) {
var upgradePrice = intialPrice
AutoClicks += 1
totalClicks = totalClicks - Int(upgradePrice)
upgradePrice = upgradePrice + (upgradePrice * 0.1)
burritoLvl1Label.text = ("$" + "\(Int(upgradePrice))")
}
I am very new to coding so if you see any way I could make this shorter that would be very helpful.
The other answers have suggested using an inout parameter, and while this will work, it is not a pattern I would recommend. Generally it is better to avoid "side effects" in functions and use inout only when there is a very good reason.
The more general approach would be to have a function that returns the new value.
You might also want to consider using a Decimal rather than a Float when dealing with currency.
Similarly, updating the label probably shouldn't be in this function
func upgrade(initialPrice : Decimal) -> Decimal {
autoClicks += 1
totalClicks -= Int(truncating: initialPrice as NSDecimalNumber)
return initialPrice * 1.1
}
You can then call this function like so:
price = upgrade(initialPrice: price)
burritoLvl1Label.text = ("$" + "\(Int(truncating:price as NSDecimal))")
You need to pass the value in as an inout argument to be able to mutate it. You can also make your implementation more concise by using the compound assignment operators, +=, -= and *=. You should also make sure that you conform to the Swift naming convention, which is loweCamelCase for function and variable names (autoClicks) and UpperCamelCase for types.
func upgrade(initialPrice: inout Float) {
autoClicks += 1
totalClicks -= Int(upgradePrice)
upgradePrice *= 1.1
burritoLvl1Label.text = ("$" + "\(Int(upgradePrice))")
}
However, you should be aware that in most cases you should return the mutated value from a function instead of mutating it by passing it in as an inout parameter. It is especially bad practice to mutate some instance variables of your class without passing them into the function explicitly, while modifying some others using an inout argument.
You should make your input parameter as inout.
More info about swift functions.
func upgrade(intialPrice: inout Float) {
var upgradePrice = intialPrice
AutoClicks += 1
totalClicks = totalClicks - Int(upgradePrice)
upgradePrice = upgradePrice + (upgradePrice * 0.1)
burritoLvl1Label.text = ("$" + "\(Int(upgradePrice))")
}
Hope it helps!

RXSwift - takeUntil canceling before next event

Following a similar example to question 39 here: http://reactivex.io/learnrx/
I'm trying to transform a method call search(query: String) into a sequence of those calls.
They way I'm achieving this is by creating a Variable which I update with the query value every time
the search(query: String) method is called.
Then I have this in my init():
_ = queryVariable.asObservable().flatMap({ query -> Observable<[JSON]> in
return self.facebookSearch(query).takeUntil(self.queryVariable.asObservable())
}).subscribeNext({ result in
if let name = result[0]["name"].string {
print(name)
} else {
print("problem")
}
})
If I type "ABC", my search(query: String) method will be called 3 times with "A", "AB", "ABC".
That would be mapped to seq(["A", "AB", "ABC"]) with queryVariable.asObservable().
Then I'm mapping it to Facebook searches (searching people by their names on Facebook).
And with subscribeNext I print the name.
If I don't use the takeUntil, it works as I'd expect, I get 3 sets of results, one for each of my queries("A", "AB", "ABC").
But if I type fast (before Facebook has time to respond to the request), I'd want only one result, for the query "ABC". That's why I added the takeUntil. With it I'd expect the facebookSearch(query: String) call to be ignored when the next query comes in, but it is being canceled for the current query, so with this takeUntil I end up printing nothing.
Is this a known issue or am I doing something wrong?
I used your code and found two solutions to your problem:
1. Use flatMapLatest
You can just use flatMapLatest instead of flatMap and takeUntil. flatMapLatest only returns the results of the latest search request and cancels all older requests that have not returned yet:
_ = queryVariable.asObservable()
.flatMapLatest { query -> Observable<String> in
return self.facebookSearch(query)
}
.subscribeNext {
print($0)
}
2. Use share
To make your approach work you have to share the events of your queryVariable Observable when you also use it for takeUntil:
let queryObservable = queryVariable.asObservable().share()
_ = queryObservable
.flatMap { query -> Observable<String> in
return self.facebookSearch(query).takeUntil(queryObservable)
}
.subscribeNext {
print($0)
}
If you do not share the events, the searchQuery.asObservable() in takeUntil creates its own (duplicate) sequence. Then when a new value is set on the searchQuery Variable it immediately fires a Next event in the takeUntil() sequence and that cancels the facebookSearch results.
When you use share() the sequence in takeUntil is observing the same event as the other sequence and in that case the takeUntil sequence handles the Next event after the facebookSearch has returned a response.
IMHO the first way (flatMapLatest) is the preferred way on how to handle this scenario.

Closures In Swift?

I am new to iOS coding and I am stuck in closures feature of SWIFT. I have referred to many tutorials and found that closures are self written codes which can be used in many ways eg. as arguments in function call,parameters in function definition,variables. I am giving below an example below with my associated thoughts about the code & questions. Please help me if I am wrong in my understanding. I know I am wrong at many points,so please rectify me.
1.1st Part
func TEST(text1:String,text2:String,flag: (S1:String,S2:String)->Bool)//In this line,I think,I am using flag is a closure which is passed as parameter in a function. And if so why doesn't it follow the standard closure syntax?
{
if flag(S1: text1, S2: text2) == true//I want to check the return type what flag closure gets when it compares the both string during function call. Why can't I write as if flag == true as flag is the name of the closure and ultimately refers to the return type of the closure?
{
print("they are equal")
}
else
{
//
}
}
2nd Part
This part is the most troublesome part that really confuses me when I am calling the function. Here I am also using the same closure. What is happening over here? How is the closure being used? Is it capturing values or something else?
TEST("heyy", text2: "heyy") { (S1, S2) -> Bool in
S1==S2
}
Thanks for your kind consideration.
Your closure usage is ok. A closure is some code that can be passed to be executed somewhere else. In your case you can choose to pass the real test you want to the function TEST, simple string test or case-insensitive test, etc. This is one of the first usage of closure: obtain more genericity.
And yes closures capture something, it captures some part of the environnement, i.e. the context in which they are defined. Look:
var m = "foo"
func test(text1:String, text2:String, testtFunc: (s1:String, s2:String) -> Bool) {
m = "bar"
if testFunc(s1: text1, s2: text2) { print("the test is true") }
}
m = "baz"
test("heyy", text2: "heyy") { (s1, s2) -> Bool in
Swift.print("Value for m is \(m)")
return s1==s2
}
The closure captures m (a variable that is defined in the context in which you define the closure), this means that this will print bar because at the time the closure is executed, the captured m equals to bar. Comment bar-line and baz will be printed; comment baz-line and foo will be printed. The closure captures m, not its value, m by itself, and this is evaluated to the correct value when the closure is evaluated.
Your first function works like this :
arguments :
String 1
String 2
a function that takes two Strings as arguments and returns a Bool
body :
execute the function (flag) with text1 and text2 and check the result.
The function doesn't know at all what you are testing, it only knows that two pieces of text are needed and a Bool will be returned.
So this function allows you to create a general way of handling different functions that all have two Strings as input. You can check for equality or if the first pieces of text is a part of the second and so on.
This is useful for many things and not so far from how array filtering / sorting / map works.
2nd Part :
This is just how you call a function with a closure.
TEST("heyy", text2: "heyy") { (S1, S2) -> Bool in
S1 == S2
}
You can also call it like this :
func testStringEqualityFor(text:String, and:String) -> Bool {
return text == and
}
TEST("hey", text2: "hey", flag: testStringEqualityFor)
Instead of using the trailing closure syntax to pass an unnamed function, you now pass a named function as one of the arguments.
It al becomes a lot clearer when you simplify it.
This is a function that takes another function as an argument.
Now we can call/use this function inside it. The argument function takes a bool as it's argument. So we give it a true
func simpleFunctionWithClosure(closure:(success:Bool) -> Void) {
// use the closure
closure(success: true)
}
When we use the function we need to pass it a function. In Swift you have the trailing closure syntax for that, but that is only available (and even then optional) to the first function as argument.
Trailing closure syntax means that instead of passing a named function you can write:
myFunction { arguments-for-closure-as-tuple -> return-for-closure-as-tuple in
function-body
}
The closure will receive an argument of Bool and returns nothing so Void.
In the body we can handle the arguments and do stuff with them.
But it is important to remember that what is inside the closure is not called directly. It is a function declaration that will be executed by simpleFunctionWithClosure
// use the function
simpleFunctionWithClosure { (success) -> Void in
if success {
print("Yeah")
} else {
print("Ow")
}
}
Or with a named function :
func argumentFunction(success:Bool) -> Void {
if success {
print("Yeah")
} else {
print("Ow")
}
}
simpleFunctionWithClosure(argumentFunction)
Compiler wouldn't have any expectation how your closure is for. For example in your first case, it could not estimate the closure's intake parameters is always just reflect to the first and second parameters of the TEST function when we are always able to write the following code :
func Test(str1:String,str2:String,closure:(String,String)->Bool){
if closure(str[str1.startIndex...str1.startIndex.advanced(2)],str2[str2.startIndex.advanced(1)...str2.endIndex])
{ ... }else{ ... }
//Just an example, everybody know no one write their code like this.
}
The second case, I thought you've just overlooked the syntax sugar:
For a trailing closure A->B:
{ a:A -> B in a.bValue() }
is equal to :
{ a:A -> B in return a.bValue() }
Also, I think this TEST function isn't a good example when the task of it can be done without using closure. I think you can write a map function by yourself for a better understand of why and when to use closure.

Resources