I'm getting a very weird error using RXSwift. I'm calling a service that is a Single trait and when I just print the error my code compile without problem, but if I try to do something else in "onError", the code doens't compile. Someone had the same problem?
The piece of code that doesn't compile:
NetworkManager.shared.authorizeService(with: parameters)
.subscribe(onSuccess: { [weak self] status in
}, onError: { [weak self] error in
paymentAuthorizationFinishedWithError.onNext(APIResponseError.paymentAlreadyInProgress)
}).disposed(by: bag)
The code that compiles:
NetworkManager.shared.authorizeService(with: parameters)
.subscribe(onSuccess: { [weak self] status in
}, onError: { [weak self] error in
print(error)
}).disposed(by: bag)
Swift complier sometimes is very laggy. Especially when it comes to Rx. The problem is with this line
self.paymentAuthorizationFinishedWithError.onNext(APIResponseError.paymentAlreadyInProgress)
you forgot to add ? since you're using weak reference:
self?.paymentAuthorizationFinishedWithError.onNext(APIResponseError.paymentAlreadyInProgress)
Related
Got a IAP purchased notification, then I request the transaction from my server.
To download a song and play , if transaction OK.
I use RxSwift, The following code works, I want to improve it.
NotificationCenter.default.rx.notification( .purchase )
.takeUntil(self.rx.deallocated)
.map { (noti) -> String in
return "Not care"
// I want to optimize this step
}.concat(self.transactionRequest())
.flatMap{ self.downloadSong($0) }.subscribe(onNext: { downloaded in
if downloaded{
self.playMusic()
}
})
.disposed(by: rx.disposeBag)
func transactionRequest() -> Observable<String> { // ... }
func downloadSong(_ src: String) -> Observable<Bool> { // ... }
I can not use like this
NotificationCenter.default.rx.notification( .purchase )
.takeUntil(self.rx.deallocated)
.concat(self.transactionRequest())
because
Instance method 'concat' requires the types 'Notification' and
'String' be equivalent
So I add a boilerplate map
Any more proper operator, or custom operator?
The return type of the Observable that is feeding concat and the one that is passed to concat must be the same. I suggest you use flatMap instead. Also, you are capturing self all over the place which means memory issues.
Here's how I would do it:
NotificationCenter.default.rx.notification(.purchase)
.flatMapLatest { [unowned self] _ in self.transactionRequest() }
.flatMapLatest { [unowned self] in self.downloadSong($0) }
.subscribe(onNext: { [unowned self] downloaded in
if downloaded {
self.playMusic()
}
})
.disposed(by: rx.disposeBag)
If you didn't put all your functions inside the class, you could get rid of the self. and not have to worry about capturing self.
I had a very slow bottom sheet, showing up blank then after a while loading data. I tried to apply a completionHandler isLoadedCompletionHandler, solution worked, but my colleague told me this is not a "completion handler". Could you explain me why this is working. And how? Is this a proper completion handler?
func buttonDetailTapped(with travelSolutionId: String) {
guard let currentPurchaseSolution = purchaseSolutions.value.first(where: { $0.xmlId == purchaselSolutionId }) else {return}
getAllPurchaseDetail(searchId: searchId.value, solutionId: purchaseSolutionId)
.subscribe(onNext: { [weak self] purchaseDetails in
let isLoadedCompletionHandler: ([PurchaseDetail]) -> Void = { theArray in
self?.result.onNext(.showPurhcaseSolutionDetails(purchaseDetails, currentTravelSolution))
}
isLoadedCompletionHandler(purchaseDetails)
})
.disposed(by: disposeBag)
}
A completion handler is a function that you pass into your function, which usually gets called upon the completion of some asynchronous task.
Your function of buttonDetailTapped contains no parameters that are functions (i.e. (Thing, Error) -> Void) which are called upon completion, and thus you cannot know from calling this function when it completes.
Your function may go ahead and do other things when it's done, but there is no completion handler.
isLoadedCompletionHandler is not a completion handler because it is called immediately after it is assigned.
A completion handler is a closure that you pass into a function, that will be called when whatever that function is doing asynchronously completes. You are not passing isLoadedCompletionHandler anywhere.
You could have just written
getAllPurchaseDetail(searchId: searchId.value, solutionId: purchaseSolutionId)
.subscribe(onNext: { [weak self] purchaseDetails in
self?.result.onNext(.showPurhcaseSolutionDetails(purchaseDetails, currentTravelSolution))
})
.disposed(by: disposeBag)
and achieved the same result.
In rx code, .disposed get called without any doing job like flatmap, subscribe. This happens only when I build my app at first time.
Does anybody knows what happens here?
This is my code
HTTP.getSomething()
.flatMap { (list) -> Single<Void> in
return HTTP.getList(withList: list)
}
.subscribe(onSuccess: { (storeList) in
log.debug("Finish!!!")
}, onError: { [weak self] (error) in
self?.presentAlert(error: error)
})
.disposed(by: self.disposeBag)
The only way the code you presented can possibly be disposed without attempting the work inside of the flatMap is if getSomething emits a completed without emitting a value, or if it emits an error, or if the disposeBag is deleted. One of those three things is happening.
Since you say it only happens on first build, I suspect that getSomething is trying to make a network call before it has all the info it needs which is causing it to emit an error.
I have following code in Swift 4 with RxSwift
worthReacting.flatMap{ (userSearch) in
translator.getTranslation(ofWord: userSearch)
}.subscribe(
onSuccess: {(dataModel) in
state.value = .translation(word: dataModel.definition,
translations: dataModel.translations)
},
onError: {(error) in
state.value = .networkError
},
onCompleted: {
state.value = .unknownWord
}).disposed(by: disposeBag)
worthReacting has type of Observable<String>
translator.getTranslation returns Maybe<DataModel>
I'm getting build error
Extra argument 'onError' in call
Maybe flatmaped into Observable produces Observable. Observable can not emit onSuccess event, instead it will emit onNext. Following code will work:
worthReacting.flatMap{ (userSearch) in
translator.getTranslation(ofWord: userSearch)
}.subscribe(
onNext: {(dataModel) in
self.state.value = .translation(word: dataModel.definition,
translations: dataModel.translations)
},
onError: {(error) in
self.state.value = .networkError
},
onCompleted: {
self.state.value = .unknownWord
}).disposed(by: disposeBag)
For those who get the OP's error, but have a different cause, check that you have not incidentally make one of your closures throwing by not making your do-catch clause exhaustive.
Also, take care that you don't use a single-statement closure (in this case you should explicitly return).
I'm trying to use Action / CocoaAction library.
The primary usage for now is to show an UIAlertController and when an UIAlertAction button is tapped it has to call a function defined in my viewModel (changeAddress that returns an Observable).
My understanding of this would be:
let ac = CocoaAction(workFactory: {[unowned self] _ in
self.viewModel!.requestChangeAddress()
.subscribeNext({ [unowned self] data in
if let response = data?.result
{
self.showResultOperation(response)
}
})
.addDisposableTo(self.disposeBag)
return .empty()
})
let OKAction = UIAlertAction.Action("OK", style: .Default)
OKAction.rx_action = ac
But unfortunately it doesn't work. The workFactory closure is correctly called but the subscription doesn't take effect. I know something is wrong when I return .empty but I cannot understand how to solve.
How can I correct this? What I'm doing wrong?
great question! Thanks for posting the code.
So your code is getting the observable from requestChangeAddress, but then it's subscribing to it and adding it to a dispose bag. The Action class is actually going to take care of that for you, you only need to return the disposable.
The problem is that you want to do something with the values sent on the action, and returning the observable won't let you do that. So you need to include a call to doOnNext in the observer chain. Here's what it might look like:
let ac = CocoaAction(workFactory: {[unowned self] _ in
return self.viewModel!
.requestChangeAddress()
.doOnNext({ [unowned self] data in
if let response = data?.result
{
self.showResultOperation(response)
}
})
.map { _ in Void() }
})
Using doOn functions in RxSwift is a great way to inject side-effects into your observables when necessary, like it is here.
EDIT: I added the map at the end to fix a compiler error, because the return type from the factory method is Observable<Void>.