I need to return Mono / Flux for a function but this has 2 nested subscriptions. I am looking for a better solution to publish Mono/Flux only after this 2 subscription values are available then perform some operation to derieve finalValue.
The final Objective is, The subscribers of the function getFinalValue() should be able to subscribe to final value. I have a similar need for Flux also. What should be the best approach to do this?
fun <T> getFinalValue(): Mono<T> {
object1.getValue1().subscribe { value1 ->
object2.getValue2(value1.id).subscribe{ value2 ->
// perform some operation with value 1 and 2
// derieve finalValue
}
}
return //I need to return Mono<T> which should publish finalValue to the subscribers of this function.
}
You can use .cache() to store value1 and move forward with Mono.zip.
Then in zip flatMap you have tuple with value1 and value2
fun <T> getFinalValue(): Mono<T> {
val value1 = object1.getValue1().cache();
val value2 = object1.getValue1().flatMap(value -> object2.getValue2(value));
return Mono.zip(value1, value2)
.flatMap(tuple -> {
// logic with tuple.T1 and tuple.T2
})
}
Did you want to do like it?
fun <T> getFinalValue(): Mono<T> {
return object1.getValue1()
.flatMap { value1 ->
object2.getValue2(value1.id)
.map { value2 ->
// perform some operation with value 1 and 2
// derieve finalValue
}
}
}
Related
I want to write a general-purpose Swift function that serves the following simple purpose:
Take any function as argument
Take a Bool argument
If the bool argument is TRUE, invoke the input function with its args. Otherwise No-op.
The purpose is to eliminate a lot of clumsy if statements in the code that meet a specific criteria.
Something like:
typealias ClosureType = (Any...) -> Any. // Notice the variable argument of any kind
func invokeIfConditionIsTrue(closure: Closure, condition: Bool) {
if condition {
if let myFunc = closure as? ClosureType {
myFunc()
print("executed")
} else {
print("not executed")
}
}
}
func testIntToInt(i: Int) -> Int {
return i*i
}
func testIntToDouble(i: Int) -> Double {
return Double(i*i)
}
invokeIfConditionIsTrue(testIntToInt, true). // executed
invokeIfConditionIsTrue(testIntToDouble, false). // not executed
However, I am struggling to come up with syntax that will enable the argument passing to the input myFunc() func.
The example is pretty basic, and my input function closure could be accepting and emitting any type of input/outputs, including structs, classes and objective c stuff.
I have a hunch this is possible via a mechanism called function object, but I am not familiar enough with it.
Should I reinvent the wheel, or is there already a library/known way which is doing it successfully, and I am missing out?
I have no idea why you think
invokeIfConditionIsTrue(testIntToInt, condition)
is somehow superior to
if condition { result = testIntToInt(n) }
Or
result = condition ? testIntToInt(n) : 0
but what you want is pretty much impossible unless you wrap the function in a closure because there is no way to express "function with any arguments" in Swift as a type. The best you can do is wrap your function in a closure with known argument types. There's also no general Closure type that represents any closure.
func invokeIfConditionIsTrue(closure: () -> (), condition: Bool) {
if condition {
closure()
print("executed")
}
}
invokeIfConditionIsTrue(closure: { result = testIntToInt(n) }, condition: true)
But, as you can see, that's not really any better than an if statement. In fact, it's much worse.
Another possibility is to define a function that returns a function, but it still needs to know the argument types.
func invokeIfConditionIsTrue(closure: (Int) -> Int, condition: Bool) -> (Int) -> Int?
{
if condition {
return closure
}
else
{
return { _ in 0 } // A dummy function
}
}
invokeConditionIfTrue(closure: testIntToInt, condition: true)(n)
After some haggling and searching for syntax (I wasn't good in FP, as I mentioned in the beginning), I was able to compile and run the below solution in my XCode 14.2 playground.
My desired function:
func executeIfCondition(function: (#escaping (Any...) -> Any), condition: Bool) {
if condition {
function()
}
}
This was supposed to replace following types of calls across my codebase:
if condition {
function()
}
Test functions whom I want to invoke, using executeIfCondition if condition = true.
func printStringAndReturnInt(i: Int, s: String) -> Int {
print(s)
return i
}
func printInt(i: Int) -> Void {
print("\(i)")
}
func printArray(arr: [Int]) -> Void {
arr.forEach({ print("\($0)") })
}
struct Struct1 {
var value1: Int
var value2: Int
}
func printStruct(t: Struct1) {
print("Struct1: \(t.value1) - \(t.value2)")
}
class Class1 {
var value1: Double = 100.0
var value2: Double = 200.0
}
func printClass(c: Class1) {
print("Class1: \(c.value1) - \(c.value2)")
}
Example Usage:
Instead of:
if (true) {
printStringAndReturnInt(i: 5, s: "Wow!")
}
I will now use:
executeIfCondition(function: { _ in printStringAndReturnInt(i: 5, s: "Wow!") }, condition: true)
The rest:
executeIfCondition(function: { _ in printInt(i: 61) }, condition: false)
executeIfCondition(function: { _ in printArray(arr:[9,10,11,12]) }, condition: true)
executeIfCondition(function: { _ in printStruct(t: Struct1(value1: 100, value2: 200)) }, condition: true)
executeIfCondition(function: { _ in printClass(c: Class1()) }, condition: true)
This isn't exhaustive still, but enough for a start.
My program services offer some delete methods that return Mono<Void>, e.g.: fun delete(clientId: String) : Mono<Void>
After calling .delete("x") I would like to propagate the clientId downstream to do other operations:
userService.get(id).map{ user ->
userService.delete(user.id) //This returns Mono<Void>
.map {
user.id //Never called!!!
}
.map { userId ->
//other calls using the propagated userId
}
}
The problem is since delete returns a Mono<Void>, the following .map {
user.id } is never called. So how can I transform the Mono<Void> into a Mono<String> to propagate the userId?
You can use thenReturn operator:
userService.get(id)
.flatMap { user -> userService.delete(user.id).thenReturn(user.id) }
.flatMap { id -> //other calls using the propagated userId }
I managed to work around it using hasNext that transforms it into a Boolean:
#Test
fun `should`() {
val mono: Mono<String> = "1".toMono()
.flatMap { id ->
val map = Mono.empty<Void>()
.hasElement()
.map {
id + "a"
}.map {
(it + "1")
}
map
}
mono.doOnNext {
println(mono)
}.subscribe()
}
I have come across the array forEach function that is a higher order function and it takes only one parameter, i.e., a closure. Now this closure internally loops through all the elements of the array one by one but does not return anything. The implementation of the closure is left to the choice of the user.
I have a custom class MyClass that has a private variable inside it num and a public function setNum(num: Int) to set the value of that private variable from outside. I am just trying to create a similar function factorial inside my custom class that takes only one parameter, i.e., a closure. However, I have to manually call the closure inside factorial, pass the value of num as a parameter to the closure.
Is there a way that the closure can act on num without having passed it as a parameter? Basically I am just trying to replicate the array forEach function. Syntax of array forEach is:
array.forEach(body: (Int) -> Void) -> Void)
Implementation:
arr1.forEach { print($0) }
My code is as below:
import Foundation
public class MyClass {
private var factorialNumber: Double = 0
internal static var instance: MyClass?
public func setFactorialNumber(number value: Double) {
factorialNumber = value
}
public func factorial(body closure: (String?) -> Void) -> Void {
var outputString: String?
var result: Double = 1
if factorialNumber <= 0 {
outputString = nil
} else {
outputString = ""
while(factorialNumber >= 1) {
if factorialNumber == 1 {
outputString = outputString! + "\(factorialNumber) = \(result)"
break
} else {
outputString = outputString! + "\(factorialNumber) x "
}
result = result * factorialNumber
factorialNumber -= 1
}
}
// Finally closure call
closure(outputString)
}
private init() {}
public static func getInstance() -> MyClass {
if self.instance == nil {
self.instance = MyClass()
}
return self.instance!
}
}
And here is how I have to call my function to calculate the factorial:
var obj1 = MyClass.getInstance()
obj1.setFactorialNumber(number: 5)
obj1.factorial{ (result) in
print(result ?? "Factorial Result is Nil")
}
Please note that I have to pass a parameter result inside my closure to get the result of factorial.
Is there a way that the closure can act on num without having passed it as a parameter? Basically I am just trying to replicate the array forEach function ... [And, in your comment:] All I am trying to do is learn how to create higher order functions like array.forEach.
It's hard to understand what you think you're after, but taking you at your word, let's write forEach. Here we go:
extension Sequence {
func myForEach(f: (Element) -> ()) {
for e in self {
f(e)
}
}
}
Let's test it:
[1,2,3].myForEach { print($0) } // prints 1, then 2, then 3
We've done it! We wrote a higher-order function that acts exactly like forEach. So this must be how forEach actually works, more or less.
You can see from the example that it makes no sense to ask not to have to pass a parameter into the function that our forEach takes as a parameter. That is exactly what we must be able to do in order for that function to have an element to operate on.
It seems that rx-swift has not yet implemented DefaultIfEmpty. Is there another way I can mimic this behavior?
let myList:[Int] = []
myList.toObservable()
.switchIfEmpty { () in // <- Not (yet?) implemented
return Observable.of(1)
}.subscribeNext { num in
print(num)
}
// prints 1
I had similar problem but needed only 1 value from observable so simple solution is concatenate and than take 1:
yourSingleObservable.concat(Observable.just(defaultValue)).take(1)
I have used toArray to mimic this behavior. An example with PublishSubject (can be easily converted to use myList, I hope):
let subjectInt = PublishSubject<Int>()
let emptyReplacement = subjectInt
.toArray()
.filter {$0.isEmpty}
.map{ _ in return 42}
let defaultIfEmpty = [subjectInt, emptyReplacement].toObservable().merge()
defaultIfEmpty.subscribeNext {
print("defaultIfEmpty: \($0)")
}
subjectInt.on(.Next(11)) //
subjectInt.on(.Next(33)) //comment these 2 lines out to see the default value 42 printed
subjectInt.on(.Completed)
To refine what Michal said, toArray() offers a solution:
extension Observable {
func defaultIfEmpty(_ other: Observable<E>) -> Observable<E> {
let replacement = toArray()
.flatMap { array -> Observable<E> in
if array.isEmpty {
return other
} else {
return Observable.empty()
}
}
return concat(replacement)
}
}
Usage:
let a = Observable.of(1, 2, 3)
let b = Observable.of(-1, -2, -3)
let c = Observable<Int>.of()
_ = a.defaultIfEmpty(b).subscribe(onNext: { print($0) })
// 1
// 2
// 3
_ = c.defaultIfEmpty(b).subscribe(onNext: { print($0) })
// -1
// -2
// -3
This is what I'd like to do, but quantity property isn't available.
struct MyStruct {
var quantity: Int
let valueFn: () -> (Int)
}
var s1 = MyStruct(quantity: 2) { () -> (Int) in
return quantity * 2 // error: can't use `quantity`
}
It doesn't work with classes either:
class MyClass {
var quantity: Int
let valueFn: () -> (Int)
init(valueFn: () -> (Int)) {
self.valueFn = valueFn
}
}
var c1 = MyClass { () -> (Int) in
return quantity // error: can't use quantity or self
}
This one works, but I have to explicitly pass in the struct or struct property. My question is this: Can I do this without having a third property to wrap and pass in the reference? Is there a more Swifty approach?
struct MyOtherStruct {
var quantity: Int
private let valueFn: (Int) -> (Int)
var value: Int {
return valueFn(quantity)
}
}
var s3 = MyOtherStruct(quantity: 2) { (quantity) -> (Int) in
return quantity * 2
}
s3.value // -> 4
I've fibbled with this the last 20 minutes now, and I don't believe this is possible to achieve in the initialization of of your MyStruct instances; since at the call to the initializer (which you do with the trailing closure above), the instance does not yet exist. I will show below, however, a workaround for classes; one much like your third approach above, and one with more versatile usage (but horribly worse class implementation), making use of subclassing NSObject.
Anyway, possibly the above answers your question (as "no" for structs, and "possible, but overly complicated" for classes) with regard of use directly with the initializer in a versatile manner.
Below follows
A closure "wrapping" method much your third solution: wrapping a lazy closure over the closure you send at initialization. The drawback with this method is that you can't choose, for different class instances, which class properties you would like to use in the closure, as the wrapper (calling the mutable initialized closure) is already set at compile time.
Subclassing NSObject: a method that is probably overly complicated for any practical use (and also quite non-swifty), that however give you more versatile control over which class properties to use in the closure. By subclassing NSObject you get access to the method .valueForKey to apply to self. Added for the technical discussion/curiosity.
Method 1
class MyClass {
var quantity: Int
private let closure: (Int) -> (Int)
init(quantity: Int, closure: (Int) -> (Int)) {
self.quantity = quantity
self.closure = closure
}
lazy var valueFn : () -> Int = {
[unowned self] () -> Int in
return self.closure(self.quantity)
}
}
/* Example usage */
var c1 = MyClass(quantity: 2) { (a) -> (Int) in
return a * 2
}
c1.valueFn() // 4
c1.quantity = 4
c1.valueFn() // 8
Method 2
Class setup (...):
class MyClass : NSObject {
var quantity: Int = 0
var anotherQuantity: Int = 0
private var keyExists : Bool = true
private let key : String
private let foo: (Int) -> Int
init(operateClosureOn: (propertyWithKey: String, withInitialValue: Int),
closure: (Int) -> Int) {
key = operateClosureOn.propertyWithKey
foo = closure
super.init()
let val = operateClosureOn.withInitialValue
if let _ = (Mirror(reflecting: self).children.filter{ $0.label == key }).first {
self.setValue(val, forKey: key)
}
else { keyExists = false }
}
lazy var valueFn: () -> Int = {
[unowned self] () -> Int in
if !self.keyExists {
return 0
}
guard let a = self.valueForKey(self.key) as? Int else {
print("Unexpected: property for key '\(self.key)' is not if type 'Int'.")
return 0
}
return self.foo(a)
}
}
Example usage:
/* Example usage */
var c2 = MyClass(operateClosureOn: ("quantity", 2)) {
(val) -> Int in
return 2 * val
}
c2.valueFn() // 4
c2.quantity = 4
c2.valueFn() // 8
var c3 = MyClass(operateClosureOn: ("anotherQuantity", 20)) {
(val) -> Int in
return val / 2
}
c3.valueFn() // 10
c3.anotherQuantity = 40
c3.valueFn() // 20
var c4 = MyClass(operateClosureOn: ("aTypo", 20)) {
(val) -> Int in
return val / 2
}
c4.valueFn() // 0, OK, at least no runtime exception with this non-safe non-swifty solution :0)