Update Kotlin docs for 2.0.0 (#18912)

* Rename gotrue to auth

* update columns

* rename gotrue to auth (again)

* improve postgrest docs

* add new modules

* rename logout

* Rename some more stuff

* Rename more stuff

* Rename guides

* Rename SSO docs

* Rename "phoneNumber" to "phone"

* rename some more stuff

* Add v2 docs

* Rename postgrest[..] to supabase.from(..)

* Migrate postgrest to new syntax

* Add explain, single & csv

* Rename storage

* Rename some more storage storage methods

* fix invalid docs

* fix duplicate id

* exclude sections for kotlin

* Rename password reset method

* Update tutorial

* Update realtime docs

* Update more realtime docs

* Update module docs

* add link to migration guide

* update installing

* document custom KotlinX Serializer

* update code to pass formatter

* Remove supabase.realtime.connect() docs

* fix typo

* update kotlin quick start guide

* add a note about making network request from the UI code

* run formatter

* add small kotlinx serialization change

* Apply suggestions from code review

Co-authored-by: Charis <26616127+charislam@users.noreply.github.com>

* improve quickstart

---------

Co-authored-by: Tyler <18113850+dshukertjr@users.noreply.github.com>
Co-authored-by: Charis <26616127+charislam@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2023-12-12 01:34:53 +09:00
1 parent e12f467106
commit e426517484
49 files changed
+4839 -682

No files matched your search

@@ -208,7 +208,7 @@ export const REFERENCES: References = {
kotlin: {
name: 'Kotlin',
library: 'supabase-kt',
versions: ['v1'],
versions: ['v2', 'v1'],
icon: '/docs/img/libraries/kotlin-icon.svg',
},
cli: {
@@ -1624,7 +1624,14 @@ export const reference_swift_v1 = {
parent: '/reference',
}
export const reference_kotlin_v0 = {
export const reference_kotlin_v1 = {
icon: 'reference-kotlin',
title: 'kotlin',
url: 'guides/reference/kotlin',
parent: '/reference',
}
export const reference_kotlin_v2 = {
icon: 'reference-kotlin',
title: 'kotlin',
url: 'guides/reference/kotlin',
@@ -1754,7 +1761,7 @@ export const references = [
},
{
label: 'supabase-kt',
versions: ['v0'],
versions: ['v2', 'v1'],
description: 'something about the reference',
icon: '/docs/img/icons/kotlin-icon.svg',
url: '/reference/kotlin/start',
@@ -159,12 +159,19 @@ const menus: Menu[] = [
type: 'reference',
},
{
id: 'reference_kotlin_v0',
path: '/reference/kotlin',
id: 'reference_kotlin_v1',
path: '/reference/kotlin/v1',
commonSectionsFile: 'common-client-libs-sections.json',
specFile: 'supabase_kt_v1.yml',
type: 'reference',
},
{
id: 'reference_kotlin_v2',
path: '/reference/kotlin',
commonSectionsFile: 'common-client-libs-sections.json',
specFile: 'supabase_kt_v2.yml',
type: 'reference',
},
{
id: 'reference_cli',
path: '/reference/cli',
+15 -2
View File
@@ -51,7 +51,17 @@ custom_edit_url: https://github.com/supabase/supabase/edit/master/web/spec/supab
</TabPanel>
</Tabs>
The available modules are: [**gotrue-kt**](https://github.com/supabase-community/supabase-kt/tree/master/GoTrue), [**realtime-kt**](https://github.com/supabase-community/supabase-kt/tree/master/Realtime), [**storage-kt**](https://github.com/supabase-community/supabase-kt/tree/master/Storage), [**functions-kt**](https://github.com/supabase-community/supabase-kt/tree/master/Functions), [**postgrest-kt**](https://github.com/supabase-community/supabase-kt/tree/master/Postgrest), [**apollo-graphql**](https://github.com/supabase-community/supabase-kt/tree/master/plugins/ApolloGraphQL), [**compose-auth**](https://github.com/supabase-community/supabase-kt/tree/master/plugins/ComposeAuth) and [**compose-auth-ui**](https://github.com/supabase-community/supabase-kt/tree/master/plugins/ComposeAuthUI)
The available modules are:
- [**gotrue-kt**](https://github.com/supabase-community/supabase-kt/tree/master/GoTrue)
- [**realtime-kt**](https://github.com/supabase-community/supabase-kt/tree/master/Realtime)
- [**storage-kt**](https://github.com/supabase-community/supabase-kt/tree/master/Storage)
- [**functions-kt**](https://github.com/supabase-community/supabase-kt/tree/master/Functions)
- [**postgrest-kt**](https://github.com/supabase-community/supabase-kt/tree/master/Postgrest)
- [apollo-graphql](https://github.com/supabase-community/supabase-kt/tree/master/plugins/ApolloGraphQL) - Creates an [Apollo GraphQL Client](https://github.com/apollographql/apollo-kotlin) for interacting with the Supabase API.
- [compose-auth](https://github.com/supabase-community/supabase-kt/tree/master/plugins/ComposeAuth) - Provides easy Native Google & Apple Auth for Compose Multiplatform targets.
- [compose-auth-ui](https://github.com/supabase-community/supabase-kt/tree/master/plugins/ComposeAuthUI) - Provides UI Components for Compose Multiplatform.
- [coil-integration](https://github.com/supabase-community/supabase-kt/tree/master/plugins/CoilIntegration) - Provides a [Coil](https://github.com/coil-kt/coil) Integration for displaying images stored in Supabase Storage.
- [imageloader-integration](https://github.com/supabase-community/supabase-kt/tree/master/plugins/ImageLoaderIntegration) - Provides a [Compose ImageLoader](https://github.com/qdsfdhvh/compose-imageloader) Integration for displaying images stored in Supabase Storage.
When using multiple modules, you can also use the BOM dependency to ensure that all modules use the same version:
@@ -265,7 +275,10 @@ By default, [KotlinX Serialization](https://github.com/Kotlin/kotlinx.serializat
</Tabs>
```kotlin
val supabase = createSupabaseClient(supabaseUrl, supabaseKey) {
//Already the default serializer
//Already the default serializer, but you can provide a custom Json instance (optional):
defaultSerializer = KotlinXSerializer(Json {
//apply your custom config
})
}
```
</RefSubLayout.Examples>
@@ -18,6 +18,8 @@ This reference documents every object and method available in Supabase's Kotlin
To see supported Kotlin targets, check the corresponding module README on [GitHub](https://github.com/supabase-community/supabase-kt).
To migrate from version 1.4.X to 2.0.0, see the [migration guide](https://github.com/supabase-community/supabase-kt/blob/master/MIGRATION.md)
</div>
<div className="max-w-xl bg-slate-300 px-4 py-2 rounded-md">
+1 -1
View File
@@ -12,7 +12,7 @@ const clientLibFiles = [
{ fileName: 'supabase_py_v2', label: 'python', version: 'v2', versionSlug: false },
{ fileName: 'supabase_csharp_v0', label: 'csharp', version: 'v0', versionSlug: false },
{ fileName: 'supabase_swift_v1', label: 'swift', version: 'v1', versionSlug: false },
{ fileName: 'supabase_kt_v1', label: 'kotlin', version: 'v0', versionSlug: false },
{ fileName: 'supabase_kt_v2', label: 'kotlin', version: 'v2', versionSlug: false },
]
export function generateReferencePages() {
+6 -2
View File
@@ -102,9 +102,13 @@ const levelsData = {
icon: '/docs/img/icons/menu/reference-swift',
name: 'Swift Reference v1.0',
},
reference_kotlin_v0: {
reference_kotlin_v1: {
icon: '/docs/img/icons/menu/reference-kotlin',
name: 'Kotlin Reference v0.0',
name: 'Kotlin Reference v1.0',
},
reference_kotlin_v2: {
icon: '/docs/img/icons/menu/reference-kotlin',
name: 'Kotlin Reference v2.0',
},
reference_cli: {
icon: '/docs/img/icons/menu/reference-cli',
+5 -5
View File
@@ -65,7 +65,7 @@ To sign up the user, call [signUpWith(Email)](/docs/reference/kotlin/auth-signup
```kotlin
suspend fun signUpNewUser() {
supabase.gotrue.signUpWith(Email) {
supabase.auth.signUpWith(Email) {
email = "example@email.com"
password = "example-password"
}
@@ -110,7 +110,7 @@ When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) t
```kotlin
suspend fun logout() {
supabase.gotrue.logout()
supabase.auth.signOut()
}
```
@@ -156,11 +156,11 @@ Future<void> signInWithEmail() async {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
When your user signs in, call [loginWith(Email)](/docs/reference/kotlin/auth-signinwithpassword) with their email address and password:
When your user signs in, call [signInWith(Email)](/docs/reference/kotlin/auth-signinwithpassword) with their email address and password:
```kotlin
suspend fun signInWithEmail() {
supabase.gotrue.loginWith(Email) {
supabase.auth.signInWith(Email) {
email = "example@email.com"
password = "example-password"
}
@@ -208,7 +208,7 @@ When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) t
```kotlin
suspend fun logout() {
supabase.gotrue.logout()
supabase.auth.signOut()
}
```
@@ -35,10 +35,10 @@ await supabase.auth.resetPasswordForEmail('hello@example.com', {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
Supabase provides a convenient method [`.sendRecoveryEmail`](/docs/reference/kotlin/auth-resetpasswordforemail) to reset a user password. This method takes a parameter of `redirectUrl` which we will use to pass an absolute URL to the update password page. This URL must be saved in your allowed [Redirect URLs](https://supabase.com/dashboard/project/_/auth/url-configuration) list found at [Authentication > Redirect Configuration](https://supabase.com/dashboard/project/_/auth/url-configuration) or it won't redirect the user.
Supabase provides a convenient method [`.resetPasswordForEmail`](/docs/reference/kotlin/auth-resetpasswordforemail) to reset a user password. This method takes a parameter of `redirectUrl` which we will use to pass an absolute URL to the update password page. This URL must be saved in your allowed [Redirect URLs](https://supabase.com/dashboard/project/_/auth/url-configuration) list found at [Authentication > Redirect Configuration](https://supabase.com/dashboard/project/_/auth/url-configuration) or it won't redirect the user.
```kotlin
supabase.gotrue.sendRecoveryEmail(
supabase.auth.resetPasswordForEmail(
email = "hello@example.com",
redirectUrl = "http://example.com/account/update-password"
)
@@ -76,7 +76,7 @@ await supabase.auth.updateUser({ password: new_password })
To update the password we call the [`.modifyUser`](/docs/reference/kotlin/auth-updateuser) method and pass along the new password to this method.
```kotlin
supabase.gotrue.modifyUser {
supabase.auth.modifyUser {
password = "new_password"
}
```
@@ -114,10 +114,10 @@ We are using `next` as our query parameter, but this can name whatever you like.
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
Supabase provides a convenient method [`.sendRecoveryEmail`](/docs/reference/kotlin/auth-resetpasswordforemail) to reset a user password. This method takes a parameter of `redirectUrl` which we will use to pass an absolute URL to the update password page. This URL must be saved in your allowed [Redirect URLs](https://supabase.com/dashboard/project/_/auth/url-configuration) list found at [Authentication > Redirect Configuration](https://supabase.com/dashboard/project/_/auth/url-configuration) or it won't redirect the user.
Supabase provides a convenient method [`.resetPasswordForEmail`](/docs/reference/kotlin/auth-resetpasswordforemail) to reset a user password. This method takes a parameter of `redirectUrl` which we will use to pass an absolute URL to the update password page. This URL must be saved in your allowed [Redirect URLs](https://supabase.com/dashboard/project/_/auth/url-configuration) list found at [Authentication > Redirect Configuration](https://supabase.com/dashboard/project/_/auth/url-configuration) or it won't redirect the user.
```kotlin
supabase.gotrue.sendRecoveryEmail(
supabase.auth.resetPasswordForEmail(
email = "hello@example.com",
redirectUrl = "http://example.com/api/auth/callback?next=/account/update-password"
)
@@ -237,7 +237,7 @@ await supabase.auth.updateUser({ password: new_password })
To update the password we call the [`.modifyUser`](/docs/reference/kotlin/auth-updateuser) method and pass along the new password to this method.
```kotlin
supabase.gotrue.modifyUser {
supabase.auth.modifyUser {
password = "new_password"
}
```
@@ -114,14 +114,14 @@ const { data: profile } = await supabase
```kotlin
// This will return nothing while the user is logged out
val data = supabase.postgrest["profiles"].select(Columns.list("id", "username", "avatar_url", "website"))
val data = supabase.from("profiles").select(Columns.list("id", "username", "avatar_url", "website"))
// After the user is logged in, this will only return
// the logged-in user's data - in this case a single row
supabase.gotrue.sendOtpTo(Email) {
supabase.auth.signInWith(OTP) {
this.email = email
}
val data = supabase.postgrest["profiles"].select(Columns.list("id", "username", "avatar_url", "website"))
val data = supabase.from("profiles").select(Columns.list("id", "username", "avatar_url", "website"))
```
</TabPanel>
@@ -169,7 +169,7 @@ const { data, error } = await supabase.auth.signUp({
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val data = supabase.gotrue.signUpWith(Email) {
val data = supabase.auth.signUpWith(Email) {
email = "example@email.com"
password = "example-password"
data = buildJsonObject {
@@ -204,9 +204,9 @@ let metadata = user.user_metadata
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val user = supabase.gotrue.retrieveUserForCurrentSession()
val user = supabase.auth.retrieveUserForCurrentSession()
//Or you can use the user from the current session:
val user = supabase.gotrue.currentUserOrNull()
val user = supabase.auth.currentUserOrNull()
val metadata = user?.userMetadata
```
@@ -59,11 +59,11 @@ Future<void> signInWithEmailOtp() async {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
When your user signs in, call [sendOtpTo()](/docs/reference/kotlin/auth-signinwithotp) with their email address:
When your user signs in, call [signInWith(OTP)](/docs/reference/kotlin/auth-signinwithotp) with their email address:
```kotlin
suspend fun signInWithEmailOtp() {
supabase.gotrue.sendOtpTo(Email) {
supabase.auth.signInWith(OTP) {
email = "example@email.com"
}
}
@@ -70,7 +70,7 @@ Read the [Deep Linking Documentation](/docs/guides/auth/native-mobile-deep-linki
</TabPanel>
<TabPanel id="dart" label="Dart">
To start the sign in process call [signIn()](/docs/reference/dart/auth-signinwithotp) with their email address:
When your user signs in, call [signIn()](/docs/reference/dart/auth-signinwithotp) with their email address:
```dart
Future<void> signInWithEmail() async {
@@ -81,11 +81,11 @@ Future<void> signInWithEmail() async {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
To start the sign in process call [sendOtpTo()](/docs/reference/kotlin/auth-signinwithotp) with their email address:
To start the sign in process call [signInWith(OTP)](/docs/reference/kotlin/auth-signinwithotp) with their email address:
```kotlin
suspend fun loginWithEmail() {
supabase.gotrue.sendOtpTo(Email) {
suspend fun signInWithEmail() {
supabase.auth.signInWith(OTP) {
email = "example@email.com"
}
}
@@ -128,7 +128,7 @@ When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) t
```kotlin
suspend fun logout() {
supabase.gotrue.logout()
supabase.auth.signOut()
}
```
@@ -105,8 +105,8 @@ const {
<TabPanel id="kotlin" label="Kotlin">
```kotlin
supabase.gotrue.signUpWith(Phone) {
phoneNumber = "+13334445555"
supabase.auth.signUpWith(Phone) {
phone = "+13334445555"
password = "some-password"
}
```
@@ -156,9 +156,9 @@ const {
You should present a form to the user so they can input the 6 digit pin, then send it along with the phone number to `verifyPhoneOtp`:
```kotlin
supabase.gotrue.verifyPhoneOtp(
supabase.auth.verifyPhoneOtp(
type = OtpType.Phone.SMS,
phoneNumber = "+13334445555",
phone = "+13334445555",
token = "123456"
)
```
@@ -215,8 +215,8 @@ const { user, error } = await supabase.auth.signInWithPassword({
<TabPanel id="kotlin" label="Kotlin">
```kotlin
supabase.gotrue.loginWith(Phone) {
phoneNumber = "+13334445555"
supabase.auth.signInWith(Phone) {
phone = "+13334445555"
password = "some-password"
}
```
@@ -261,11 +261,11 @@ const { data, error } = await supabase.auth.signInWithOtp({
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
In Kotlin, we can use the `sendOtpTo` method and change the property `phoneNumber`
In Kotlin, we can use the `signInWith(OTP)` method and change the property `phone`
```kotlin
supabase.gotrue.sendOtpTo(Phone) {
phoneNumber = "+13334445555"
supabase.auth.signInWith(OTP) {
phone = "+13334445555"
}
```
@@ -309,9 +309,9 @@ const {
<TabPanel id="kotlin" label="Kotlin">
```kotlin
supabase.gotrue.verifyPhoneOtp(
supabase.auth.verifyPhoneOtp(
type = OtpType.Phone.SMS,
phoneNumber = "+13334445555",
phone = "+13334445555",
token = "123456"
)
```
@@ -159,8 +159,8 @@ const {
<TabPanel id="kotlin" label="Kotlin">
```kotlin
supabase.gotrue.signUpWith(Phone) {
phoneNumber = "+13334445555"
supabase.auth.signUpWith(Phone) {
phone = "+13334445555"
password = "some-password"
}
```
@@ -211,9 +211,9 @@ const {
You should present a form to the user so they can input the 6 digit pin, then send it along with the phone number to `verifyPhoneOtp`:
```kotlin
supabase.gotrue.verifyPhoneOtp(
supabase.auth.verifyPhoneOtp(
type = OtpType.Phone.SMS,
phoneNumber = "+13334445555",
phone = "+13334445555",
token = "123456"
)
```
@@ -270,8 +270,8 @@ const { user, error } = await supabase.auth.signInWithPassword({
<TabPanel id="kotlin" label="Kotlin">
```kotlin
supabase.gotrue.loginWith(Phone) {
phoneNumber = "+13334445555"
supabase.auth.signInWith(Phone) {
phone = "+13334445555"
password = "some-password"
}
```
@@ -316,11 +316,11 @@ const { data, error } = await supabase.auth.signInWithOtp({
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
In Kotlin, we can use the `sendOtpTo` method and change the property `phoneNumber`
In Kotlin, we can use the `signInWith(OTP)` method and change the property `phone`
```kotlin
supabase.gotrue.sendOtpTo(Phone) {
phoneNumber = "+13334445555"
supabase.auth.signInWith(OTP) {
phone = "+13334445555"
}
```
@@ -365,9 +365,9 @@ const {
<TabPanel id="kotlin" label="Kotlin">
```kotlin
supabase.gotrue.verifyPhoneOtp(
supabase.auth.verifyPhoneOtp(
type = OtpType.Phone.SMS,
phoneNumber = "+13334445555",
phone = "+13334445555",
token = "123456"
)
```
@@ -100,8 +100,8 @@ const {
<TabPanel id="kotlin" label="Kotlin">
```kotlin
supabase.gotrue.signUpWith(Phone) {
phoneNumber = "+13334445555"
supabase.auth.signUpWith(Phone) {
phone = "+13334445555"
password = "some-password"
}
```
@@ -151,9 +151,9 @@ const {
You should present a form to the user so they can input the 6 digit pin, then send it along with the phone number to `verifyPhoneOtp`:
```kotlin
supabase.gotrue.verifyPhoneOtp(
supabase.auth.verifyPhoneOtp(
type = OtpType.Phone.SMS,
phoneNumber = "+13334445555",
phone = "+13334445555",
token = "123456"
)
```
@@ -210,8 +210,8 @@ const { user, error } = await supabase.auth.signInWithPassword({
<TabPanel id="kotlin" label="Kotlin">
```kotlin
supabase.gotrue.loginWith(Phone) {
phoneNumber = "+13334445555"
supabase.auth.signInWith(Phone) {
phone = "+13334445555"
password = "some-password"
}
```
@@ -256,11 +256,11 @@ const { data, error } = await supabase.auth.signInWithOtp({
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
In Kotlin, we can use the `sendOtpTo` method and change the property `phoneNumber`
In Kotlin, we can use the `signInWith(OTP)` method and change the property `phone`
```kotlin
supabase.gotrue.sendOtpTo(Phone) {
phoneNumber = "+13334445555"
supabase.auth.signInWith(OTP) {
phone = "+13334445555"
}
```
@@ -304,9 +304,9 @@ const {
<TabPanel id="kotlin" label="Kotlin">
```kotlin
supabase.gotrue.verifyPhoneOtp(
supabase.auth.verifyPhoneOtp(
type = OtpType.Phone.SMS,
phoneNumber = "+13334445555",
phone = "+13334445555",
token = "123456"
)
```
@@ -315,7 +315,7 @@ When developing with Expo, you can test Sign in with Apple via the Expo Go app,
## Using Native Sign in with Apple in Kotlin
When using [Compose Multiplatform](https://github.com/JetBrains/compose-multiplatform/), you can use the [compose-auth](https://supabase.com/docs/reference/kotlin/installing) plugin. On iOS it uses Native Apple Login automatically and on other platforms it uses `gotrue.loginWith(Apple)`.
When using [Compose Multiplatform](https://github.com/JetBrains/compose-multiplatform/), you can use the [compose-auth](https://supabase.com/docs/reference/kotlin/installing) plugin. On iOS it uses Native Apple Login automatically and on other platforms it uses `gotrue.signInWith(Apple)`.
**Initialize the Supabase Client**
@@ -140,11 +140,11 @@ async function signInWithAzure() {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
When your user signs in, call [loginWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Azure` as the `Provider`:
When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Azure` as the `Provider`:
```kotlin
suspend fun signInWithAzure() {
supabase.gotrue.loginWith(Azure) {
supabase.auth.signInWith(Azure) {
scopes.add("email")
}
}
@@ -177,7 +177,7 @@ When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) t
```kotlin
suspend fun signOut() {
supabase.gotrue.logout()
supabase.auth.signOut()
}
```
@@ -213,7 +213,7 @@ async function signInWithAzure() {
```kotlin
suspend fun signInWithAzure() {
supabase.gotrue.loginWith(Azure) {
supabase.auth.signInWith(Azure) {
scopes.add("offline_access")
}
}
@@ -70,11 +70,11 @@ async function signInWithBitbucket() {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
When your user signs in, call [loginWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Bitbucket` as the `Provider`:
When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Bitbucket` as the `Provider`:
```kotlin
suspend fun signInWithBitbucket() {
supabase.gotrue.loginWith(Bitbucket)
supabase.auth.signInWith(Bitbucket)
}
```
@@ -105,7 +105,7 @@ When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) t
```kotlin
suspend fun signOut() {
supabase.gotrue.logout()
supabase.auth.signOut()
}
```
@@ -69,11 +69,11 @@ async function signInWithDiscord() {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
When your user signs in, call [loginWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Discord` as the `Provider`:
When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Discord` as the `Provider`:
```kotlin
suspend fun signInWithDiscord() {
supabase.gotrue.loginWith(Discord)
supabase.auth.signInWith(Discord)
}
```
@@ -106,7 +106,7 @@ When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) t
```kotlin
suspend fun signOut() {
supabase.gotrue.logout()
supabase.auth.signOut()
}
```
@@ -86,11 +86,11 @@ async function signInWithFacebook() {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
When your user signs in, call [loginWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Facebook` as the `Provider`:
When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Facebook` as the `Provider`:
```kotlin
suspend fun signInWithFacebook() {
supabase.gotrue.loginWith(Facebook)
supabase.auth.signInWith(Facebook)
}
```
@@ -121,7 +121,7 @@ When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) t
```kotlin
suspend fun signOut() {
supabase.gotrue.logout()
supabase.auth.signOut()
}
```
@@ -70,11 +70,11 @@ async function signInWithFigma() {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
When your user signs in, call [loginWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Figma` as the `Provider`:
When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Figma` as the `Provider`:
```kotlin
suspend fun signInWithFigma() {
supabase.gotrue.loginWith(Figma)
supabase.auth.signInWith(Figma)
}
```
@@ -105,7 +105,7 @@ When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) t
```kotlin
suspend fun signOut() {
supabase.gotrue.logout()
supabase.auth.signOut()
}
```
@@ -81,11 +81,11 @@ async function signInWithGithub() {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
When your user signs in, call [loginWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Github` as the `Provider`:
When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Github` as the `Provider`:
```kotlin
suspend fun signInWithGithub() {
supabase.gotrue.loginWith(Github)
supabase.auth.signInWith(Github)
}
```
@@ -116,7 +116,7 @@ When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) t
```kotlin
suspend fun signOut() {
supabase.gotrue.logout()
supabase.auth.signOut()
}
```
@@ -67,11 +67,11 @@ async function signInWithGitLab() {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
When your user signs in, call [loginWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Gitlab` as the `Provider`:
When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Gitlab` as the `Provider`:
```kotlin
suspend fun signInWithGitLab() {
supabase.gotrue.loginWith(Gitlab)
supabase.auth.signInWith(Gitlab)
}
```
@@ -102,7 +102,7 @@ When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) t
```kotlin
suspend fun signOut() {
supabase.gotrue.logout()
supabase.auth.signOut()
}
```
@@ -307,7 +307,7 @@ Before you can use Sign in with Google, you need to obtain a [Google Cloud Platf
When the user provides consent, Google issues an identity token (commonly abbreviated as ID token) that is then sent to your project's Supabase Auth server. When valid, a new user session is started by issuing an access and refresh token from Supabase Auth.
When using [Compose Multiplatform](https://github.com/JetBrains/compose-multiplatform/), you can use the [compose-auth](https://supabase.com/docs/reference/kotlin/installing) plugin. On Android it uses Google OneTap automatically and on other platforms it uses `gotrue.loginWith(Google)`.
When using [Compose Multiplatform](https://github.com/JetBrains/compose-multiplatform/), you can use the [compose-auth](https://supabase.com/docs/reference/kotlin/installing) plugin. On Android it uses Google OneTap (or Credential Manager on Android 14+) automatically and on other platforms it uses `auth.signInWith(Google)`.
Alternatively, you can have a look at [Google One Tap](https://developers.google.com/identity/one-tap/android/overview), just login with the IdToken after you receive it.
**Initialize the Supabase Client**
@@ -92,11 +92,11 @@ async function signInWithKakao() {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
When your user signs in, call [loginWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Kakao` as the `Provider`:
When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Kakao` as the `Provider`:
```kotlin
suspend fun signInWithKakao() {
supabase.gotrue.loginWith(Kakao)
supabase.auth.signInWith(Kakao)
}
```
@@ -127,7 +127,7 @@ When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) t
```kotlin
suspend fun signOut() {
supabase.gotrue.logout()
supabase.auth.signOut()
}
```
@@ -86,11 +86,11 @@ async function signInWithKeycloak() {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
When your user signs in, call [loginWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Keycloak` as the `Provider`:
When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Keycloak` as the `Provider`:
```kotlin
suspend fun signInWithKeycloak() {
supabase.gotrue.loginWith(Keycloak) {
supabase.auth.signInWith(Keycloak) {
scopes.add("openid")
}
}
@@ -123,7 +123,7 @@ When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) t
```kotlin
suspend fun signOut() {
supabase.gotrue.logout()
supabase.auth.signOut()
}
```
@@ -78,11 +78,11 @@ async function signInWithLinkedIn() {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
When your user signs in, call [loginWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `LinkedIn` as the `Provider`:
When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `LinkedIn` as the `Provider`:
```kotlin
suspend fun signInWithKaLinkedIn() {
supabase.gotrue.loginWith(LinkedIn)
supabase.auth.signInWith(LinkedIn)
}
```
@@ -113,7 +113,7 @@ When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) t
```kotlin
suspend fun signOut() {
supabase.gotrue.logout()
supabase.auth.signOut()
}
```
@@ -70,11 +70,11 @@ async function signInWithNotion() {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
When your user signs in, call [loginWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Notion` as the `Provider`:
When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Notion` as the `Provider`:
```kotlin
suspend fun signInWithNotion() {
supabase.gotrue.loginWith(Notion)
supabase.auth.signInWith(Notion)
}
```
@@ -105,7 +105,7 @@ When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) t
```kotlin
suspend fun signOut() {
supabase.gotrue.logout()
supabase.auth.signOut()
}
```
@@ -88,11 +88,11 @@ async function signInWithSlack() {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
When your user signs in, call [loginWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Slack` as the `Provider`:
When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Slack` as the `Provider`:
```kotlin
suspend fun signInWithSlack() {
supabase.gotrue.loginWith(Slack)
supabase.auth.signInWith(Slack)
}
```
@@ -123,7 +123,7 @@ When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) t
```kotlin
suspend fun signOut() {
supabase.gotrue.logout()
supabase.auth.signOut()
}
```
@@ -74,11 +74,11 @@ async function signInWithSpotify() {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
When your user signs in, call [loginWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Spotify` as the `Provider`:
When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Spotify` as the `Provider`:
```kotlin
suspend fun signInWithSpotify() {
supabase.gotrue.loginWith(Spotify)
supabase.auth.signInWith(Spotify)
}
```
@@ -109,7 +109,7 @@ When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) t
```kotlin
suspend fun signOut() {
supabase.gotrue.logout()
supabase.auth.signOut()
}
```
@@ -85,11 +85,11 @@ async function signInWithTwitch() {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
When your user signs in, call [loginWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Twitch` as the `Provider`:
When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Twitch` as the `Provider`:
```kotlin
suspend fun signInWithTwitch() {
supabase.gotrue.loginWith(Twitch)
supabase.auth.signInWith(Twitch)
}
```
@@ -120,7 +120,7 @@ When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) t
```kotlin
suspend fun signOut() {
supabase.gotrue.logout()
supabase.auth.signOut()
}
```
@@ -76,11 +76,11 @@ async function signInWithTwitter() {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
When your user signs in, call [loginWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Twitter` as the `Provider`:
When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Twitter` as the `Provider`:
```kotlin
suspend fun signInWithTwitter() {
supabase.gotrue.loginWith(Twitter)
supabase.auth.signInWith(Twitter)
}
```
@@ -111,7 +111,7 @@ When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) t
```kotlin
suspend fun signOut() {
supabase.gotrue.logout()
supabase.auth.signOut()
}
```
@@ -83,11 +83,11 @@ Refer to the [WorkOS Documentation](https://workos.com/docs/reference/sso/author
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
When your user signs in, call [loginWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `WorkOS` as the `Provider`:
When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `WorkOS` as the `Provider`:
```kotlin
suspend fun signInWithWorkOS() {
supabase.gotrue.loginWith(WorkOS) {
supabase.auth.signInWith(WorkOS) {
queryParams["connection"] = "<your_connection>"
queryParams["organization"] = "<your_organization>"
queryParams["workos_provider"] = "<your_provider>"
@@ -124,7 +124,7 @@ When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) t
```kotlin
suspend fun signOut() {
supabase.gotrue.logout()
supabase.auth.signOut()
}
```
@@ -83,11 +83,11 @@ async function signInWithZoom() {
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
When your user signs in, call [loginWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Zoom` as the `Provider`:
When your user signs in, call [signInWith(Provider)](/docs/reference/kotlin/auth-signinwithoauth) with `Zoom` as the `Provider`:
```kotlin
suspend fun signInWithZoom() {
supabase.gotrue.loginWith(Zoom)
supabase.auth.signInWith(Zoom)
}
```
@@ -118,7 +118,7 @@ When your user signs out, call [logout()](/docs/reference/kotlin/auth-signout) t
```kotlin
suspend fun signOut() {
supabase.gotrue.logout()
supabase.auth.signOut()
}
```
@@ -208,10 +208,12 @@ Calling [`signInWithSSO`](/docs/reference/javascript/auth-signinwithsso) starts
<TabPanel id="kotlin" label="Kotlin">
```kotlin
supabase.gotrue.loginWith(SSO.withDomain("company.com"))
supabase.auth.signInWith(SSO) {
domain = "company.com"
}
```
Calling [`loginWith(SSO)`](/docs/reference/kotlin/auth-signinwithsso) starts the sign-in process using the identity provider registered for the `company.com` domain name. It is not required that identity providers be assigned one or multiple domain names, in which case you can use the provider's unique ID instead.
Calling [`signInWith(SSO)`](/docs/reference/kotlin/auth-signinwithsso) starts the sign-in process using the identity provider registered for the `company.com` domain name. It is not required that identity providers be assigned one or multiple domain names, in which case you can use the provider's unique ID instead.
</TabPanel>
</Tabs>
@@ -21,22 +21,17 @@ export const meta = {
<StepHikeCompact.Code>
```sql SQL_EDITOR
-- Create products table
create table
public.products (
_id bigint generated by default as identity not null,
productid text not null,
name text null,
description text null,
price real null,
image text null,
category text null,
nutrition text null,
constraint products_pkey primary key (productid),
) tablespace pg_default;
````
```sql SQL_EDITOR
-- Create the table
CREATE TABLE countries (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
-- Insert some sample data into the table
INSERT INTO countries (name) VALUES ('United States');
INSERT INTO countries (name) VALUES ('Canada');
INSERT INTO countries (name) VALUES ('Mexico');
```
</StepHikeCompact.Code>
@@ -53,15 +48,28 @@ export const meta = {
<StepHikeCompact.Step step={3}>
<StepHikeCompact.Details title="Install the Supabase client library">
Import Supabase and all required dependencies. Replace the version placeholders `$supabase_version` and `$ktor_version` with the respective latest versions.
<StepHikeCompact.Details title="Install the Dependencies">
Open `build.gradle.kts` (app) file and add the serialization plug, Ktor client, and Supabase client.
Replace the version placeholders `$kotlin_version` with the Kotlin version of the project, and `$supabase_version` and `$ktor_version` with the respective latest versions.
The latest supabase-kt version can be found [here](https://github.com/supabase-community/supabase-kt/releases) and Ktor version can be found [here](https://ktor.io/docs/welcome.html).
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```kotlin
implementation "io.github.jan-tennert.supabase:postgrest-kt:$supabase_version"
implementation "io.ktor:ktor-client-android:$ktor_version"
plugins {
...
kotlin("plugin.serialization") version "$kotlin_version"
}
...
dependencies {
...
implementation(platform("io.github.jan-tennert.supabase:bom:$supabase_version"))
implementation("io.github.jan-tennert.supabase:postgrest-kt")
implementation("io.ktor:ktor-client-android:$ktor_version")
}
```
</StepHikeCompact.Code>
@@ -70,17 +78,15 @@ export const meta = {
<StepHikeCompact.Step step={4}>
<StepHikeCompact.Details title="Install the serializable plugin">
Open the `build.gradle` (app), add the serialization plugin to use annotation for data parsing. Please note that the plugin version should be the same as the Kotlin version in your app.
<StepHikeCompact.Details title="Add internet access permission">
Add the following line to the `AndroidManifest.xml` file under the `manifest` tag and outside the `application` tag.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```kotlin
plugins {
...
id 'org.jetbrains.kotlin.plugin.serialization' version '$kotlin_version'
...
}
```xml
...
<uses-permission android:name="android.permission.INTERNET" />
...
```
</StepHikeCompact.Code>
@@ -90,18 +96,25 @@ export const meta = {
<StepHikeCompact.Step step={5}>
<StepHikeCompact.Details title="Initialize the Supabase client">
You can create a Supabase client whenever you need to perform an API call. That being said, it is recommended to use a dependency injection library like [Hilt](https://developer.android.com/training/dependency-injection/hilt-android#kts).
You can create a Supabase client whenever you need to perform an API call.
For the sake of simplicity, we will create a client in the `MainActivity.kt` file at the top just below the imports.
Replace the `supabaseUrl` and `supabaseKey` with your own found in [your dashboard](https://supabase.com/dashboard/project/_/settings/api).
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```kotlin
val client = createSupabaseClient(
import ...
val supabase = createSupabaseClient(
supabaseUrl = "https://xyzcompany.supabase.co",
supabaseKey = "public-anon-key"
supabaseKey = "your_public_anon_key"
) {
install(Postgrest)
}
...
```
</StepHikeCompact.Code>
@@ -110,29 +123,18 @@ export const meta = {
<StepHikeCompact.Step step={6}>
<StepHikeCompact.Details title="Create a data transfer object">
<StepHikeCompact.Details title="Create a data model for countries">
Create a serializable data class to represent the data from the database.
Add the following below the `createSupabaseClient` function in the `MainActivity.kt` file.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```kotlin
@Serializable
data class ProductDto(
@SerialName("productid")
val productId: String,
@SerialName("name")
data class Country(
val id: Int,
val name: String,
@SerialName("description")
val description: String,
@SerialName("price")
val price: Double,
@SerialName("image")
val image: String,
@SerialName("category")
val category: String,
@SerialName("nutrition")
val nutrition: String,
@SerialName("_id")
val _id: Int,
)
```
@@ -142,48 +144,52 @@ export const meta = {
<StepHikeCompact.Step step={7}>
<StepHikeCompact.Details title="Create a domain object">
This kind of object will be consumed by the view.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```kotlin
data class Product(
val productId: String,
val name: String,
val description: String,
val price: Double,
val image: String,
val category: String,
val nutrition: String,
val _id: Int,
)
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={8}>
<StepHikeCompact.Details title="Query data from the app">
Create a repository to interact with the data source.
Use `LaunchedEffect` to fetch data from the database and display it in a `LazyColumn`.
Replace the default `MainActivity` class with the following code.
Note that we are making a network request from our UI code. In production, you should probably use a `ViewModel` to separate the UI and data fetching logic.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```kotlin
interface ProductRepository {
fun getProducts(): List<ProductDto>
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
SupabaseTutorialTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
CountriesList()
}
}
}
}
}
class ProductRepositoryImpl @Inject constructor(
private val postgrest: Postgrest,
) : ProductRepository {
override suspend fun getProducts(): List<ProductDto> {
val result = client.postgrest["products"]
.select().decodeList<ProductDto>()
// Handle result data for next step
return result
@Composable
fun CountriesList() {
var countries by remember { mutableStateOf<List<Country>>(listOf()) }
LaunchedEffect(Unit) {
withContext(Dispatchers.IO) {
countries = supabase.from("countries")
.select().decodeList<Country>()
}
}
LazyColumn {
items(
countries,
key = { country -> country.id },
) { country ->
Text(
country.name,
modifier = Modifier.padding(8.dp),
)
}
}
}
```
@@ -191,105 +197,9 @@ export const meta = {
</StepHikeCompact.Step>
<StepHikeCompact.Step step={9}>
<StepHikeCompact.Details title="Create a module to provide repository">
Use [Hilt](https://developer.android.com/training/dependency-injection/hilt-android) for dependency injection.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```kotlin
InstallIn(SingletonComponent::class)
@Module
abstract class RepositoryModule {
@Binds
abstract fun bindProductRepository(impl: ProductRepositoryImpl): ProductRepository
}
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={10}>
<StepHikeCompact.Details title="Get data from ViewModel inside a coroutine scope">
Add the `@Inject` annotation to use the repository in a ViewModel.
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```kotlin
class ProductListViewModel @Inject constructor(
private val productRepository: ProductRepository
) : ViewModel() {
private val _productList = MutableStateFlow<List<Product>?>(listOf())
val productList: Flow<List<Product>?> = _productList
init {
getProducts()
}
fun getProducts() {
viewModelScope.launch {
val products = productRepository.getProducts()
_productList.emit(products?.map { it -> it.asDomainModel() })
}
}
private fun ProductDto.asDomainModel(): Product {
return Product(
productId = this.productId,
name = this.name,
price = this.price,
image = this.image,
description = this.description,
category = this.category,
nutrition = this.nutrition,
_id = this._id
)
}
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={11}>
<StepHikeCompact.Details title="Observe data in a Composable">
</StepHikeCompact.Details>
<StepHikeCompact.Code>
```kotlin
@Composable
fun ProductListScreen(
modifier: Modifier = Modifier,
navController: NavController,
viewModel: ProductListViewModel = hiltViewModel(),
) {
val productList = viewModel.productList.collectAsState(initial = listOf()).value
if (!productList.isNullOrEmpty()) {
LazyColumn(
modifier = modifier.padding(24.dp),
contentPadding = PaddingValues(5.dp)
) {
items(productList) { item ->
ProductListItem(
product = item,
modifier = modifier,
onClick = {
navController.navigate(
ProductDetailsDestination.createRouteWithParam(
item.id
)
)
},
)
}
}
}
}
```
</StepHikeCompact.Code>
</StepHikeCompact.Step>
<StepHikeCompact.Step step={12}>
<StepHikeCompact.Step step={8}>
<StepHikeCompact.Details title="Start the app">
Run the app on an emulator or a physical device by clicking the `Run app` button in Android Studio.
</StepHikeCompact.Details>
</StepHikeCompact.Step>
@@ -127,11 +127,12 @@ Open the `AndroidManifest.xml` file, update name property of Application tag:
```
Add annotation for the `MainActivity`:
Create the `MainActivity`:
```kotlin
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
//This will come later
}
```
@@ -152,7 +153,7 @@ object SupabaseModule {
supabaseKey = BuildConfig.SUPABASE_ANON_KEY
) {
install(Postgrest)
install(GoTrue) {
install(Auth) {
flowType = FlowType.PKCE
scheme = "app"
host = "supabase.com"
@@ -169,8 +170,8 @@ object SupabaseModule {
@Provides
@Singleton
fun provideSupabaseGoTrue(client: SupabaseClient): GoTrue {
return client.gotrue
fun provideSupabaseAuth(client: SupabaseClient): Auth {
return client.auth
}
@@ -246,7 +247,7 @@ class ProductRepositoryImpl @Inject constructor(
name = product.name,
price = product.price,
)
postgrest["products"].insert(productDto)
postgrest.from("products").insert(productDto)
true
}
true
@@ -257,7 +258,7 @@ class ProductRepositoryImpl @Inject constructor(
override suspend fun getProducts(): List<ProductDto>? {
return withContext(Dispatchers.IO) {
val result = postgrest["products"]
val result = postgrest.from("products")
.select().decodeList<ProductDto>()
result
}
@@ -266,7 +267,7 @@ class ProductRepositoryImpl @Inject constructor(
override suspend fun getProduct(id: String): ProductDto {
return withContext(Dispatchers.IO) {
postgrest["products"].select {
postgrest.from("products").select {
eq("id", id)
}.decodeSingle<ProductDto>()
}
@@ -274,7 +275,7 @@ class ProductRepositoryImpl @Inject constructor(
override suspend fun deleteProduct(id: String) {
return withContext(Dispatchers.IO) {
postgrest["products"].delete {
postgrest.from("products").delete {
eq("id", id)
}
}
@@ -290,12 +291,12 @@ class ProductRepositoryImpl @Inject constructor(
withContext(Dispatchers.IO) {
if (imageFile.isNotEmpty()) {
val imageUrl =
storage["Product%20Image"].upload(
storage.from("Product%20Image").upload(
path = "$imageName.png",
data = imageFile,
upsert = true
)
postgrest["products"].update({
postgrest.from("products").update({
set("name", name)
set("price", price)
set("image", buildImageUrl(imageFileName = imageUrl))
@@ -303,7 +304,7 @@ class ProductRepositoryImpl @Inject constructor(
eq("id", id)
}
} else {
postgrest["products"].update({
postgrest.from("products").update({
set("name", name)
set("price", price)
}) {
@@ -332,11 +333,11 @@ interface AuthenticationRepository {
```kotlin
class AuthenticationRepositoryImpl @Inject constructor(
private val goTrue: GoTrue
private val auth: Auth
) : AuthenticationRepository {
override suspend fun signIn(email: String, password: String): Boolean {
return try {
goTrue.loginWith(Email) {
auth.signInWith(Email) {
this.email = email
this.password = password
}
@@ -348,7 +349,7 @@ class AuthenticationRepositoryImpl @Inject constructor(
override suspend fun signUp(email: String, password: String): Boolean {
return try {
goTrue.signUpWith(Email) {
auth.signUpWith(Email) {
this.email = email
this.password = password
}
@@ -360,7 +361,7 @@ class AuthenticationRepositoryImpl @Inject constructor(
override suspend fun signInWithGoogle(): Boolean {
return try {
goTrue.loginWith(Google)
auth.signInWith(Google)
true
} catch (e: Exception) {
false
@@ -371,6 +372,49 @@ class AuthenticationRepositoryImpl @Inject constructor(
### Implement screens
To navigate screens, use the AndroidX navigation library. For routes, implement a `Destination` interface:
```kotlin
interface Destination {
val route: String
val title: String
}
object ProductListDestination : Destination {
override val route = "product_list"
override val title = "Product List"
}
object ProductDetailsDestination : Destination {
override val route = "product_details"
override val title = "Product Details"
const val productId = "product_id"
val arguments = listOf(navArgument(name = productId) {
type = NavType.StringType
})
fun createRouteWithParam(productId: String) = "$route/${productId}"
}
object AddProductDestination : Destination {
override val route = "add_product"
override val title = "Add Product"
}
object AuthenticationDestination: Destination {
override val route = "authentication"
override val title = "Authentication"
}
object SignUpDestination: Destination {
override val route = "signup"
override val title = "Sign Up"
}
```
This will help later for navigating between screens.
Create a `ProductListViewModel`:
```kotlin
@@ -1029,7 +1073,7 @@ class SignInViewModel @Inject constructor(
_password.value = password
}
fun onLogin() {
fun onSignIn() {
viewModelScope.launch {
authenticationRepository.signIn(
email = _email.value,
@@ -1162,9 +1206,77 @@ fun SignInScreen(
}
```
### Implement the `MainActivity`
In the `MainActivity` you created earlier, show your newly created screens:
```kotlin
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@Inject
lateinit var supabaseClient: SupabaseClient
@OptIn(ExperimentalMaterial3Api::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
ManageProductsTheme {
// A surface container using the 'background' color from the theme
val navController = rememberNavController()
val currentBackStack by navController.currentBackStackEntryAsState()
val currentDestination = currentBackStack?.destination
Scaffold { innerPadding ->
NavHost(
navController,
startDestination = ProductListDestination.route,
Modifier.padding(innerPadding)
) {
composable(ProductListDestination.route) {
ProductListScreen(
navController = navController
)
}
composable(AuthenticationDestination.route) {
SignInScreen(
navController = navController
)
}
composable(SignUpDestination.route) {
SignUpScreen(
navController = navController
)
}
composable(AddProductDestination.route) {
AddProductScreen(
navController = navController
)
}
composable(
route = "${ProductDetailsDestination.route}/{${ProductDetailsDestination.productId}}",
arguments = ProductDetailsDestination.arguments
) { navBackStackEntry ->
val productId =
navBackStackEntry.arguments?.getString(ProductDetailsDestination.productId)
ProductDetailsScreen(
productId = productId,
navController = navController,
)
}
}
}
}
}
}
}
```
### Create the success screen
Implement a "Sign In" success screen. Create new "Empty Activity" and in `AndroidManifest.xml`, add a deep link, make sure `scheme` and `host` are the same as the ones set in the GoTrue instance.
To handle OAuth and OTP signins, create a new activity to handle the deeplink you set in `AndroidManifest.xml`:
```xml
<?xml version="1.0" encoding="utf-8"?>
@@ -1209,6 +1321,8 @@ Implement a "Sign In" success screen. Create new "Empty Activity" and in `Androi
</manifest>
```
Then create the `DeepLinkHandlerActivity`:
```kotlin
@AndroidEntryPoint
class DeepLinkHandlerActivity : ComponentActivity() {
+298 -287
View File
@@ -24,46 +24,46 @@ Go to your Supabase project's [API Settings](https://supabase.com/dashboard/proj
defaultActiveId="js"
queryGroup="language"
>
<TabPanel id="js" label="JavaScript">
<TabPanel id="js" label="JavaScript">
```js
import { createClient } from '@supabase/supabase-js'
```js
import { createClient } from '@supabase/supabase-js'
const SUPABASE_URL = 'https://<project>.supabase.co'
const SUPABASE_KEY = '<your-anon-key>'
const SUPABASE_URL = 'https://<project>.supabase.co'
const SUPABASE_KEY = '<your-anon-key>'
const client = createClient(SUPABASE_URL, SUPABASE_KEY)
```
const client = createClient(SUPABASE_URL, SUPABASE_KEY)
```
</TabPanel>
<TabPanel id="dart" label="Dart">
</TabPanel>
<TabPanel id="dart" label="Dart">
```dart
import 'package:supabase_flutter/supabase_flutter.dart';
```dart
import 'package:supabase_flutter/supabase_flutter.dart';
void main() async {
Supabase.initialize(
url: 'https://<project>.supabase.co',
anonKey: '<your-anon-key>',
);
runApp(MyApp());
}
void main() async {
Supabase.initialize(
url: 'https://<project>.supabase.co',
anonKey: '<your-anon-key>',
);
runApp(MyApp());
}
final supabase = Supabase.instance.client;
```
final supabase = Supabase.instance.client;
```
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val supabaseUrl = "https://<project>.supabase.co"
val supabaseKey = "<your-anon-key>"
val client = createSupabaseClient(supabaseUrl, supabaseKey) {
install(Realtime)
}
```
```kotlin
val supabaseUrl = "https://<project>.supabase.co"
val supabaseKey = "<your-anon-key>"
val supabase = createSupabaseClient(supabaseUrl, supabaseKey) {
install(Realtime)
}
```
</TabPanel>
</TabPanel>
</Tabs>
### Listening to Broadcast messages
@@ -77,62 +77,61 @@ You can provide a callback for the `broadcast` channel to receive message. In th
defaultActiveId="js"
queryGroup="language"
>
<TabPanel id="js" label="JavaScript">
<TabPanel id="js" label="JavaScript">
{/* prettier-ignore */}
```js
// Join a room/topic. Can be anything except for 'realtime'.
const channelA = clientA.channel('room-1')
{/* prettier-ignore */}
```js
// Join a room/topic. Can be anything except for 'realtime'.
const channelA = clientA.channel('room-1')
// Simple function to log any messages we receive
function messageReceived(payload) {
console.log(payload)
}
// Subscribe to the Channel
channelA
.on(
'broadcast',
{ event: 'test' },
(payload) => messageReceived(payload)
)
.subscribe()
```
</TabPanel>
<TabPanel id="dart" label="Dart">
```dart
// Simple function to log any messages we receive
void messageReceived(payload) {
print(payload);
}
// Subscribe to the Channel
channelA
.onBroadcast(
event: 'test', callback: (payload) => messageReceived(payload))
.subscribe();
```
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val channelA = clientA.realtime.createChannel("room-1")
//Listen for broadcast messages
val broadcastFlow: Flow<JsonObject> = channelA.broadcastFlow<JsonObject>("test")
.onEach {
println(it)
// Simple function to log any messages we receive
function messageReceived(payload) {
console.log(payload)
}
.launchIn(yourCoroutineScope) //you can also use .collect { } here
clientA.realtime.connect()
channelA.join()
```
// Subscribe to the Channel
channelA
.on(
'broadcast',
{ event: 'test' },
(payload) => messageReceived(payload)
)
.subscribe()
```
</TabPanel>
</TabPanel>
<TabPanel id="dart" label="Dart">
```dart
// Simple function to log any messages we receive
void messageReceived(payload) {
print(payload);
}
// Subscribe to the Channel
channelA
.onBroadcast(
event: 'test', callback: (payload) => messageReceived(payload))
.subscribe();
```
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val channelA = clientA.channel("room-1")
//Listen for broadcast messages
val broadcastFlow: Flow<JsonObject> = channelA.broadcastFlow<JsonObject>("test")
.onEach {
println(it)
}
.launchIn(yourCoroutineScope) //you can also use .collect { } here
channelA.subscribe()
```
</TabPanel>
</Tabs>
### Sending Broadcast messages
@@ -144,70 +143,68 @@ channelA.join()
defaultActiveId="js"
queryGroup="language"
>
<TabPanel id="js" label="JavaScript">
<TabPanel id="js" label="JavaScript">
We can send Broadcast messages using `channelB.send()`. Let's set up another client to send messages.
We can send Broadcast messages using `channelB.send()`. Let's set up another client to send messages.
{/* prettier-ignore */}
```js
// Join a room/topic. Can be anything except for 'realtime'.
const channelB = clientA.channel('room-1')
{/* prettier-ignore */}
```js
// Join a room/topic. Can be anything except for 'realtime'.
const channelB = clientA.channel('room-1')
channelB.subscribe((status) => {
// Wait for successful connection
if (status !== 'SUBSCRIBED') {
return null
}
channelB.subscribe((status) => {
// Wait for successful connection
if (status !== 'SUBSCRIBED') {
return null
}
// Send a message once the client is subscribed
channelB.send({
type: 'broadcast',
event: 'test',
payload: { message: 'hello, world' },
})
})
```
// Send a message once the client is subscribed
channelB.send({
type: 'broadcast',
event: 'test',
payload: { message: 'hello, world' },
})
})
```
</TabPanel>
<TabPanel id="dart" label="Dart">
</TabPanel>
<TabPanel id="dart" label="Dart">
```dart
// Join a room/topic. Can be anything except for 'realtime'.
final channelB = supabase.channel('room-1');
```dart
// Join a room/topic. Can be anything except for 'realtime'.
final channelB = supabase.channel('room-1');
channelB.subscribe((status, error) {
// Wait for successful connection
if (status != RealtimeSubscribeStatus.subscribed) {
return;
}
channelB.subscribe((status, error) {
// Wait for successful connection
if (status != RealtimeSubscribeStatus.subscribed) {
return;
}
// Send a message once the client is subscribed
channelB.sendBroadcastMessage(
event: 'test',
payload: {'message': 'hello, world'},
);
});
```
// Send a message once the client is subscribed
channelB.sendBroadcastMessage(
event: 'test',
payload: {'message': 'hello, world'},
);
});
```
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
We can send Broadcast messages using `channelB.broadcast()`. Let's set up another client to send messages.
We can send Broadcast messages using `channelB.broadcast()`. Let's set up another client to send messages.
```kotlin
val channelB = clientA.realtime.createChannel("room-1")
```kotlin
val channelB = clientA.channel("room-1")
clientA.realtime.connect()
channelB.subscribe(blockUntilSubscribed = true) //You can also use the channelA.status flow instead, but this parameter will block the coroutine until the status is joined.
channelB.join(blockUntilJoined = true) //You can also use the channelA.status flow instead, but this parameter will block the coroutine until the status is joined.
channelB.broadcast(
event = "test",
payload = YourMessage(message = "hello, world!")
)
```
channelB.broadcast(
event = "test",
payload = YourMessage(message = "hello, world!")
)
```
</TabPanel>
</TabPanel>
</Tabs>
Before sending messages we need to ensure the client is connected, which we have done within the `subscribe()` callback.
@@ -225,90 +222,89 @@ You can pass configuration options while initializing the Supabase Client.
defaultActiveId="js"
queryGroup="language"
>
<TabPanel id="js" label="JavaScript">
<TabPanel id="js" label="JavaScript">
By default, broadcast messages are only sent to other clients. You can broadcast messages back to the sender by setting Broadcast's `self` parameter to `true`.
By default, broadcast messages are only sent to other clients. You can broadcast messages back to the sender by setting Broadcast's `self` parameter to `true`.
{/* prettier-ignore */}
```js
const myChannel = supabase.channel('room-2', {
config: {
broadcast: { self: true },
},
})
{/* prettier-ignore */}
```js
const myChannel = supabase.channel('room-2', {
config: {
broadcast: { self: true },
},
})
myChannel.on(
'broadcast',
{ event: 'test-my-messages' },
(payload) => console.log(payload)
)
myChannel.subscribe((status) => {
if (status !== 'SUBSCRIBED') { return }
channelC.send({
type: 'broadcast',
event: 'test-my-messages',
payload: { message: 'talking to myself' },
})
})
```
</TabPanel>
<TabPanel id="dart" label="Dart">
```dart
final myChannel = supabase.channel(
'room-2',
opts: const RealtimeChannelConfig(
self: true,
),
);
myChannel.onBroadcast(
event: 'test-my-messages',
callback: (payload) => print(payload),
);
myChannel.subscribe((status, error) {
if (status != RealtimeSubscribeStatus.subscribed) return;
// channelC.send({
myChannel.sendBroadcastMessage(
event: 'test-my-messages',
payload: {'message': 'talking to myself'},
);
});
```
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
By default, broadcast messages are only sent to other clients. You can broadcast messages back to the sender by setting Broadcast's `receiveOwnBroadcasts` parameter to `true`.
```kotlin
val myChannel = supabase.realtime.createChannel("room-2") {
broadcast {
receiveOwnBroadcasts = true
}
}
val broadcastFlow: Flow<JsonObject> = myChannel.broadcastFlow<JsonObject>("test-my-messages")
.onEach {
println(it)
}
.launchIn(yourCoroutineScope)
clientA.realtime.connect()
myChannel.join(blockUntilJoined = true) //You can also use the myChannel.status flow instead, but this parameter will block the coroutine until the status is joined.
myChannel.broadcast(
event = "test-my-messages",
payload = YourMessage(
message = "talking to myself"
myChannel.on(
'broadcast',
{ event: 'test-my-messages' },
(payload) => console.log(payload)
)
)
```
</TabPanel>
myChannel.subscribe((status) => {
if (status !== 'SUBSCRIBED') { return }
channelC.send({
type: 'broadcast',
event: 'test-my-messages',
payload: { message: 'talking to myself' },
})
})
```
</TabPanel>
<TabPanel id="dart" label="Dart">
```dart
final myChannel = supabase.channel(
'room-2',
opts: const RealtimeChannelConfig(
self: true,
),
);
myChannel.onBroadcast(
event: 'test-my-messages',
callback: (payload) => print(payload),
);
myChannel.subscribe((status, error) {
if (status != RealtimeSubscribeStatus.subscribed) return;
// channelC.send({
myChannel.sendBroadcastMessage(
event: 'test-my-messages',
payload: {'message': 'talking to myself'},
);
});
```
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
By default, broadcast messages are only sent to other clients. You can broadcast messages back to the sender by setting Broadcast's `receiveOwnBroadcasts` parameter to `true`.
```kotlin
val myChannel = supabase.channel("room-2") {
broadcast {
receiveOwnBroadcasts = true
}
}
val broadcastFlow: Flow<JsonObject> = myChannel.broadcastFlow<JsonObject>("test-my-messages")
.onEach {
println(it)
}
.launchIn(yourCoroutineScope)
myChannel.subscribe(blockUntilSubscribed = true) //You can also use the myChannel.status flow instead, but this parameter will block the coroutine until the status is joined.
myChannel.broadcast(
event = "test-my-messages",
payload = YourMessage(
message = "talking to myself"
)
)
```
</TabPanel>
</Tabs>
### Acknowledge messages
@@ -320,73 +316,72 @@ myChannel.broadcast(
defaultActiveId="js"
queryGroup="language"
>
<TabPanel id="js" label="JavaScript">
<TabPanel id="js" label="JavaScript">
You can confirm that Realtime received your message by setting Broadcast's `ack` config to `true`.
You can confirm that Realtime received your message by setting Broadcast's `ack` config to `true`.
{/* prettier-ignore */}
```js
const myChannel = clientD.channel('room-3', {
config: {
broadcast: { ack: true },
},
})
{/* prettier-ignore */}
```js
const myChannel = clientD.channel('room-3', {
config: {
broadcast: { ack: true },
},
})
myChannel.subscribe(async (status) => {
if (status !== 'SUBSCRIBED') { return }
myChannel.subscribe(async (status) => {
if (status !== 'SUBSCRIBED') { return }
const serverResponse = await myChannel.send({
type: 'broadcast',
event: 'acknowledge',
payload: {},
})
const serverResponse = await myChannel.send({
type: 'broadcast',
event: 'acknowledge',
payload: {},
})
console.log('serverResponse', serverResponse)
})
```
console.log('serverResponse', serverResponse)
})
```
</TabPanel>
<TabPanel id="dart" label="Dart">
</TabPanel>
<TabPanel id="dart" label="Dart">
```dart
final myChannel = supabase.channel('room-3',opts: const RealtimeChannelConfig(
ack: true,
),
```dart
final myChannel = supabase.channel('room-3',opts: const RealtimeChannelConfig(
ack: true,
),
);
);
myChannel.subscribe( (status, error) async {
if (status != RealtimeSubscribeStatus.subscribed) return;
myChannel.subscribe( (status, error) async {
if (status != RealtimeSubscribeStatus.subscribed) return;
final serverResponse = await myChannel.sendBroadcastMessage(
final serverResponse = await myChannel.sendBroadcastMessage(
event: 'acknowledge',
payload: {},
);
event: 'acknowledge',
payload: {},
);
print('serverResponse: $serverResponse');
});
```
print('serverResponse: $serverResponse');
});
```
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
By default, broadcast messages are only sent to other clients. You can broadcast messages back to the sender by setting Broadcast's `acknowledgeBroadcasts` parameter to `true`.
By default, broadcast messages are only sent to other clients. You can broadcast messages back to the sender by setting Broadcast's `acknowledgeBroadcasts` parameter to `true`.
```kotlin
val myChannel = supabase.realtime.createChannel("room-2") {
broadcast {
acknowledgeBroadcasts = true
}
}
```kotlin
val myChannel = supabase.channel("room-2") {
broadcast {
acknowledgeBroadcasts = true
}
}
supabase.realtime.connect()
myChannel.join(blockUntilJoined = true) //You can also use the myChannel.status flow instead, but this parameter will block the coroutine until the status is joined.
myChannel.subscribe(blockUntilSubscribed = true) //You can also use the myChannel.status flow instead, but this parameter will block the coroutine until the status is joined.
myChannel.broadcast(event = "acknowledge", buildJsonObject { })
```
myChannel.broadcast(event = "acknowledge", buildJsonObject { })
```
</TabPanel>
</TabPanel>
</Tabs>
Use this to guarantee that the server has received the message before resolving `channelD.send`'s promise. If the `ack` config is not set to `true` when creating the channel, the promise returned by `channelD.send` will resolve immediately.
@@ -408,42 +403,58 @@ You can also send a Broadcast message by making an HTTP request to Realtime serv
defaultActiveId="js"
queryGroup="language"
>
<TabPanel id="js" label="JavaScript">
```js
const channel = client.channel('test-channel')
<TabPanel id="js" label="JavaScript">
```js
const channel = client.channel('test-channel')
// No need to subscribe to channel
// No need to subscribe to channel
channel
.send({
type: 'broadcast',
event: 'test',
payload: { message: 'Hi' },
})
.then((resp) => console.log(resp))
channel
.send({
type: 'broadcast',
event: 'test',
payload: { message: 'Hi' },
})
.then((resp) => console.log(resp))
// Remember to clean up the channel
// Remember to clean up the channel
client.removeChannel(channel)
client.removeChannel(channel)
````
```
</TabPanel>
<TabPanel id="dart" label="Dart">
```dart
// No need to subscribe to channel
</TabPanel>
<TabPanel id="dart" label="Dart">
```dart
// No need to subscribe to channel
final channel = supabase.channel('test-channel');
final res = await channel.sendBroadcastMessage(
event: "test",
payload: {
'message': 'Hi',
},
);
print(res);
````
final channel = supabase.channel('test-channel');
final res = await channel.sendBroadcastMessage(
event: "test",
payload: {
'message': 'Hi',
},
);
print(res);
```
</TabPanel>
</TabPanel>
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val myChannel = supabase.channel("room-2") {
broadcast {
acknowledgeBroadcasts = true
}
}
// No need to subscribe to channel
myChannel.broadcast(event = "test", buildJsonObject {
put("message", "Hi")
})
```
</TabPanel>
</Tabs>
export const Page = ({ children }) => <Layout meta={meta} children={children} />
@@ -244,7 +244,7 @@ supabase
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val myChannel = supabase.realtime.createChannel("schema-db-changes")
val myChannel = supabase.channel("schema-db-changes")
val changes = myChannel.postgresChangeFlow<PostgresAction>(schema = "public")
@@ -258,8 +258,7 @@ changes
}
.launchIn(yourCoroutineScope)
supabase.realtime.connect()
myChannel.join()
myChannel.subscribe()
```
</TabPanel>
@@ -313,7 +312,7 @@ final changes = supabase
Use `PostgresAction.Insert` as type to listen only to database `INSERT`s:
```kotlin
val myChannel = supabase.realtime.createChannel("db-changes")
val myChannel = supabase.channel("db-changes")
val changes = myChannel.postgresChangeFlow<PostgresAction.Insert>(schema = "public")
@@ -323,8 +322,7 @@ changes
}
.launchIn(yourCoroutineScope)
supabase.realtime.connect()
myChannel.join()
myChannel.subscribe()
```
</TabPanel>
@@ -378,7 +376,7 @@ supabase
Use `PostgresAction.Update` as type to listen only to database `UPDATE`s:
```kotlin
val myChannel = supabase.realtime.createChannel("db-changes")
val myChannel = supabase.channel("db-changes")
val changes = myChannel.postgresChangeFlow<PostgresAction.Update>(schema = "public")
@@ -388,8 +386,7 @@ changes
}
.launchIn(yourCoroutineScope)
supabase.realtime.connect()
myChannel.join()
myChannel.subscribe()
```
</TabPanel>
@@ -443,7 +440,7 @@ supabase
Use `PostgresAction.Delete` as type to listen only to database `DELETE`s:
```kotlin
val myChannel = supabase.realtime.createChannel("db-changes")
val myChannel = supabase.channel("db-changes")
val changes = myChannel.postgresChangeFlow<PostgresAction.Delete>(schema = "public")
@@ -453,8 +450,7 @@ changes
}
.launchIn(yourCoroutineScope)
supabase.realtime.connect()
myChannel.join()
myChannel.subscribe()
```
</TabPanel>
@@ -508,7 +504,7 @@ supabase
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val myChannel = supabase.realtime.createChannel("db-changes")
val myChannel = supabase.channel("db-changes")
val changes = myChannel.postgresChangeFlow<PostgresAction>(schema = "public") {
table = "todos"
@@ -520,8 +516,7 @@ changes
}
.launchIn(yourCoroutineScope)
supabase.realtime.connect()
myChannel.join()
myChannel.subscribe()
```
</TabPanel>
@@ -589,15 +584,14 @@ supabase
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val myChannel = supabase.realtime.createChannel("db-changes")
val myChannel = supabase.channel("db-changes")
val messageChanges = myChannel.postgresChangeFlow<PostgresAction>(schema = "public") {
table = "messages"
}
val userChanges = myChannel.postgresChangeFlow<PostgresAction.Insert>(schema = "public") {
table = "users"
}
supabase.realtime.connect()
myChannel.join()
myChannel.subscribe()
```
</TabPanel>
@@ -655,7 +649,7 @@ const changes = client
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val myChannel = supabase.realtime.createChannel("db-changes")
val myChannel = supabase.channel("db-changes")
val changes = myChannel.postgresChangeFlow<PostgresAction.Insert>(schema = "public") {
table = "todos"
@@ -668,8 +662,7 @@ changes
}
.launchIn(yourCoroutineScope)
supabase.realtime.connect()
myChannel.join()
myChannel.subscribe()
```
</TabPanel>
@@ -731,7 +724,7 @@ supabase
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val myChannel = supabase.realtime.createChannel("db-changes")
val myChannel = supabase.channel("db-changes")
val changes = myChannel.postgresChangeFlow<PostgresAction.Update>(schema = "public") {
table = "messages"
@@ -744,8 +737,7 @@ changes
}
.launchIn(yourCoroutineScope)
supabase.realtime.connect()
myChannel.join()
myChannel.subscribe()
```
</TabPanel>
@@ -879,7 +871,7 @@ supabase
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val myChannel = supabase.realtime.createChannel("db-changes")
val myChannel = supabase.channel("db-changes")
val changes = myChannel.postgresChangeFlow<PostgresAction.Insert>(schema = "public") {
table = "profiles"
@@ -892,8 +884,7 @@ changes
}
.launchIn(yourCoroutineScope)
supabase.realtime.connect()
myChannel.join()
myChannel.subscribe()
```
</TabPanel>
@@ -953,7 +944,7 @@ supabase
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val myChannel = supabase.realtime.createChannel("db-changes")
val myChannel = supabase.channel("db-changes")
val changes = myChannel.postgresChangeFlow<PostgresAction.Update>(schema = "public") {
table = "profiles"
@@ -966,8 +957,7 @@ changes
}
.launchIn(yourCoroutineScope)
supabase.realtime.connect()
myChannel.join()
myChannel.subscribe()
```
</TabPanel>
@@ -1027,7 +1017,7 @@ supabase
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val myChannel = supabase.realtime.createChannel("db-changes")
val myChannel = supabase.channel("db-changes")
val changes = myChannel.postgresChangeFlow<PostgresAction.Update>(schema = "public") {
table = "products"
@@ -1040,8 +1030,7 @@ changes
}
.launchIn(yourCoroutineScope)
supabase.realtime.connect()
myChannel.join()
myChannel.subscribe()
```
</TabPanel>
@@ -1101,7 +1090,7 @@ supabase
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val myChannel = supabase.realtime.createChannel("db-changes")
val myChannel = supabase.channel("db-changes")
val changes = myChannel.postgresChangeFlow<PostgresAction.Update>(schema = "public") {
table = "products"
@@ -1114,8 +1103,7 @@ changes
}
.launchIn(yourCoroutineScope)
supabase.realtime.connect()
myChannel.join()
myChannel.subscribe()
```
</TabPanel>
@@ -1175,7 +1163,7 @@ supabase
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val myChannel = supabase.realtime.createChannel("db-changes")
val myChannel = supabase.channel("db-changes")
val changes = myChannel.postgresChangeFlow<PostgresAction.Update>(schema = "public") {
table = "products"
@@ -1188,8 +1176,7 @@ changes
}
.launchIn(yourCoroutineScope)
supabase.realtime.connect()
myChannel.join()
myChannel.subscribe()
```
</TabPanel>
@@ -1303,7 +1290,7 @@ val supabase = createSupabaseClient(supabaseUrl, supabaseKey) {
jwtToken = "your-custom-jwt"
}
}
val myChannel = supabase.realtime.createChannel("db-changes")
val myChannel = supabase.channel("db-changes")
val changes = myChannel.postgresChangeFlow<PostgresAction.Update>(schema = "public") {
table = "products"
@@ -1316,8 +1303,7 @@ changes
}
.launchIn(yourCoroutineScope)
supabase.realtime.connect()
myChannel.join()
myChannel.subscribe()
```
</TabPanel>
+7 -8
View File
@@ -58,7 +58,7 @@ final supabase = Supabase.instance.client;
```kotlin
val supabaseUrl = "https://<project>.supabase.co"
val supabaseKey = "<your-anon-key>"
val client = createSupabaseClient(supabaseUrl, supabaseKey) {
val supabase = createSupabaseClient(supabaseUrl, supabaseKey) {
install(Realtime)
}
```
@@ -120,7 +120,7 @@ roomOne.onPresenceSync((_) {
Listen to the presence change flow, emitting new a new `PresenceAction` whenever someone joins or leaves:
```kotlin
val roomOne = supabase.realtime.createChannel("room_01")
val roomOne = supabase.channel("room_01")
val presenceFlow: Flow<PresenceAction> = roomOne.presenceChangeFlow()
presenceFlow
.onEach {
@@ -128,8 +128,8 @@ presenceFlow
println(it.leaves) //You can also use it.decodeLeavesAs<YourType>()
}
.launchIn(yourCoroutineScope) //You can also use .collect { } here
supabase.realtime.connect()
roomOne.join()
roomOne.subscribe()
```
</TabPanel>
@@ -188,15 +188,14 @@ roomOne.subscribe((status, error) async {
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val roomOne = supabase.realtime.createChannel("room_01")
val roomOne = supabase.channel("room_01")
val userStatus = UserStatus( //Your custom class
user = "user-1",
onlineAt = Clock.System.now().toEpochMilliseconds()
)
supabase.realtime.connect()
roomOne.join(blockUntilJoined = true) //You can also use the roomOne.status flow instead, but this parameter will block the coroutine until the status is joined.
roomOne.subscribe(blockUntilSubscribed = true) //You can also use the roomOne.status flow instead, but this parameter will block the coroutine until the status is joined.
roomOne.track(userStatus)
```
@@ -299,7 +298,7 @@ final channelC = supabase.channel(
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val channelC = supabase.realtime.createChannel("test") {
val channelC = supabase.channel("test") {
presence {
key = "userId-123"
}
@@ -43,7 +43,7 @@ supabase.storage.from('bucket').getPublicUrl('image.jpg', {
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val url = supabase.storage["bucket"].publicRenderUrl("image.jpg") {
val url = supabase.storage.from("bucket").publicRenderUrl("image.jpg") {
size(width = 500, height = 600)
}
```
@@ -81,7 +81,7 @@ supabase.storage.from('bucket').createSignedUrl('image.jpg', 60000, {
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val url = supabase.storage["bucket"].createSignedUrl("image.jpg", 60.seconds) {
val url = supabase.storage.from("bucket").createSignedUrl("image.jpg", 60.seconds) {
size(200, 200)
}
```
@@ -117,13 +117,13 @@ supabase.storage.from('bucket').download('image.jpg', {
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val data = supabase.storage["bucket"].downloadAuthenticated("image.jpg") {
val data = supabase.storage.from("bucket").downloadAuthenticated("image.jpg") {
size(800, 300)
}
//Or on JVM stream directly to a file
val file = File("image.jpg")
supabase.storage["bucket"].downloadAuthenticatedTo("image.jpg", file) {
supabase.storage.from("bucket").downloadAuthenticatedTo("image.jpg", file) {
size(800, 300)
}
```
@@ -170,14 +170,14 @@ await storage.from('bucket').download('image.jpeg', {
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val data = supabase.storage["bucket"].downloadAuthenticated("image.jpg") {
val data = supabase.storage.from("bucket").downloadAuthenticated("image.jpg") {
size(200, 200)
format = "origin"
}
//Or on JVM stream directly to a file
val file = File("image.jpg")
supabase.storage["bucket"].downloadAuthenticatedTo("image.jpg", file) {
supabase.storage.from("bucket").downloadAuthenticatedTo("image.jpg", file) {
size(200, 200)
format = "origin"
}
@@ -309,14 +309,14 @@ supabase.storage.from('bucket').download('image.jpg', {
<TabPanel id="kotlin" label="Kotlin">
```kotlin
val data = supabase.storage["bucket"].downloadAuthenticated("image.jpg") {
val data = supabase.storage.from("bucket").downloadAuthenticated("image.jpg") {
size(800, 300)
resize = ImageTransformation.Resize.CONTAIN
}
//Or on JVM stream directly to a file
val file = File("image.jpg")
supabase.storage["bucket"].downloadAuthenticatedTo("image.jpg", file) {
supabase.storage.from("bucket").downloadAuthenticatedTo("image.jpg", file) {
size(800, 300)
resize = ImageTransformation.Resize.CONTAIN
}
@@ -88,7 +88,7 @@ Supabase Storage implements the [TUS protocol](https://tus.io/) to enable resuma
```kotlin
suspend fun uploadFile(file: File) {
val upload: ResumableUpload = supabase.storage["bucket_name"]
val upload: ResumableUpload = supabase.storage.from("bucket_name")
.resumable.createOrContinueUpload("file_path", file)
upload.stateFlow
.onEach {
@@ -100,7 +100,7 @@ Supabase Storage implements the [TUS protocol](https://tus.io/) to enable resuma
// On other platforms you might have to give the bytes directly and specify a source if you want to continue it later:
suspend fun uploadData(bytes: ByteArray) {
val upload: ResumableUpload = supabase.storage["bucket_name"]
val upload: ResumableUpload = supabase.storage.from("bucket_name")
.resumable.createOrContinueUpload(bytes, "source", "file_path")
upload.stateFlow
@@ -55,12 +55,12 @@ val supabase = createSupabaseClient(supabaseUrl, supabaseKey) {
}
suspend fun uploadData(bytes: ByteArray) {
supabase.storage["bucket_name"].upload("file_path", bytes)
supabase.storage.from("bucket_name").upload("file_path", bytes)
}
//Or on JVM/Android: (This will stream the data from the file to supabase)
suspend fun uploadFile(file: File) {
supabase.storage["bucket_name"].upload("file_path", file)
supabase.storage.from("bucket_name").upload("file_path", file)
}
```
@@ -1,5 +1,5 @@
import clientLibsCommonSections from '~/../../spec/common-client-libs-sections.json'
import spec from '~/../../spec/supabase_kt_v1.yml' assert { type: 'yml' }
import spec from '~/../../spec/supabase_kt_v2.yml' assert { type: 'yml' }
import RefSectionHandler from '~/components/reference/RefSectionHandler'
import { flattenSections } from '~/lib/helpers'
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
@@ -1,6 +1,6 @@
import clientLibsCommonSections from '~/../../spec/common-client-libs-sections.json'
import typeSpec from '~/../../spec/enrichments/tsdoc_v2/combined.json'
import spec from '~/../../spec/supabase_kt_v1.yml' assert { type: 'yml' }
import spec from '~/../../spec/supabase_kt_v2.yml' assert { type: 'yml' }
import RefSectionHandler from '~/components/reference/RefSectionHandler'
import { flattenSections } from '~/lib/helpers'
import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
@@ -14,7 +14,7 @@ const libraryPath = '/kotlin'
export default function KotlinReference(props) {
const router = useRouter()
const slug = router.query.slug[0]
const filteredSection = sections.filter((section) => section.id === slug)
const filteredSection = sections.filter((section) => section.slug === slug)
const pageTitle = filteredSection[0]?.title
? `${filteredSection[0]?.title} | Supabase`
@@ -6,10 +6,18 @@ import handleRefGetStaticPaths from '~/lib/mdx/handleRefStaticPaths'
import handleRefStaticProps from '~/lib/mdx/handleRefStaticProps'
const sections = flattenSections(clientLibsCommonSections)
const libraryPath = '/kotlin/v0'
const libraryPath = '/kotlin/v1'
export default function KotlinReference(props) {
return <RefSectionHandler sections={sections} spec={spec} pageProps={props} type="client-lib" />
return (
<RefSectionHandler
sections={sections}
spec={spec}
pageProps={props}
type="client-lib"
isOldVersion
/>
)
}
export async function getStaticProps() {
@@ -9,7 +9,7 @@ import { useRouter } from 'next/router'
import RefSEO from '~/components/reference/RefSEO'
const sections = flattenSections(clientLibsCommonSections)
const libraryPath = '/kotlin/v0'
const libraryPath = '/kotlin/v1'
export default function KotlinReference(props) {
const router = useRouter()
+6 -3
View File
@@ -33,7 +33,8 @@
"reference_python_v2",
"reference_csharp_v0",
"reference_swift_v1",
"reference_kotlin_v0"
"reference_kotlin_v1",
"reference_kotlin_v2"
]
},
{
@@ -47,7 +48,8 @@
"reference_python_v2",
"reference_csharp_v0",
"reference_swift_v1",
"reference_kotlin_v0"
"reference_kotlin_v1",
"reference_kotlin_v2"
]
},
{
@@ -963,7 +965,8 @@
"reference_python_v2",
"reference_csharp_v0",
"reference_swift_v1",
"reference_kotlin_v0"
"reference_kotlin_v1",
"reference_kotlin_v2"
],
"items": [
{
+3 -3
View File
@@ -2098,15 +2098,15 @@ functions:
Creates a new user.
- By default, the user needs to verify their email address before logging in. To turn this off, disable **Confirm email** in [your project](https://supabase.com/dashboard/project/_/auth/providers).
- **Confirm email** determines if users need to confirm their email address after signing up.
- If **Confirm email** is enabled, the return value is null and you will be logged in instead.
- If **Confirm email** is disabled, the return value is the user and you won't be logged in automatically.
- If **Confirm email** is enabled, the return value is the user and you won't be logged in automatically.
- If **Confirm email** is disabled, the return value is null and you will be logged in instead.
- When the user confirms their email address, they are redirected to the [`SITE_URL`](https://supabase.com/docs/reference/auth/config#site_url) by default. You can modify your `SITE_URL` or add additional redirect URLs in [your project](https://supabase.com/dashboard/project/_/auth/url-configuration).
- To learn how to handle OTP links & OAuth refer to [initializing](/docs/reference/kotlin/initializing)
- If signUpWith() is called for an existing confirmed user:
- If **Confirm email** is enabled in [your project](https://supabase.com/dashboard/project/_/auth/providers), an obfuscated/fake user object is returned.
- If **Confirm email** is disabled, the error message, `User already registered` is returned.
examples:
- id: sign-up-phone
- id: sign-up-email
name: Sign up with email
isSpotlight: true
code: |
File diff suppressed because it is too large. Load diff