How do I get from Observable<BleHandler.BlePeripheral> to BleHandler.BlePeripheral? - ios

I have a variable that gets me the type Observable<BleHandler.BlePeripheral> after using flatMap on the array.
let scannedPeripheral: Observable<BleHandler.BlePeripheral> = instance.bleScan()
.flatMap{ Observable.from($0)}
But now I need to use that variable in another function that takes BleHandler.BlePeripheral:
instance.bleEstablishConnection(scannedPeripheral: scannedPeripheral)
Obviously it doesn't work. Is there a way to get my Observable<BleHandler.BlePeripheral> to just BleHandler.BlePeripheral

It depends on whether or not the function returns a value and what type of value it returns...
If the function is void and you are just calling it for side effects then:
let disposable = scannedPeripheral
.subscribe(onNext: { instance.bleEstablishConnection(scannedPeripheral: $0) })
If your function has side effects and returns an Observable then:
let returnValue = scannedPeripheral
.flatMap { instance.bleEstablishConnection(scannedPeripheral: $0) }
If the function has no side effects and you are just calling it to transform your value into a different value then:
let returnValue = scannedPeripheral
.map { instance.bleEstablishConnection(scannedPeripheral: $0) }
This last one is unlikely based on the name of the function, but I put it here for completeness.

Related

RxSwift merge two api request into one result clears first result

I have a refreshTrigger and BehaviourRelay of items:
var refreshTrigger = PublishSubject<Void>()
var rootItems: BehaviorRelay<[Codable]> = BehaviorRelay(value: [])
Then, I use UITextField to run search query when user enters text:
let queryText = queryTextField.rx.text.orEmpty
.throttle(.milliseconds(300), scheduler: MainScheduler.instance)
.distinctUntilChanged()
Finally, I have an observable which combines this observable with additional trigger to refresh manually:
let refreshText = refreshTrigger.withLatestFrom(queryText)
Observable.merge(refreshText, queryText)
.flatMapLatest { [weak self] query -> Observable<[Codable]> in
guard let strongSelf = self else { return .empty() }
let ctgs = try strongSelf.getCategories()
.startWith([])
.catchErrorJustReturn([])
let itms = try strongSelf.getSearchResults(query)
.retry(3)
.startWith([])
.catchErrorJustReturn([])
return Observable.merge(ctgs, itms)
}
.bind(to: rootItems)
.disposed(by: disposeBag)
As you can see, I want to send 2 requests: fetch categories and items, because I'm displaying them in the same UITableView. It sends both requests at the same time, but first result disappear when the second comes in. But I use merge, so it should work.
Why it doesn't show combined results?
Headers of the getCategories and getSearchResults looks like this:
func getSearchResults(_ text: String) throws -> Observable<[Codable]>
func getCategories() throws -> Observable<[Codable]>
they both use alamofire's rx extension to run queries.
Both of your getters return Observable arrays. This means that when the call completes, the observable emits an array of items. When you merge the two Observables, the follow on code can't distinguish between the items from one getter and the items from the other getter. It just sees an array come in (from one of them,) then another array come in (from the other.) In other words, you misunderstood how merge works.
To achieve the result you want, you should use zip or possibly combineLatest instead of merge. Something like this:
Observable.zip(ctgs, itms) { $0 + $1 }

In RxCocoa/RxSwift, how to observe BehaviorRelay<[object]> array size changed

I'd like to subscribe to a BehaviorRelay<[object]>, and I'd like to execute some functions whenever we append or remove elements.
I've used the distinctUntilChange method
BehaviorRelay<[object]>.asObservable().distinctUntilChanged{ $0.count != $1.count}.subscribe{....}
But didn't work. What should I try? Should I try using other Subjects or Relays to achieve this purpose?
var objects = BehaviorRelay<[Object]>(value: [])
let disposeBag = DisposeBag()
objects.asObservable()
.subscribe(onNext: { (objects) in
//Do something only when appending or removing elements.
}).disposed(by: disposeBag)
//For example
let tempObj = objects.value
tempObj.append(newObj)
objects.accept(tempObj)//this will be called
tempObj.removeAll()
objects.accept(tempObj)//this will be called
tempObj.property = "Change Property"
objects.accept(tempObj)//this will NOT be called
From documentation:
parameter comparer: Equality comparer for computed key values.
I believe you should check the Equality with == operator. So, in your case, try this way:
BehaviorRelay<[object]>
.asObservable()
.distinctUntilChanged{ $0.count == $1.count}
.subscribe{....}

RxSwift - behavior of subscribe(observer:)

I'm confused about behavior of subscribe method in RxSwift.
This is sample code.
let observer1 = PublishSubject<String>()
let observer2 = PublishSubject<String?>()
let observable1 = Observable.just("")
let observable2 = observable1.map { $0 }
_ = observable1.subscribe(observer1) // #1. OK
_ = observable1.subscribe(observer2) // #2. Error
_ = observable2.subscribe(observer2) // #3. Error
_ = observable1.map{ $0 }.subscribe(observer2) // #4. OK
I understand that #2 and #3 get an error.
Because the observer is a nullable-string type, it is strictly different from the type that the observable holds.
But I can not understand #4.
If directly subscribe of the mapped observable, it did not get an error.
As shown in #3, the return value of the mapped observable1 was Observable.
I look forward to reply.
This is because .map { $0 } actually returns a Observable<String?> in the fourth case!
Ww can cast the return value to a Observable<String>:
_ = (observable1.map{ $0 } as Observable<String>).subscribe(observer2)
It stops working! This implies that the value returned by map without the cast must be different. And there's only one possibility - Observable<String?>. $0 can't be implicitly convertible to other types.
The compiler sees that you are calling subscribe(Observable<String?>) and infers the return type of map to be Observable<String?> because only then can subscribe be called successfully.
But if you don't give the compiler enough context to figure out the type that map should return, like you did in the let observable2 = ... line, then the compiler thinks you want a Observable<String>.
Basically, the compiler is being smart.

confused with the functionality of `return` in swift

I am confused with return in Swift. I understand it's used to return the value in a function, if used like this:
func double(value: int) -> Int {
return value * 2
}
But I often just see return being used, like in a guard statement in an optional binding like this:
guard let value = value else (
print ("nothing")
return
}
So what is the purpose of having just return in the guard statement like this? Actually, I often see this not only in guard statements when unwrapping optional values. I always find this problem when writing code, when I want to use an optional string from a dictionary.
let info = ["name": "sarah", "hometown": "sydney"]
class UserInfo {
func getTheName() -> String {
guard let name = info["name"] else { return }
return name
}
}
// Compile time error: "Non-void function should return a value"
I get this error even though I have written return name. Xcode still complains that I have not returned a value. Is it because of the return in the guard statement?
So, could you please tell me the purpose of return in Swift? It is confusing for me.
return without any argument returns Void. This form of the return statement can only be used with a function that returns Void.
Once the return statement executes, the function exits and no more code in your function executes. Since you have a return in the guard statement, the second return name won't be executed (it couldn't anyway since it wouldn't have a name to return), which is why you get a compiler error; the compiler looks at all of the paths that your function could take to return something and ensures that all of those paths return what the function signature says it will.
The function in your question states that it returns a String, so you can't simply say return in the guard statement as that returns Void, violating the contract expressed by your function signature.
You could return a default value that isn't Void:
func getTheName () -> String {
guard let name = info["name"] else {
return ""
}
return name
}
This could be written much more succinctly using the nil-coalescing operator; return info["name"] ?? ""
You can also use return in a function that returns Void (or has no explicit return type, in which case it is implicitly understood to return Void)
So you could have a function like:
func maybePrint(theMessage: String?) -> Void {
guard let msg = theMessage else {
return
}
print(msg)
}
You're on the right track.
In your guard statement inside getTheName(), the 'return' keyword will try to exit the function itself if the guard fails. But the function requires you to return a String and as such you get the compiler error.
Here is a portion of another SO answer to a similar question:
guard forces you to exit the scope using a control transfer statement.
There are 4 available to you:
return and throw both exit the function/method continue can be used
within loops (while/for/repeat-while) break can be used in loops
(while/for/repeat-while) to exit the immediate scope. Specifying a
label to break to will allow you to exit multiple scopes at once (e.g.
breaking out of nested loop structure). When using a label, break can
also be used in if scopes. Additionally, you may exit the scope by
calling a function that returns Never, such as fatalError.
Stack Overflow: If the Swift 'guard' statement must exit scope, what is the definition of scope?

why I have to unwrap value before I use

A block is defined like below
// Declare block ( optional )
typealias sorting = (([Schedule], [String]) -> [Schedule])?
var sortSchedule: sorting = { (schedules, sortDescription) in
var array = [Schedule]()
for string in sortDescription
{
for (index, schedule) in schedules.enumerate()
{
if string == schedule.startTime
{
array.append(schedule)
break
}
}
}
return array
}
At some points, I am invoking a block by doing
let allSchedules = sortSchedule?(result, sortDescription())
for schedule in allSchedules // Xcode complains at here
{
..........
}
Im using ? because I want to make sure that if the block exists, then do something. However, Xcode complains for the for loop
value of optional type [Schedule]? not upwrapped, did you mean to use '!' or '?'?
Im not sure why because the return type of a block is an array which can have 0 or more than one items.
Does anyone know why xcode is complaining.
You are use ? in line let allSchedules = sortSchedule?(result, sortDescription()) not "for sure that if the block exists", but just for note, that you understand that it can be nil. Behind scene allSchedules have type Array<Schedule>?. And you can not use for in cycle for nil. You better use optional binding:
if let allSchedules = sortSchedule?(result, sortDescription())
{
for schedule in allSchedules
{
//..........
}
}

Resources