Skip to content
Merged
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
170 changes: 114 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,85 +1,143 @@
# rees46_flutter_sdk
# rees46_sdk

Flutter plugin wrapper around REES46 native SDKs (Android/iOS).
[![pub package](https://img.shields.io/pub/v/rees46_sdk.svg)](https://pub.dev/packages/rees46_sdk)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)

## Getting Started
Flutter plugin for the REES46 personalization platform — a thin bridge over the
native [Android](https://github.com/rees46/android-sdk) and
[iOS](https://github.com/rees46/ios-sdk) SDKs. Storage, sessions, identity and
push delivery happen natively; Dart only routes calls to the right shop instance.

### Install
## Add the package

Add dependency in your app:

```yaml
dependencies:
rees46_sdk: ^0.0.1
```bash
flutter pub add rees46_sdk
```

### Initialize
Everything is exported from a single import:

```dart
import 'package:rees46_sdk/rees46_sdk.dart';
```

The native dependencies come with it — `com.github.rees46:android-sdk` from
JitPack and the `REES46` pod from CocoaPods trunk.

final sdk = PersonalizationSdk();

await sdk.initialize(
const SdkInitConfig(
shopId: 'YOUR_SHOP_ID',
apiDomain: 'api.rees46.ru',
// stream defaults to 'ios' on iOS and 'android' on Android if omitted.
stream: 'ios',
enableLogs: false,
autoSendPushToken: true,
sendAdvertisingId: false,
enableAutoPopupPresentation: true,
needReInitialization: false,
),
);
### Android

The plugin brings its own Gradle settings; the only value your app must agree on
is **`minSdk 24`**. Current Flutter versions already default to it, so a freshly
generated project needs no changes — set it only if you hardcoded something lower:

```kotlin
android {
defaultConfig {
minSdk = 24
}
}
```

### API structure
Your app's Java version does **not** have to match the plugin's — `compileOptions`
and `jvmTarget` only govern the module they are declared in.

The public API is exported from
`package:rees46_sdk/rees46_sdk.dart`:
If your `android/settings.gradle.kts` centralizes repositories
(`RepositoriesMode.FAIL_ON_PROJECT_REPOS` / `PREFER_SETTINGS`), add JitPack there —
otherwise the native SDK cannot be resolved:

- **`PersonalizationSdk`** — the SDK entrypoint (tracking, search,
recommendations, profile).
- **`SdkInitConfig`** — initialization config passed to `initialize()`.
```kotlin
maven(url = "https://jitpack.io")
```

### Run demo app
### iOS

```bash
cd example
fvm flutter run
Nothing to add: the plugin's podspec pulls the `REES46` pod and declares the
iOS 13.0 minimum itself, `pod install` runs as part of `flutter run`, and the
plugin registers its own application delegate — your `AppDelegate` stays untouched.

## Initialize

Initialize once, as early as possible — typically in `main()`, before `runApp`:

```dart
import 'package:flutter/widgets.dart';
import 'package:rees46_sdk/rees46_sdk.dart';

late final PersonalizationSdk sdk;

void main() {
WidgetsFlutterBinding.ensureInitialized();

sdk = Rees46.initialize(
const Rees46Config(shopId: 'YOUR_SHOP_ID'),
);

runApp(const MyApp());
}
```

### Notes
`Rees46` is the entry point. `Rees46.initialize` **returns the handle
synchronously** and starts native initialization in the background; calls issued
right after are queued natively until the session is ready, so the handle is
usable straight away. A broken setup surfaces as a `PlatformException` on the
first call you make with it (`bad_args` for an empty `shopId`, `init_failed` if
native init threw).

`shopId` is the only required field:

| Field | Default | Notes |
|---|---|---|
| `shopId` | — (**required**) | Your REES46 shop key |
| `apiDomain` | `api.rees46.ru` | API host |
| `stream` | `android` / `ios` | Traffic stream label; defaults to the current platform |
| `autoSendPushToken` | `true` | Fetches and sends the push token during init |
| `needReInitialization` | `false` | Forces a fresh session / device id |

- **Android**: uses Maven dependency `com.rees46:rees46-sdk:2.28.0` and calls `SDK.initialize(...)`. Some iOS-only init flags are accepted by Dart API but ignored on Android.
- **iOS**: uses CocoaPods dependency `REES46 (3.23.0)` and calls `createPersonalizationSDK(...)`.
- **Pushes**:
- **Android**: when `autoSendPushToken=true`, the native SDK fetches the FCM token via `FirebaseMessaging.getInstance().token` during initialization and sends it.
- **iOS**: when `autoSendPushToken=true`, the native SDK requests notification permission and registers for remote notifications. The Flutter plugin also forwards `didRegisterForRemoteNotificationsWithDeviceToken` and `didReceiveRemoteNotification` AppDelegate callbacks to the native SDK.
Push delivery needs platform setup of its own — a Firebase config on Android, the
Push Notifications capability on iOS. Without it initialization still succeeds;
there is simply no token to send.

Keep one place that owns the handle, so no widget re-initializes:

```dart
class Rees46Service {
static const _shopId = 'YOUR_SHOP_ID';

### Android push notification icon
static PersonalizationSdk get sdk => Rees46.isInitialized(_shopId)
? Rees46.getInstance(_shopId)
: Rees46.initialize(const Rees46Config(shopId: _shopId));
}
```

On Android the plugin displays incoming pushes itself, so the notification's **small icon** is resolved from the **host app**, never from REES46. You should point it at your own icon:
### Check that it worked

```xml
<!-- android/app/src/main/AndroidManifest.xml, inside <application> -->
<meta-data
android:name="com.rees46.push.notification_icon"
android:resource="@drawable/ic_stat_notify" />
```dart
final sid = await sdk.getSid(); // session id
final did = await sdk.getDid(); // device id issued by REES46
```

The icon must be a **white, alpha-only silhouette** — Android tints the small icon, so a full-colour image renders as a solid white/grey square. Generate one via Android Studio → *New → Image Asset → Notification Icons*.
A non-empty `did` means the native SDK completed its handshake with the API.

### Several shops in one app

One app can run several shops at once — regional storefronts, super-app tenants.
Each gets its own native instance with isolated storage, session and `did`.

**Already using Firebase?** If your app already declares `com.google.firebase.messaging.default_notification_icon`, you don't need to set anything — the plugin reuses that icon. Set `com.rees46.push.notification_icon` only if you want a different icon for REES46 pushes specifically.
```dart
// Registered now, initialized on first use.
Rees46.registerShops(const [
Rees46Config(shopId: 'shop-a'),
Rees46Config(shopId: 'shop-b'),
]); // pass eagerInit: true to initialize up front

final shopA = Rees46.getInstance('shop-a');
```

Resolution order:
Address instances explicitly once more than one is registered: `getInstance()`
without an id resolves only while exactly one shop is registered, and throws
`AmbiguousShopException` otherwise (`UnknownShopIdException` for an id that was
never registered).

1. The `com.rees46.push.notification_icon` meta-data icon above (recommended if you want a dedicated icon).
2. The existing Firebase `com.google.firebase.messaging.default_notification_icon`, if declared.
3. The app's launcher icon, if neither is set (may look like a square, since a launcher icon is not a silhouette).
4. A neutral non-branded default bundled in the plugin, only if the host has no icon at all.

For Flutter plugin development basics, see Flutter docs: [develop plugins](https://flutter.dev/to/develop-plugins).
## License

MIT — see [LICENSE](LICENSE).
Loading