Skip to content
Back to skills

Stack Mobile

ASecurity

Architecture, quality and test checklist for mobile apps — cross-platform with Dart/Flutter, and native with Kotlin on Android or Swift/Objective-C on iOS. Covers layering and state management, the platform lifecycle, offline/sync and on-device storage, permissions and secure credential storage, store-release constraints, and the mobile-specific test pyramid. Invoke when the project contains a Flutter/Dart app, an Android module (Gradle, Jetpack Compose, Kotlin), or an iOS target (Xcode, Swif...

  • 3 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 6, 2026
ai-agentsgoswiftkotlinsqltestingapibackendsecurity

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 6, 2026

npx -y skills add cyber93de/aiflow --skill stack-mobile --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Stack Mobile?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Stack Mobile
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/cyber93de-stack-mobile/badge)](https://www.skillsdirectory.com/skills/cyber93de-stack-mobile)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: stack-mobile
description: Architecture, quality and test checklist for mobile apps — cross-platform with Dart/Flutter, and native with Kotlin on Android or Swift/Objective-C on iOS. Covers layering and state management, the platform lifecycle, offline/sync and on-device storage, permissions and secure credential storage, store-release constraints, and the mobile-specific test pyramid. Invoke when the project contains a Flutter/Dart app, an Android module (Gradle, Jetpack Compose, Kotlin), or an iOS target (Xcode, SwiftUI/UIKit), or on explicit request — "mobile", "Flutter", "Android", "iOS", "Kotlin", "Swift", "app".
---

# Mobile — Flutter/Dart · Kotlin/Android · Swift/iOS

Applies `AGENTS.md` §2 (architecture rules) and §3a (quality gates) to a client that runs on
someone else's device, loses the network, gets killed by the OS, and ships through a review queue.

## Layering (§2a, mobile dialect)

```
UI            (Widgets / Composables / SwiftUI Views — no business rules, no I/O)
   ↓
presentation  (ViewModel / Bloc / Cubit / Presenter — state + intent, no framework types leaking up)
   ↓
domain        (use cases, entities — pure, no Flutter/Android/UIKit imports)
   ↑
data          (repositories → remote DTOs + local DAOs; maps DTO ↔ domain)
```

- **The domain must compile without the UI framework.** No `BuildContext`, no `Context`, no
  `UIViewController` below the presentation layer. That is the §2a inward-dependency rule, and it
  is what makes the domain unit-testable without an emulator.
- **DTO ≠ entity** (§2a): the JSON shape the backend returns and the model your UI binds to change
  for different reasons. Map explicitly in the repository. Same for the persistence model
  (Room/Drift/SwiftData/Realm entities are a third shape).
- **Repository is the port**, declared in the domain, implemented in data. Injected —
  `get_it`/`riverpod`, Hilt/Koin, or plain initialiser injection in Swift.

## State management

Pick **one** per app and stay with it: Bloc/Riverpod/Provider (Flutter), ViewModel+StateFlow with
Compose (Android), Observable/`@Observable` or TCA (iOS). Mixing two is how a codebase becomes
unreviewable. State is immutable and unidirectional; UI renders state and emits intents, nothing
else. Every async state has all four cases modelled — loading, data, empty, error — an app that
only models "data" ships a blank screen to real users.

## Platform reality

- **Lifecycle:** the OS kills backgrounded apps. Anything the user typed must survive process
  death (saved state / `onSaveInstanceState` / `SceneStorage`), and in-flight work must be
  cancellable and resumable. Long work belongs in WorkManager / BGTaskScheduler / a foreground
  service — not a coroutine tied to a screen.
- **Offline is a requirement, not a feature.** Decide explicitly: read-through cache, offline-first
  with a sync queue, or online-only with an honest error state. Record the decision. Conflict
  resolution is part of it — "last write wins" is a choice you have to make on purpose.
- **Permissions** are requested in context, with a rationale, and every denial path is implemented
  (including "denied forever"). Never block the whole app on an optional permission.
- **Secrets never live in the bundle.** API keys in the APK/IPA are extractable in minutes. Tokens
  go to Keystore / Keychain (`flutter_secure_storage` on top), never `SharedPreferences`,
  `UserDefaults`, or plain SQLite. Certificate pinning where the threat model asks for it.
- **Accessibility is not optional** — semantic labels, dynamic type / text scaling, contrast, and
  a usable focus order. The `accessibility-checker` audits it; don't wait for it.
- **Store constraints** shape the design: review latency means you cannot hotfix in an hour, so
  feature flags and a forced-update path are architecture, not extras. Privacy manifests, data
  safety declarations and tracking disclosures follow from what the code actually does.

## Toolchain & style (§3)

| | Flutter/Dart | Android/Kotlin | iOS/Swift |
|---|---|---|---|
| Format | `dart format` | `ktfmt`/`ktlint` | `swift-format` |
| Lint | `flutter analyze` + `very_good_analysis` | `detekt` + Android Lint | `SwiftLint` |
| Build | `flutter build` | Gradle (version catalogs, no dynamic versions) | Xcode / SPM |
| DI | `get_it` / `riverpod` | Hilt / Koin | initialiser injection / Factory |

Warnings are errors. Pin dependency versions; a `+`/`latest` range makes a build unreproducible
and is a supply-chain risk (see the **security** skill).

## Testing (§3a, mobile pyramid)

- **Unit** — domain + presentation, no emulator, no network. > 80 % of changed logic, every
  non-static method (§3a). Fake the repository, not the HTTP client.
- **Widget / component** — `flutter test`, Compose UI tests, XCTest view tests. Assert on
  behaviour and semantics, not on pixel layout.
- **Integration** — repository against a real local DB and a stubbed server (mock web server).
- **BDD E2E** (§3a mandatory) — `integration_test` / Espresso / XCUITest, driven as
  Given/When/Then scenarios on a device or emulator, in CI.
- Test the states the happy path hides: airplane mode mid-request, token expiry, backgrounding
  during a payment, a rotated device, a 400 %-scaled font, an empty list, a 10 000-item list.

## Typical findings to raise

Business logic in a Widget/Composable/View · `BuildContext`/`Context` below the presentation layer
· the API DTO used directly as the UI model · tokens in SharedPreferences/UserDefaults · no error
state modelled · network call not cancellable on screen exit · a `setState`/`ObservableObject`
sprawl next to an existing Bloc/ViewModel · hardcoded strings blocking localisation · missing
semantic labels.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…