Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 31 additions & 44 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,37 @@
# Change log

## 1.2.0-rc.4

* Fixed: `host()` resolves with the live connection state instead of always `null`
* Fixed: `onConnection(false)` fires when a retired or background connection closes
* Fixed: notification taps are recorded after launching the app, so tap-opened screens land on top
* Fixed: `host()` reports `false` when there was nothing to subscribe instead of a spurious open

## 1.2.0-rc.3

* Added: `getInitialNotification()`, `onNotificationOpened()`, and the `PushNotificationOpened` type (topic + data)
* Added: live push connection open/close events surfaced via `onOpen`/`onClose`
* Fixed: notification taps delivered reliably once, via a `PushTaps` queue
* Fixed: foreground notifications de-duplicated per title
* Updated: `resume` leaves a live Push host's connection untouched

## 1.2.0-rc.2

* Added: `Push.getInitialNotification()` returns the notification whose tap launched the app
* Added: `Push.onNotificationOpened(callback)` fires on each notification tap
* Added: `Push.backgroundStatus()` reports Android exact-alarm, battery, and foreground-service state
* Added: `Push.requestExactAlarms()` and `Push.requestIgnoreBatteryOptimizations()` helpers
* Added: `SubscribeOptions.notifyInForeground` to post notifications while the app is visible
* Added: `PushBackgroundStatus` and `PushNotificationOpened` exported types
* Fixed: reliable Android background wake-ups via a periodic watchdog and alternating jobs
* Fixed: `resume` keeps subscriptions only for the same signed-in user
* Fixed: notification images are size-capped to prevent out-of-memory crashes

## 1.2.0-rc.1

* Added: background push notifications render the server `notification` title, body, and image
* Added: Android shows notification images (BigPictureStyle) and long bodies (BigTextStyle)
* Added: the SDK requests Android 13+ `POST_NOTIFICATIONS` itself on first background subscribe
* Added: notification `data` now carries `topic` and `payload`
* Fixed: a server-sent title dedupes to one notification per topic instead of one per subscription

## 1.2.0-rc.0

* Added: `Apps` service to manage OAuth2 apps, keys, secrets, and installations
* Added: `Oauth2` service for authorization, consent, device, PAR, and token flows
* Added: `Push` service for MQTT realtime push with Android background delivery
* Added: `Topic` builder and native TCP modules for push
* Added: `Avatars.updatePhoto()` and `Avatars.deletePhoto()` methods
* Added: `App`, `Oauth2*`, and `Account` models
## 2.0.0

* Breaking: requests send `X-Appwrite-Response-Format: 2.5.0`; responses follow the 2.5 shapes
* Breaking: missing response fields are filled with their documented defaults
* Breaking: nullable model fields are typed `T | null`, such as `Locale.city` and `Presence.status`
* Breaking: the package ships native Android and iOS modules, so apps need a native rebuild and do not run in Expo Go
* Added: `Apps` service to manage apps, installations, keys and secrets
* Added: `Oauth2` service for the OAuth2 authorize, token, device and grant flows
* Added: `Push` service, with optional peers `expo-notifications`, `react-native-background-actions` and `react-native-tcp-socket`
* Added: `Topic` helper
* Added: Android push code ships under `io.appwrite.reactnative`, so apps can also depend on `sdk-for-android`; set the notification icon with the `io.appwrite.reactnative.push.notification_icon` meta-data
* Added: `Query` aggregates (`count`, `countDistinct`, `sum`, `avg`, `min`, `max` and more), `groupBy`, `having`, `distinct`, joins, `relationship`, `covers`, `spatialEquals` and `page`
* Added: `Query.builder()` to chain queries; realtime subscriptions accept a builder for `queries`
* Added: `ExecutionTrigger.Email`
* Fixed: list and object defaults filled into a response are copied per model, and object defaults are `{}` instead of `[]`

## 1.3.0

* Added: `analytics.createEvent` in a new `Analytics` service to send tracking events
* Added: `avatars.updatePhoto` and `avatars.deletePhoto` to manage the user's custom profile photo, and the `Account` model
* Added: `Webflow` to the `OAuthProvider` enum
* Fixed: cookie session fallback is guarded to Expo web where `localStorage` exists
* Added: optional `state` on `account.createOAuth2Session` and `account.createOAuth2Token`, returned unchanged on the success and failure URLs
* Added: optional `duration` on `account.createEmailPasswordSession` to set the session length
* Added: optional `current` on `account.deleteSessions` to keep the current session signed in
* Added: optional `membershipId` on `teams.createMembership`
* Added: optional `total` on `account.listSessions`, the `locale` list methods and `listTransactions`

## 1.2.0

* Added: `account` passkey methods `listPasskeys`, `createPasskey`, `getPasskey`, `updatePasskey`, `deletePasskey` and `updatePasskeyVerification`
* Added: `account.createPasskeyToken` and `account.updatePasskeyToken` for passkey sign-in
* Added: `Passkey`, `PasskeyList`, `PasskeyChallenge` and `TokenSecret` models

## 1.1.0

Expand Down
76 changes: 51 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
# Appwrite React Native SDK

![License](https://img.shields.io/github/license/appwrite/sdk-for-react-native.svg?style=flat-square)
![Version](https://img.shields.io/badge/api%20version-2.3.0-blue.svg?style=flat-square)
![Version](https://img.shields.io/badge/api%20version-2.5.0-blue.svg?style=flat-square)
[![Build Status](https://img.shields.io/travis/com/appwrite/sdk-generator?style=flat-square)](https://travis-ci.com/appwrite/sdk-generator)
[![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite)
[![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord)

**This SDK targets Appwrite server version 2.3.x as shipped on Appwrite Cloud.** Self-hosted releases can lag behind Cloud — if you run an older self-hosted build, use a matching older SDK from [previous releases](https://github.com/appwrite/sdk-for-react-native/releases) when APIs differ.
**This SDK targets Appwrite server version 2.5.x as shipped on Appwrite Cloud.** Self-hosted releases can lag behind Cloud — if you run an older self-hosted build, use a matching older SDK from [previous releases](https://github.com/appwrite/sdk-for-react-native/releases) when APIs differ.

Appwrite is an open-source backend as a service server that abstracts and simplifies complex and repetitive development tasks behind a very simple to use REST API. Appwrite aims to help you develop your apps faster and in a more secure way. Use the React Native SDK to integrate your app with the Appwrite server to easily start interacting with all of Appwrite backend APIs and tools. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)

Expand Down Expand Up @@ -45,7 +45,9 @@ than a short-lived JWT.
On Android 13 and later, the first background subscription asks the user for the
`POST_NOTIFICATIONS` runtime permission. If they decline, the subscription still delivers to your
callback but posts no notification. Notifications show the title, body and image sent with
`createPush`, and fall back to the subscription's `title` and the raw payload for other messages.
`createPush`, falling back to the subscription's `title` when only a body is sent. A push with no
title or body, such as a data-only push, posts no notification and reaches only your callback.
Messages that are not a JSON object show the subscription's `title` with the raw payload as the body.

```js
const sub = await push.subscribe('news', (message) => console.log(message.data), {
Expand Down Expand Up @@ -93,17 +95,26 @@ With Expo, list them under `android.permissions` in `app.json` instead.
nothing to ask, including when the permission is not declared. The SDK never opens these
screens on its own.

If `backgroundRestricted` is true, the user restricted the app's background usage and messages can
wait until the app is opened. Exact alarms don't lift it, and neither does foreground mode: Android
stops a restricted app's foreground service once it leaves the screen. Ask the user to choose
Unrestricted in the app's battery settings (Settings > Apps > your app > Battery); granting the
battery optimisation exemption also lets the SDK's alarms and jobs run, and `bestEffort` then
reflects only the exact-alarm state. It is always false below Android 9.

If the user force-stops the app (Settings > Force stop, and on some devices swiping it away from
recents), Android cancels its alarms and jobs: nothing is delivered until the app is opened
again, and the broker then replays what was sent meanwhile.

Set the notification icon with
`<meta-data android:name="io.appwrite.push.notification_icon" android:resource="@drawable/..." />` in your
`<meta-data android:name="io.appwrite.reactnative.push.notification_icon" android:resource="@drawable/..." />` in your
`<application>`; without it a generic icon is used.

```js
const status = await push.backgroundStatus(); // null outside Android
if (status?.bestEffort) {
if (status?.backgroundRestricted && !status.ignoringBatteryOptimizations) {
// Ask the user to choose Unrestricted in the app's battery settings.
} else if (status?.bestEffort) {
// Explain why, then from a button press:
await push.requestExactAlarms();
}
Expand Down Expand Up @@ -140,7 +151,7 @@ On Android the SDK's native module reports the taps. Elsewhere they come from `e

Foreground mode runs a `remoteMessaging` foreground service, which Google Play asks apps to
declare in the Play Console. Apps that never enable it can remove the service from their merged
manifest with `tools:node="remove"` on `io.appwrite.services.PushService` and
manifest with `tools:node="remove"` on `io.appwrite.reactnative.services.PushService` and
`android.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING`.


Expand Down Expand Up @@ -194,7 +205,12 @@ Once your SDK object is set, access any of the Appwrite services and choose any
const account = new Account(client);

// Register User
account.create(ID.unique(), 'me@example.com', 'password', 'Jane Doe')
account.create({
userId: ID.unique(),
email: 'me@example.com',
password: 'password',
name: 'Jane Doe'
})
.then(function (response) {
console.log(response);
}, function (error) {
Expand All @@ -206,7 +222,7 @@ account.create(ID.unique(), 'me@example.com', 'password', 'Jane Doe')
### Full Example

```js
import { Client, Account } from 'react-native-appwrite';
import { Client, Account, ID } from 'react-native-appwrite';
// Init your React Native SDK
const client = new Client();

Expand All @@ -219,7 +235,12 @@ client
const account = new Account(client);

// Register User
account.create(ID.unique(), 'me@example.com', 'password', 'Jane Doe')
account.create({
userId: ID.unique(),
email: 'me@example.com',
password: 'password',
name: 'Jane Doe'
})
.then(function (response) {
console.log(response);
}, function (error) {
Expand All @@ -229,11 +250,11 @@ account.create(ID.unique(), 'me@example.com', 'password', 'Jane Doe')

### Type Safety with Models

The Appwrite React Native SDK provides type safety when working with database documents through generic methods. Methods like `listDocuments`, `getDocument`, and others accept a generic type parameter that allows you to specify your custom model type for full type safety.
The Appwrite React Native SDK provides type safety when working with table rows through generic methods. Methods like `listRows`, `getRow`, and others accept a generic type parameter that allows you to specify your custom model type for full type safety.

**TypeScript:**
```typescript
interface Book {
interface Book extends Models.Row {
name: string;
author: string;
releaseYear?: string;
Expand All @@ -242,15 +263,15 @@ interface Book {
isCheckedOut: boolean;
}

const databases = new Databases(client);
const tablesDB = new TablesDB(client);

try {
const documents = await databases.listDocuments<Book>(
'your-database-id',
'your-collection-id'
);
const result = await tablesDB.listRows<Book>({
databaseId: 'your-database-id',
tableId: 'your-table-id'
});

documents.documents.forEach(book => {
result.rows.forEach(book => {
console.log(`Book: ${book.name} by ${book.author}`); // Now you have full type safety
});
} catch (error) {
Expand All @@ -270,16 +291,16 @@ try {
* @property {boolean} isCheckedOut
*/

const databases = new Databases(client);
const tablesDB = new TablesDB(client);

try {
/** @type {Models.DocumentList<Book>} */
const documents = await databases.listDocuments(
'your-database-id',
'your-collection-id'
);
/** @type {Models.RowList<Book>} */
const result = await tablesDB.listRows({
databaseId: 'your-database-id',
tableId: 'your-table-id'
});

documents.documents.forEach(book => {
result.rows.forEach(book => {
console.log(`Book: ${book.name} by ${book.author}`); // Type hints available in IDE
});
} catch (error) {
Expand All @@ -295,7 +316,12 @@ The Appwrite React Native SDK raises an `AppwriteException` object with `message

```javascript
try {
const user = await account.create(ID.unique(), "email@example.com", "password", "Walter O'Brien");
const user = await account.create({
userId: ID.unique(),
email: "email@example.com",
password: "password",
name: "Walter O'Brien"
});
console.log('User created:', user);
} catch (error) {
console.error('Appwrite error:', error.message);
Expand Down
10 changes: 5 additions & 5 deletions android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,26 @@

<application>
<activity
android:name="io.appwrite.services.PushOpenActivity"
android:name="io.appwrite.reactnative.services.PushOpenActivity"
android:excludeFromRecents="true"
android:exported="false"
android:noHistory="true"
android:taskAffinity=""
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
<service
android:name="io.appwrite.services.PushService"
android:name="io.appwrite.reactnative.services.PushService"
android:exported="false"
android:foregroundServiceType="remoteMessaging"
android:stopWithTask="false" />
<service
android:name="io.appwrite.services.PushJobService"
android:name="io.appwrite.reactnative.services.PushJobService"
android:exported="false"
android:permission="android.permission.BIND_JOB_SERVICE" />
<receiver
android:name="io.appwrite.services.PushAlarmReceiver"
android:name="io.appwrite.reactnative.services.PushAlarmReceiver"
android:exported="false" />
<receiver
android:name="io.appwrite.services.PushBootReceiver"
android:name="io.appwrite.reactnative.services.PushBootReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.modules.core.DeviceEventManagerModule
import io.appwrite.services.PushBridge
import io.appwrite.services.PushMessage
import io.appwrite.services.PushTap
import io.appwrite.services.PushTaps
import io.appwrite.reactnative.services.PushBridge
import io.appwrite.reactnative.services.PushMessage
import io.appwrite.reactnative.services.PushTap
import io.appwrite.reactnative.services.PushTaps
import org.json.JSONObject

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package io.appwrite.exceptions
package io.appwrite.reactnative.exceptions

/** A push error. When the broker explained a refusal or a disconnect, that is its message. */
class AppwriteException(
Expand Down
Loading