RxSwift: Using BehaviorRelay I get this error: Instance method 'concatMap' requires that '[Int]' conform to 'ObservableConvertibleType' - rx-swift

I'm trying to use concatMap on BehaviorRelay but I'm getting this error:
Instance method 'concatMap' requires that '[Int]' conform to 'ObservableConvertibleType'
This is my implementation:
class MyClass{
var disposeBag = DisposeBag()
var subject: BehaviorRelay<[Int]> = BehaviorRelay(value: [1,2,3,4,5])
func doSomething() {
subject.asObservable().concatMap { $0 }
.subscribe { print($0) }
.disposed(by: disposeBag)
}
}
I'm getting the error on this line:
subject.asObservable().concatMap { $0 }
Any of you knows why I'm getting this error or how can fix this error on my implementation ?
I'll really appreciate your help.

The problem here is conceptual. It doesn't make any sense to use concatMap on an array of Ints. The error is basically telling you that an array of Ints is not an Observable. There aren't any Observables to concat here.
You need to go back and think about what you are trying to accomplish and find the right operator for the job.

Related

I need help Debugging/Fixing this code, by the way this is in Xcode, I am using the OpenAI Package by adamrushy

I am having an issue, I don't know if it related to the package but it probably is so here is my code. This is the package I used; https://github.com/adamrushy/OpenAISwift
// Send query to OpenAI
openAI.send(prompt: query) { [weak self] (response, error) in
guard let self = self else { return }
if let error = error {
print("Error: \(error.localizedDescription)")
self.startListening()
return
}
if let response = response {
// Use Text to Speech to speak response
let synthesizer = AVSpeechSynthesizer()
let utterance = AVSpeechUtterance(string: response.text)
synthesizer.speak(utterance)
}
By the way the below Bugs are on this line "openAI.send(prompt: query) { [weak self] (response, error) in"
Bug 1: Unable to infer type of closure parameter 'error' in the current context
Bug 2: Unable to infer type of a closure parameter 'response' in the current context
Bug 3: Value of type 'OpenAI' has no member 'send'
I tried removing the error thing itself, it gave more issues. I tried changing 'send' to other things, it just kept giving the same error. And I could not touch 'response' as it is important.

How Can I use the Bind Method in RXSwift?

The following sample code throws an error stating
No exact matches in call to instance method 'bind'
How can I bind the onNext of my publish subject to my observable?
let publish = PublishSubject<Void>()
// Error Here
publish.bind(to: myMethod())
func myMethod() -> Observable<Void> {
return Observable.create{ observer in
observer.onNext(())
observer.onCompleted()
return Disposables.create()
}
}
So what I want is everytime my publish subject emits an onNext event I want to trigger the observable of 'myMethod'
I'm not sure how to interpret this, but it sounds like you want something like:
let publish = PublishSubject<Void>()
let response = publish.flatMap { myMethod() }
response
.bind(onNext: { print($0) })
func myMethod() -> Observable<Void> {
Observable.create{ observer in
observer.onNext(())
observer.onCompleted()
return Disposables.create()
}
}
But it all seems rather pointless since all myMethod() does is emit a next event.
First, the bind(to:) method is in the RxCocoa Framework so you need to add import RxCocoa in your file.
Then, the observable created by myMethod will only be able to emit the event from the .create block. You can't use bind on it. If you need an observable with both the events from your subject and from the myMethod observable, you can do that :
let thirdObservable = Observable.merge(publish, myMethod())

SwiftUI - How to get access to "WindowScene"

In watching the WWDC 21 videos reference StoreKit 2, there are a few functions that they reference wherein they let a value = WindowScene as follows:
func manageSubscriptions() async {
if let windowScene = self.view.window?.windowScene {
do {
try await AppStore.showManageSubscriptions(in: windowScene)
} catch {
//error
}
}
}
The let line errors out with the message: Type of expression is ambiguous without more context
If I try and provide more context with something like:
if let windowScene = (self.view.window?.windowScene)! as UIWindowScene {
I am told: Value of type 'MyStruct' has no member 'view'
What am I missing, must be something simple, to gain access to this needed UI element?
Thank you
Added:
I'd like to add that I am using a SwiftUI app that was created using a SceneDelegate and AppDelegate, not a simple struct: App, type of structure. So I am guessing I need to access something in the SceneDelegate to get the right object..
Just to provide an answer for anyone interested, with all credit to #aheze for finding it and #Matteo Pacini for the solution, to get this specific method to work when using a SwiftUI app that has an AppDelegate/SceneDelegate structure, this will work:
#MainActor
func manageSubscriptions() async {
if let windowScene = UIApplication.shared.connectedScenes.first {
do {
try await AppStore.showManageSubscriptions(in: windowScene as! UIWindowScene)
} catch {
//error
}
}
}
You can conversely use the view modifier manageSubscriptionsSheet(isPresented:) instead. This is Apple's recommended approach when using SwiftUI and will mitigate the need for getting a reference to the window scene.
Source:
If you’re using SwiftUI, call the manageSubscriptionsSheet(isPresented:)view modifier.

IOS Rxswift use Kingfisher to prefetch cell Image

I'm trying to implement Kingfisher prefetch feature inside an Rxswift project. The problem is with these 2 function
collectionView.rx.prefetchItems
collectionView.rx.cancelPrefetchingForItems
The instruction at Kingfisher github is quite short
override func viewDidLoad() {
super.viewDidLoad()
collectionView?.prefetchDataSource = self
}
extension ViewController: UICollectionViewDataSourcePrefetching {
func collectionView(_ collectionView: UICollectionView, prefetchItemsAt indexPaths: [IndexPath]) {
let urls = indexPaths.flatMap { URL(string: $0.urlString) }
ImagePrefetcher(urls: urls).start()
}
}
How can we implement with Rxswift? anyway to get the models and then the urls of models from array of indexpath. Thank.
I will walk you thru how I figured out the solution to help you figure out future solutions...
I'm assuming that the struct that holds the URL strings is called Item and that you have an Observable<[Item]> that you are currently using to load up the collection view. I'm also assuming that you only have one section in your collection.
First, we know that something needs to happen when the prefetchItemsAt sends an event so we start with that:
let foo = collectionView.rx.prefetchItems
Now inspect the type of foo to see that it is a ControlEvent<[IndexPath]>. A ControlEvent is a kind of observable. We just need the items part of the IndexPaths, so lets map that:
let foo = collectionView.rx.prefetchItems
.map { $0.map { $0.item } }
(The double map is an unfortunate consequence of Swift not supporting higher kinded types) Now inspect the type of foo. It is an Observable array of ints. Those ints are indexes into our items array. So we need access to the most recently emitted items:
(as above)
.withLatestFrom(items) { indexes, elements in
indexes.map { elements[$0] }
}
So withLatestFrom is like combineLatest except it only fires when the primary observable emits a value, not when the secondary observable does.
Now, inspecting the type of foo will find that it's an Observable array of Items. The exact items who's urls' we want to send to the ImagePrefetcher. So we need to extract the urlStrings into URLs.
(as above)
.map { $0.compactMap { URL(string: $0.urlString) } }
And with that, we have the array of URLs we want ImagePrefetcher to consume. Since it consumes data, it needs to be wrapped it in a subscribe block.
(as above)
.subscribe(onNext: {
ImagePrefetcher(urls: $0).start()
})
At this point, foo is a disposable so it just needs to be collected in our dispose bag... Here is the entire block of code.
collectionView.rx.prefetchItems
.map { $0.map { $0.item } }
.withLatestFrom(items) { indexes, elements in
indexes.map { elements[$0] }
}
.map { $0.compactMap { URL(string: $0.urlString) } }
.subscribe(onNext: {
ImagePrefetcher(urls: $0).start()
})
.disposed(by: bag)
One last bit to make everything clean... Everything between prefetchItems and the subscribe can be moved into the ViewModel if you have one.
The key takeaway here is that you can use the types to guide you, and you need to know what operators are available to manipulate Observables which you can find at http://reactivex.io/documentation/operators.html

How to get a non-observable value from a function?

I have this function which doesn't work, because immediately returns without setting data
func fetchedData() -> String {
var data: String
networkRequest.suscribe(
onNext: {
data = "successful"
},
onError: {
data = "unsuccesful"
}).addDisposableTo(self.disposeBag)
return data
}
How can I make it work? Sorry I am fairly new to RxSwift
Hope this will helps you.
func fetchedData(isSuccess: #escaping ((Bool) -> Void)) {
networkRequest.suscribe(
onNext: {
isSuccess(true)
},
onError: {
isSuccess(false)
}).addDisposableTo(self.disposeBag)
}
fetchedData { (isSuccess) in
if isSuccess {
print("Successfull")
} else {
print("Unsuccessfull")
}
}
Well, actually what you are trying to achieve can be done with semaphores for example. But your approach is really bad, as I believe you misunderstood the concepts of RxSwift and reactive programming in general.
The first thing you need to understand is that everything in RxSwift is an observable sequence or something that operates on or subscribes to events emitted by an observable sequence.
Arrays, Strings or Dictionaries will be converted to observable sequences in RxSwift. You can create an observable sequence of any Object that conforms to the Sequence Protocol from the Swift Standard Library.
You should not use such function in your code func fetchedData() -> String. In your case you may want to use something like:
func fetchedData() -> Observable<String> {
return networkRequest.map { _ -> String in
return "Successfull"
}.catchErrorJustReturn("Unsuccessfull")
}
Then you have your code aligned with reactive principles. You may bind this sequence to some variables, transform it, share and so on.

Resources