From 139a9ad4a38a45ff5a29effdf41cc9da5085cc93 Mon Sep 17 00:00:00 2001 From: Levi Bostian Date: Sun, 8 Apr 2018 19:08:16 -0500 Subject: [PATCH 1/2] Update README with new changes in API. --- README.md | 52 ++++++++++--------- .../wendy/service/PendingTasksFactory.kt | 6 +-- .../com/levibostian/wendy/service/Wendy.kt | 8 +-- 3 files changed, 32 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index c070f7f..65922f4 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # Wendy -Remove the difficulty in making offline-first Android apps. Sync your offline device storage with remote cloud storage easily. +Remove the difficulty in making offline-first Android apps. Sync your offline device storage with remote cloud storage easily. When building offline-first mobile apps, there are *lots* of use cases to think about. Wendy takes care of handling them all for you! ![project logo](misc/wendy_logo.jpg) @@ -10,25 +10,25 @@ Remove the difficulty in making offline-first Android apps. Sync your offline de Wendy is an Android library designed to help you make your app offline-first. Use Wendy to define sync tasks, then Wendy will run those tasks periodically to keep your app's device offline data in sync with it's online remote storage. -Essentially, Wendy is a step up from the Android job runner API. - Wendy is a FIFO task runner. You give it tasks, one by one, it persists those tasks to storage, then when Wendy has determined it's a good time for your task to run, it will call your task's sync function to perform a sync. If your user's device is online and has a good amount of battery, Wendy goes through all of the tasks available one by one running them to succeed or fail and try again. *Note: Wendy is currently in an alpha stage. The API most definitely could change, but it is used in production apps today. Use the latest release of Wendy as you wish but be prepared for having to update your code base in future releases.* ## Why use Wendy? -When creating offline-first mobile apps there are 2 parts. 1. Persisting data to the user's Android device storage and 2. Sync that Android device's local storage with an online storage service. +When creating offline-first mobile apps there are 2 tasks you need to do in your code. 1. Persisting data to the user's Android device storage and 2. Sync that user's storage with remote online storage. Wendy helps you with item #2. You define how the local storage is supposed to sync with the remote storage and Wendy takes care of running those tasks for you periodically when the time is right. Wendy currently has the following functionality: * Wendy uses the Android Job scheduler API to run tasks every 15 minutes when the device is online to keep data in sync without using the user's battery too much. -* Wendy is not opinionated. You may use whatever method you choose to sync data with it's remote storage and whatever method you choose to store data locally on the device. Wendy works with your workflow you already have. Store user data in Sqlite locally and a Rails API for the cloud storage. Store user data in Realm locally and a Parse server for the cloud storage. Whatever you want. +* Wendy is not opinionated. You may use whatever method you choose to sync data with it's remote storage and whatever method you choose to store data locally on the device. Wendy works with your workflow you already have. Store user data in Sqlite locally and a Rails API for the cloud storage. Store user data in Realm locally and a Parse server for the cloud storage. Use just shared preferences and GraphQL. Whatever you want, Wendy works with it. * Dynamically allow and disallow tasks to sync at runtime. Wendy works in a FIFO style with it's tasks. When Wendy is about to run a certain task, it always asks the task if it is able to run. * Mark tasks to manually run instead of automatically from Wendy. This allows you to use the same Wendy API to define all of your sync tasks, but Wendy will simply not attempt to run these tasks periodically automatically. -* Group tasks together to say, "if one of these tasks fails, skip all the future tasks in this group". +* Group tasks together to enforce they all run (and succeed) in an exact order from start to finish. +* Wendy also comes with an error reporter to report errors that your user needs to fix for a task to succeed. +* Wendy takes care of all of the use cases that could happen with building an offline-first mobile app. "What if this task succeeds but this one doesn't? What happens when the network is flaky and a couple of tasks fail but should retry? What happens if this task needs to succeed in order for this task to succeed on my API?" Wendy takes care of handling all of this for you. You define the behavior, Wendy takes care of running it when it is confident it can run the task and succeed. # Install @@ -60,10 +60,8 @@ First, create a `PendingTasksFactory` subclass that stores all of your app's Wen ``` class GroceryListPendingTasksFactory : PendingTasksFactory { - override fun getTask(tag: String): PendingTask { - return when (tag) { - else -> throw RuntimeException("No idea what task that is... tag: $tag") - } + override fun getTask(tag: String): PendingTask? { + return null } } @@ -72,22 +70,22 @@ class GroceryListPendingTasksFactory : PendingTasksFactory { Add the following code to your Application `onCreate()`: ``` -PendingTasks.init(this, GroceryListPendingTasksFactory()) +Wendy.init(this, GroceryListPendingTasksFactory()) ``` Wendy is now configured. It's time to use it! For each separate task that you need to sync local storage with remote cloud storage, you define a `PendingTask` subclass. -In our `Grocery List` app, we want to allow users to create new grocery items. Every time that a user creates a new grocery list item, we don't want to show them a progress bar saying, "Saving grocery list item..." while we perform an API call! We want to be able to *instantly* save that grocery list item and sync it with the cloud storage later. +In our Grocery List app, we want to allow users to create new grocery items. Every time that a user creates a new grocery list item, we don't want to show them a progress bar saying, "Saving grocery list item..." while we perform an API call! We want to be able to *instantly* save that grocery list item and sync it with the cloud storage later so our user can get on with their life (can't you just see your Play Store reviews going up, up, up right now? ⭐⭐⭐⭐⭐). Let's create our first `PendingTask` subclass for creating new grocery items. ``` class CreateGroceryListItemPendingTask(groceryStoreItemId: Long) : PendingTask( - manually_run = false, - data_id = groceryStoreItemId.toString(), - group_id = null, + manuallyRun = false, + dataId = groceryStoreItemId.toString(), + groupId = null, tag = CreateGroceryListItemPendingTask::class.java.simpleName) { val GROCERY_STORE_ITEM_TEXT_TOO_LONG = "GROCERY_STORE_ITEM_TEXT_TOO_LONG" @@ -100,7 +98,7 @@ class CreateGroceryListItemPendingTask(groceryStoreItemId: Long) : PendingTask( // Here, instantiate your dependencies, talk to your DB, your API, etc. Run the task. // After the task succeeds or fails, return to Wendy the result. - val groceryStoreItem = localDatabase.queryGroceryStoreItem(data_id) + val groceryStoreItem = localDatabase.queryGroceryStoreItem(dataId) // Your SQL queries, API calls, etc. in `runTask()` need to be synchronous. Don't worry, you are running on a background thread already so it's all good. // If you still feel you want to run asynchronous code, [check out the Wendy best practices doc](BEST_PRACTICES.md) to learn how to do so. @@ -112,7 +110,7 @@ class CreateGroceryListItemPendingTask(groceryStoreItemId: Long) : PendingTask( // If it's an error that deserves the attention of your user to fix, make sure and record it with Wendy. // If the error is a network error, for example, that does not require the user's attention to fix, do *not* record an error to Wendy. // Wendy will not run your task if there is a recorded error for it. Record an error, prompt your user to fix it, then resolve it ASAP so it can run. - PendingTasks.shared.recordError(task_id, "Grocery store item too long. Please shorten it up for me.", GROCERY_STORE_ITEM_TEXT_TOO_LONG) + Wendy.shared.recordError(taskId, "Grocery store item too long. Please shorten it up for me.", GROCERY_STORE_ITEM_TEXT_TOO_LONG) } return if (successful) PendingTaskResult.SUCCESSFUL else PendingTaskResult.FAILED @@ -126,10 +124,10 @@ Each time that you create a new subclass of `PendingTask`, you need to add that ``` class GroceryListPendingTasksFactory : PendingTasksFactory { - override fun getTask(tag: String): PendingTask { + override fun getTask(tag: String): PendingTask? { return when (tag) { CreateGroceryListItemPendingTask::class.java.simpleName -> CreateGroceryListItem.blank() - else -> throw RuntimeException("No idea what task that is... tag: $tag") + else -> null } } @@ -138,19 +136,19 @@ class GroceryListPendingTasksFactory : PendingTasksFactory { Just about done. -Let's take a look at the function that runs when the user creates a new grocery store list item in the app. +Let's check out the code you wrote in your Grocery List app when your users want to create a new grocery store item in the app. ``` fun createNewGroceryStoreItem(itemName: String) { // First thing you need to do to make a mobile app offline-first is to save it to the device's storage. - // Below, we are saving to a `localDatabase`. Whatever that is. It could be whatever you wish. Sqlite, Realm, shared preferences, whatever. After we save to the database, we get an ID back. You will need some sort of identifier whether that be the shared preferences key, the database row ID, it doesn't matter. + // Below, we are saving to a `localDatabase`. Whatever that is. It could be whatever you wish. Sqlite, Realm, shared preferences, whatever you decide to use works. After we save to the database, we probably get an ID back to reference that piece of data in the database. This ID could be the shared preferences key, the database row ID, it doesn't matter. Simply some way to identify that piece of data *to query later*. val id: Long = localDatabase.createNewGroceryStoreItem(itemName) // We will now create a new `CreateGroceryListItemPendingTask` pending task instance and give it to Wendy. - val pendingTaskId: Long = PendingTasks.sharedInstance().addTask(CreateGroceryListItemPendingTask(id)) + val pendingTaskId: Long = Wendy.sharedInstance().addTask(CreateGroceryListItemPendingTask(id)) // When you add a task to Wendy, you get back an ID for that new `PendingTask`. It's your responsibility to save that ID (or ignore it). It's best practice to save that ID with the data that this `PendingTask` links to. In our example here, the grocery store item in our localDatabase is where we should save the ID. - localDatabase.queryGroceryStoreItem(id).pending_task_id = pendingTaskId + localDatabase.queryGroceryStoreItem(id).pendingTaskId = pendingTaskId // The reason you may want to save the ID of the `PendingTask` is to assert that it runs successfully. Also, you can show in the UI of your app the syncing status of that data to the user. This is all optional, but recommended for the best user experience. WendyConfig.addTaskStatusListenerForTask(pendingTaskId, object : PendingTaskStatusListener { @@ -182,9 +180,13 @@ This library comes with an example app. You may open it in Android Studio to tes ## Documentation -There is a Javadoc (Kotlin doc, actually) for all of the public classes of Wendy. They are in the `docs/` directory. Check em out! +There is a Javadoc (Kotlin doc, actually) for all of the public classes of Wendy [hosted here](https://levibostian.github.io/Wendy-Android/wendy/) for the `master` branch. + +The docs are installed in the `docs/` directory and can be generated from any branch with command: `./gradlew dokka` + +## Configure Wendy -## Advanced functionality +Use the class `WendyConfig` to configure the behavior of Wendy. * Register listeners to Wendy task runner. diff --git a/wendy/src/main/java/com/levibostian/wendy/service/PendingTasksFactory.kt b/wendy/src/main/java/com/levibostian/wendy/service/PendingTasksFactory.kt index 03bdc43..de11aea 100644 --- a/wendy/src/main/java/com/levibostian/wendy/service/PendingTasksFactory.kt +++ b/wendy/src/main/java/com/levibostian/wendy/service/PendingTasksFactory.kt @@ -13,15 +13,15 @@ interface PendingTasksFactory { * Wendy will provide a [tag], you provide a blank [PendingTask] for that specific [tag]. * * Example: - * class WendyExamplePendingTasksFactory : PendingTasksFactory { + * class WendyExamplePendingTasksFactory : PendingTasksFactory? { * override fun getTask(tag: String): PendingTask { * return when (tag) { * FooPendingTask::class.java.simpleName -> FooPendingTask.blank() - * else -> throw RuntimeException("No idea what task that is... tag: $tag") + * else -> null * } * } * } */ - fun getTask(tag: String): PendingTask + fun getTask(tag: String): PendingTask? } \ No newline at end of file diff --git a/wendy/src/main/java/com/levibostian/wendy/service/Wendy.kt b/wendy/src/main/java/com/levibostian/wendy/service/Wendy.kt index 164be4b..10a808d 100644 --- a/wendy/src/main/java/com/levibostian/wendy/service/Wendy.kt +++ b/wendy/src/main/java/com/levibostian/wendy/service/Wendy.kt @@ -90,14 +90,10 @@ class Wendy private constructor(context: Context, internal val tasksFactory: Pen * * @param pendingTask Task you want to add to Wendy. * - * @throws IllegalArgumentException Wendy will check to make sure that you have remembered to add your argument's [PendingTask] subclass to your instance of [PendingTasksFactory] when you call this method. If your [PendingTasksFactory] throws an exception (which probably means that you forgot to include a [PendingTask]) then an [IllegalArgumentException] will be thrown. + * @throws RuntimeException Wendy will check to make sure that you have remembered to add your argument's [PendingTask] subclass to your instance of [PendingTasksFactory] when you call this method. If your [PendingTasksFactory] returns null (which probably means that you forgot to include a [PendingTask]) then an [RuntimeException] will be thrown. */ fun addTask(pendingTask: PendingTask, resolveErrorIfTaskExists: Boolean = true): Long { - try { - tasksFactory.getTask(pendingTask.tag) - } catch (t: Throwable) { - throw IllegalArgumentException("Exception thrown while calling ${tasksFactory::class.java.simpleName}'s getTask(). Did you forgot to add ${pendingTask::class.java.simpleName} to your instance of ${tasksFactory::class.java.simpleName}?") - } + tasksFactory.getTask(pendingTask.tag) ?: RuntimeException("You forgot to add ${pendingTask.tag} to your ${PendingTasksFactory::class.java.simpleName}") tasksManager.getExistingTask(pendingTask)?.let { existingPersistedPendingTask -> if (doesErrorExist(existingPersistedPendingTask.id) && resolveErrorIfTaskExists) { From 2641f0bc1d16007141fe18b5a9dd07dfd0d85d98 Mon Sep 17 00:00:00 2001 From: Levi Bostian Date: Fri, 13 Apr 2018 11:14:08 -0500 Subject: [PATCH 2/2] Make PendingTasksFactory getTask() return optional PendingTask. --- .idea/caches/build_file_checksums.ser | Bin 588 -> 588 bytes .idea/compiler.xml | 22 ------------------ .idea/copyright/profiles_settings.xml | 3 --- .idea/misc.xml | 9 +++---- .idea/modules.xml | 1 - build.gradle | 2 +- .../-pending-tasks-factory/get-task.html | 6 ++--- .../-pending-tasks-factory/index.html | 2 +- .../-wendy/add-task.html | 4 ++-- docs/wendy/index-outline.html | 4 ++-- .../wendy/db/PendingTasksManager.kt | 7 +++--- .../PendingTasksFactoryExtensions.kt | 8 +++++++ .../wendy/service/PendingTasksFactory.kt | 4 ++-- .../com/levibostian/wendy/service/Wendy.kt | 3 ++- 14 files changed, 30 insertions(+), 45 deletions(-) delete mode 100644 .idea/compiler.xml delete mode 100644 .idea/copyright/profiles_settings.xml create mode 100644 wendy/src/main/java/com/levibostian/wendy/extension/PendingTasksFactoryExtensions.kt diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 6bee5324e5bcbb79444d406321b13df2aaf30351..d4d4d7ac06d18bdb0697e29f136141ed74ac5eb2 100644 GIT binary patch delta 56 zcmV-80LTB#1k41Gm;}GJuq2V3pAdTy2gnrVOl?#i*ACc>WM`900UZz#5!BiPS4`dL O-NtsFCymgPcmdR{Nf_q< delta 56 zcmV-80LTB#1k41Gm;{_*C>)WTpAaw#?YorgC?ni;K;!GW+s2bi0UZ#9wUr~c=0P%n Oa=Y#bWapcccmdShxEja+ diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index 96cc43e..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml deleted file mode 100644 index e7bedf3..0000000 --- a/.idea/copyright/profiles_settings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index ba7052b..c0f68ed 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -5,11 +5,12 @@ @@ -24,7 +25,7 @@ - + diff --git a/.idea/modules.xml b/.idea/modules.xml index 90add40..a3efb61 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,6 @@ - diff --git a/build.gradle b/build.gradle index 687348b..00e9d46 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ buildscript { google() } dependencies { - classpath 'com.android.tools.build:gradle:3.1.0' + classpath 'com.android.tools.build:gradle:3.1.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:${dokka_version}" diff --git a/docs/wendy/com.levibostian.wendy.service/-pending-tasks-factory/get-task.html b/docs/wendy/com.levibostian.wendy.service/-pending-tasks-factory/get-task.html index 6c07f71..30553ba 100644 --- a/docs/wendy/com.levibostian.wendy.service/-pending-tasks-factory/get-task.html +++ b/docs/wendy/com.levibostian.wendy.service/-pending-tasks-factory/get-task.html @@ -9,14 +9,14 @@

getTask

-abstract fun getTask(tag: String): PendingTask +abstract fun getTask(tag: String): PendingTask?

Wendy will provide a tag, you provide a blank PendingTask for that specific tag.

Example: class WendyExamplePendingTasksFactory : PendingTasksFactory { - override fun getTask(tag: String): PendingTask { + override fun getTask(tag: String): PendingTask? { return when (tag) { FooPendingTask::class.java.simpleName -> FooPendingTask.blank() - else -> throw RuntimeException("No idea what task that is... tag: $tag") + else -> null } } }

diff --git a/docs/wendy/com.levibostian.wendy.service/-pending-tasks-factory/index.html b/docs/wendy/com.levibostian.wendy.service/-pending-tasks-factory/index.html index 0170d28..79d9d2e 100644 --- a/docs/wendy/com.levibostian.wendy.service/-pending-tasks-factory/index.html +++ b/docs/wendy/com.levibostian.wendy.service/-pending-tasks-factory/index.html @@ -20,7 +20,7 @@

Functions

getTask

-abstract fun getTask(tag: String): PendingTask +abstract fun getTask(tag: String): PendingTask?

Wendy will provide a tag, you provide a blank PendingTask for that specific tag.

diff --git a/docs/wendy/com.levibostian.wendy.service/-wendy/add-task.html b/docs/wendy/com.levibostian.wendy.service/-wendy/add-task.html index f95d5c3..b40b809 100644 --- a/docs/wendy/com.levibostian.wendy.service/-wendy/add-task.html +++ b/docs/wendy/com.levibostian.wendy.service/-wendy/add-task.html @@ -18,7 +18,7 @@

Parameters

pendingTask - Task you want to add to Wendy.

Exceptions

-

-IllegalArgumentException - Wendy will check to make sure that you have remembered to add your argument's PendingTask subclass to your instance of PendingTasksFactory when you call this method. If your PendingTasksFactory throws an exception (which probably means that you forgot to include a PendingTask) then an IllegalArgumentException will be thrown.

+

+RuntimeException - Wendy will check to make sure that you have remembered to add your argument's PendingTask subclass to your instance of PendingTasksFactory when you call this method. If your PendingTasksFactory returns null (which probably means that you forgot to include a PendingTask) then an RuntimeException will be thrown.

diff --git a/docs/wendy/index-outline.html b/docs/wendy/index-outline.html index cd14f11..b02fac3 100644 --- a/docs/wendy/index-outline.html +++ b/docs/wendy/index-outline.html @@ -106,7 +106,7 @@ -abstract fun getTask(tag: String): PendingTask
+abstract fun getTask(tag: String): PendingTask?
@@ -416,7 +416,7 @@ -abstract fun getTask(tag: String): PendingTask
+abstract fun getTask(tag: String): PendingTask?
diff --git a/wendy/src/main/java/com/levibostian/wendy/db/PendingTasksManager.kt b/wendy/src/main/java/com/levibostian/wendy/db/PendingTasksManager.kt index 7911282..2861c1d 100644 --- a/wendy/src/main/java/com/levibostian/wendy/db/PendingTasksManager.kt +++ b/wendy/src/main/java/com/levibostian/wendy/db/PendingTasksManager.kt @@ -1,6 +1,7 @@ package com.levibostian.wendy.db import android.content.Context +import com.levibostian.wendy.extension.getTaskAssertPopulated import com.levibostian.wendy.service.PendingTask import com.levibostian.wendy.service.Wendy import com.levibostian.wendy.service.PendingTasksRunner @@ -109,7 +110,7 @@ internal class PendingTasksManager(context: Context) { select(PersistedPendingTask.TABLE_NAME) .exec { parseList(classParser()).map { - tasksFactory.getTask(it.tag).fromSqlObject(it) + tasksFactory.getTaskAssertPopulated(it.tag).fromSqlObject(it) } } } @@ -143,7 +144,7 @@ internal class PendingTasksManager(context: Context) { .whereArgs("${PersistedPendingTask.COLUMN_ID} = $taskId") .exec { val task = parseOpt(classParser()) - if (task == null) null else tasksFactory.getTask(task.tag).fromSqlObject(task) + if (task == null) null else tasksFactory.getTaskAssertPopulated(task.tag).fromSqlObject(task) } } } @@ -164,7 +165,7 @@ internal class PendingTasksManager(context: Context) { .whereArgs(whereArgs) .exec { parseList(classParser()).firstOrNull() } - if (nextTask == null) null else tasksFactory.getTask(nextTask!!.tag).fromSqlObject(nextTask!!) + if (nextTask == null) null else tasksFactory.getTaskAssertPopulated(nextTask!!.tag).fromSqlObject(nextTask!!) } } diff --git a/wendy/src/main/java/com/levibostian/wendy/extension/PendingTasksFactoryExtensions.kt b/wendy/src/main/java/com/levibostian/wendy/extension/PendingTasksFactoryExtensions.kt new file mode 100644 index 0000000..0368094 --- /dev/null +++ b/wendy/src/main/java/com/levibostian/wendy/extension/PendingTasksFactoryExtensions.kt @@ -0,0 +1,8 @@ +package com.levibostian.wendy.extension + +import com.levibostian.wendy.service.PendingTask +import com.levibostian.wendy.service.PendingTasksFactory + +internal fun PendingTasksFactory.getTaskAssertPopulated(tag: String): PendingTask { + return this.getTask(tag) ?: throw RuntimeException("You forgot to add $tag to your ${PendingTasksFactory::class.java.simpleName}.") +} \ No newline at end of file diff --git a/wendy/src/main/java/com/levibostian/wendy/service/PendingTasksFactory.kt b/wendy/src/main/java/com/levibostian/wendy/service/PendingTasksFactory.kt index de11aea..4b6baba 100644 --- a/wendy/src/main/java/com/levibostian/wendy/service/PendingTasksFactory.kt +++ b/wendy/src/main/java/com/levibostian/wendy/service/PendingTasksFactory.kt @@ -13,8 +13,8 @@ interface PendingTasksFactory { * Wendy will provide a [tag], you provide a blank [PendingTask] for that specific [tag]. * * Example: - * class WendyExamplePendingTasksFactory : PendingTasksFactory? { - * override fun getTask(tag: String): PendingTask { + * class WendyExamplePendingTasksFactory : PendingTasksFactory { + * override fun getTask(tag: String): PendingTask? { * return when (tag) { * FooPendingTask::class.java.simpleName -> FooPendingTask.blank() * else -> null diff --git a/wendy/src/main/java/com/levibostian/wendy/service/Wendy.kt b/wendy/src/main/java/com/levibostian/wendy/service/Wendy.kt index 10a808d..ffe7273 100644 --- a/wendy/src/main/java/com/levibostian/wendy/service/Wendy.kt +++ b/wendy/src/main/java/com/levibostian/wendy/service/Wendy.kt @@ -7,6 +7,7 @@ import com.levibostian.wendy.WendyConfig import com.levibostian.wendy.db.PendingTaskError import com.levibostian.wendy.db.PendingTasksManager import com.levibostian.wendy.db.PersistedPendingTask +import com.levibostian.wendy.extension.getTaskAssertPopulated import com.levibostian.wendy.job.PendingTaskJobCreator import com.levibostian.wendy.job.PendingTasksJob import com.levibostian.wendy.listeners.PendingTaskStatusListener @@ -93,7 +94,7 @@ class Wendy private constructor(context: Context, internal val tasksFactory: Pen * @throws RuntimeException Wendy will check to make sure that you have remembered to add your argument's [PendingTask] subclass to your instance of [PendingTasksFactory] when you call this method. If your [PendingTasksFactory] returns null (which probably means that you forgot to include a [PendingTask]) then an [RuntimeException] will be thrown. */ fun addTask(pendingTask: PendingTask, resolveErrorIfTaskExists: Boolean = true): Long { - tasksFactory.getTask(pendingTask.tag) ?: RuntimeException("You forgot to add ${pendingTask.tag} to your ${PendingTasksFactory::class.java.simpleName}") + tasksFactory.getTaskAssertPopulated(pendingTask.tag) tasksManager.getExistingTask(pendingTask)?.let { existingPersistedPendingTask -> if (doesErrorExist(existingPersistedPendingTask.id) && resolveErrorIfTaskExists) {