Search by

creatix / app-version-manager

CreatixInfotech

Enterprise-ready Laravel package for mobile app version policy enforcement, forced/optional update gates (HTTP 426), in-app update checks, and automated App Store & Google Play Store release sync.

v1.0.1 2026-10-30 10:59 UTC

This package is auto-updated.

Last update: 2026-10-30 11:01:17 UTC


README

Latest Version on Packagist Total Downloads License PHP Version Laravel Version Tests Status

Enterprise-grade Laravel package for mobile app version policy enforcement, forced & optional update gates, in-app update checks, and automated App Store & Google Play release synchronization.

๐Ÿ“– Table of Contents

๐ŸŒŸ Overview

When maintaining mobile applications (iOS, Android, Flutter, React Native), breaking API changes, deprecated database schemas, and critical security patches require a dependable way to enforce minimum supported app versions.

App Version Manager provides a plug-and-play solution for Laravel applications:

  • Intercepts requests from outdated builds before they reach your controller logic with HTTP 426 Upgrade Required.
  • Notifies mobile clients when a newer version is available without blocking functionality (optional updates).
  • Synchronizes newly published releases directly from Apple App Store Connect and Google Play Developer API.
  • Delivers sub-millisecond response times with zero database overhead via smart policy caching.

๐Ÿ’ก Why Use App Version Manager?

Traditional Approach With Creatix App Version Manager
Hardcoding minimum versions in .env or route files Clean database-driven version management per platform (ios, android)
Manually updating version numbers after every store release Automatic scheduled background synchronization with App Store & Google Play
Checking database on every single mobile API hit Multi-layer cached policies that eliminate redundant database queries
Custom, inconsistent response codes across endpoints Standardized HTTP 426 Upgrade Required with store links & update notes

๐Ÿš€ Key Features

  • ๐Ÿ›ก๏ธ Forced Update Middleware Gate: Intercepts requests containing outdated client build numbers and returns a clean HTTP 426 Upgrade Required payload with store URLs.
  • ๐Ÿ’ก Optional In-App Update Prompts: Allows mobile apps to display non-intrusive upgrade prompts when newer features are available.
  • โšก Zero-Overhead Caching: Policies are cached with customizable TTL and automatically invalidated on updates, keeping your API lightning fast.
  • ๐Ÿ”„ Automated Store Sync: Command-line tool and scheduler task to fetch live release data directly from Apple App Store Connect and Google Play Console.
  • ๐Ÿ“ฑ RESTful Version Check Endpoint: Ready-to-use /api/v1/app-version route for splash screen or launch checks.
  • ๐Ÿ”” Event-Driven Architecture: Dispatches StoreVersionReleased events to trigger notifications, Slack alerts, or emails when new versions go live.
  • ๐Ÿงฉ Multi-Platform Support: Tailored configurations for iOS, Android, and cross-platform frameworks (Flutter, React Native, Ionic, Capacitor).

๐Ÿ“ Architecture & Request Flow

flowchart TD
    Client["๐Ÿ“ฑ Mobile Client (iOS / Android)"] --> Request["HTTP Request + Headers (X-App-Platform, X-App-Build)"]
    Request --> Middleware["๐Ÿ›ก๏ธ EnforceMinAppVersion Middleware"]
    
    Middleware --> CacheCheck{"Cached Version Policy Exists?"}
    CacheCheck -- Yes --> ReadCache["Read Active Policy from Cache"]
    CacheCheck -- No --> ReadDB["Query Database & Cache Policy"]
    
    ReadCache --> Evaluate{"Compare Client Build vs Policy"}
    ReadDB --> Evaluate
    
    Evaluate -- "Client Build < Minimum Build" --> HTTP426["๐Ÿ›‘ 426 Upgrade Required\n(Forced Update JSON + Store URL)"]
    Evaluate -- "Client Build >= Minimum Build" --> Controller["โœ… Pass to API Controller / App Logic"]
Loading

๐Ÿ“ฆ Compatibility

Environment Supported Versions
PHP 8.2.x, 8.3.x, 8.4.x, 8.5.x
Laravel 11.x, 12.x, 13.x
Databases MySQL, PostgreSQL, SQLite, SQL Server
Client Platforms iOS, Android, Flutter, React Native, Swift, Kotlin

๐Ÿ› ๏ธ Installation

Install the package via Composer:

composer require creatix/app-version-manager

Publish the package configuration and database migration files:

php artisan vendor:publish --tag=app-version-manager-config
php artisan vendor:publish --tag=app-version-manager-migrations

Execute the database migration:

php artisan migrate

โš™๏ธ Configuration

The published config/app-version-manager.php file allows you to customize headers, cache TTL, API routes, and store API credentials:

return [
    /*
    |--------------------------------------------------------------------------
    | Database Table
    |--------------------------------------------------------------------------
    */
    'table_name' => 'app_versions',

    /*
    |--------------------------------------------------------------------------
    | Request Headers
    |--------------------------------------------------------------------------
    | Custom headers sent by mobile clients to identify platform and build.
    */
    'headers' => [
        'platform' => 'X-App-Platform', // 'ios' or 'android'
        'build'    => 'X-App-Build',    // integer build number (e.g. 42)
    ],

    /*
    |--------------------------------------------------------------------------
    | Version Check API Endpoint
    |--------------------------------------------------------------------------
    */
    'api' => [
        'enabled' => true,
        'prefix'  => 'api/v1',
        'route'   => 'app-version',
    ],

    /*
    |--------------------------------------------------------------------------
    | Cache Settings
    |--------------------------------------------------------------------------
    | Reduces database load by caching current platform policies.
    */
    'cache' => [
        'enabled' => true,
        'ttl'     => 3600, // seconds
    ],

    /*
    |--------------------------------------------------------------------------
    | Store API Credentials
    |--------------------------------------------------------------------------
    | Credentials for App Store Connect and Google Play Developer API.
    */
    'stores' => [
        'appstore' => [
            'issuer_id'   => env('ASC_ISSUER_ID'),
            'key_id'      => env('ASC_KEY_ID'),
            'private_key' => env('ASC_PRIVATE_KEY'),
            'app_id'      => env('ASC_APP_ID'),
        ],
        'googleplay' => [
            'service_account' => env('GOOGLE_PLAY_SERVICE_ACCOUNT'),
            'package_name'    => env('GOOGLE_PLAY_PACKAGE_NAME'),
        ],
    ],
];

๐Ÿ’ป Usage Guide

1. Protecting Routes with Forced Update Middleware

Apply the enforce.min.app.version middleware to any API route or group:

use Illuminate\Support\Facades\Route;

Route::middleware(['api', 'enforce.min.app.version'])->group(function () {
    Route::get('/user/profile', [UserController::class, 'profile']);
    Route::post('/orders', [OrderController::class, 'store']);
});

When a mobile client sends a request with headers:

  • X-App-Platform: android
  • X-App-Build: 12

If your minimum required build is 15, the middleware intercepts the request and responds with HTTP 426 Upgrade Required:

{
    "success": false,
    "message": "A newer version of the app is required to continue.",
    "code": 426,
    "data": {
        "platform": "android",
        "update_required": "forced",
        "minimum_version": "1.5.0",
        "minimum_build": 15,
        "latest_version": "2.0.0",
        "latest_build": 20,
        "store_url": "https://play.google.com/store/apps/details?id=com.example",
        "update_message": "Please update to continue using the app."
    }
}

2. Mobile App Launch Version Check Endpoint

Call the built-in endpoint on app startup (splash screen) to verify version status:

GET /api/v1/app-version?platform=ios&build=18

(Alternatively, send via headers X-App-Platform: ios and X-App-Build: 18)

Example Response (Optional Update Available):

{
    "success": true,
    "message": "App version status retrieved successfully.",
    "data": {
        "platform": "ios",
        "update_required": "optional",
        "is_supported": true,
        "latest_version": "2.0.0",
        "latest_build": 20,
        "minimum_version": "1.5.0",
        "minimum_build": 15,
        "store_url": "https://apps.apple.com/app/id123456789",
        "update_message": "A new version with performance improvements is available!"
    }
}

Status Enum:

  • forced: Client build is below minimum build. App should block user interaction and redirect to store.
  • optional: Client build meets minimum build, but a newer release exists. Display a dismissible dialog.
  • none: Client is running the latest available build.

3. Programmatic Usage via Facade

Use the AppVersion facade anywhere in your Laravel services:

use Creatix\AppVersionManager\Facades\AppVersion;

// Retrieve the active policy model for a given platform
$policy = AppVersion::currentFor('android');

// Check requirement status for a specific build
$check = AppVersion::check('ios', 42);

if ($check['update_required'] === 'forced') {
    // Custom fallback or analytics logging
}

4. Automated App Store & Google Play Sync

Keep your database up-to-date automatically whenever a new build is approved on the stores:

php artisan app-versions:sync

Schedule it in your routes/console.php (Laravel 11+):

use Illuminate\Support\Facades\Schedule;

Schedule::command('app-versions:sync')->hourly();

When a new version is detected, a StoreVersionReleased event is triggered:

use Creatix\AppVersionManager\Events\StoreVersionReleased;
use Illuminate\Support\Facades\Event;

Event::listen(StoreVersionReleased::class, function ($event) {
    // Notify your mobile team or send an alert via Slack/Email
    Log::info("New version released for {$event->platform}: {$event->version} (Build {$event->build})");
});

๐Ÿ“ฑ Client Integration Examples

Flutter (Dart)

Using a Dio interceptor to handle forced updates globally:

import 'package:dio/dio.dart';
import 'package:url_launcher/url_launcher.dart';

class ForceUpdateInterceptor extends Interceptor {
  @override
  void onError(DioException err, ErrorInterceptorHandler handler) {
    if (err.response?.statusCode == 426) {
      final storeUrl = err.response?.data['data']['store_url'];
      final message = err.response?.data['message'];
      
      // Show non-dismissible dialog and launch store URL
      launchUrl(Uri.parse(storeUrl), mode: LaunchMode.externalApplication);
      return;
    }
    super.onError(err, handler);
  }
}

React Native (Axios)

import axios from 'axios';
import { Alert, Linking } from 'react-native';

const api = axios.create({
  baseURL: 'https://api.yourdomain.com',
  headers: {
    'X-App-Platform': Platform.OS, // 'ios' or 'android'
    'X-App-Build': 12,
  },
});

api.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response && error.response.status === 426) {
      const { store_url, update_message } = error.response.data.data;
      Alert.alert(
        'Update Required',
        update_message || 'Please update the app to continue.',
        [{ text: 'Update Now', onPress: () => Linking.openURL(store_url) }],
        { cancelable: false }
      );
    }
    return Promise.reject(error);
  }
);

iOS (Swift)

func handleResponse(response: HTTPURLResponse, data: Data) {
    if response.statusCode == 426 {
        if let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
           let responseData = json["data"] as? [String: Any],
           let storeUrlString = responseData["store_url"] as? String,
           let storeUrl = URL(string: storeUrlString) {
            DispatchQueue.main.async {
                UIApplication.shared.open(storeUrl)
            }
        }
    }
}

Android (Kotlin)

if (response.code() == 426) {
    val errorBody = response.errorBody()?.string()
    val storeUrl = JSONObject(errorBody).getJSONObject("data").getString("store_url")
    val intent = Intent(Intent.ACTION_VIEW, Uri.parse(storeUrl))
    context.startActivity(intent)
}

โ“ Frequently Asked Questions (FAQ)

How does App Version Manager achieve zero database overhead?

When caching is enabled (config('app-version-manager.cache.enabled')), the active version policy for each platform is stored in your configured Laravel cache store (Redis, Memcached, DynamoDB, etc.). All incoming mobile API requests read directly from cache without hitting the database. Cache keys are automatically invalidated whenever a policy is created or updated.

What happens if a request does not contain version headers?

If the configured headers (X-App-Platform and X-App-Build) are missing, the middleware gracefully allows the request to pass through by default, ensuring non-mobile clients (such as web frontends or webhooks) are never inadvertently blocked.

Why does the package use HTTP 426 Upgrade Required?

HTTP 426 Upgrade Required is the official IETF RFC 7231 standard status code designed specifically to inform clients that the server refuses to perform the request using the current protocol or build and will proceed only after the client upgrades.

Can I bypass version enforcement in automated testing or local development?

Yes. Simply omit the X-App-Platform / X-App-Build headers in test suites, or mock the AppVersion facade using AppVersion::shouldReceive('check')->andReturn(...).

๐Ÿงช Testing

Run tests with Composer:

composer test

Or invoke PHPUnit directly:

vendor/bin/phpunit

๐Ÿ“œ Changelog

Please see CHANGELOG.md for details on recent releases and updates.

๐Ÿค Contributing

Contributions, bug reports, and pull requests are welcome! Please review CONTRIBUTING.md for guidelines.

๐Ÿ”’ Security Vulnerabilities

If you discover any security-related issues, please review our Security Policy or email hello@creatixinfotech.com instead of using the public issue tracker.

๐Ÿ“„ License

The MIT License (MIT). Please see License File for more information.

Crafted with โค๏ธ by Creatix Infotech