Skip to content
Closed
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
61 changes: 61 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# AGENTS.md

## Project

Flutter plugin for in-app updates. iOS checks via iTunes Lookup API (pure Dart), shows App Store page via StoreKit. Android uses Play Core API (Kotlin native).

## Structure

```
lib/
in_app_update_flutter.dart # Public API, delegates to method channel
src/
method_channel/
in_app_update_flutter_method_channel.dart # Native method channel calls
models/
models.dart # Barrel export
app_update_info.dart # Unified cross-platform result
app_update_info_ios.dart # iOS-specific result
app_update_info_android.dart # Android-specific result (pre-existing)
update_config.dart # UpdateConfig, AndroidUpdateType enum
update_availability_android.dart # Android availability enum (pre-existing)
update_result_android.dart # Android result enum (pre-existing)
install_state_android.dart # Android install state (pre-existing)
install_status_android.dart # Android install status enum (pre-existing)
ios_update_check.dart # Pure Dart iTunes Lookup API call
android/ # Kotlin native (Play Core)
ios/ # Swift native (StoreKit presentation only)
test/ # Unit tests
```

## Key patterns

- `InAppUpdateFlutter` is the public entry point, takes optional `UpdateConfig`
- All methods delegate to `MethodChannelInAppUpdateFlutter`
- iOS update checking is pure Dart (`ios_update_check.dart`) — no native code
- iOS native code (`InAppUpdateFlutterPlugin.swift`) only handles `showStoreUpdateIos` (StoreKit)
- Android native code handles all Play Core methods
- `UpdateConfig` holds defaults: `appStoreId`, `iosAppStoreRegion`, `androidUpdateType`

## Commands

```bash
dart format lib/
flutter analyze
flutter test
```

Always run `dart format lib/` before committing.

## Dependencies

- `package_info_plus` — get installed version and bundle ID (used by iOS check)
- `pub_semver` — semantic version comparison (used by iOS check)
- No `plugin_platform_interface` — was removed, not needed

## Conventions

- Models use `const` constructors and factory methods
- Method channel methods are `snake_case` strings matching native side
- Deprecation via `@Deprecated` annotation, not removal
- Cross-platform methods (`checkUpdate`, `startUpdate`, `checkAndUpdate`) route via `Platform.isIOS`/`Platform.isAndroid`
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Read AGENTS.md
120 changes: 93 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A Flutter plugin for in-app updates on both iOS and Android.

On **iOS**, it presents the App Store product page using `SKStoreProductViewController` (StoreKit), keeping users inside the app during the update flow. On **Android**, it integrates with Google Play's In-App Updates API to support both immediate (blocking) and flexible (background) update flows.
On **iOS**, it checks for updates via the iTunes Lookup API and presents the App Store product page using `SKStoreProductViewController` (StoreKit), keeping users inside the app during the update flow. On **Android**, it integrates with Google Play's In-App Updates API to support both immediate (blocking) and flexible (background) update flows.

---

Expand All @@ -16,14 +16,15 @@ On **iOS**, it presents the App Store product page using `SKStoreProductViewCont

## Features

- iOS: Show the App Store update prompt using `SKStoreProductViewController` without navigating users away from the app
- iOS: Native Swift implementation with zero AppDelegate configuration required
- iOS: Supports both Swift Package Manager (SPM) and CocoaPods
- Android: Check update availability and metadata via the Play Core API
- Android: Immediate update flow — full-screen, blocking prompt the user must accept
- Android: Flexible update flow — background download while the user continues using the app
- Android: Install state stream for monitoring flexible update download progress
- Works on Flutter with a simple, unified API
- **iOS**: Check for updates via iTunes Lookup API (pure Dart, no native code)
- **iOS**: Show the App Store update prompt using `SKStoreProductViewController` without navigating users away from the app
- **iOS**: Native Swift implementation with zero AppDelegate configuration required
- **iOS**: Supports both Swift Package Manager (SPM) and CocoaPods
- **Android**: Check update availability and metadata via the Play Core API
- **Android**: Immediate update flow — full-screen, blocking prompt the user must accept
- **Android**: Flexible update flow — background download while the user continues using the app
- **Android**: Install state stream for monitoring flexible update download progress
- **Cross-platform**: Unified `checkUpdate()`, `startUpdate()`, and `checkAndUpdate()` APIs

---

Expand All @@ -44,16 +45,89 @@ flutter pub get

---

## iOS Usage
## Setup

Pass your numeric App Store ID to `showUpdateForIos`. The ID can be found in your App Store Connect URL or the app's public App Store link.
Create an `InAppUpdateFlutter` instance with an `UpdateConfig` to set your app's defaults:

### Both iOS and Android

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

await InAppUpdateFlutter().showUpdateForIos(appStoreId: '1234567890');
final updater = InAppUpdateFlutter(UpdateConfig(
appStoreId: '1234567890', // Required for iOS
iosAppStoreRegion: 'us', // Optional: specific App Store region
androidUpdateType: AndroidUpdateType.flexible, // Optional: immediate (default) or flexible
));
```

### iOS only

```dart
final updater = InAppUpdateFlutter(UpdateConfig(
appStoreId: '1234567890',
));
```

### Android only

```dart
final updater = InAppUpdateFlutter(UpdateConfig(
androidUpdateType: AndroidUpdateType.flexible, // or .immediate (default)
));
```

---

## Quick Start

```dart
await updater.checkAndUpdate();
```

If an update is available, it starts the flow automatically using your config defaults.

---

## Usage

### Check for updates

```dart
final info = await updater.checkUpdate();

if (info.updateAvailable) {
print('Update available: ${info.storeVersion}');
}
```

### Start the update flow

```dart
// On iOS: presents App Store page via StoreKit
// On Android: starts immediate (blocking) update
await updater.startUpdate();
```

### Per-call overrides

Config values can be overridden on any call:

```dart
await updater.checkUpdate(iosAppStoreRegion: 'gb');
await updater.startUpdate(appStoreId: '9876543210');
await updater.checkAndUpdate(
iosAppStoreRegion: 'jp',
appStoreId: '9876543210',
);
```

---

## iOS Details

Pass your numeric App Store ID to `showUpdateForIos` (or set it in `UpdateConfig`). The ID can be found in your App Store Connect URL or the app's public App Store link.

**How to find your App Store ID:**

1. Open your app's App Store URL — for example: `https://apps.apple.com/app/id1234567890`
Expand All @@ -66,7 +140,7 @@ await InAppUpdateFlutter().showUpdateForIos(appStoreId: '1234567890');

---

## Android Usage
## Android Details

Android uses Google Play's In-App Updates API. The typical flow is:

Expand All @@ -78,15 +152,11 @@ Android uses Google Play's In-App Updates API. The typical flow is:
An immediate update presents a full-screen prompt that the user must complete before continuing. Use this for critical updates.

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

final plugin = InAppUpdateFlutter();

final info = await plugin.checkUpdateAndroid();
final info = await updater.checkUpdateAndroid();

if (info.updateAvailability == UpdateAvailabilityAndroid.updateAvailable &&
info.isImmediateUpdateAllowed) {
final result = await plugin.startImmediateUpdateAndroid();
final result = await updater.startImmediateUpdateAndroid();
// result is UpdateResultAndroid.success or UpdateResultAndroid.userCanceled
}
```
Expand All @@ -96,19 +166,15 @@ if (info.updateAvailability == UpdateAvailabilityAndroid.updateAvailable &&
A flexible update downloads in the background while the user continues using the app. When the download completes, call `completeUpdateAndroid()` to apply the update.

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

final plugin = InAppUpdateFlutter();

final info = await plugin.checkUpdateAndroid();
final info = await updater.checkUpdateAndroid();

if (info.updateAvailability == UpdateAvailabilityAndroid.updateAvailable &&
info.isFlexibleUpdateAllowed) {
await plugin.startFlexibleUpdateAndroid();
await updater.startFlexibleUpdateAndroid();

plugin.installStateStreamAndroid.listen((state) {
updater.installStateStreamAndroid.listen((state) {
if (state.installStatus == InstallStatusAndroid.downloaded) {
plugin.completeUpdateAndroid();
updater.completeUpdateAndroid();
}
});
}
Expand Down
Loading