I'm trying to find a way to handle asynchronous network requests in Swift. My plan is to isolate these calls in their own class, so I need a way to handle callbacks. In Android, I'm doing something like the following using an Interface:
apiService.fetch(data, new Callback() {
#Override
public void onResponse(Response r) {
// success
}
#Override
public void onFailure(Error e) {
// error
}
});
Can someone point me in the direction on how I can handle this similarly in Swift 3? Thanks!
It's possible to have multiple completion handlers. Here is a short example:
func request(url:String, success:#escaping(Any) -> Void, failure:#escaping(Any?) -> Void)
{
let task = URLSession.shared.dataTask(with: URL.init(string: url)!) { (data, response, error) in
if let responseError = error
{
failure(responseError)
}
else if let responseData = data //Make additional checks if there is an error
{
success(responseData) //Call when you are sure that there is no error
}
else
{
failure(nil)
}
}
task.resume()
}
Example of usage:
self.request(url: "https://jsonplaceholder.typicode.com/posts", success: { (data) in
//Do if success
}) { (error) in
//Do if error
}
Related
I'm very new to Java, android and Rxjava. I recently noticed that in an existing project (not written by me) a chat notification that is supposed to be received isn't received. Thus I started to do some tracing. Below is part of the code.
Note: Notifications that do get received seems to always go to onSuccess in the file FCMServices
I've put breakpoints pretty much everywhere in the code below. What I noticed was for the notifications that I do not receive onSuccess and onError do not get called but onComplete does. However I find that strange as I thought either onSuccess or onError must be called before onComplete.
My understanding of those functions is based on http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/MaybeObserver.html
//FCMService.java
currentConversationRepo.getCurrentConversation()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new MaybeObserver<CurrentConversation>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
currentChatDisposable = d;
}
#Override
public void onSuccess(#NonNull CurrentConversation currentConversation) {
System.out.println("This is SUCCESS");
if (channelSid == null && author == null && usedAdId == null){
buildNotifyNotification(body, action, "", userId);
}
if (channelSid != null && author != null) {
if (!channelSid.equals(currentConversation.getChannelSid())) {
createChatNotification(author, channelSid, body);
}
}
currentChatDisposable.dispose();
}
#Override
public void onError(#NonNull Throwable e) {
System.out.println("Error getting current conversation: " + e.getMessage());
currentChatDisposable.dispose();
}
#Override
public void onComplete() {
System.out.println("This is onComplete");
currentChatDisposable.dispose();
}
});
I then started to do some tracing of where onComplete was called and appears that it was called by another onSuccess from the class TestObserver in reactivex.io
http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/observers/TestObserver.html
//TestObserver.java
#Override
public void onSuccess(T value) {
onNext(value);
onComplete();
}
Which was in turn called by the onSuccess in MaybeFlatMapBiSelector class. (Also a reactivex.io class I believe)
//MaybeFlatMapBiSSelector.java
#Override
public void onSuccess(U value) {
T t = this.value;
this.value = null;
R r;
try {
r = ObjectHelper.requireNonNull(resultSelector.apply(t, value), "The resultSelector returned a null value");
} catch (Throwable ex) {
Exceptions.throwIfFatal(ex);
actual.onError(ex);
return;
}
actual.onSuccess(r);
}
This turned out to be from the MaybeObserver interface
http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/MaybeObserver.html#onComplete--
My question is what exactly are the onSuccess of TestObserver and MaybeFlatMapBiSelector doing? And if it is even possible based on the information I have provided, why is it that some notifications goes to onComplete without going to onSuccess or onError in FCMServices.java
Have you tried to comment currentChatDisposable.dispose(); ? I've had the same issue not long ago where I was disposing of my disposable too early and no data where showing
Usually you call .dispose() when onPause() or onDestroy() of the lifecycle
PS: In case you didn't know Maybe in RxJava return either a single value, nothing at all or an exception.
Is there any way to implement an interface in dart/flutter without having to use a class?
Currently, how I implement it is with the code below
class _UserSignupInterface extends _SignupSelectUsernamePageState
implements UserSignupInterface {
#override
void onSuccess() {
_navigateToUserPage();
}
#override
void onError() {
setState(() {
_isSignupClickable = true;
});
}
}
_attemptSignup() {
UserSingleton userSingletonInstance = UserSingleton().getInstance();
UserSignupInterface _userSignupInterface = _UserSignupInterface();
UserSingleton().getInstance().user.username = _username;
UserLoginController.attemptSignup(_userSignupInterface,
userSingletonInstance.user, userSingletonInstance.userDetail, _groupID);
}
However, I would like to implement these interface methods without having to use a class, just as I would in java. Something that would look like the code below.
UserController.attemptSignup(context, new UserSignupRequest() {
#Override
public void onSuccess(User user, UserDetail userDetail, Group group) {
btnContinueWithFacebook.setEnabled(true);
Intent intent = new Intent(context, ScoopActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
progressBar.setVisibility(View.GONE);
startActivity(intent);
}
#Override
public void onFail() {
Log.d(APP.TAG, "Signup request has failed");
btnContinueWithFacebook.setEnabled(true);
progressBar.setVisibility(View.GONE);
/**
* TODO:: Notify user of signup attempt failure
*/
}
}, user, userDetail, group_id);
There is no such feature in Dart. In order to implement an interface, you have to declare a class.
The alternatives is to define the API to accept individual functions instead of a single object, or to declare a helper class which takes the behavior of the necessary methods as constructor arguments.
Example:
class _UserSignupInterface extends _SignupSelectUsernamePageState
implements UserSignupInterface {
void Function(_UserSingupInterface self) _onSuccess;
void Function(_UserSingupInterface self) _onError;
_UserSignupInterface(this._onSuccess, this._onError);
#override
void onSuccess() {
_onSuccess(this);
}
#override
void onError() {
_onError(this);
}
}
Then you can call it as:
... _UserSignupInterface((self) {
self._navigateToUserPage();
}, (self) {
self.setState(() {
self._isSignupClickable = true;
});
})
It's not as pretty as Java, admittedly.
I know this question already has an answer but I would like to add a more neater implementation close to Java inline interface which I normally use.
First, we have the class which acts as our interface:
class HttpRequestCallback {
/// Called when http request is completed
final void Function() onCompleted;
/// Called when http request is successful
/// * [message] is a dynamic object returned by the http server response
final void Function(dynamic message) onSuccess;
/// Called when http request fail
/// * [message] is a dynamic object returned by the http server response
final void Function(dynamic message) onError;
HttpRequestCallback(
{required this.onCompleted,
required this.onSuccess,
required this.onError});
}
Secondly, we have a function that expects the interface as parameter:
Future<void> login(LoginModel model, {HttpRequestCallback? callback}) async {
var response = await httpClient.doPost(app_constants.ApiEndpoints.Login,
body: model.toJson());
// Api request completed
callback?.onCompleted();
if (response.success) {
// Api request successful
callback?.onSuccess(LoginResponseModel.fromJson(
response.message as Map<String, dynamic>));
} else {
// Api request failed
callback?.onError(response.message);
}
}
Finally, we call the function passing our interface as an argument:
...
apiService.login(loginModel,
callback: HttpRequestCallback(
onCompleted: () {
//...
},
onSuccess: (message) {
//...
},
onError: (message) {
//...
}
));
...
I think you are looking for anonymous class in Dart, but it's not supported.
If i understood well what you are trying to do, you can achieve something similar by passing function as parameter in this way:
enum ResultLogin { OK, ERROR }
class Login {
Function _listener; // generic function
Login(listener) {
_listener = listener;
}
void run(){
ResultLogin result = *DO_YOUR_LOGIN_FUNCTION*;
_listener(result);
}
}
class Main {
void doLogin(){
Login myLogin = new Login((ResultLogin result){
switch(result){
case OK:
print("OK");
break;
case ERROR:
print("ERROR");
break;
default:
break;
}
}
);
}
}
In this way you can handle your result and refresh some widget state according to your needs.
I am looking at a code that I have to work on. And basically I have to add a validation to a listener of a button.
The code has already multiple validations. They are kind of set in a cascade.
The listener of the buttons calls an asyncCallBack method that if everything is ok, on the onsuccess part of the method calls for the next one, an that one on the next one, until it reaches the end and goes to the next page. I am not a fan of this approach because it is kind of messy. What would the best way to do that using best practices.
An example of the code:
Button btnOK = new Button("Aceptar");
btnOK.addListener(Events.Select, new Listener<ButtonEvent>() {
public void handleEvent(ButtonEvent e) {
myService.getInfo1(1, txt, "N",
new AsyncCallback<List<InfoService>>() {
public void onFailure(Throwable caught) {
// goes back
return
}
public void onSuccess(
List<Object> result) {
// do some validation with the result
validation2();
}
}
}
}
public void validation2(){
myService.getDireccionCanalesElectronicos(id, new AsyncCallback<MyResult>() {
public void onSuccess(MyResult result) {
// do some validation with the result
validation3();
}
...
}
}
public void validation3(){
myService.getDireccionCanalesElectronicos(id, new AsyncCallback<MyResult>() {
public void onSuccess(MyResult result) {
// do some validation with the result
validation4();
}
...
}
}
Is there a better way of doing this, it seems messy and hard to follow. Adding another validation is complicated. It doesnt seem like a good practice.
Create 1 method in the servlet that calls all the validation methods and do just one call in the client ?
public void validation()
{
boolean ok = validation1();
if (ok) ok = validation2();
return validation;
}
Using mirco services is sometimes hard to deal with. As #Knarf mentioned, this is a way to go. But sometime you may want to handle the calls on the client side. Another one will be using this tiny framework: sema4g. It will help you to solve your problem.
A solution might look like that:
First create the sem4g commands:
private SeMa4gCommand createGetInfoCommand() {
return new AsyncCommand() {
// create callback
MethodCallbackProxy<List<InfoService>> proxy = new MethodCallbackProxy<List<InfoService>>(this) {
#Override
protected void onProxyFailure(Method method,
Throwable caught) {
// Enter here the code, that will
// be executed in case of failure
}
#Override
protected void onProxySuccess(Method method,
List<InfoService> response) {
// Enter here the code, that will
// be executed in case of success
}
};
#Override
public void execute() {
// That's the place for the server call ...
myService.getInfo1(1, txt, "N", proxy);
}
};
}
do that for all your calls;
private SeMa4gCommand createCommandGetDireccionCanalesElectronicos() {
return new AsyncCommand() {
// create callback
MethodCallbackProxy<MyResult> proxy = new MethodCallbackProxy<MyResult>(this) {
#Override
protected void onProxyFailure(Method method,
Throwable caught) {
// Enter here the code, that will
// be executed in case of failure
}
#Override
protected void onProxySuccess(Method method,
List<MyResult> response) {
// Enter here the code, that will
// be executed in case of success
}
};
#Override
public void execute() {
// That's the place for the server call ...
myService. getDireccionCanalesElectronicos(id, proxy);
}
};
}
Once you have done this for all your calls, create a sema4g context and run it:
try {
SeMa4g.builder()
.addInitCommand(new InitCommand() {
#Override
public void onStart() {
// Enter here your code, that
// should be executed when
// the context is started
})
.addFinalCommand(new FinalCommand() {
#Override
public void onSuccess() {
// Enter here the code, that will
// be executed in case the context
// ended without error
}
#Override
public void onFailure() {
// Enter here the code, that will
// be executed in case the context
// ended with an error
})
.add(createGetInfoCommand())
.add(createCommandGetDireccionCanalesElectronicos())
.build()
.run();
} catch (SeMa4gException e) {
// Ups, something wrong with the context ...
}
For more informations, read the documentation. If you have questions, feel free to ask: SeMa4g Gitter room.
Hope that helps.
I'm so frustrated on searching what is the equivalent of this java code on swift:
public abstract class BaseApiSubscriber<T> extends Subscriber<T> {
private WeakReference<MvpView> mvpViewWeakReference;
private WeakReference<BasePresenter> basePresenterWeakReference;
public BaseApiSubscriber(BasePresenter basePresenter, MvpView mvpView) {
this.basePresenterWeakReference = new WeakReference<>(basePresenter);
this.mvpViewWeakReference = new WeakReference<>(mvpView);
}
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
//handle generic errors here, call also the mvpView to handle generic responses on UI
}
#Override
public void onNext(T t) {
}
Basically here, I'm extending Subscriber so all of the generic response of API is handled on a single file. This works on my java (android), but I can't find how to make this work on swift. I tried searching about extensions, protocols but it seems they can't be extended. I did try to search, but I don't know the keyword for this, I try to code blindly (hoping it will work) but I can't. Maybe just a keyword, basic sample, or an explanation on this. Am I doing it right, right? Ready also for the downvotes because I just can't post a good code on swift. It is not even close to this.
Update:
Somehow I got close to it, thanks to #luk2302, but then how can I implement this? Here's my code:
class BaseSubscriber: ObserverType {
typealias E = Response
func on(_ event: Event<Response>) {
switch event {
case .next(let _):
print("Successing")
break
case .error(let error):
print("Erorring")
if let serviceError = error as? ServiceError {
print("service error: " + serviceError.errorDescription!)
}
print(error)
break
case .completed:
print("Completing")
break
}
}
}
Then I need to call this from here:
let base = BaseSubscriber()
repo.login(param: loginParam).subscribe(
//Ive tried this:
//base.on: {
//}
//but got an syntax error maybe hahahaha
)
What do you call this? So I can search and read about it. Thank you.
Update 2:
Thanks to #Cristik, I've managed to do it, and by passing a closures, I can now pass methods to do specific task per request. My updated code:
func baseSubscriber<T>(mvpView: BaseMvpView, onNext: #escaping (T) -> Void, onError: #escaping (Error) -> Void, onCompleted: #escaping () -> Void) -> (RxSwift.Event<T>) -> Void {
return { [weak mvpView] event in
switch event {
case let .next(element):
mvpView?.hideLoading()
print("super next")
onNext(element)
case .completed:
mvpView?.hideLoading()
print("super completed")
onCompleted()
case let .error(error):
mvpView?.hideLoading()
print("super error")
if let serviceError = error as? ServiceError {
print("Service error: \(serviceError.errorDescription ?? "Something wrong")")
} else {
onError(error)
}
}
}
}
But this is different to my approach on java in which I can override the onError() method so in case I want to disregard the generic error handling, I can do it. How can I apply it to swift?
Update 3:
BaseMvpView.swift
protocol BaseMvpView: class {
func showLoading(message: String)
func hideLoading()
}
BaseTableViewController.swift
class BaseTableViewController: UITableViewController, BaseMvpView {
var indicator: UIActivityIndicatorView?
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewDidLoad() {
super.viewDidLoad()
indicator = UIActivityIndicatorView(activityIndicatorStyle: UIActivityIndicatorViewStyle.gray)
indicator?.frame = CGRect(origin: CGPoint.init(x: 0, y: 0), size: CGSize.init(width: 40, height: 40));
indicator?.center = view.center
view.addSubview(indicator!)
indicator?.bringSubview(toFront: view)
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
func showLoading(message: String) {
indicator?.startAnimating()
}
func hideLoading() {
indicator?.stopAnimating()
}
}
You don't need a dedicated class for this, seems the only thing you need is a subscriber that weakly references the view and the presenter, and that handles common event handling logic.
Since an RxSwift subscriber can also be a closure with one event argument, you could implement a free function that will create it:
func customSubscriber<T>(mvpView: MvpView, presenter: BasePresenter) -> (RxSwift.Event<T>) -> Void {
return { [weak mvpView, weak presenter] event in
switch event {
case let .next(element):
print("Received \(element)")
case .completed:
print("Done")
case let .error(error):
if let serviceError = error as? ServiceError {
print("Service error: \(serviceError)")
} else {
print("Some error: \(error)")
}
}
}
}
which you can use it like this:
myObserver.subscribe(customSubscriber(mvpView: someMvpView, presenter: somePresenter))
RxSwift paradigms are centered around closures: you create observers from closures, subscribers are closures, operators work with closures. Also in Swift other types like structs and enums (aka value types) are preferred over classes (aka reference types), they just work better in most of the contexts.
Update Based on the latest snippet, seems you also want to allow different event handling based on the receiver. This can be elegantly achieved by using a protocol with default implementations.
protocol Subscribable: class {
associatedtype DataType
func onNext(_ data: DataType)
func onCompleted()
func onError()
}
extension Subscribable where Self: BaseMvpView {
func onNext(_ data: DataType) {
hideLoading()
print("super next")
}
func onCompleted() {
hideLoading()
print("super completed")
}
func onError() {
hideLoading()
print("super error")
if let serviceError = error as? ServiceError {
print("Service error: \(serviceError.errorDescription ?? "Something wrong")")
}
}
}
// BaseMvpView will get all above methos implemented, child classes can implement their own version.
extension BaseMvpView: Subscribable {
typealias DataType = DataTypeThatIsUsedByMvpView
}
// the subscriber function got simplifier as it no longer needs
// the callback parameters, as those are not part of the protocol
// this also makes the function more flexible as it's not tied to a
// concrete class
func baseSubscriber<S: Subscribable, T>(_ subscribable: S) -> (RxSwift.Event<T>) -> Void where S.DataType == T {
return { [weak subscribable] event in
switch event {
case let .next(element):
subscribable?.onNext(element)
case .completed:
subscribable.onCompleted()
case let .error(error):
subscribable.onError(error)
}
}
}
You can call the new function like this:
baseSubscriber(someMvpView)
i'm having trouble implementing rxJava in order to check if there is internet connection on android i'm doing it like this:
on my launcher activity i have this in onCreate:
AndroidObservable.bindActivity(this,
Observable.just(Utils.isActiveInternetConnection(Launcher.this)))
.subscribeOn(Schedulers.newThread())
.subscribe(new Action1<Boolean>() {
#Override
public void call(Boolean aBoolean) {
if (aBoolean) {
Toast.makeText(Launcher.this, "There is internet connection", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(Launcher.this, "There is no internet connection", Toast.LENGTH_SHORT).show();
}
}
});
i have an Utils class it is a final class with static methods the methods in that the observable is using are this ones:
public static boolean isActiveInternetConnection(Context context) {
if (isNetworkAvailable(context)) {
try {
HttpURLConnection urlc = (HttpURLConnection) (new URL("http://www.google.com").openConnection());
urlc.setRequestProperty("User-Agent", "Test");
urlc.setRequestProperty("Connection", "close");
urlc.setConnectTimeout(1500);
urlc.connect();
return (urlc.getResponseCode() == 200);
} catch (IOException e) {
Log.e("network", "Error checking internet connection", e);
}
} else {
Log.d("network", "No network available!");
}
return false;
}
private static boolean isNetworkAvailable(Context context){
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
if (null != activeNetwork) {
return true;
}
else {
return false;
}
}
i'm receiving android.os.NetworkOnMainThreadException and i can't find why, thanks in advance.
Observable.just(...) is called immediately on the calling thread (the main thread, in this case). Your code is effectively just an inlined version of this:
boolean activeConn = Utils.isActiveInternetConnection(Launcher.this);
AndroidObservable.bindActivity(this,
Observable.just(activeConn))
.subscribeOn(...)
...
You've tried to move it off the main thread by calling subscribeOn() - but the call has already happened.
The way we handle this (and I'm not sure that this is the best way, but it works) is to defer the network or blocking call until subscription happens, set up the observable to run on the correct threads, and then subscribe:
AndroidObservable.bindActivity(this,
Observable.defer(new Func0<Boolean>() {
#Override
public Observable<Observable<Boolean>> call() {
return Observable.just(Utils.isActiveInternetConnection(Launcher.this));
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Boolean>() {
#Override
public void call(Boolean aBoolean) {
if (aBoolean) {
Toast.makeText(Launcher.this, "There is internet connection", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(Launcher.this, "There is no internet connection", Toast.LENGTH_SHORT).show();
}
}
});
AdamS is correct, however RxJava 2 now offers Observable.fromCallable() to defer an observable operation till subscription.
A good reference:
https://caster.io/lessons/fromcallable-converting-slow-methods-into-an-observable/
Some example code from my use-case:
Single.fromCallable(new Callable<Response>() {
#Override
public Response call() throws Exception {
return NetworkGateway.networkRequest();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
I guess just just calls the method synchronously as it expects the boolean value and it tries to get it.
I am rather bad at RxJava but you may try something like this:
Observable<Boolean> onlineObservable = Observable.create(new Observable.OnSubscribe<Boolean>() {
#Override
public void call(Subscriber subscriber) {
subscriber.onNext(Utils.isActiveInternetConnection(context));
}
});
onlineObservable.subscribeOn(Schedulers.newThread()).subscribe(result -> {...});
this is my retrieve data from DataBase by RXAndroid code:
Observable.create(new Observable.OnSubscribe<List<GRO_VO>>() {
#Override
public void call(Subscriber<? super List<GRO_VO>> subscriber) {
String jsonIn;
jsonIn =retrieveDataFromDB();
Gson gson = new Gson();
Type listType = new TypeToken<List<GRO_VO>>() {
}.getType();
eventJoinList = gson.fromJson(jsonIn, listType);
Log.d("RX",jsonIn);
subscriber.onNext(eventJoinList);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<GRO_VO>>() {
#Override
public void call(List<GRO_VO> eventJoinList) {
Log.d("RX", ".subscribe");
recyclerView.setAdapter(new EventJoinAdapter(eventJoinList));
}
});
I think the just operator will emit data immediately, so it's is not useful to retrieve data from a database via network. It is very easy to use, but it can only be used for data that's already in the ram of the device.
I also had that problem, like #Baniares had, but after I use the create operator, all the problem gone...
From the RXJava documentation:
static <T> Observable<T> create(Observable.OnSubscribe<T> f)
Returns an Observable that will execute the specified function when a Subscriber subscribes to it.
Using the create operator can establish the standard process:
1 .subscribe(...) Subscriber(the sub class of class Observer) start the connection to Observable.
2 .subscribeOn(Schedulers.io()) collect a backGround Thread from the RX-ThreadPool
3 .create(...) retrieve data from server...during some netWork..etc
4 .observeOn(AndroidSchedulers.mainThread()) this means data will be set by the UI-Thread
5 Once we get the data , we can set the data in the onNext( ) method in the .subscribe( ) , the data will be set on the UI by UI-Thread since we make UI-thread do the work on the .observerOn(AndroidSchedulers.mainThread())
And note that if you use .create( ) operator,you must finished your observable in the .create( ) , others operator such like map,flatMap will not be executed after the .create( ) operator.
A very important concept we must need to know before start to use RXJava/RXAndroid. RX is a callback-based library, you tell RX what it should to do in what condition, it invoke your pass-in function(or in JAVA I may should to call them anonymous inner class... ) to achieve what you want.