This library declares a base interactor interface aswell as a corresponding progress-interactor class. In order to use them there are quiet a lot of modifiers that can be used to do actions inside the invocation-flow of an interactor.
Type name | Parameterized | Resulting |
---|---|---|
ParameterizedResultInteractor | Yes | Yes |
ParameterizedInteractor | Yes | No |
ResultInteractor | No | Yes |
Interactor | No | No |
How to call an interactor in your code:
// Define an interactor that does something. He must extend/implement a type mentioned above.
final class StringToIntConverter implements ParameterizedResultInteractor<String, int> {
@override
Future<int> execute(String input) async {
return int.parse(input);
}
}
/// ...
// Create an instance of the interactor
final converter = StringToIntConverter();
/// ...
Future<int> _ = converter.getOrThrow("123"); // Outputs: 123
Future<int?> _ = converter.getOrNull("not-a-number"); // Outputs: null
Future<int> _ = converter.getOrElse("word", (_) => -1); // Outputs: -1
Future<void> _ = converter.run("123"); // Outputs: Nothing (void)
Future<void> _ = converter.run("word"); // Doesn't throw & returns void
Future<void> _ = converter.runUnsafe("123"); // Outputs: Nothing (void)
Future<void> _ = converter.runUnsafe("word"); // Throws exception
Method name | Description |
---|---|
getOrThrow |
Calls the interactor and throws an exception if the interactor fails. |
getOrNull |
Calls the interactor and returns null if the interactor fails. |
getOrElse |
Calls the interactor and returns a fallback value if the interactor fails. |
run |
Calls the interactor and ignores the result. Also this method does not throw. |
runUnsafe |
Calls the interactor and throws an exception in case of a failure. The return type is void. |
The core feature of uic-interactor is the ability to customize the invocation-flow of an interactor. This can be achieved by chaining multiple decorators to the interactor.
In the end your invocation-flow might look like this:
final result = stringToIntConverter
.timeout(const Duration(seconds: 5))
.before((input) => print("Trying to convert $input to string."))
.after((output) => print("Successfully converted number to string. Result: $output"))
.intercept((exception) => print("Failed to convert number to string. Exception caught: $exception"))
.getOrNull("123") // Call the interactor with a parameter
// ...
Right now there are couple of decorators available:
The graphic below shows in which order each decorator is going to append itself around the execution.
It is possible to write custom decorators that modify that invocation-flow of the interactor.
Examples can be found here.
extension CustomModifier<Input, Output> on ParameterizedResultInteractor<Input, Output> {
ParameterizedResultInteractor<Input, Output> customModifier() {
return InlinedParameterizedResultInteractor((input) {
print("I am here!")
return execute(input);
});
}
}
In some cases the interactor might need to publish progress information.
Given a FileDownloadInteractor
that downloads a file from the internet, it might look like this:
typedef SourceUrl = String;
typedef DestinationFilepath = String;
typedef Parameter = ({
SourceUrl sourceUrl,
DestinationFilepath destinationFilepath
});
typedef DownloadedBytes = int;
typedef DownloadProgress = int;
final class FileDownloadInteractor extends ParameterizedResultProgressInteractor<
Parameter, DownloadedBytes, DownloadProgress> {
@override
Future<DownloadedBytes> execute(Parameter input) async {
// TODO: Implement your file download here
await emitProgress(0);
// Download ...
await emitProgress(100);
}
}
// ...
void main() {
final downloadService = FileDownloadInteractor();
final result = await downloadService
.receiveProgress((progress) async {
print('Download-Progress: $progress%');
})
.getOrThrow((
sourceUrl: 'https://example.com/image.jpg',
destinationFilepath: 'image.jpg'
));
print(result);
}
Just like the default interactor types written above, the ProgressInteractor provides a single method called onProgress
which must be called before all other decorators. It gets called whenever the interactor wants to publish a progress-value to the caller. Due to API limitations it can only be registerd once in the method-pipe.
The naming-convention mirrors the previously declared interactors from above.
Type name | Parameterized | Resulting |
---|---|---|
ParameterizedResultProgressInteractor | Yes | Yes |
ParameterizedProgressInteractor | Yes | No |
ResultProgressInteractor | No | Yes |
ProgressInteractor | No | No |
An example might look like this:
myFileDownloadInteractor
.receiveProgress((progress) => println("Downloaded ${progress}% of the file."))
.timeout(const Duration(seconds: 30))
.before((input) => println("Downloading file from ${input.sourceUrl} to ${input.destinationFilepath}."))
.after((_) => println("Successfully downloaded file."))
.intercept((exception) => println("Failed to download file. Exception caught: $it"))
.eventually(() => println("Finished downloading file from."))
.getOrNull(FileDownloadInteractorInput("https://example.com/file.txt", "/path/to/file.txt"))