# [Philipp Lackner - Android Testing](https://www.youtube.com/watch?v=EkfVL5vCDmo&list=PLQkwcJG4YTCSYJ13G4kVIJ10X5zisB2Lq)
```Resource.kt
data class Resource<out T>(val status: Status, val data: T?, val message: String?) {
companion object {
fun <T> success(data: T?): Resource<T> {
return Resource(Status.SUCCESS, data, null)
}
fun <T> error(msg: String, data: T?): Resource<T> {
return Resource(Status.ERROR, data, msg)
}
fun <T> loading(data: T?): Resource<T> {
return Resource(Status.LOADING, data, null)
}
}
}
enum class Status {
SUCCESS,
ERROR,
LOADING
}
```
## [How to Build an MVI Clean Code Weather App](https://www.youtube.com/watch?v=eAbKK7JNxCE)
```Resource.kt
sealed class Resource<T>(val data: T? = null, val message: String? = null) {
class Success<T>(data: T?): Resource<T>(data)
class Error<T>(message: String, data: T? = null): Resource<T>(data, message)
class Loading<T>(data: T? = null): Resource<T>(data)
}
```
其他出處
[MindOrks - Using Retrofit with Kotlin Coroutines in Android]([MindOrks - Using Retrofit with Kotlin Coroutines in Android](https://blog.mindorks.com/using-retrofit-with-kotlin-coroutines-in-android/)/)
---
# [Renaro Santos - Android MVVM + Clean Architecture](https://www.youtube.com/playlist?list=PLMVOtgo-eaZ5AoTBQOXr01O_OQdvcuzQt)
```Result.kt
sealed class Result<out T> {
data class Error(val exception: Exception) : Result<Nothing>()
data class Success<T>(val data: T) : Result<T>()
}
```
# Kotlin
[Result](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-result/)
# [套用 Repository Pattern 完成最後架構](https://ithelp.ithome.com.tw/articles/10195487)
Resource
```Resource.java
public class Resource<T> {
@NonNull
public final Status status;
@Nullable
public final T data;
@Nullable
public final String message;
public Resource(@NonNull Status status, @Nullable T data, @Nullable String message) {
this.status = status;
this.data = data;
this.message = message;
}
public static <T> Resource<T> success(@Nullable T data) {
return new Resource<>(SUCCESS, data, null);
}
public static <T> Resource<T> error(@Nullable T data, String msg) {
return new Resource<>(ERROR, data, msg);
}
public static <T> Resource<T> loading(@Nullable T data) {
return new Resource<>(LOADING, data, null);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Resource<?> resource = (Resource<?>) o;
return Objects.equals(status, resource.status) &&
Objects.equals(message, resource.message) &&
Objects.equals(data, resource.data);
}
@Override
public int hashCode() {
int result = status.hashCode();
result = 31 * result + (message != null ? message.hashCode() : 0);
result = 31 * result + (data != null ? data.hashCode() : 0);
return result;
}
@Override
public String toString() {
return "Resource{" +
"status=" + status +
", message='" + message + '\'' +
", data=" + data +
'}';
}
}
```
Status
```Status.java
public enum Status {
SUCCESS,
ERROR,
LOADING
}
```
# Android
## Architecture
### nowinandroid
### sunflower
### compose-samples
Jetcaster
```
sealed class PodcastRssResponse {
data class Error(
val throwable: Throwable?,
) : PodcastRssResponse()
data class Success(
val podcast: Podcast,
val episodes: List<Episode>,
val categories: Set<Category>
) : PodcastRssResponse()
}
```
# ListenableWorker
```ListenableWorker.Result.java
public abstract static class Result {
/**
* Returns an instance of {@link Result} that can be used to indicate that the work
* completed successfully. Any work that depends on this can be executed as long as all of
* its other dependencies and constraints are met.
*
* @return An instance of {@link Result} indicating successful execution of work
*/
@NonNull
public static Result success() {
return new Success();
}
/**
* Returns an instance of {@link Result} that can be used to indicate that the work
* completed successfully. Any work that depends on this can be executed as long as all of
* its other dependencies and constraints are met.
*
* @param outputData A {@link Data} object that will be merged into the input Data of any
* OneTimeWorkRequest that is dependent on this work
* @return An instance of {@link Result} indicating successful execution of work
*/
@NonNull
public static Result success(@NonNull Data outputData) {
return new Success(outputData);
}
/**
* Returns an instance of {@link Result} that can be used to indicate that the work
* encountered a transient failure and should be retried with backoff specified in
* {@link WorkRequest.Builder#setBackoffCriteria(BackoffPolicy, long, TimeUnit)}.
*
* @return An instance of {@link Result} indicating that the work needs to be retried
*/
@NonNull
public static Result retry() {
return new Retry();
}
/**
* Returns an instance of {@link Result} that can be used to indicate that the work
* completed with a permanent failure. Any work that depends on this will also be marked as
* failed and will not be run. <b>If you need child workers to run, you need to use
* {@link #success()} or {@link #success(Data)}</b>; failure indicates a permanent stoppage
* of the chain of work.
*
* @return An instance of {@link Result} indicating failure when executing work
*/
@NonNull
public static Result failure() {
return new Failure();
}
/**
* Returns an instance of {@link Result} that can be used to indicate that the work
* completed with a permanent failure. Any work that depends on this will also be marked as
* failed and will not be run. <b>If you need child workers to run, you need to use
* {@link #success()} or {@link #success(Data)}</b>; failure indicates a permanent stoppage
* of the chain of work.
*
* @param outputData A {@link Data} object that can be used to keep track of why the work
* failed
* @return An instance of {@link Result} indicating failure when executing work
*/
@NonNull
public static Result failure(@NonNull Data outputData) {
return new Failure(outputData);
}
/**
* @return The output {@link Data} which will be merged into the input {@link Data} of
* any {@link OneTimeWorkRequest} that is dependent on this work request.
*/
@NonNull
public abstract Data getOutputData();
/**
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
Result() {
// Restricting access to the constructor, to give Result a sealed class
// like behavior.
}
/**
* Used to indicate that the work completed successfully. Any work that depends on this
* can be executed as long as all of its other dependencies and constraints are met.
*
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static final class Success extends Result {
private final Data mOutputData;
public Success() {
this(Data.EMPTY);
}
/**
* @param outputData A {@link Data} object that will be merged into the input Data of
* any OneTimeWorkRequest that is dependent on this work
*/
public Success(@NonNull Data outputData) {
super();
mOutputData = outputData;
}
@Override
public @NonNull Data getOutputData() {
return mOutputData;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Success success = (Success) o;
return mOutputData.equals(success.mOutputData);
}
@Override
public int hashCode() {
String name = Success.class.getName();
return 31 * name.hashCode() + mOutputData.hashCode();
}
@NonNull
@Override
public String toString() {
return "Success {" + "mOutputData=" + mOutputData + '}';
}
}
/**
* Used to indicate that the work completed with a permanent failure. Any work that depends
* on this will also be marked as failed and will not be run. <b>If you need child workers
* to run, you need to return {@link Result.Success}</b>; failure indicates a permanent
* stoppage of the chain of work.
*
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static final class Failure extends Result {
private final Data mOutputData;
public Failure() {
this(Data.EMPTY);
}
/**
* @param outputData A {@link Data} object that can be used to keep track of why the
* work failed
*/
public Failure(@NonNull Data outputData) {
super();
mOutputData = outputData;
}
@Override
public @NonNull Data getOutputData() {
return mOutputData;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Failure failure = (Failure) o;
return mOutputData.equals(failure.mOutputData);
}
@Override
public int hashCode() {
String name = Failure.class.getName();
return 31 * name.hashCode() + mOutputData.hashCode();
}
@NonNull
@Override
public String toString() {
return "Failure {" + "mOutputData=" + mOutputData + '}';
}
}
/**
* Used to indicate that the work encountered a transient failure and should be retried with
* backoff specified in
* {@link WorkRequest.Builder#setBackoffCriteria(BackoffPolicy, long, TimeUnit)}.
*
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static final class Retry extends Result {
public Retry() {
super();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
// We are treating all instances of Retry as equivalent.
return o != null && getClass() == o.getClass();
}
@Override
public int hashCode() {
String name = Retry.class.getName();
return name.hashCode();
}
@NonNull
@Override
public Data getOutputData() {
return Data.EMPTY;
}
@NonNull
@Override
public String toString() {
return "Retry";
}
}
}
```
# PagingSource
```PagingSource.LoadResult.kt
public sealed class LoadResult<Key : Any, Value : Any> {
/**
* Error result object for [PagingSource.load].
*
* This return type indicates an expected, recoverable error (such as a network load
* failure). This failure will be forwarded to the UI as a [LoadState.Error], and may be
* retried.
*
* @sample androidx.paging.samples.pageKeyedPagingSourceSample
*/
public data class Error<Key : Any, Value : Any>(
val throwable: Throwable
) : LoadResult<Key, Value>() {
override fun toString(): String {
return """LoadResult.Error(
| throwable: $throwable
|) """.trimMargin()
}
}
/**
* Invalid result object for [PagingSource.load]
*
* This return type can be used to terminate future load requests on this [PagingSource]
* when the [PagingSource] is not longer valid due to changes in the underlying dataset.
*
* For example, if the underlying database gets written into but the [PagingSource] does
* not invalidate in time, it may return inconsistent results if its implementation depends
* on the immutability of the backing dataset it loads from (e.g., LIMIT OFFSET style db
* implementations). In this scenario, it is recommended to check for invalidation after
* loading and to return LoadResult.Invalid, which causes Paging to discard any
* pending or future load requests to this PagingSource and invalidate it.
*
* Returning [Invalid] will trigger Paging to [invalidate] this [PagingSource] and
* terminate any future attempts to [load] from this [PagingSource]
*/
public class Invalid<Key : Any, Value : Any> : LoadResult<Key, Value>() {
override fun toString(): String {
return "LoadResult.Invalid"
}
}
/**
* Success result object for [PagingSource.load].
*
* As a convenience, iterating on this object will iterate through its loaded [data].
*
* @sample androidx.paging.samples.pageKeyedPage
* @sample androidx.paging.samples.pageIndexedPage
*/
public data class Page<Key : Any, Value : Any> constructor(
/**
* Loaded data
*/
val data: List<Value>,
/**
* [Key] for previous page if more data can be loaded in that direction, `null`
* otherwise.
*/
val prevKey: Key?,
/**
* [Key] for next page if more data can be loaded in that direction, `null` otherwise.
*/
val nextKey: Key?,
/**
* Count of items before the loaded data. Must be implemented if
* [jumping][PagingSource.jumpingSupported] is enabled. Optional otherwise.
*/
@IntRange(from = COUNT_UNDEFINED.toLong())
val itemsBefore: Int = COUNT_UNDEFINED,
/**
* Count of items after the loaded data. Must be implemented if
* [jumping][PagingSource.jumpingSupported] is enabled. Optional otherwise.
*/
@IntRange(from = COUNT_UNDEFINED.toLong())
val itemsAfter: Int = COUNT_UNDEFINED
) : LoadResult<Key, Value>(), Iterable<Value> {
/**
* Success result object for [PagingSource.load].
*
* @param data Loaded data
* @param prevKey [Key] for previous page if more data can be loaded in that direction,
* `null` otherwise.
* @param nextKey [Key] for next page if more data can be loaded in that direction,
* `null` otherwise.
*/
public constructor(
data: List<Value>,
prevKey: Key?,
nextKey: Key?
) : this(data, prevKey, nextKey, COUNT_UNDEFINED, COUNT_UNDEFINED)
init {
require(itemsBefore == COUNT_UNDEFINED || itemsBefore >= 0) {
"itemsBefore cannot be negative"
}
require(itemsAfter == COUNT_UNDEFINED || itemsAfter >= 0) {
"itemsAfter cannot be negative"
}
}
override fun iterator(): Iterator<Value> {
return data.listIterator()
}
override fun toString(): String {
return """LoadResult.Page(
| data size: ${data.size}
| first Item: ${data.firstOrNull()}
| last Item: ${data.lastOrNull()}
| nextKey: $nextKey
| prevKey: $prevKey
| itemsBefore: $itemsBefore
| itemsAfter: $itemsAfter
|) """.trimMargin()
}
public companion object {
public const val COUNT_UNDEFINED: Int = Int.MIN_VALUE
@Suppress("MemberVisibilityCanBePrivate") // Prevent synthetic accessor generation.
internal val EMPTY = Page(emptyList(), null, null, 0, 0)
@Suppress("UNCHECKED_CAST") // Can safely ignore, since the list is empty.
internal fun <Key : Any, Value : Any> empty() = EMPTY as Page<Key, Value>
}
}
}
```
# [Kotlin Coroutines](https://developer.android.com/kotlin/coroutines?hl=zh-tw)
```Result.kt
sealed class Result<out R> {
data class Success<out T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
}
```
# Using Retrofit with Kotlin Coroutines
## [MindOrks - Using Retrofit with Kotlin Coroutines in Android](https://blog.mindorks.com/using-retrofit-with-kotlin-coroutines-in-android/)
# Shopify - GraphCallResult
```
/**
* Represents `GraphQL` operation execution result.
*/
sealed class GraphCallResult<out T : AbstractResponse<out T>> {
/**
* `GraphQL` operation result returned for success execution, with the typed operation response that will contain fields that match
* exactly the fields requested.
*/
class Success<T : AbstractResponse<T>>(val response: GraphResponse<T>) : GraphCallResult<T>()
/**
* `GraphQL` operation result returned for failed execution, with the error caused this call to fail.
*
* @see GraphError
*/
class Failure(val error: GraphError) : GraphCallResult<Nothing>()
}
```