# Installation

## Table of Contents

- <a name="toc-overview"></a>[Overview](#overview)
- <a name="toc-pubdev"></a>[pub.dev Dependency Setup](#pubdev)
- <a name="toc-cli-wizard"></a>[CLI Installation Wizard](#cli-wizard)
- <a name="toc-service-provider"></a>[Service Provider Registration](#service-provider)
- <a name="toc-main-dart"></a>[Config Factory in main.dart](#main-dart)
- <a name="toc-platform-setup"></a>[Platform Setup Summary](#platform-setup)
- <a name="toc-next-steps"></a>[Next Steps](#next-steps)

---

## <a name="overview"></a>Overview

Magic Notifications is a multi-channel notification plugin for Flutter apps built on the Magic Framework. It ships with a built-in CLI that automates the entire setup process — dependency declaration, config file creation, platform-specific files, and provider injection.

> [!NOTE]
> The CLI wizard is the recommended installation path. Manual setup requires you to replicate every step the CLI performs; use it only when your CI/CD pipeline demands fully scripted control.

---

## <a name="pubdev"></a>pub.dev Dependency Setup

Add the package to your `pubspec.yaml`:

```yaml
dependencies:
  magic_notifications: ^0.3.12
```

Then fetch dependencies:

```bash
flutter pub get
```

For local development within a monorepo (e.g., `plugins/magic_notifications`):

```yaml
dependencies:
  magic_notifications:
    path: ./plugins/magic_notifications
```

---

## <a name="cli-wizard"></a>CLI Installation Wizard

Run the interactive wizard from your Flutter project root:

```bash
dart run magic_notifications install
```

The wizard guides you through four steps:

1. **OneSignal App ID** — prompts for your UUID-format App ID (get it from the [OneSignal Dashboard](https://onesignal.com/))
2. **Platform selection** — detects existing platform directories; confirm each one
3. **Web configuration** (web only) — optional Safari Web ID, optional notify button
4. **Soft prompt** — whether to show a custom dialog before the OS permission prompt

### Non-Interactive Mode (CI/CD)

```bash
dart run magic_notifications install \
  --non-interactive \
  --app-id 12345678-1234-1234-1234-123456789012 \
  --platforms android,ios,web
```

```bash
# With web extras
dart run magic_notifications install \
  --non-interactive \
  --app-id YOUR_APP_ID \
  --platforms web \
  --safari-web-id web.onesignal.auto.xxx \
  --notify-button
```

```bash
# Disable soft prompt and overwrite any existing config
dart run magic_notifications install \
  --non-interactive \
  --app-id YOUR_APP_ID \
  --no-soft-prompt \
  --force
```

### What the CLI Produces

| Artifact | Path |
|----------|------|
| Notification config | `lib/config/notifications.dart` |
| Android permission | `android/app/src/main/AndroidManifest.xml` |
| iOS background mode | `ios/Runner/Info.plist` (`UIBackgroundModes` gains `remote-notification`) |
| iOS entitlement | `ios/Runner/Runner.entitlements` (`aps-environment`; created if absent) |
| iOS build setting | `ios/Runner.xcodeproj/project.pbxproj` (`CODE_SIGN_ENTITLEMENTS` points at the entitlements file) |
| Web service worker | `web/OneSignalSDKWorker.js` |
| Web SDK script | `web/index.html` (injected) |
| Provider injection | `lib/config/app.dart` (injected) |
| Config factory | `lib/main.dart` (injected) |

> [!TIP]
> Run `dart run magic_notifications doctor` immediately after installation to verify every required artifact is in place.

---

## <a name="service-provider"></a>Service Provider Registration

If you ran the CLI, `lib/config/app.dart` is already updated. For manual setup, add the import and provider entry:

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

final providers = [
  // ... other providers
  (app) => NotificationServiceProvider(app),
];
```

The `NotificationServiceProvider` runs two phases during framework boot:

- `register()` — binds `NotificationManager` as a singleton under the `'notifications'` key
- `boot()` — reads config, initializes the OneSignal push driver, starts no polling on its own

See [Service Provider Architecture](https://anilcancakir.com/magic/docs/notifications/v0.3/architecture/service-provider.md) for the full boot sequence.

---

## <a name="main-dart"></a>Config Factory in main.dart

The notification config must be passed to `Magic.init` as a config factory:

```dart
import 'config/notifications.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Magic.init(
    configFactories: [
      () => appConfig,
      () => notificationConfig,  // add this
    ],
  );

  runApp(MyApp());
}
```

The `notificationConfig` getter is defined in `lib/config/notifications.dart` (created by the CLI or `publish` command). Its shape:

```dart
Map<String, dynamic> get notificationConfig => {
  'notifications': {
    'push': {
      'driver': 'onesignal',
      'app_id': const String.fromEnvironment('ONESIGNAL_APP_ID'),
      'notify_button_enabled': false,
    },
    'database': {
      'enabled': true,
      'polling_interval': 30,
    },
    'mail': {
      'enabled': false,
    },
    'soft_prompt': {
      'enabled': true,
      'title': 'Stay Updated',
      'message': 'Get notified about important events.',
    },
  },
};
```

Add the App ID to your `.env`:

```
ONESIGNAL_APP_ID=your-onesignal-app-id-here
```

---

## <a name="platform-setup"></a>Platform Setup Summary

### iOS

The CLI writes the three project-file markers a push needs directly, without
opening Xcode: `UIBackgroundModes` gains `remote-notification` in
`ios/Runner/Info.plist`, `aps-environment` is declared in
`ios/Runner/Runner.entitlements` (created if the project never had one), and
`CODE_SIGN_ENTITLEMENTS` in `ios/Runner.xcodeproj/project.pbxproj` is pointed
at that entitlements file (without which Xcode never reads it, and signing
silently ignores the entitlement).

What is still manual, because it needs an Apple/OneSignal account rather than
a project-file edit:

1. Run `cd ios && pod install --repo-update && cd ..`
2. Create an APNs key in [Apple Developer Portal](https://developer.apple.com/account/resources/authkeys/list) (.p8 file + Key ID + Team ID). One key covers Sandbox and Production, it is team-wide, and it downloads exactly once.
3. Upload the .p8 key to OneSignal Dashboard → Settings → Platforms → Apple iOS (APNs), with the Key ID, your Team ID and your bundle identifier.

> [!IMPORTANT]
> The entitlement this installer writes says `development`, which is what a
> development provisioning profile carries and what a distribution one
> refuses. Before you archive anything, read
> [Shipping push in a release build](https://anilcancakir.com/magic/docs/notifications/v0.3/basics/shipping-push.md): that and
> three other differences are silent, and the first evidence of each is a
> notification nobody receives.

#### The Notification Service Extension, and why nothing here can create it

Push works without the next two steps, which is the reason to read them: a
build with no extension delivers notifications normally and quietly reports no
confirmed deliveries, no rich media and no badge counts. The absence looks like
the product working. `notifications:doctor` warns when either half is missing.

Neither can be automated from a pub package, because both add or change an
Xcode target:

4. **App Group.** In Xcode, add the App Groups capability to the **Runner**
   target with a container named `group.<your.bundle.id>.onesignal`.
5. **Notification Service Extension.** File → New → Target →
   Notification Service Extension, named `OneSignalNotificationServiceExtension`,
   then add the **same** App Group to that new target and follow OneSignal's
   current [Flutter SDK setup](https://documentation.onesignal.com/docs/flutter-sdk-setup)
   for the extension's source and its deployment target.

The two halves fail independently. An extension with no shared App Group gives
rich media and still no confirmed delivery, because the container is how the
extension hands what it saw back to the app.

> [!NOTE]
> Push notifications do not work on iOS Simulator. A physical device is required for testing.

> [!WARNING]
> Test the cold-start path with a **profile or release** build. iOS refuses to
> launch a debug Flutter build outside the Flutter tooling, and OneSignal
> documents that on iOS in Debug a force-closed app opened from a notification
> does not register the click listener at all. A cold tap that appears to do
> nothing in Debug is usually this, not your wiring.

### Android

**`android/app/build.gradle`** — set minimum SDK:

```gradle
android {
    defaultConfig {
        minSdkVersion 21
    }
}
```

**`android/app/src/main/AndroidManifest.xml`** — add permissions (the CLI injects `POST_NOTIFICATIONS` automatically):

```xml
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
```

### Web

The CLI creates the service worker and injects the SDK script. Manually:

**`web/OneSignalSDKWorker.js`**:

```javascript
importScripts("https://cdn.onesignal.com/sdks/web/v16/OneSignalSDK.sw.js");
```

> [!NOTE]
> Use `OneSignalSDK.sw.js` for the service worker file and `OneSignalSDK.page.js` for the `<script>` tag in `index.html`. They are different build targets.

**`web/manifest.json`** — add GCM sender ID:

```json
{
  "gcm_sender_id": "482941778795",
  "gcm_sender_id_comment": "Do not change. Required for OneSignal Web Push."
}
```

> [!IMPORTANT]
> **iOS Safari only receives web push after the site is added to the Home Screen.** Per [OneSignal's documentation](https://documentation.onesignal.com/docs/en/web-push-for-ios), this "will work on iOS devices only after users add your site to their home screen and open it from there. This is Apple's design requirement." There is no workaround for a plain Safari tab; the app has to tell the person to add the site to their Home Screen first.

For the full platform guides, see [README.md](../../README.md).

---

## <a name="next-steps"></a>Next Steps

1. Run `dart run magic_notifications doctor` to confirm setup health
2. Read [Configuration](https://anilcancakir.com/magic/docs/notifications/v0.3/getting-started/configuration.md) for all config key details
3. Add polling and push login to your auth flow — see [README.md](../../README.md)
4. Explore [Channels](https://anilcancakir.com/magic/docs/notifications/v0.3/basics/channels.md) and [Drivers](https://anilcancakir.com/magic/docs/notifications/v0.3/basics/drivers.md)

---

**Related**

- [Configuration](https://magic.fluttersdk.com/packages/notifications/getting-started/configuration)
- [Channels](https://magic.fluttersdk.com/packages/notifications/basics/channels)
- [CLI Reference](https://magic.fluttersdk.com/packages/notifications/basics/cli)
