How to refactor functional error handling in flutter with dartz - flutter

I have two methods
#override
Future<Option<StorageFailure>> init() async {
final root = await getRootDir();
return root.fold(
(failure) => some(failure),
(dir) {
final images = Directory("$dir/images");
final videos = Directory("$dir/videos");
images.create(); // more error handling here (try|either)
videos.create();
},
);
}
#override
Future<Either<StorageFailure, Directory>> getRootDir() async {
try {
final root = await getApplicationDocumentsDirectory();
return right(Directory("${root.path}/files"));
} catch (e) {
return left(StorageFailure(reason: e.toString()));
}
}
On the init method after folding i need to do more error handling but i don't like to nest too much my code. Also i don't know how to return a Failure from the right function.
What would be a better way to chain those values?

The cool thing about Either is, that you can chain actions and take care of error handling just once at the end. If the program fails to get the root directory, the part about creating the media subdirectories is never executed. So there is no need for nested error handling.
I am in the process of learning functional programming myself. So there might be an even better solution, but here is how I would do it:
// I intentionally added all type annotations for better understanding.
Future<Option<StorageFailure>> init() async {
final Either<StorageFailure, Directory> root = await getRootDir();
final Either<StorageFailure, Either<StorageFailure, Success>> result = await root.traverseFuture(createImagesAndVideosSubfolders);
final Either<StorageFailure, Success> flattenedResult = result.flatMap(id);
return flattenedResult.fold((failure) => some(failure), (success) => none());
}
Future<Either<StorageFailure, Success>> createImagesAndVideosSubfolders(Directory dir) async {
try {
await Directory('${dir.path}/images').create();
await Directory('${dir.path}/videos').create();
return right(Success('success'));
} catch (e) {
return left(StorageFailure(reason: e.toString()));
}
}
get the root directory
create the media directories (if getRootDir failed, the root.traverseFuture method just returns the existing StorageFailure)
Flatten the nested Eithers with flatMap and the id function. The traverseFuture method is the map version for futures. If there would be a flatMap equivalent for futures as well, the result would not be wrapped in an either and the last step to flatten the result would not be necessary.
You could simplify the init function even more by just returning Either instead of converting it to an Option.
Like so:
Future<Either<StorageFailure, Success>> init() async {
...
return result.flatMap(id);
}

Related

async/await method throws exception -type int is not a subtype of bool

I am calling a graphql endpoint which returns success, but I do get an exception on the calling method.
Here is my calling method -
await AmplifyInstance()// this is where I get the exception. Snip below
.createUserOnAzureCosmosDB(user)
.then((result) {
print(result['data']['userPhoneNumber']);
_intlPhoneFieldController.text =
(result['data']['userPhoneNumber'].toString())
.substring(1);
_incrementStep('continueOnProfilePictureWidget');
});
Here is the called method -
Future<dynamic> createUserOnAzureCosmosDB(User user) async {
HttpLink link = GlobalVariables().graphqlEndpoint;
GraphQLClient graphQlClient = GraphQLClient(
link: link,
cache: GraphQLCache(
store: InMemoryStore(),
),
);
try {
QueryResult mutationResult = await graphQlClient.mutate(
//Mutation query here
if (mutationResult.data?['createUser'] != null) {
print('Created user on Cosmos DB');
registerUserStatus['result'] = true;
registerUserStatus['data'] = mutationResult.data?['createUser'];
}
} on ApiException catch (e) {
print('Mutation failed: $e');
registerUserStatus['result'] = false;
registerUserStatus['errorMessage'] = e.message;
}
return registerUserStatus;
}
And the returned registerUserStatus is just an array -
var registerUserStatus = {};
Here is the exception -
UPDATE eamirho3ein
Here is the result of print("result=$result);
I/flutter (14224): result = {result: true, data: {__typename: User, partitionKey: user, userPhoneNumber: 14160000000, userDisplayName: testuser, avatarUrl: www.url.com, createdAt: Today}}
This is not actually an answer, but rather a way to find the answer more easily yourself:
then chains make it increasingly hard to find your problem, because the compiler/debugger/IDE has a harder time pointing you to it. So don't do it.
With async/await available from the beginning, there never has been a reason to use then in any Dart program.
await AmplifyInstance().createUserOnAzureCosmosDB(user).then((result) {
Is equivalent to just writing:
final result = await AmplifyInstance().createUserOnAzureCosmosDB(user);
And then continuing on with the code you had put in the lambda function in the then part. Obviously, you need to remove the closing bracket somewhere too now.
This way, your error will actually pop up where it happens, not at the await of a huge chain that leaves you wondering what the problem might be.

Flutter Bloc/Cubit Error Handling - what is the best architectural approach?

I'm a beginner developer and I have problem with implementation of BloC framework. Let's assume that I have this code (Model, NetworkService, Repository, Cubit, State, Widget):
class NetworkService {
Future getData(Uri uri) async {
try {
http.Response httpsResponse = await http.get(
uri,
headers: {
// some headers //
},
);
if (httpsResponse.statusCode == 200) {
return httpsResponse.body;
} else {
throw 'Request failed with status: ${httpsResponse.statusCode}';
}
} catch (e) {
// What I shloud return here?
return e.toString();
}
}
Future<List<dynamic>> fetchData() async {
final uri = Uri.parse('some url');
var data = await getData(uri);
return = jsonDecode(data) as List;
}
}
class Repository {
final NetworkService networkService = NetworkService();
Future<List<SomeObject>> fetchDataList() async {
final dataRaw =
await networkService.fetchDataList();
return dataRaw.map((e) => SomeObject.fromJson(e)).toList();
}
}
class SomeCubit extends Cubit<CubitState> {
final Repository repository;
SomeCubit(this.repository) : super(LoadingState()) {
fetchDataList();
}
void fetchDataList() {
try {
repository
.fetchDataList()
.then((dataList) => emit(LoadedState(dataList)));
} catch (e) {
// What I shloud return here?
emit(ErrorState(e.toString()));
}
}
}
How to make this code "bullet proof" because I don't know how to "pass" error from NetworkService to Cubit? It works fine till I have dynamic responses in functions but in Repository class I want to return List of specific objects and when function fail I will return null. If I write try/catch I have to provide return statement in catch block - and I can't return List. I want to return some kind of Error...
I suggest that you use the excellent class named Either from the dartz package. It will allow you to return X if things went bad, and return Y if all is well, as such: Future<Either<X, Y>>
Then you can check on your variable (e.g. result) as follows: result.isLeft() for error, or do result.fold( ... ) to easily handle the return type (error or success).
In your particular case you could do as follows when returning from the repository to the cubit:
Future<Either<RepositoryError, List<SomeObject>>> fetchDataList() async { ... }
Where RepositoryError could be a class containing information about the type of error.
So in the cubit you do:
final result = await repository.fetchDataList();
emit(
result.fold(
(error) => ErrorState(error),
(dataList) => LoadedState(dataList)
)
);
Then you continue with this pattern all the way to NetworkService getData(). Either with the same common "error class" in the Repository and the NetworkService, or separate ones in the different layers and you "translate" between different "error classes". Perhaps it makes sense to have a NetworkServiceError that is returned there..
In your NetworkService you could do as follows:
Future<Either<NetworkServiceError, String>> getData(Uri uri) async { ... }
Future<Either<NetworkServiceError, List<dynamic>>> fetchData() async { ... }
This will give you great flexibility and passing of information from the service, to the repository and to the cubit.
You can let exceptions propagate through Futures from NetworkService up to the cubit, by removing the try/catch from getData.

Function expressions can't be named: then(

Im having issues with my code and since I'm new at using Flutter, I have no clue on how to fix it. I was trying to fix the http.get(string) and I kind of did, but now I'm having issues with then(()).
void submitForm(FeedbackForm feedbackForm) async {
try {
await http.get(Uri.parse(URL + feedbackForm.toParams()).then((response)) {
callback(convert.jsonDecode(response.body['status']));
});
} catch (e) {
print(e);
}
}
}
It seems you got a parenthesis missplaced:
await http.get(...).then((response) => callback(...))
The them allows you to use the result of the previous Future, as soon as it becomes available. If you find it confusing you can declare one variable at a time.
final response = await http.get(...);
// Check if response was as expected
await callback();

What is right way to pass error to widget

What is the right way to pass exception/failure from services or other parts to your widget.
I would like to pass error code from register to onSignIn. Whats the right way. Is it ok to do it the way I am doing or should I
again throw exception from register and catch it in onSignIn.
don't catch in register but catch in onSignIn
File A
void onSignIn() async {
dynamic result = await _auth.register();
if (result.runtimeType == String) {
print(result);
}
}
File B
Future register() async {
try {
AuthResult result = await _auth.createUser();
return _user(result.user);
} catch (e) {
return e.code;
}
}
For error handling I usually use a wrapper object to help me out better handling, errors or even "loading". This is a patter from one of Google's Android examples and I've used on flutter with success. So basically you create a generic class, in my case I call it "Response" that has the structure of:
class Response<T> {
final ApiError error;
final T data;
final ResponseType type;
Response({
this.error,
this.data,
this.type,
});
bool get isOk {
return type == ResponseType.ok;
}
bool get isLoading {
return type == ResponseType.loading;
}
bool get hasError {
return type == ResponseType.error;
}
factory Response.loading() {
return Response<T>(type: ResponseType.loading);
}
factory Response.ok(T data) {
return Response<T>(type: ResponseType.ok, data: data);
}
factory Response.empty() {
return Response<T>.ok(null);
}
factory Response.error(ApiError data) {
return Response<T>(type: ResponseType.error, error: data);
}
}
enum ResponseType { loading, error, ok }
ApiError is my custom implementation that handles errors for a specific project, it can be replaced with a generic Error.
This approach is super helpful when you're using Streams, because it won't close a stream in case of error, you have the possibility to mark the beginning of the task by replying with a loading and it will handle succes/error properly.
This approach would also allow you to send a more well defined error to the UI, let's say you need the error code, error message, and maybe you get an input field name where you want to show that error on.
Altho, in your case it might be a bit of overkill, I would still use it like this:
Future<Response<AuthResult>> register() async {
try {
AuthResult result = await _auth.createUser();
return Response.ok(_user(result.user));
} catch (e) {
return Response.error(e);
}
}
void onSignIn() async {
Response<AuthResult> result = await _auth.register();
if (result.isOk) {
//TODO: all good let's use data with result.data
}
else if (result.isLoading) {
// TOOD: well...show a loading indicator
}
else {
//TODO: we got error handle it using result.error
}
}
In flutters' documentation, they almost always caught the error in the parent function(take a look at here as an example). Also using dynamic may be dangerous since it accepts all kinds of objects, a better approach would be using the final keyword. so the preferred way would be:
Future<AuthResult> register() async {
AuthResult result = await _auth.createUser();
return _user(result.user);
}
and then:
void onSignIn() async {
try{
final result = await _auth.register();
print(result);
} on Exception catch(ex){
// do something with error
}
}
I usually like to use Provider for such kind of things.
class Auth with ChangeNotifier{
AuthResult _authResult;
AuthResult get authResult => _authResult;
}
enum AuthResult{
Successful,
Error,
OTPError,
}
Then, I will use the provider package to get the data wherever needed.

Dart Flutter, help me understand futures

See this code:
class SomeClass{
String someVariable;
SomeClass();
Future<String> getData () async {
Response response = await get('http://somewebsite.com/api/content');
Map map = jsonDecode(response.body); // do not worry about statuscode, trying to keep it minimal
someVariable = map['firstName'];
return 'This is the first name : $someVariable';
}
}
Now look at main:
void main(){
String someFunction() async {
SomeClass instance = SomeClass(); // creating object
String firstNameDeclaration = await instance.getData().then((value) => value);
return firstNameDeclaration;
}
}
When working with Future, like in the case of firstNameDeclaration why do I have to use .then() method to access the string object, since I am waiting for the function to finish?
When searching on the web, some people use .then() others don't, I am confused.
Kindly help me have a clearer understanding of how Futures and async functions overall work.
Background
Asynchronous operations let your program complete work while waiting for another operation to finish. Here are some common asynchronous operations:
Fetching data over a network.
Writing to a database.
Reading data from a file.
To perform asynchronous operations in Dart, you can use the Future class and the async and await keywords.
When an async function invokes "await", it is converted into a Future, and placed into the execution queue. When the awaited future is complete, the calling function is marked as ready for execution and it will be resumed at some later point. The important difference is that no Threads need to be paused in this model.
Futures vs async-await
When an async function invokes "await", it is converted into a Future, and placed into the execution queue. When the awaited future is complete, the calling function is marked as ready for execution and it will be resumed at some later point. The important difference is that no Threads need to be paused in this model.
async-await is just a a declarative way to define asynchronous functions and use their results into Future and it provides syntactic sugar that help you write clean code involving Futures.
Consider this dart code snipped involving Futures -
Future<String> getData(int number) {
return Future.delayed(Duration(seconds: 1), () {
return 'this is a future string $number.';
});
}
main(){
getData(10).then((data) => {
print(data)
});
}
As you can see when you use Futures, you can use then callback when the function return a future value. This is easy to manage if there is single "then" callback but the situation escalates quickly as soon as there are many nested "then" callbacks for example -
Future<String> getProductCostForUser() {
return getUser().then((user) => {
var uid = user.id;
return getOrder(uid).then((order) => {
var pid = order.productId;
return getProduct(pid).then((product) => {
return product.totalCost;
});
});
});
}
main(){
getProductCostForUser().then((cost) => {
print(cost);
});
}
As you can when there multiple chained "then" callback the code become very hard to read and manage. This problem is solved by "async-await". Above chained "then" callbacks can be simplified by using "async-await" like so -
Future<String> getProductCostForUser() async {
var user = await getUser();
var order = await getOrder(user.uid);
var product = await getProduct(order.productId);
return product.totalCost;
}
main() async {
var cost = await getProductCostForUser();
print(cost);
}
As you can above code is much more readable and easy to understand when there are chained "then" callbacks.
I hope this explains some basic concepts and understanding regarding the "async-await" and Futures.
You can further read about topic and examples here
Basically, you should either use await OR then(). However, Dart guidelines advocates that you should prefer use await over then() :
This code :
Future<int> countActivePlayers(String teamName) {
return downloadTeam(teamName).then((team) {
if (team == null) return Future.value(0);
return team.roster.then((players) {
return players.where((player) => player.isActive).length;
});
}).catchError((e) {
log.error(e);
return 0;
});
}
should be replaced by :
Future<int> countActivePlayers(String teamName) async {
try {
var team = await downloadTeam(teamName);
if (team == null) return 0;
var players = await team.roster;
return players.where((player) => player.isActive).length;
} catch (e) {
log.error(e);
return 0;
}
}
In your case, you should write :
void main(){
Future<String> someFunction() async {
SomeClass instance = SomeClass(); // creating object
String firstNameDeclaration = await instance.getData();
return firstNameDeclaration;
// Or directly : return await instance.getData();
// Or : return instance.getData();
}
}