diff --git a/README.md b/README.md index 10a4d378d..911b85862 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ We want to make contributing to this project as easy and transparent as possible - [Parse FCM](/fcm) - [Parse KTX](/ktx) + - [Parse Coroutines](/coroutines) - [ParseUI](https://github.com/parse-community/ParseUI-Android) - [ParseLiveQuery](https://github.com/parse-community/ParseLiveQuery-Android) - [ParseFacebookUtils](https://github.com/parse-community/ParseFacebookUtils-Android) diff --git a/coroutines/.gitignore b/coroutines/.gitignore new file mode 100644 index 000000000..796b96d1c --- /dev/null +++ b/coroutines/.gitignore @@ -0,0 +1 @@ +/build diff --git a/coroutines/README.md b/coroutines/README.md new file mode 100644 index 000000000..14c60a329 --- /dev/null +++ b/coroutines/README.md @@ -0,0 +1,72 @@ +# Parse SDK Android Coroutines +Kotlin coroutines support for Parse Android + +## Setup + +### Installation +After including JitPack: + +```groovy +dependencies { + implementation "com.github.parse-community.Parse-SDK-Android:coroutines:latest.version.here" +} +``` + +## Use Parse Coroutines + +### ParseQuery + +Now we can call a parse query using a synchronous style, this is possible when we use coroutines. We need to use a regular coroutine builder: + +```kotlin +launch { // Coroutine builder + val cat = ParseQuery.getQuery(...).find() + // get cats without callback +} +``` +We use a coroutine builder because `find()` is a suspend function. + +### ParseCloud + +We can call cloud function inline: + + ```kotlin +launch { // Coroutine builder + val catThumb = callCloudFunction("getThumbnail", mapOf("url" to "https://cat.jpg")) + // get cats without callback +} +``` + +### ParseUser + +SignUp: + +```kotlin +launch { // Coroutine builder + user = ParseUser().apply { + setUsername("my name") + setPassword("my pass") + setEmail("email@example.com") + }.also { + signUp() + } +} +``` +Login: + +```kotlin +launch { // Coroutine builder + val user = parseLogIn("username", "password") +} +``` + +## Contributing +When contributing to the `coroutines` module, please first consider if the extension function you are wanting to add would potentially be better suited in the main `parse` module. If it is something specific to Kotlin users or only useful in a Kotlin project, feel free to make a PR adding it to this module. Otherwise, consider adding the addition to the `parse` module itself, so that it is still usable in Java. + +## License + Copyright (c) 2015-present, Parse, LLC. + All rights reserved. + + This source code is licensed under the BSD-style license found in the + LICENSE file in the root directory of this source tree. An additional grant + of patent rights can be found in the PATENTS file in the same directory. diff --git a/coroutines/build.gradle b/coroutines/build.gradle new file mode 100644 index 000000000..11a35c631 --- /dev/null +++ b/coroutines/build.gradle @@ -0,0 +1,36 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + compileSdkVersion rootProject.ext.compileSdkVersion + + defaultConfig { + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode 1 + versionName "1.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + lintOptions { + abortOnError false + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + +} + +dependencies { + api "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + api 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.2' + api 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.2' + implementation project(':parse') +} + +apply from: 'https://raw.githubusercontent.com/Commit451/gradle-android-javadocs/1.1.0/gradle-android-javadocs.gradle' diff --git a/coroutines/proguard-rules.pro b/coroutines/proguard-rules.pro new file mode 100644 index 000000000..9631e4a17 --- /dev/null +++ b/coroutines/proguard-rules.pro @@ -0,0 +1,5 @@ +-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {} +-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {} +-keepclassmembernames class kotlinx.** { + volatile ; +} \ No newline at end of file diff --git a/coroutines/src/main/AndroidManifest.xml b/coroutines/src/main/AndroidManifest.xml new file mode 100644 index 000000000..2c8595932 --- /dev/null +++ b/coroutines/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/coroutines/src/main/java/com/parse/coroutines/ParseCloudCoroutinesExtensions.kt b/coroutines/src/main/java/com/parse/coroutines/ParseCloudCoroutinesExtensions.kt new file mode 100644 index 000000000..d6af14b34 --- /dev/null +++ b/coroutines/src/main/java/com/parse/coroutines/ParseCloudCoroutinesExtensions.kt @@ -0,0 +1,17 @@ +@file:JvmName("ParseCloudCoroutinesExtensions") + +package com.parse.coroutines + +import com.parse.ParseCloud +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +suspend fun callCloudFunction(functionName: String, params: Map): T { + return suspendCoroutine { continuation -> + ParseCloud.callFunctionInBackground(functionName, params) { result, e -> + if (e == null) continuation.resume(result) + else continuation.resumeWithException(e) + } + } +} \ No newline at end of file diff --git a/coroutines/src/main/java/com/parse/coroutines/ParseQueryCoroutinesExtensions.kt b/coroutines/src/main/java/com/parse/coroutines/ParseQueryCoroutinesExtensions.kt new file mode 100644 index 000000000..47de4fec8 --- /dev/null +++ b/coroutines/src/main/java/com/parse/coroutines/ParseQueryCoroutinesExtensions.kt @@ -0,0 +1,36 @@ +@file:JvmName("ParseQueryCoroutinesExtensions") +@file:Suppress("EXTENSION_SHADOWED_BY_MEMBER") + +package com.parse.coroutines + +import com.parse.ParseObject +import com.parse.ParseQuery +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException + +suspend fun ParseQuery.find(): List { + return suspendCancellableCoroutine { continuation -> + continuation.invokeOnCancellation { + cancel() + } + + findInBackground { objects, e -> + if (e == null) continuation.resume(objects) + else continuation.resumeWithException(e) + } + } +} + +suspend fun ParseQuery.count(): Int { + return suspendCancellableCoroutine { continuation -> + continuation.invokeOnCancellation { + cancel() + } + + countInBackground { count, e -> + if (e == null) continuation.resume(count) + else continuation.resumeWithException(e) + } + } +} \ No newline at end of file diff --git a/coroutines/src/main/java/com/parse/coroutines/ParseUserCoroutinesExtensions.kt b/coroutines/src/main/java/com/parse/coroutines/ParseUserCoroutinesExtensions.kt new file mode 100644 index 000000000..ea06f6bd8 --- /dev/null +++ b/coroutines/src/main/java/com/parse/coroutines/ParseUserCoroutinesExtensions.kt @@ -0,0 +1,26 @@ +@file:JvmName("ParseUserCoroutinesExtensions") + +package com.parse.coroutines + +import com.parse.ParseUser +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine + +suspend fun ParseUser.singUp(): ParseUser { + return suspendCoroutine { continuation -> + signUpInBackground { e -> + if (e == null) continuation.resume(this) + else continuation.resumeWithException(e) + } + } +} + +suspend fun parseLogIn(username: String, password: String): ParseUser { + return suspendCoroutine { continuation -> + ParseUser.logInInBackground(username, password) { user, e -> + if (e == null) continuation.resume(user) + else continuation.resumeWithException(e) + } + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 2578bc150..afc5327fa 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,2 @@ -include ':parse', ':fcm', ':gcm', ':ktx' +include ':parse', ':fcm', ':gcm', ':ktx', ':coroutines'