native-update 1.4.9 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Readme.md +35 -9
- package/android/manifest-additions.xml +28 -0
- package/android/src/main/AndroidManifest.xml +3 -7
- package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundNotificationManager.kt +21 -13
- package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundUpdatePlugin.kt +15 -0
- package/android/src/main/java/com/aoneahsan/nativeupdate/BackgroundUpdateWorker.kt +23 -7
- package/android/src/main/java/com/aoneahsan/nativeupdate/LiveUpdatePlugin.kt +346 -31
- package/android/src/main/java/com/aoneahsan/nativeupdate/NativeUpdatePlugin.kt +32 -34
- package/android/src/main/java/com/aoneahsan/nativeupdate/NotificationActionReceiver.kt +10 -1
- package/android/src/main/java/com/aoneahsan/nativeupdate/SecurityManager.kt +18 -18
- package/cli/AGENTS.md +43 -0
- package/cli/CLAUDE.md +65 -0
- package/cli/commands/backend-create.js +56 -15
- package/cli/commands/bundle-create.js +39 -7
- package/cli/commands/monitor.js +72 -51
- package/cli/index.js +10 -24
- package/cli/package.json +11 -3
- package/dist/esm/app-review/app-review-manager.js +0 -1
- package/dist/esm/app-review/index.js +0 -1
- package/dist/esm/app-review/platform-review-handler.js +0 -1
- package/dist/esm/app-review/review-conditions-checker.js +0 -1
- package/dist/esm/app-review/review-rate-limiter.js +0 -1
- package/dist/esm/app-review/types.js +0 -1
- package/dist/esm/app-update/app-update-checker.js +0 -1
- package/dist/esm/app-update/app-update-installer.d.ts +1 -0
- package/dist/esm/app-update/app-update-installer.js +19 -8
- package/dist/esm/app-update/app-update-manager.js +0 -1
- package/dist/esm/app-update/app-update-notifier.js +0 -1
- package/dist/esm/app-update/index.js +0 -1
- package/dist/esm/app-update/platform-app-update.js +12 -3
- package/dist/esm/app-update/types.js +0 -1
- package/dist/esm/background-update/background-scheduler.js +0 -1
- package/dist/esm/background-update/index.js +0 -1
- package/dist/esm/background-update/notification-manager.js +0 -1
- package/dist/esm/config/support.js +0 -1
- package/dist/esm/core/analytics.d.ts +13 -0
- package/dist/esm/core/analytics.js +15 -1
- package/dist/esm/core/cache-manager.js +0 -1
- package/dist/esm/core/config.d.ts +6 -30
- package/dist/esm/core/config.js +1 -8
- package/dist/esm/core/errors.d.ts +1 -0
- package/dist/esm/core/errors.js +1 -1
- package/dist/esm/core/event-emitter.d.ts +0 -3
- package/dist/esm/core/event-emitter.js +8 -3
- package/dist/esm/core/logger.js +0 -1
- package/dist/esm/core/performance.js +0 -1
- package/dist/esm/core/plugin-manager.js +3 -1
- package/dist/esm/core/security.d.ts +22 -3
- package/dist/esm/core/security.js +46 -7
- package/dist/esm/definitions.d.ts +13 -40
- package/dist/esm/definitions.js +0 -1
- package/dist/esm/index.d.ts +3 -4
- package/dist/esm/index.js +3 -3
- package/dist/esm/live-update/bundle-manager.d.ts +8 -0
- package/dist/esm/live-update/bundle-manager.js +20 -1
- package/dist/esm/live-update/certificate-pinning.js +0 -1
- package/dist/esm/live-update/delta-processor.d.ts +1 -1
- package/dist/esm/live-update/delta-processor.js +3 -1
- package/dist/esm/live-update/download-manager.d.ts +36 -5
- package/dist/esm/live-update/download-manager.js +70 -24
- package/dist/esm/live-update/rollout-checker.d.ts +1 -1
- package/dist/esm/live-update/rollout-checker.js +5 -6
- package/dist/esm/live-update/update-manager.d.ts +12 -1
- package/dist/esm/live-update/update-manager.js +38 -11
- package/dist/esm/live-update/version-manager.d.ts +9 -12
- package/dist/esm/live-update/version-manager.js +40 -68
- package/dist/esm/plugin.js +82 -89
- package/dist/esm/security/crypto.js +11 -7
- package/dist/esm/security/validator.js +0 -1
- package/dist/esm/types/rollout.d.ts +85 -0
- package/dist/esm/types/rollout.js +25 -0
- package/dist/esm/web.d.ts +26 -1
- package/dist/esm/web.js +214 -75
- package/dist/plugin.cjs.js +1 -2
- package/dist/plugin.esm.js +1 -2
- package/dist/plugin.js +2 -3
- package/docs/AGENTS.md +38 -0
- package/docs/CHANGELOG.md +275 -0
- package/docs/CLAUDE.md +101 -0
- package/docs/MANUAL-TASKS.md +17 -0
- package/docs/MARKETING_WEBSITE_TRACKER.md +18 -2
- package/docs/MIGRATION.md +170 -0
- package/docs/PACKAGES.md +167 -0
- package/docs/PERFORMANCE.md +110 -0
- package/docs/PROJECT_COMPLETION_TRACKER.md +4 -35
- package/docs/README.md +12 -2
- package/docs/REMAINING_FEATURES.md +123 -76
- package/docs/ROADMAP.md +200 -120
- package/docs/SECURITY-IF-REPO-PUBLIC.md +232 -0
- package/docs/TESTING_REQUIREMENTS.md +1 -112
- package/docs/ai-knowledge-base/00-project-profile.md +38 -0
- package/docs/ai-knowledge-base/01-features.md +46 -0
- package/docs/ai-knowledge-base/02-routes.md +72 -0
- package/docs/ai-knowledge-base/03-tech-stack.md +57 -0
- package/docs/ai-knowledge-base/README.md +23 -0
- package/docs/analytics-tracking.md +37 -0
- package/docs/capacitor-official-plugins.md +46 -0
- package/docs/capawesome-plugins.md +29 -0
- package/docs/deployment/HOSTINGER_DEPLOY.md +370 -0
- package/docs/deployment/PRODUCTION-DEPLOY-CHECKLIST.md +184 -0
- package/docs/docs-site/plan.md +201 -0
- package/docs/docs-site/scope.md +127 -0
- package/docs/docs-site/tracker.json +235 -0
- package/docs/error-handling-tracking.md +37 -0
- package/docs/features/laravel-nova-backend/ASSESSMENT-SUMMARY.md +96 -0
- package/docs/features/laravel-nova-backend/IMPLEMENTATION-PLAN.md +504 -0
- package/docs/features/laravel-nova-backend/progress-tracker.json +184 -0
- package/docs/guides/testing-guide.md +15 -41
- package/docs/package-audit.md +41 -0
- package/docs/play-store/ASO-METADATA.md +5 -0
- package/docs/play-store/DATA-SAFETY.md +5 -0
- package/docs/play-store/DECLARATIONS.md +5 -0
- package/docs/play-store/README.md +27 -0
- package/docs/play-store/RELEASE-NOTES.md +99 -0
- package/docs/play-store/RELEASE-STEPS.md +6 -0
- package/docs/play-store/STORE-LISTING.md +7 -0
- package/docs/play-store/SUBMISSION-CHECKLIST.md +5 -0
- package/docs/production-readiness.md +1 -1
- package/docs/project-audit/2026-05-16/00-FINALIZATION-PLAN.md +123 -0
- package/docs/project-audit/2026-05-16/01-BATCH-PLAN.md +227 -0
- package/docs/project-audit/2026-05-16/02-AUDIT-FINDINGS.md +144 -0
- package/docs/project-audit/2026-05-16/sessions/batch-01.md +42 -0
- package/docs/project-audit/2026-05-16/sessions/batch-02.md +74 -0
- package/docs/project-audit/2026-05-16/sessions/batch-03.md +71 -0
- package/docs/project-audit/2026-05-16/sessions/batch-04.md +88 -0
- package/docs/project-audit/2026-05-16/sessions/batch-05.md +63 -0
- package/docs/project-audit/2026-05-16/sessions/batch-06.md +103 -0
- package/docs/project-audit/2026-05-16/sessions/batch-07.md +61 -0
- package/docs/project-audit/2026-05-16/sessions/batch-08.md +101 -0
- package/docs/project-audit/2026-05-16/sessions/batch-09.md +105 -0
- package/docs/project-audit/2026-05-16/sessions/batch-10.md +102 -0
- package/docs/project-audit/AUDIT-HISTORY.md +74 -0
- package/docs/project-audit/BATCH-PLAN.md +168 -0
- package/docs/project-audit/MARKETING-WEBSITE-FINDINGS-2026-05-11.md +73 -0
- package/docs/project-audit/PRODUCTION-READINESS-AUDIT-2026-05-08.md +275 -0
- package/docs/project-finalization/00-tracker.json +60 -0
- package/docs/project-knowledge-base/01-system-overview.md +227 -0
- package/docs/project-knowledge-base/02-routes-pages-forms-users.md +352 -0
- package/docs/project-knowledge-base/03-tech-stack-modules-services.md +379 -0
- package/docs/project-knowledge-base/04-data-models-integrations.md +244 -0
- package/docs/project-knowledge-base/05-docs-corpus-inventory.md +256 -0
- package/docs/project-knowledge-base/06-operations-testing-legal-content.md +208 -0
- package/docs/project-knowledge-base/README.md +103 -0
- package/docs/project-profiles/native-update-capacitor-update-platform-project-profile-last-updated-2026-05-19.md +81 -0
- package/docs/rules-tracker.md +102 -0
- package/docs/security/PRE-LAUNCH-PENTEST-CHECKLIST.md +116 -0
- package/docs/seo-aeo-rules.json +3043 -0
- package/docs/social-media-marketing/README.md +81 -0
- package/docs/social-media-marketing/posts-batch-01.md +613 -0
- package/docs/social-media-marketing/posts-batch-02.md +528 -0
- package/docs/social-media-marketing/whatsapp-messages-batch-01.md +458 -0
- package/docs/social-media-marketing/whatsapp-status-batch-01.md +355 -0
- package/docs/tracking/finalization-2026-05-16-tracker.json +346 -0
- package/docs/tracking/full-audit-tracker.json +121 -0
- package/docs/tracking/production-readiness-audit-tracker.json +429 -0
- package/docs/tracking/seo-checklist-tracker.json +333 -0
- package/ios/Plugin/BackgroundUpdate/BackgroundUpdatePlugin.swift +66 -9
- package/ios/Plugin/LiveUpdate/LiveUpdatePlugin.swift +306 -52
- package/ios/Plugin/NativeUpdatePlugin.swift +22 -9
- package/ios/Plugin/Security/SecurityManager.swift +13 -14
- package/package.json +37 -38
- package/cli/node_modules/.yarn-integrity +0 -16
- package/cli/node_modules/commander/LICENSE +0 -22
- package/cli/node_modules/commander/Readme.md +0 -1148
- package/cli/node_modules/commander/esm.mjs +0 -16
- package/cli/node_modules/commander/index.js +0 -26
- package/cli/node_modules/commander/lib/argument.js +0 -145
- package/cli/node_modules/commander/lib/command.js +0 -2179
- package/cli/node_modules/commander/lib/error.js +0 -43
- package/cli/node_modules/commander/lib/help.js +0 -462
- package/cli/node_modules/commander/lib/option.js +0 -329
- package/cli/node_modules/commander/lib/suggestSimilar.js +0 -100
- package/cli/node_modules/commander/package-support.json +0 -16
- package/cli/node_modules/commander/package.json +0 -80
- package/cli/node_modules/commander/typings/esm.d.mts +0 -3
- package/cli/node_modules/commander/typings/index.d.ts +0 -884
- package/cli/yarn.lock +0 -8
- package/dist/esm/__tests__/bundle-manager.test.d.ts +0 -1
- package/dist/esm/__tests__/bundle-manager.test.js +0 -151
- package/dist/esm/__tests__/bundle-manager.test.js.map +0 -1
- package/dist/esm/__tests__/config.test.d.ts +0 -1
- package/dist/esm/__tests__/config.test.js +0 -70
- package/dist/esm/__tests__/config.test.js.map +0 -1
- package/dist/esm/__tests__/delta-processor.test.d.ts +0 -1
- package/dist/esm/__tests__/delta-processor.test.js +0 -77
- package/dist/esm/__tests__/delta-processor.test.js.map +0 -1
- package/dist/esm/__tests__/firestore-schema.test.d.ts +0 -1
- package/dist/esm/__tests__/firestore-schema.test.js +0 -74
- package/dist/esm/__tests__/firestore-schema.test.js.map +0 -1
- package/dist/esm/__tests__/integration.test.d.ts +0 -1
- package/dist/esm/__tests__/integration.test.js +0 -78
- package/dist/esm/__tests__/integration.test.js.map +0 -1
- package/dist/esm/__tests__/manifest-reader.test.d.ts +0 -1
- package/dist/esm/__tests__/manifest-reader.test.js +0 -271
- package/dist/esm/__tests__/manifest-reader.test.js.map +0 -1
- package/dist/esm/__tests__/rollout-checker.test.d.ts +0 -1
- package/dist/esm/__tests__/rollout-checker.test.js +0 -210
- package/dist/esm/__tests__/rollout-checker.test.js.map +0 -1
- package/dist/esm/__tests__/security.test.d.ts +0 -1
- package/dist/esm/__tests__/security.test.js +0 -54
- package/dist/esm/__tests__/security.test.js.map +0 -1
- package/dist/esm/__tests__/version-manager.test.d.ts +0 -1
- package/dist/esm/__tests__/version-manager.test.js +0 -45
- package/dist/esm/__tests__/version-manager.test.js.map +0 -1
- package/dist/esm/app-review/app-review-manager.js.map +0 -1
- package/dist/esm/app-review/index.js.map +0 -1
- package/dist/esm/app-review/platform-review-handler.js.map +0 -1
- package/dist/esm/app-review/review-conditions-checker.js.map +0 -1
- package/dist/esm/app-review/review-rate-limiter.js.map +0 -1
- package/dist/esm/app-review/types.js.map +0 -1
- package/dist/esm/app-update/app-update-checker.js.map +0 -1
- package/dist/esm/app-update/app-update-installer.js.map +0 -1
- package/dist/esm/app-update/app-update-manager.js.map +0 -1
- package/dist/esm/app-update/app-update-notifier.js.map +0 -1
- package/dist/esm/app-update/index.js.map +0 -1
- package/dist/esm/app-update/platform-app-update.js.map +0 -1
- package/dist/esm/app-update/types.js.map +0 -1
- package/dist/esm/background-update/background-scheduler.js.map +0 -1
- package/dist/esm/background-update/index.js.map +0 -1
- package/dist/esm/background-update/notification-manager.js.map +0 -1
- package/dist/esm/config/support.js.map +0 -1
- package/dist/esm/core/analytics.js.map +0 -1
- package/dist/esm/core/cache-manager.js.map +0 -1
- package/dist/esm/core/config.js.map +0 -1
- package/dist/esm/core/errors.js.map +0 -1
- package/dist/esm/core/event-emitter.js.map +0 -1
- package/dist/esm/core/logger.js.map +0 -1
- package/dist/esm/core/performance.js.map +0 -1
- package/dist/esm/core/plugin-manager.js.map +0 -1
- package/dist/esm/core/security.js.map +0 -1
- package/dist/esm/definitions.js.map +0 -1
- package/dist/esm/firestore/firestore-client.d.ts +0 -109
- package/dist/esm/firestore/firestore-client.js +0 -264
- package/dist/esm/firestore/firestore-client.js.map +0 -1
- package/dist/esm/firestore/index.d.ts +0 -11
- package/dist/esm/firestore/index.js +0 -11
- package/dist/esm/firestore/index.js.map +0 -1
- package/dist/esm/firestore/manifest-reader.d.ts +0 -87
- package/dist/esm/firestore/manifest-reader.js +0 -294
- package/dist/esm/firestore/manifest-reader.js.map +0 -1
- package/dist/esm/firestore/schema.d.ts +0 -507
- package/dist/esm/firestore/schema.js +0 -73
- package/dist/esm/firestore/schema.js.map +0 -1
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/live-update/bundle-manager.js.map +0 -1
- package/dist/esm/live-update/certificate-pinning.js.map +0 -1
- package/dist/esm/live-update/delta-processor.js.map +0 -1
- package/dist/esm/live-update/download-manager.js.map +0 -1
- package/dist/esm/live-update/rollout-checker.js.map +0 -1
- package/dist/esm/live-update/update-manager.js.map +0 -1
- package/dist/esm/live-update/version-manager.js.map +0 -1
- package/dist/esm/plugin.js.map +0 -1
- package/dist/esm/security/crypto.js.map +0 -1
- package/dist/esm/security/validator.js.map +0 -1
- package/dist/esm/web.js.map +0 -1
- package/dist/plugin.cjs.js.map +0 -1
- package/dist/plugin.esm.js.map +0 -1
- package/dist/plugin.js.map +0 -1
- package/docs/FIREBASE_INTEGRATION_TRACKER.md +0 -321
- package/docs/FIREBASE_QUERIES_AND_INDEXES_AUDIT.md +0 -221
- package/docs/examples/firebase-backend-example.md +0 -27
- package/docs/guides/no-cost-backend-implementation-plan.md +0 -77
- package/docs/guides/no-cost-firestore-google-drive-backend.md +0 -60
- package/docs/play-console-rejection-rules.json +0 -428
- /package/docs/{MARKETING_WEBSITE_PLAN.md → archive/MARKETING_WEBSITE_PLAN.md} +0 -0
|
@@ -0,0 +1,613 @@
|
|
|
1
|
+
# Native Update — Posts Batch 01
|
|
2
|
+
|
|
3
|
+
10 LinkedIn / Facebook posts. Each: 2700–2900 chars (~490–510 words). Hard cap because LinkedIn rejects > 3,000 chars.
|
|
4
|
+
|
|
5
|
+
Story arc: Problem → Plugin intro → OTA → Store-version check → In-app review → Backend → CLI → Example apps → v3.0.0 architecture → Getting started.
|
|
6
|
+
|
|
7
|
+
Series tag: **#NativeUpdate10** — used on every post so curious readers can pull the rest.
|
|
8
|
+
|
|
9
|
+
Mark a post as consumed: replace its heading with `USED — Post NN — …`.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Post 01 — The 7-day fix that almost killed our launch
|
|
14
|
+
|
|
15
|
+
> "It's a one-line fix. We just need to push it." — Every mobile dev, two minutes before remembering App Store review exists.
|
|
16
|
+
|
|
17
|
+
If you've shipped a mobile app, you know the feeling. Production is on fire. A typo in a regex is rejecting half the signups. A pricing string says "$9" instead of "$19". The fix is three characters. The deploy path is **seven days**.
|
|
18
|
+
|
|
19
|
+
For a *web* product you'd ship the fix in 90 seconds and go to bed. For a *mobile* product you write a Slack apology, draft a status-page update, and pray for a fast reviewer.
|
|
20
|
+
|
|
21
|
+
I've been on the wrong side of that timer enough times that I built the thing I wished existed: **Native Update** — an MIT-licensed Capacitor plugin (and full SaaS ecosystem around it) that lets your app pull a fresh JS/HTML/CSS bundle on next launch. No resubmission required.
|
|
22
|
+
|
|
23
|
+
### What OTA actually means (and what it does NOT)
|
|
24
|
+
|
|
25
|
+
OTA replaces the **web layer** of your hybrid app — the bundled JavaScript, HTML, and CSS that renders inside the WebView. It does **not** replace native Kotlin or Swift code, does **not** add new Capacitor plugins to a shipped binary, and does **not** circumvent App Store / Play Store policy on what you're allowed to change.
|
|
26
|
+
|
|
27
|
+
If your fix is "change the price in a React component" — OTA, 60 seconds.
|
|
28
|
+
If your fix is "add a new Bluetooth Capacitor plugin" — that's still a binary release.
|
|
29
|
+
|
|
30
|
+
Important boundary. A lot of "live update" pitches blur it. We don't.
|
|
31
|
+
|
|
32
|
+
### What's in the box
|
|
33
|
+
|
|
34
|
+
- The plugin: `native-update` on NPM
|
|
35
|
+
- Native Kotlin (Android) + Swift (iOS) — real native code, not a JS shim
|
|
36
|
+
- Laravel 11 + Nova 5 SaaS backend (MIT, self-host)
|
|
37
|
+
- CLI for release management — one command builds, hashes, uploads, rolls out
|
|
38
|
+
- React marketing/dashboard site (Sentry + Clarity + Amplitude wired)
|
|
39
|
+
- 3 example apps as living integration tests
|
|
40
|
+
- 81 passing unit tests + Detox E2E suite
|
|
41
|
+
- MIT license throughout
|
|
42
|
+
|
|
43
|
+
This is the opener of a 10-part series (`#NativeUpdate10`). Posts 02–10 cover: ecosystem tour, OTA deep-dive, store-version checks, in-app review, the Laravel backend, CLI, example apps, v3.0.0 BREAKING story, getting started.
|
|
44
|
+
|
|
45
|
+
If you build with Capacitor and you've felt the 7-day-review handcuffs, save this post and follow the series.
|
|
46
|
+
|
|
47
|
+
### Why this exists
|
|
48
|
+
|
|
49
|
+
The bet behind Native Update: hybrid mobile apps deserve the same iteration speed as web apps. Capacitor already has the runtime. What was missing was a credible OTA story — plugin + backend + CLI + examples — that wasn't paid-SaaS lock-in. So I built one. MIT, self-hosted, the piece that closes the loop between "I shipped a fix" and "every user has it."
|
|
50
|
+
|
|
51
|
+
**👉 Try it: `yarn add native-update` or https://nativeupdate.aoneahsan.com**
|
|
52
|
+
|
|
53
|
+
#NativeUpdate #CapacitorPlugin #OTAUpdates #Aoneahsan #TopFree
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Post 02 — What's actually in the box (Native Update, end to end)
|
|
58
|
+
|
|
59
|
+
Most "Capacitor plugin" announcements ship one thing: a TypeScript package on NPM. You install, read a 40-line README, discover the iOS side won't compile, open an issue, wait.
|
|
60
|
+
|
|
61
|
+
I wanted to ship something different. **Native Update is a monorepo, not just a package.** Eight workspaces, all MIT-licensed:
|
|
62
|
+
|
|
63
|
+
- `src/` — the Capacitor plugin (TypeScript)
|
|
64
|
+
- `android/` — Kotlin native implementation
|
|
65
|
+
- `ios/` — Swift native implementation
|
|
66
|
+
- `cli/` — release-management CLI
|
|
67
|
+
- `backend/` — Laravel 11 + Nova 5 SaaS backend
|
|
68
|
+
- `website/` — React + Vite marketing/dashboard
|
|
69
|
+
- `example-apps/` — 3 reference integrations
|
|
70
|
+
- `e2e/` — Detox E2E tests on real devices
|
|
71
|
+
|
|
72
|
+
Post 02 in the **`#NativeUpdate10`** series.
|
|
73
|
+
|
|
74
|
+
### Why ecosystem instead of "just the plugin"
|
|
75
|
+
|
|
76
|
+
Every hybrid-update library I integrated before played out the same:
|
|
77
|
+
|
|
78
|
+
1. Plugin is fine.
|
|
79
|
+
2. Plugin docs assume a backend you don't have.
|
|
80
|
+
3. "Recommended backend" is paid SaaS with no migration path.
|
|
81
|
+
4. Six months in, you've built three quarters of a backend yourself.
|
|
82
|
+
|
|
83
|
+
So I shipped the backend in the same repo. MIT. Self-host. The plugin and CLI talk to it over HTTP — but the protocol is portable; the Express example app shows how to wrap your own service in it.
|
|
84
|
+
|
|
85
|
+
### Three independent capabilities
|
|
86
|
+
|
|
87
|
+
The plugin exposes three things. Use one, two, or all three:
|
|
88
|
+
|
|
89
|
+
1. **Live / OTA updates** — pull a fresh bundle on next launch
|
|
90
|
+
2. **App-store update checks** — detect users on stale binaries, soft nudge or hard gate
|
|
91
|
+
3. **In-app review prompts** — wrap iOS `SKStoreReviewController` and Google Play In-App Review
|
|
92
|
+
|
|
93
|
+
Each has its own API surface, its own native implementations in `android/` and `ios/`, and its own unit tests. None depends on the others.
|
|
94
|
+
|
|
95
|
+
### What's in the box, in detail
|
|
96
|
+
|
|
97
|
+
- The plugin: `native-update` on NPM, Capacitor SDK
|
|
98
|
+
- Native Kotlin (Android) — real Kotlin, full atomic-swap implementation, hash verification on device
|
|
99
|
+
- Native Swift (iOS) — same parity, same correctness
|
|
100
|
+
- Laravel 11 + Nova 5 backend — bundle hosting, channel + rollout management, Nova admin UI
|
|
101
|
+
- CLI — Node.js binary; one command does build → hash → upload → register → rollout %
|
|
102
|
+
- React + Vite marketing/dashboard site — Sentry + Microsoft Clarity + Amplitude wired with page-view auto-track since 2026-05-06
|
|
103
|
+
- Detox E2E suite — runs the full OTA flow on real devices in CI
|
|
104
|
+
|
|
105
|
+
### What's NOT in the box (honest disclosure)
|
|
106
|
+
|
|
107
|
+
- No native code OTA — JS/HTML/CSS only
|
|
108
|
+
- No bypass of App Store / Play Store policy
|
|
109
|
+
- Not a React Native plugin (Capacitor / Ionic / hybrid-WebView only)
|
|
110
|
+
- Not free-tier hostable — the Laravel backend needs a small VPS, ~$5–10/month
|
|
111
|
+
|
|
112
|
+
**👉 5-minute integration: clone the repo, `cd example-apps/react-capacitor`, `yarn && yarn dev`. https://nativeupdate.aoneahsan.com**
|
|
113
|
+
|
|
114
|
+
#NativeUpdate #Capacitor #OpenSource #Aoneahsan #BestOpenSourceCommunityProject
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Post 03 — OTA updates: the 60-second deploy path
|
|
119
|
+
|
|
120
|
+
> "Production is broken." → 60 seconds later → "Production is fixed."
|
|
121
|
+
|
|
122
|
+
Post 03 in the **`#NativeUpdate10`** series. Today: how Native Update's OTA path actually works.
|
|
123
|
+
|
|
124
|
+
### The flow
|
|
125
|
+
|
|
126
|
+
1. You build your web bundle (`yarn build`)
|
|
127
|
+
2. The CLI hashes it, uploads to your backend, registers a release on a channel (`production` / `staging` / `canary`)
|
|
128
|
+
3. The app on next launch asks the backend: "Newer release on my channel for my binary version?"
|
|
129
|
+
4. If yes: download, verify SHA-256 hash, atomic swap on next start, report back
|
|
130
|
+
5. If the new bundle crashes on boot: **rollback** to the prior known-good bundle automatically
|
|
131
|
+
|
|
132
|
+
That's the whole loop.
|
|
133
|
+
|
|
134
|
+
### What you control
|
|
135
|
+
|
|
136
|
+
- **Channel** — production / staging / canary / whatever you name
|
|
137
|
+
- **Rollout %** — ship to 5% → watch error rate → ramp to 100%
|
|
138
|
+
- **Binary version constraint** — a bundle built for `2.4.x` never lands on `2.3.x`
|
|
139
|
+
- **Mandatory vs. optional** — block app entry until applied, or apply on next natural restart
|
|
140
|
+
|
|
141
|
+
### What the plugin handles for you
|
|
142
|
+
|
|
143
|
+
Atomic swap (no half-applied bundles). SHA-256 verification on the device (no MITM swap on hostile coffee-shop Wi-Fi). Rollback on crash-on-boot — automatic, the prior bundle stays on disk. Background download throttling so you're not killing battery / data on cellular. Telemetry: success / fail / which release landed where.
|
|
144
|
+
|
|
145
|
+
### Where OTA earns its keep
|
|
146
|
+
|
|
147
|
+
- Hot fixes for production JS crashers
|
|
148
|
+
- Copy / pricing / banner / legal text changes
|
|
149
|
+
- A/B tests that don't need new native capabilities
|
|
150
|
+
- Region-specific variants (channel-pin a subset of users)
|
|
151
|
+
- Fast rollbacks when a release misbehaves — flip the channel back, devices pull prior bundle next launch
|
|
152
|
+
|
|
153
|
+
### Where OTA does NOT apply
|
|
154
|
+
|
|
155
|
+
- New native Capacitor plugins
|
|
156
|
+
- Anything in `Info.plist` / `AndroidManifest.xml`
|
|
157
|
+
- New permissions
|
|
158
|
+
- Anything Apple or Google would call "a different app"
|
|
159
|
+
|
|
160
|
+
I keep saying this on purpose — the OTA pitch gets fluffed in marketing, and integrators end up planning around capabilities the technology doesn't have. The boundary is real. Inside it, OTA is great. Outside it, you're shipping a binary.
|
|
161
|
+
|
|
162
|
+
### The "but App Store policy?" question
|
|
163
|
+
|
|
164
|
+
Both Apple and Google allow JS / HTML / CSS bundle updates within hybrid apps. The constraint is: don't use OTA to deliver a fundamentally different app than the one they reviewed. Bug fixes, copy changes, A/B tests, feature flags = all fine and within policy. Pivot from a productivity app to a betting app overnight = they will notice. Be a good citizen and you'll be fine.
|
|
165
|
+
|
|
166
|
+
**👉 5 minutes against the example app: `yarn add native-update` or https://nativeupdate.aoneahsan.com**
|
|
167
|
+
|
|
168
|
+
#NativeUpdate #OTAUpdates #LiveUpdates #Aoneahsan #TopFree
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Post 04 — Your users are on v1.0. The store has v3.2. Now what?
|
|
173
|
+
|
|
174
|
+
Post 04 in the **`#NativeUpdate10`** series.
|
|
175
|
+
|
|
176
|
+
Hybrid apps live in two worlds: the web layer (you can OTA) and the native binary (you can't). Sooner or later you ship a native change — a new Capacitor plugin, a permission, a major bump — and the binary on the user's device is a generation behind the binary in the store.
|
|
177
|
+
|
|
178
|
+
OTA can't fix that. The user has to update the binary. The question is whether you ask them or surprise them.
|
|
179
|
+
|
|
180
|
+
### The polite-nudge problem
|
|
181
|
+
|
|
182
|
+
Every mobile team has seen these tickets:
|
|
183
|
+
- "Feature X doesn't work for me." (User on v1.0; feature shipped in v3.0.)
|
|
184
|
+
- "Why does it keep crashing?" (Crash patched in v2.4; user has auto-update off.)
|
|
185
|
+
- "I can't sign in." (Auth library upgraded in v2.7; old binary signs incompatible JWTs.)
|
|
186
|
+
|
|
187
|
+
You can spend three engineers triaging these, or detect the version drift in the app and tell the user, in the app, that a fresh binary is waiting.
|
|
188
|
+
|
|
189
|
+
### What the plugin does
|
|
190
|
+
|
|
191
|
+
Native Update's app-store update check exposes:
|
|
192
|
+
- A check call: "is there a newer binary in the store?"
|
|
193
|
+
- Current store version (so you can show "1.4.0 → 3.2.0")
|
|
194
|
+
- A boolean for "is the gap big enough to enforce an update?" — controlled by your backend, not hardcoded
|
|
195
|
+
- Native deep-link helpers to open App Store / Play Store
|
|
196
|
+
|
|
197
|
+
You decide the UX. The plugin gives you the signal. Common patterns:
|
|
198
|
+
|
|
199
|
+
- **Soft nudge.** Dismissible banner: "A newer version is available." User updates when convenient.
|
|
200
|
+
- **Hard gate.** "This version is no longer supported." Used sparingly — for security patches.
|
|
201
|
+
- **Educational toast.** "You're on 1.4.0. Latest is 3.2.0. Tap to see what's new."
|
|
202
|
+
|
|
203
|
+
### The "what's new" trick
|
|
204
|
+
|
|
205
|
+
When you nudge a user, *show them why*. The plugin gives you the version string; your backend can give you the changelog. Two-line copy like "We patched the sign-in bug you hit yesterday" converts five times better than "An update is available."
|
|
206
|
+
|
|
207
|
+
### Three patterns that work in production
|
|
208
|
+
|
|
209
|
+
1. **Banner on every cold launch.** Dismissible, low-friction. 90% of teams should start here.
|
|
210
|
+
2. **Modal every 7 days.** More aggressive — for apps where version drift causes broken-state experiences.
|
|
211
|
+
3. **Hard gate at a backend-defined floor.** Reserve for security patches and "binary contains an irrecoverable bug."
|
|
212
|
+
|
|
213
|
+
The plugin gives the signal; UX and backend rules are yours.
|
|
214
|
+
|
|
215
|
+
### Honest limits
|
|
216
|
+
|
|
217
|
+
App stores rate-limit version-check endpoints — once per session is plenty. iOS doesn't allow programmatic in-place updates; the button you show must deep-link to App Store. Android allows in-app updates via Play Core; the plugin exposes that path natively.
|
|
218
|
+
|
|
219
|
+
**👉 Wire the version check on cold launch + soft-nudge banner — cut "you should update" tickets within a week. https://nativeupdate.aoneahsan.com**
|
|
220
|
+
|
|
221
|
+
#NativeUpdate #AppStoreUpdates #MobileDev #Aoneahsan #TopFree
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Post 05 — The polite way to ask for a 5-star review
|
|
226
|
+
|
|
227
|
+
Post 05 in the **`#NativeUpdate10`** series.
|
|
228
|
+
|
|
229
|
+
iOS (since 10.3) and Android (Google Play In-App Review) both ship a **native review prompt** — a small modal that asks for a rating without yanking the user out to the store. They're rate-limited by the OS, styled to fit the platform, and the platform owners actively prefer them over custom prompts.
|
|
230
|
+
|
|
231
|
+
Native Update wraps both in a one-line API.
|
|
232
|
+
|
|
233
|
+
### Why use the native prompt instead of building your own
|
|
234
|
+
|
|
235
|
+
- **Recommended path.** Apple's HIG says use `SKStoreReviewController`. Google says use Play In-App Review. Doing your own is harder and gains nothing.
|
|
236
|
+
- **Silent on failure.** If the user already rated, or OS rate-limit kicks in, the prompt doesn't show. No spam.
|
|
237
|
+
- **Inline.** Whole review happens without leaving your app. Tap stars, write a sentence, submit, back to your app.
|
|
238
|
+
- **Trusted UI.** Users have seen this exact modal on twenty other apps.
|
|
239
|
+
|
|
240
|
+
### The capability surface
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
if (await NativeUpdate.canRequestReview()) {
|
|
244
|
+
await NativeUpdate.requestReview();
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
Two lines. Cross-platform branching for free.
|
|
249
|
+
|
|
250
|
+
### When to ask
|
|
251
|
+
|
|
252
|
+
Don't ask:
|
|
253
|
+
- On first open (no opinion yet)
|
|
254
|
+
- After an error (you'll get one star)
|
|
255
|
+
- During checkout (you'll lose the conversion)
|
|
256
|
+
|
|
257
|
+
Do ask:
|
|
258
|
+
- After a *successful* completion of the user's primary task
|
|
259
|
+
- After 3+ sessions
|
|
260
|
+
- Never twice in one session (the OS rate-limits anyway)
|
|
261
|
+
|
|
262
|
+
A canonical pattern: "Did the user just finish their second-or-greater positive flow? Has it been more than a week since the last ask? Yes → request." Two booleans, one if-statement, ship it.
|
|
263
|
+
|
|
264
|
+
### What this is NOT
|
|
265
|
+
|
|
266
|
+
- Not a custom review form (the OS owns the form)
|
|
267
|
+
- Not a way to force a 5-star (the user can submit any rating)
|
|
268
|
+
- Not a way to detect what they rated — by design, Apple and Google don't expose this
|
|
269
|
+
|
|
270
|
+
### What this DOES give you
|
|
271
|
+
|
|
272
|
+
- The platform-recommended prompt with one line of code
|
|
273
|
+
- Cross-platform branching for free
|
|
274
|
+
- A `canRequest` helper so you can plan UI without tripping OS rate limits
|
|
275
|
+
- Clean fallback if the OS suppresses the prompt (you keep going; nothing breaks)
|
|
276
|
+
|
|
277
|
+
### Real-world wiring example
|
|
278
|
+
|
|
279
|
+
E-commerce app: trigger after the user's *second* successful order, not their first. They've experienced the loop twice; one good experience could be luck, two is a pattern.
|
|
280
|
+
|
|
281
|
+
Content app: trigger after the user has consumed N pieces of content AND opened the app on M+ different days.
|
|
282
|
+
|
|
283
|
+
Fitness app: trigger after the user's first completed workout streak (e.g., 3 days in a row).
|
|
284
|
+
|
|
285
|
+
The plugin doesn't care — it gives you `requestReview()`. *When* you call it is product judgment.
|
|
286
|
+
|
|
287
|
+
**👉 Find your "user just succeeded" moment, drop in `requestReview()` after it. https://nativeupdate.aoneahsan.com**
|
|
288
|
+
|
|
289
|
+
#NativeUpdate #InAppReview #MobileUX #Aoneahsan #SaaSApp
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Post 06 — Laravel + Nova: the admin backend the plugin ships with
|
|
294
|
+
|
|
295
|
+
Post 06 in the **`#NativeUpdate10`** series.
|
|
296
|
+
|
|
297
|
+
Most Capacitor live-update plugins make you bring your own backend, then sell you a paid one when you give up. I shipped one in the same MIT-licensed repo.
|
|
298
|
+
|
|
299
|
+
### Why Laravel 11 + Nova 5
|
|
300
|
+
|
|
301
|
+
- **Why a real framework instead of "a few Express routes?"** Hosting bundles + managing channels + rollouts + version-gating + admin UI is a CRUD problem with auth, validation, queues, and storage. Frameworks are good at CRUD problems.
|
|
302
|
+
- **Why Laravel?** Long-term ecosystem. Robust validation. Eloquent for the data layer. Queue for background work. Storage abstraction that doesn't lock you to one provider. Stable major versions. Predictable deploy.
|
|
303
|
+
- **Why Nova?** Admin UI is half the work. Nova gives you CRUD UI for releases, channels, devices, and analytics in hours, not weeks.
|
|
304
|
+
|
|
305
|
+
### What the backend exposes
|
|
306
|
+
|
|
307
|
+
- Releases endpoint (the device asks "newer release on my channel?")
|
|
308
|
+
- Bundle download endpoint (signed, hash-checkable, served via Storage)
|
|
309
|
+
- Telemetry ingest (devices report success / fail of update applications)
|
|
310
|
+
- Admin endpoints (Nova-backed CRUD on releases, channels, rollouts, version constraints)
|
|
311
|
+
- Auth (token-based for the dashboard, signed-URL pattern for bundles)
|
|
312
|
+
|
|
313
|
+
### What's in Nova admin
|
|
314
|
+
|
|
315
|
+
Releases (create / list / detail / rollback). Channels. Devices (which release they're on, last-seen, platform). Apps (multi-app config). Analytics dashboards (preferences, /me, rollouts) — added in v3.0.0.
|
|
316
|
+
|
|
317
|
+
### What's NOT in the backend
|
|
318
|
+
|
|
319
|
+
No paywall. No cloud lock-in. No "free tier with rate limits that punish you for succeeding." No required third-party services. Storage / queues / cache are all pluggable.
|
|
320
|
+
|
|
321
|
+
### Self-hosting reality check
|
|
322
|
+
|
|
323
|
+
Small VPS (1 vCPU, 2 GB RAM) handles thousands of devices for the bundle-distribution workload. Expensive part is bandwidth — bundles are small (200 KB–1 MB), but 100k DAU × 500 KB = ~50 GB egress per release. Use a CDN. Cheap part is everything else — Laravel + Nova are dead boring to operate, which is exactly what you want from infrastructure.
|
|
324
|
+
|
|
325
|
+
### Why "ships with a backend" matters more than it sounds
|
|
326
|
+
|
|
327
|
+
Most plugin authors stop at the npm package. They list endpoints in the README and say "implement these." Six months later, the abandoned issue tracker has a dozen "any backend recommendations?" questions. Native Update gives you the reference implementation, in the same repo, MIT-licensed, with the same release cadence as the plugin. Decoupled enough that you can swap it; integrated enough that the protocol can never drift.
|
|
328
|
+
|
|
329
|
+
**👉 Clone the repo, `cd backend`, `docker compose up`, you're running. https://nativeupdate.aoneahsan.com**
|
|
330
|
+
|
|
331
|
+
#NativeUpdate #Laravel #LaravelNova #Aoneahsan #SaaSApp
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Post 07 — The CLI: one command, one rolled-out bundle
|
|
336
|
+
|
|
337
|
+
> The fewer steps between "code committed" and "users on the new bundle," the more you'll actually ship.
|
|
338
|
+
|
|
339
|
+
Post 07 in the **`#NativeUpdate10`** series.
|
|
340
|
+
|
|
341
|
+
Every release path I've seen die slowly died the same way: too many manual steps. "Build, then upload, then SSH, then update a config row, then bump a version, then notify ops." Six steps becomes weekly batches becomes "we don't really do live updates."
|
|
342
|
+
|
|
343
|
+
The Native Update CLI collapses that into one command.
|
|
344
|
+
|
|
345
|
+
### Before vs after
|
|
346
|
+
|
|
347
|
+
**Before:** 6 manual steps, two pages of Confluence runbook, prayer.
|
|
348
|
+
|
|
349
|
+
**After:**
|
|
350
|
+
|
|
351
|
+
```bash
|
|
352
|
+
nativeupdate release \
|
|
353
|
+
--channel production \
|
|
354
|
+
--rollout 10 \
|
|
355
|
+
--binary "2.4.x"
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Builds the web bundle. Computes SHA-256. Authenticates. Uploads. Registers the release. Sets rollout %. Pins the binary-version constraint. Returns release ID + a one-line "ramp to 100%" command.
|
|
359
|
+
|
|
360
|
+
### The four CLI muscles
|
|
361
|
+
|
|
362
|
+
1. **`release`** — the 90% case (above)
|
|
363
|
+
2. **`rollback <release-id>`** — flip a channel back; devices pull prior bundle next launch
|
|
364
|
+
3. **`promote --from staging --to production`** — promote between channels; metadata preserved
|
|
365
|
+
4. **`status`** — show active release on each channel + what's queued
|
|
366
|
+
|
|
367
|
+
### The auth model
|
|
368
|
+
|
|
369
|
+
Backend-issued token, scoped to an app + a list of channels. CI uses a token with rights to push to staging/canary; a human uses one with production rights. Two-key reality. Tokens revocable from Nova admin.
|
|
370
|
+
|
|
371
|
+
### Why this isn't "just a bash script"
|
|
372
|
+
|
|
373
|
+
Started as one. Got promoted when:
|
|
374
|
+
|
|
375
|
+
- Hash verification needed proper retries
|
|
376
|
+
- Bundle uploads needed resumable / chunked transfers for slow links
|
|
377
|
+
- Rollout management needed atomicity (two CI jobs landing at once shouldn't race)
|
|
378
|
+
- Operators wanted a `--dry-run` before pulling the trigger
|
|
379
|
+
|
|
380
|
+
### Where this fits in CI
|
|
381
|
+
|
|
382
|
+
Build job → produces `dist/`. Test job → must pass. Release-staging → `nativeupdate release --channel staging --rollout 100` on every successful main merge. Release-canary → manual trigger, `--rollout 5`. Release-production → manual, gradual `--rollout 10 → 50 → 100`.
|
|
383
|
+
|
|
384
|
+
### A typical week with the CLI
|
|
385
|
+
|
|
386
|
+
Monday: `release --channel staging --rollout 100` after merging the week's PRs.
|
|
387
|
+
Tuesday: QA team flags a regression on staging. `rollback`, fix the bug, `release` again.
|
|
388
|
+
Wednesday: `promote --to production --rollout 5`. Watch Sentry.
|
|
389
|
+
Thursday: `--rollout 50`. Watch some more.
|
|
390
|
+
Friday: `--rollout 100`. Done. Drinks.
|
|
391
|
+
|
|
392
|
+
That's the rhythm. The CLI is the thing that makes it possible. The fewer keystrokes between your code and your users, the more often you'll actually push.
|
|
393
|
+
|
|
394
|
+
**👉 Replace your manual upload step today and reclaim half a Friday a month. https://nativeupdate.aoneahsan.com**
|
|
395
|
+
|
|
396
|
+
#NativeUpdate #DevOps #CLI #Aoneahsan #TopFree
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## Post 08 — Three example apps, because you shouldn't trust a plugin without them
|
|
401
|
+
|
|
402
|
+
Post 08 in the **`#NativeUpdate10`** series.
|
|
403
|
+
|
|
404
|
+
My filter for adopting a new mobile plugin: **is there an example app, and does it run?** If yes to both, the plugin is real. If either is no, I'm cautious.
|
|
405
|
+
|
|
406
|
+
Native Update ships three example apps in the monorepo. Maintained alongside the plugin. Living integration tests.
|
|
407
|
+
|
|
408
|
+
### example-apps/react-capacitor
|
|
409
|
+
|
|
410
|
+
The canonical one. Vite + React + Capacitor, integrated end-to-end.
|
|
411
|
+
|
|
412
|
+
What it demonstrates:
|
|
413
|
+
- Wiring `native-update` into `App.tsx`
|
|
414
|
+
- Configuring channels and binary version in `capacitor.config.ts`
|
|
415
|
+
- `checkForUpdate()` on launch
|
|
416
|
+
- "Downloading…" UI while a bundle pulls in the background
|
|
417
|
+
- Handling the post-update reload
|
|
418
|
+
- Manual "check now" button for testing rollouts
|
|
419
|
+
- `requestReview()` after a contrived "successful action"
|
|
420
|
+
|
|
421
|
+
If you've never integrated the plugin, this is the example you copy from.
|
|
422
|
+
|
|
423
|
+
### example-apps/node-express
|
|
424
|
+
|
|
425
|
+
A small Express service mimicking the Laravel backend's HTTP surface.
|
|
426
|
+
|
|
427
|
+
Why it exists: some integrators want to validate the plugin against their own backend before adopting Laravel. The Express example documents the exact endpoints they need to implement. It's not the production answer — it's a blueprint.
|
|
428
|
+
|
|
429
|
+
### example-apps/firebase-backend
|
|
430
|
+
|
|
431
|
+
The historical Firestore-direct path was removed in v3.0.0 (post 09 is that story). The Firebase example *remains* as a reference for integrators who want the Native Update protocol wrapped around Firebase functions on top of Firestore data they own.
|
|
432
|
+
|
|
433
|
+
The plugin itself talks Laravel-shaped HTTP. The example shows the protocol can wrap any backend you control.
|
|
434
|
+
|
|
435
|
+
### Why three and not one
|
|
436
|
+
|
|
437
|
+
Each app exercises a different backend posture:
|
|
438
|
+
- React-Capacitor → canonical Laravel backend
|
|
439
|
+
- Node-Express → hand-rolled implementation of the protocol
|
|
440
|
+
- Firebase-backend → Firebase-functions wrapper
|
|
441
|
+
|
|
442
|
+
If the plugin works against all three, the protocol is portable. That's the design constraint.
|
|
443
|
+
|
|
444
|
+
### What the apps catch
|
|
445
|
+
|
|
446
|
+
API drift. Compilation breaks on Capacitor major bumps. Bundle-format regressions. iOS / Android divergences when one platform's native code changes. They're not a substitute for the 81 unit tests + Detox E2E suite, but they're a substitute for "I tried the plugin in a real app and the wheels fell off."
|
|
447
|
+
|
|
448
|
+
A plugin without an example app is a plugin you'll be integrating at 2 AM with zero reference for what "working" actually looks like.
|
|
449
|
+
|
|
450
|
+
**👉 Clone the monorepo, `cd example-apps/react-capacitor`, `yarn && yarn dev` — working integration in 5 min. https://nativeupdate.aoneahsan.com**
|
|
451
|
+
|
|
452
|
+
#NativeUpdate #Capacitor #OpenSource #Aoneahsan #BestOpenSourceCommunityProject
|
|
453
|
+
|
|
454
|
+
---
|
|
455
|
+
|
|
456
|
+
## Post 09 — Why we deleted 3,000 lines of Firestore (the v3.0.0 story)
|
|
457
|
+
|
|
458
|
+
> "The best architecture decision is the one you can describe in one sentence."
|
|
459
|
+
|
|
460
|
+
Post 09 in the **`#NativeUpdate10`** series.
|
|
461
|
+
|
|
462
|
+
Native Update v3.0.0 was a deliberate BREAKING release. ~3,000 lines deleted. The plugin SDK and the website both refactored to talk only to the Laravel HTTP API. v2.x integrators on the Firestore path had to migrate.
|
|
463
|
+
|
|
464
|
+
We don't take BREAKING releases lightly. Here's the reasoning, with all the trade-offs.
|
|
465
|
+
|
|
466
|
+
### What v2.x looked like
|
|
467
|
+
|
|
468
|
+
The plugin supported two backend postures:
|
|
469
|
+
1. **Laravel HTTP** — the canonical backend
|
|
470
|
+
2. **Firestore direct** — the device read release metadata directly via Firebase SDK
|
|
471
|
+
|
|
472
|
+
On paper this was flexible. In practice it created a shadow API surface: every backend behavior implemented twice, authorization rules modeled in two places, telemetry ingested in two places, tests covering two paths.
|
|
473
|
+
|
|
474
|
+
### The cost of "two right ways"
|
|
475
|
+
|
|
476
|
+
- **Surface duplication.** Every release-metadata change required updates in both paths.
|
|
477
|
+
- **Test matrix explosion.** "Does this work on Laravel? On Firestore? When I switch?"
|
|
478
|
+
- **Onboarding friction.** New integrators wasted time deciding which path before integrating.
|
|
479
|
+
- **Documentation rot.** Two paths means two doc trees, one always out of date.
|
|
480
|
+
- **Operational opacity.** Diagnosing a v2.x bug started with "which backend are you on?"
|
|
481
|
+
|
|
482
|
+
The plugin was carrying ~3,000 lines to support a path that was complicating every other decision.
|
|
483
|
+
|
|
484
|
+
### Why removal won
|
|
485
|
+
|
|
486
|
+
- **Laravel was already feature-complete.** Anything Firestore did, Laravel already did, often better.
|
|
487
|
+
- **Single source of truth.** One backend = one authorization model, one telemetry ingest, one set of release-metadata semantics.
|
|
488
|
+
- **Cleaner mental model.** "Native Update talks HTTP to a Laravel backend you control." One sentence. Done.
|
|
489
|
+
- **Test suite shrunk by ~40%.** Every remaining test is more meaningful.
|
|
490
|
+
- **Documentation halved.**
|
|
491
|
+
|
|
492
|
+
### What integrators on Firestore lost
|
|
493
|
+
|
|
494
|
+
Honest list:
|
|
495
|
+
- "No extra service to run" — Firestore-direct was attractive because teams already had Firebase
|
|
496
|
+
- A few minor knobs that made sense in Firestore but didn't translate cleanly
|
|
497
|
+
|
|
498
|
+
### What integrators got
|
|
499
|
+
|
|
500
|
+
Simpler integration surface. Stable, predictable API. A real admin (Nova UI, not "write Firestore queries"). Server-side-only secrets. Faster plugin releases (every change reasoned about in one model).
|
|
501
|
+
|
|
502
|
+
### The lesson
|
|
503
|
+
|
|
504
|
+
If you maintain "two right ways" inside one library and one is consistently winning on every axis (test coverage, doc clarity, ops sanity, integrator velocity), the other isn't a feature — it's a tax.
|
|
505
|
+
|
|
506
|
+
**👉 v2.x users: read `docs/MIGRATION.md`, schedule the upgrade, all new features land in v3.x. https://nativeupdate.aoneahsan.com**
|
|
507
|
+
|
|
508
|
+
#NativeUpdate #SoftwareArchitecture #BreakingChanges #Aoneahsan #BestOpenSourceCommunityProject
|
|
509
|
+
|
|
510
|
+
---
|
|
511
|
+
|
|
512
|
+
## Post 10 — Five-minute integration: from `yarn add` to your first OTA
|
|
513
|
+
|
|
514
|
+
Post 10 — final in the **`#NativeUpdate10`** series. The actual five-minute walkthrough.
|
|
515
|
+
|
|
516
|
+
### Pre-reqs
|
|
517
|
+
|
|
518
|
+
- A Capacitor app you control
|
|
519
|
+
- A backend URL where Native Update is running (self-host the Laravel from `backend/`, or your own service speaking the protocol)
|
|
520
|
+
- An app token from your backend
|
|
521
|
+
|
|
522
|
+
### Step 1 — Install
|
|
523
|
+
|
|
524
|
+
```bash
|
|
525
|
+
yarn add native-update
|
|
526
|
+
npx cap sync
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
Plugin installed, iOS pod install runs, Android Gradle module linked.
|
|
530
|
+
|
|
531
|
+
### Step 2 — Configure
|
|
532
|
+
|
|
533
|
+
In `capacitor.config.ts`:
|
|
534
|
+
|
|
535
|
+
```ts
|
|
536
|
+
plugins: {
|
|
537
|
+
NativeUpdate: {
|
|
538
|
+
backendUrl: 'https://updates.your-domain.com',
|
|
539
|
+
appKey: process.env.NATIVE_UPDATE_APP_KEY,
|
|
540
|
+
channel: 'production',
|
|
541
|
+
autoCheck: true,
|
|
542
|
+
},
|
|
543
|
+
}
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
### Step 3 — Wire the API
|
|
547
|
+
|
|
548
|
+
```ts
|
|
549
|
+
import { NativeUpdate } from 'native-update';
|
|
550
|
+
|
|
551
|
+
// On launch:
|
|
552
|
+
const r = await NativeUpdate.checkForUpdate();
|
|
553
|
+
if (r.updateAvailable) await NativeUpdate.downloadAndApply();
|
|
554
|
+
|
|
555
|
+
// Stale binary check:
|
|
556
|
+
const v = await NativeUpdate.getStoreVersion();
|
|
557
|
+
if (v.updateRecommended) showSoftNudgeBanner(v.latestVersion);
|
|
558
|
+
|
|
559
|
+
// After a successful action:
|
|
560
|
+
if (await NativeUpdate.canRequestReview()) {
|
|
561
|
+
await NativeUpdate.requestReview();
|
|
562
|
+
}
|
|
563
|
+
```
|
|
564
|
+
|
|
565
|
+
Three calls, three independent capabilities.
|
|
566
|
+
|
|
567
|
+
### Step 4 — Build a release
|
|
568
|
+
|
|
569
|
+
```bash
|
|
570
|
+
yarn build
|
|
571
|
+
nativeupdate release --channel staging --rollout 100 --binary "1.0.x"
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
CLI builds, hashes, uploads, registers. Returns release ID.
|
|
575
|
+
|
|
576
|
+
### Step 5 — Verify
|
|
577
|
+
|
|
578
|
+
Open the app on a test device. Plugin checks backend, sees newer release on `staging`, downloads, applies on next start. Restart. You're on the new bundle.
|
|
579
|
+
|
|
580
|
+
### Step 6 — Promote to production
|
|
581
|
+
|
|
582
|
+
```bash
|
|
583
|
+
nativeupdate promote --from staging --to production --rollout 10
|
|
584
|
+
# watch error rate for an hour
|
|
585
|
+
nativeupdate promote --rollout 50
|
|
586
|
+
nativeupdate promote --rollout 100
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
### Common first-time gotchas
|
|
590
|
+
|
|
591
|
+
- **Hash mismatch** → re-built but didn't re-upload. Re-run `release`.
|
|
592
|
+
- **iOS doesn't see the update** → missed `npx cap sync`. Re-sync, rebuild in Xcode.
|
|
593
|
+
- **"No update" but there is one** → channel mismatch.
|
|
594
|
+
- **Crash after apply** → rollback kicks in automatically. Check Sentry.
|
|
595
|
+
|
|
596
|
+
### After integration
|
|
597
|
+
|
|
598
|
+
- Add a "tap to check for updates" button in Settings for power users
|
|
599
|
+
- Wire `requestReview()` after your strongest "user succeeded" moment
|
|
600
|
+
- Wire store-update-check on cold launch with a soft-nudge banner
|
|
601
|
+
- Add Sentry — when an OTA bundle breaks, you want to see it before users do
|
|
602
|
+
|
|
603
|
+
**👉 Five steps. Five minutes. Run them today. `yarn add native-update` or https://nativeupdate.aoneahsan.com**
|
|
604
|
+
|
|
605
|
+
End of `#NativeUpdate10`. If a teammate is stuck on a 7-day review queue, share the package with them. Save them a week.
|
|
606
|
+
|
|
607
|
+
#NativeUpdate #GettingStarted #CapacitorPlugin #Aoneahsan #TopFree
|
|
608
|
+
|
|
609
|
+
---
|
|
610
|
+
|
|
611
|
+
## End of Posts Batch 01
|
|
612
|
+
|
|
613
|
+
10 posts, each ~2700–2900 chars, ready for LinkedIn / Facebook. Mark `USED — ` once shared.
|