UI はローカライズ済みです。スキルの紹介と説明文書は選択した言語へ AI が自動翻訳し、コード・コマンド・パス・専門用語は原文のままです。原作者の文書が優先されます。
スキルを探す
開発・技術 · SKILL.md

mobile-ionic-capacitor

mobile-ionic-capacitor

Use this skill when the user says 'Ionic', 'Capacitor', 'Ionic app', 'hybrid mobile', 'web-to-mobile', 'Capacitor plugin', 'Ionic framework', 'Ionic React', 'Ionic Angular', 'Ionic Vue'. Build hybrid mobile apps using Ionic UI components and Capacitor native bridge with web-to-native access. Do NOT use for: native mobile development (KMP/MAUI) or pure web a…

Independent GitHub snapshot · Automated classification · Original documentation governs

スキルの説明

GitHub

name: mobile-ionic-capacitor description: > Use this skill when the user says 'Ionic', 'Capacitor', 'Ionic app', 'hybrid mobile', 'web-to-mobile', 'Capacitor plugin', 'Ionic framework', 'Ionic React', 'Ionic Angular', 'Ionic Vue'. Build hybrid mobile apps using Ionic UI components and Capacitor native bridge with web-to-native access. Do NOT use for: native mobile development (KMP/MAUI) or pure web apps. compatibility: claude-code: true cursor: true codex: true windsurf: true tags: [mobile, ionic, capacitor, phase-7] version: "2.0.0" author: "j4flmao" license: "MIT"

Mobile Ionic & Capacitor

Purpose

Guide for building hybrid mobile apps with Ionic framework and Capacitor native bridge.

Agent Protocol

Trigger

Phrases: "Ionic", "Capacitor", "Ionic app", "hybrid mobile", "web-to-mobile", "Capacitor plugin", "Ionic framework", "Ionic React", "Ionic Angular", "Ionic Vue"

Input Context

  • Framework choice (React, Angular, or Vue)
  • Required native plugin list
  • Existing web app to wrap (if any)
  • Build and deployment targets

Output Artifact

Ionic project with: Capacitor config, native plugin integrations, custom plugin code, build/deploy pipeline scripts.

Response Format

<ionic-capacitor>
<project>{framework, capacitor config, structure}</project>
<plugins>{installed plugins, config, permissions}</plugins>
<custom-plugin>{swift/kotlin, call pattern}</custom-plugin>
<build>{sync, xcode, android-studio steps}</build>
</ionic-capacitor>

No preamble. No postamble. No explanations. No filler/hedging/transitions. Compress output — why use many token when few do trick.

Completion Criteria

  • ionic build succeeds with zero errors
  • npx cap sync copies web assets to both platforms
  • Native plugins function on device
  • Custom plugin call/response cycle works
  • App deploys to TestFlight and Play Console internal track

Max Response Length

8000 tokens

Architecture

Hybrid Bridge Architecture

┌─────────────────────────────────────────────┐
│           Web Application (SPA)              │
│  Ionic UI Components | Framework (React/     │
│  Angular/Vue) | App Logic                    │
└──────────────────┬──────────────────────────┘
                   │
┌──────────────────▼──────────────────────────┐
│          Capacitor Bridge (WKWebView/        │
│          Android WebView)                    │
│  • Plugin registry: maps JS calls to native  │
│  • JSON serialization/deserialization        │
│  • Error propagation via Promise reject      │
│  • Event emitter for native-to-JS events     │
└───────┬─────────────────────┬───────────────┘
        │                     │
┌───────▼─────────┐  ┌───────▼───────────────┐
│   iOS Runtime    │  │   Android Runtime      │
│   (Swift/Obj-C)  │  │   (Kotlin/Java)        │
│   CAPPlugin      │  │   CAPPlugin            │
└─────────────────┘  └───────────────────────┘

Decision Tree: Framework Selection

Team expertise?
├── React developers → Ionic React
│   Pros: hooks, largest ecosystem, TypeScript-first
│   Cons: routing less mature than Angular
├── Angular developers → Ionic Angular
│   Pros: mature routing (lazy loading, guards), full-featured
│   Cons: more boilerplate, steeper learning curve for new devs
└── Vue developers → Ionic Vue
    Pros: lightweight, composition API, growing ecosystem
    Cons: fewer community plugins, smaller talent pool

Decision Tree: Plugin Strategy

Need native device access?
├── Common feature (camera, geolocation, storage)
│   → Use official @capacitor/* plugin
│   → Configure permissions in native project files
├── Niche but exists (bluetooth, NFC, health kit)
│   → Search npm for @capacitor-community/* or capacitor-*
│   → Evaluate maintenance status, open issues, last update
├── Existing Cordova plugin
│   → Use @capacitor/cordova-plugin-compat
│   → Test thoroughly — not all Cordova patterns migrate cleanly
└── Completely custom native requirement
    → Write custom plugin (Swift + Kotlin)
    → Keep plugin surface small, test on real device

Workflow

  1. Ionic framework overview — Ionic is a UI toolkit for building cross-platform mobile apps using web technologies (HTML, CSS, JS). It provides a library of mobile-optimized UI components (ion-*), gestures, and animations that mimic native platform conventions. Three framework integrations: Ionic React (hooks, fast iteration, largest ecosystem), Ionic Angular (NgModule + standalone, mature routing, full-featured), Ionic Vue (composition API, lightweight, growing ecosystem). All share @ionic/core components and CSS custom properties theming.

  2. Capacitor vs Cordova — Capacitor is the successor to Cordova with key advantages: modern plugin API (Promise-based instead of callback), native project files committed to repo (full Xcode/Android Studio control), Swift/Kotlin plugin development (instead of Java/Obj-C), HMR support during development, PWA fallback for web, and unified config (capacitor.config.ts). Cordova plugins are compatible via @capacitor/cordova-plugin-compat. Migration path: npx cap init on existing Cordova project, then replace cordova plugin add with npm install @capacitor/plugin-name.

  3. Project creation — ionic start myApp blank --type=react-ts creates an Ionic React TypeScript project with Capacitor pre-configured. Key files: capacitor.config.ts (app ID, name, server URL for live reload), ionic.config.json (project type, integrations), src/ (web app code), ios/ (Xcode project after npx cap add ios), android/ (Android Studio project after npx cap add android). The web app is the single source of truth — native projects are generated artifacts.

  4. Live reload development — ionic serve for web-only HMR. For device live reload: ionic cap run ios -l --external starts a dev server with your machine's IP, updates capacitor.config.ts with server.url, then opens Xcode. The device loads web assets from the dev server over the network. Hot Module Replacement preserves component state. For Android: ionic cap run android -l --external. Requires device on same network as dev machine. Disable server.url for production builds.

  5. Capacitor plugin system — Plugins are npm packages that expose native APIs to JavaScript. Architecture: TypeScript API definition (@capacitor/plugin-name) -> iOS implementation (Swift, CAPPlugin subclass) -> Android implementation (Kotlin, CAPPlugin subclass) -> optional web fallback. Plugin calls are serialized via JSON through the WebView bridge. npx cap sync installs plugin pods (iOS) and copies plugin code (Android). Permissions must be configured in native project files before plugin use.

  6. PWA conversion — Capacitor apps are PWAs by default. The same web code works as a standalone PWA when served over HTTPS. Add a manifest.json with icons, service-worker.js for caching. Capacitor plugins gracefully degrade: check Capacitor.isPluginAvailable('Camera') before calling native APIs. PWA mode runs in the browser, not WebView. Use @capacitor/filesystem and @capacitor/storage as they have web fallbacks.

  7. Build and deploy pipeline — ionic build --prod generates optimized web assets in www/. npx cap copy copies to native platforms, npx cap sync also installs native dependencies. Code signing: iOS via Xcode Automatic signing (requires Apple Developer account), Android via keystore (keytool -genkey then ./gradlew bundleRelease). Distribution: App Store Connect (iOS) via Xcode Organizer or Transporter, Google Play Console (Android) via signed AAB upload. Appflow and EAS Build for cloud CI/CD.

  8. Custom plugin development — When an official plugin does not exist, create a custom plugin. Structure: npx cap plugin:generate my-plugin scaffolds the plugin template. The generated plugin has src/definitions.ts (TypeScript interface), src/web.ts (web fallback), ios/Plugin/Plugin.swift (iOS native), android/src/main/.../Plugin.kt (Android native). Implement @objc annotated methods on iOS and @PluginMethod annotated methods on Android. Return data via call.resolve() and errors via call.reject(). Test the plugin in a sample Ionic app before publishing.

  9. Deep linking and universal links — Configure deep links to open the app from URLs. iOS: configure apple-app-site-association file on your server, add associated domain in Xcode, handle in AppDelegate. Android: configure intent filters in AndroidManifest.xml for URL scheme. Capacitor's App.addListener('appUrlOpen') catches incoming URLs in the web layer. Map URL paths to navigation routes. Test with xcrun simctl openurl for iOS and adb shell am start for Android.

  10. Push notifications — Set up push notifications via @capacitor/push-notifications. iOS: configure APNs certificate in Apple Developer Portal, add Push Notifications capability in Xcode. Android: set up Firebase Cloud Messaging, upload server key to Firebase Console. Register for notifications: PushNotifications.requestPermissions() then PushNotifications.register(). Handle foreground notifications with PushNotifications.addListener('pushNotificationReceived'). Handle background tap actions with PushNotifications.addListener('pushNotificationActionPerformed'). Test with curl to APNs/FCM endpoints or use Pusher/PushCompanion tools.

Platform Compatibility

FeatureiOSAndroidWeb/PWA
Core UI componentsFullFullFull
Native pluginsAllAllGraceful fallback
Push notificationsAPNsFCMNot supported
Background modeLimitedYesNot supported
Live reloadYesYesDev only
HMRVia serverVia serverBuilt-in
Camera accessFullFullBrowser limited
GeolocationFullFullBrowser limited
Biometric authFace ID/Touch IDFingerprint/BiometricNot supported
File systemFullFullSandboxed

Best Practices

  • Use ion- components exclusively — avoid mixing with platform-specific UI
  • Commit ios/ and android/ directories to version control
  • Test on real devices before each release — simulator misses camera, sensors, push
  • Use npx cap sync after every npm dependency change — not just npx cap copy
  • Configure all permission strings in Info.plist and AndroidManifest before plugin calls
  • Keep capacitor.config.ts environment-aware: different server.url for dev/prod
  • Use TypeScript strict mode for plugin call type safety
  • Profile WebView performance: 60fps animations, avoid layout thrashing, lazy-load images
  • Use ion-content scroll assistance with keyboard plugin to handle keyboard overlap
  • Set SplashScreen.backgroundColor to match app background color to prevent white flash
  • Abstract plugin calls behind service interfaces for testability
  • Check Capacitor.isPluginAvailable() before calling any plugin method in web context
  • Use Capacitor.convertFileSrc() for filesystem paths to ensure correct URL scheme
  • Implement error boundaries around plugin calls to handle native failures gracefully

Common Pitfalls

  • Missing permissions: Plugin fails silently with no error. Always check Info.plist and AndroidManifest.
  • Stale native project: npx cap sync must run after every plugin install or npm update.
  • CORS in WebView: Capacitor WebView has no CORS restrictions — but PWA mode does. Use @capacitor/http for production API calls.
  • Keyboard overlay: Use @capacitor/keyboard with ion-content scroll assistance to handle keyboard show/hide.
  • Splash screen flicker: Configure backgroundColor in capacitor.config.ts to match splash color — prevents white flash.
  • Plugin call timeout: Heavy native operations (image processing) may exceed default timeout. Use call.resolve() in async callback.
  • Navigation state loss: When the app is backgrounded and killed, WebView state is lost. Persist critical state to storage.
  • Memory pressure on low-end devices: WebView on Android devices with 2GB RAM may crash under memory pressure. Monitor with window.performance.memory.
  • SSL certificate issues: Self-signed certs for dev servers require server.cleartext: true — never ship this to production apps.
  • Icons and splash screen not updating: Clear build cache between icon/splash updates. Use npx capacitor-assets generate for consistent asset generation.

Compared With

ApproachUse CaseTradeoff
Ionic + CapacitorHybrid apps, web-to-mobile migrationWebView performance ceiling, native API gap
React NativeNear-native perf, large ecosystemSeparate RN component lib, thicker bridge
FlutterHigh-performance cross-platformDart language, custom rendering engine
Kotlin MultiplatformNative UI, shared logicTwo UI codebases, less mature
MAUI (.NET).NET ecosystem, enterpriseWindows-focused, smaller mobile community
Pure PWANo app store, instant updatesLimited device API access, no push on iOS
Native (Swift/Kotlin)Full platform accessTwo codebases, higher maintenance cost

Performance

  • WebView startup: 200-600ms on modern devices depending on OS version and hardware
  • Ionic component rendering: ion-* components use Shadow DOM for style isolation — adds ~50-100ms initial render per component tree
  • Navigation transitions: Ionic uses GPU-accelerated CSS animations — target 60fps, avoid layout-triggering property animations (top, left, width, height)
  • Image-heavy lists: use ion-img with lazy loading (Intersection Observer-based) instead of <img> tags
  • Plugin call latency: each JS-to-native plugin call adds 5-15ms round-trip overhead. Batch related native calls into a single plugin method
  • Memory: WebView on Android is a separate process with ~100-200MB default heap. Monitor with window.performance.memory
  • Bundle size: Ionic core components ~2MB gzipped — use @ionic/core tree-shaking with Vite or webpack
  • Scrolling: ion-content uses native scrolling on iOS and synthetic scrolling on Android — virtual scrolling (ion-virtual-scroll) for lists over 100 items
  • Startup optimization: lazy-load routes with framework-specific patterns, defer heavy plugin initialization to after first meaningful paint

Tooling

ToolCategoryPurpose
Ionic CLIProject managementStart, build, serve, cap commands
Capacitor CLINative bridge managementAdd platforms, sync, copy, open
AppflowCI/CDCloud build, live deploy, package
EAS Build (Expo)CI/CD alternativeCloud builds for Capacitor apps
capacitor-assetsAsset generationAuto-generate icons and splash screens
Portals (Ionic)Micro-frontendsEmbed web apps in native apps
Cordova Plugin CompatMigrationRun Cordova plugins in Capacitor
Safari Web InspectorDebuggingiOS WebView JS console, network, elements
Chrome DevToolsDebuggingAndroid WebView JS console, network, elements
xcodebuild / GradleNative buildPlatform-specific compilation and signing
fastlaneAutomationCertificate management, beta deployment, screenshots
Maestro / DetoxE2E testingMobile UI testing for hybrid apps

Configuration Reference

// capacitor.config.ts
import { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  appId: 'com.example.app',
  appName: 'MyApp',
  webDir: 'www',
  server: {
    url: process.env.NODE_ENV === 'development' ? 'http://192.168.1.100:8100' : undefined,
    cleartext: true,
  },
  plugins: {
    SplashScreen: {
      launchShowDuration: 3000,
      backgroundColor: '#ffffff',
    },
    PushNotifications: {
      presentationOptions: ['badge', 'sound', 'alert'],
    },
  },
  ios: { contentInset: 'always' },
  android: { allowMixedContent: true },
};
export default config;

Rules

  • ios/ and android/ directories must be committed to version control — they are the source of truth for native configuration
  • All permission strings must be configured in native project files before plugin usage — silent failures are not acceptable
  • npx cap sync must be run after every npm dependency change — not just npx cap copy
  • Plugin calls must be guarded with Capacitor.isPluginAvailable() when the app also runs as a PWA
  • Custom plugins must implement both iOS (Swift) and Android (Kotlin) native layers plus a web fallback
  • server.url in capacitor.config.ts must be set for development and removed for production builds
  • Hardcoded URLs, API keys, or secrets must never appear in native project files or the web bundle
  • App icons and splash screens must be regenerated with npx capacitor-assets generate — never manually resized
  • Deep linking configuration must be tested on real devices, not just simulators
  • The WebView content must be tested on minimum supported OS versions — WebView updates are OS-dependent on Android
  • Push notification payloads must be handled both in foreground and background states
  • Navigation state must be persisted across app restarts — WebView state is volatile
  • All plugin calls should be wrapped in error handling — native failures must not crash the web layer
  • Production builds must use ionic build --prod with optimizations enabled (ahead-of-time compilation, tree shaking)
  • Bundle size must be monitored: Ionic core should be tree-shaken to exclude unused components

Performance Optimization

WebView Rendering Performance

The WebView is the bottleneck in Ionic apps. Optimize for 60fps scrolling and smooth animations:

  • Avoid layout thrashing: Batch DOM reads and writes. Use FastDom or wrapper that schedules writes after reads. Measure with Chrome DevTools Performance tab — look for "Layout" events >10ms.
  • CSS containment: contain: layout style paint on off-screen components prevents the browser from recalculating layout/style/paint for elements outside the viewport. Apply to static sidebars, headers, footers.
  • ion-img over <img>: Ionic's image component uses IntersectionObserver for lazy loading — images outside viewport don't load. Set --lazy-load-threshold for pre-load buffer. Fallback: loading="lazy" attribute on native <img>.
  • Virtual scrolling: ion-virtual-scroll renders only visible items + buffer. For lists >100 items, always use virtual scrolling. Configure approxItemHeight for smoother scroll. Replaced with ion-list + virtual-scroll in newer Ionic versions; consider cdk-virtual-scroll-viewport from Angular CDK for Angular projects.
  • Reduce DOM node count: Target <1500 DOM nodes for good performance. Audit with Chrome DevTools Elements panel. Replace nested <div> chains with CSS Grid/Flexbox single-layer layouts. Use ion-item directly without wrapping in extra <div> elements.
  • WebView pool (Android): Android WebView instances consume ~100MB each. If the app opens secondary WebViews (for external links, in-app browsers), reuse the existing WebView instance or use Custom Tabs (Chrome custom tabs) instead.

JavaScript Bundle Optimization

  • Tree-shaking: Ionic components tree-shake with Vite by default — but only if you import from specific paths: import { IonButton } from '@ionic/react' instead of importing everything. Audit with vite-plugin-inspect or webpack-bundle-analyzer.
  • Lazy load routes: Framework-specific lazy loading. Angular: loadChildren: () => import('./orders/orders.module').then(m => m.OrdersModule). React: React.lazy(() => import('./OrdersPage')). Vue: () => import('./OrdersPage.vue').
  • Capacitor plugin tree-shaking: Capacitor plugins register at build time; unused plugins don't add JS overhead. However, native SDKs (Android build.gradle dependencies, iOS Pods) always add binary size. Remove unused plugins from package.json and run npx cap sync.
  • Preload critical chunks: Use <link rel="modulepreload"> for the app shell and first-route modules. Reduces waterfall of module loading. Configure in vite.config.ts via optimizeDeps.include.

Native Bridge Optimization

  • Batch native calls: Each JS → native call costs 5-15ms round-trip. For bulk operations (reading 100 contacts), create a single plugin method that returns an array instead of 100 individual calls.
  • Capacitor runOutsideAngular (Ionic Angular): Plugin callbacks outside Angular's zone reduce change detection cycles. Wrap plugin calls:
    import { NgZone } from '@angular/core';
    constructor(private zone: NgZone) {}
    async callPlugin() {
      const result = await this.zone.runOutsideAngular(() => Camera.getPhoto({...}));
      // Re-enter zone only for UI updates
    }
    
  • Event emitter debouncing: Native-to-JS events (geolocation updates, sensor data) can flood the bridge. Debounce in native layer before emitting: DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) in Swift, Handler(Looper.getMainLooper()).postDelayed({}, 100) in Kotlin.
  • Avoid synchronous plugin calls: Capacitor doesn't support synchronous native calls. Use async/await everywhere. Never use Capacitor.convertFileSrc() synchronously — it's fine for path conversion but the underlying file may not be ready.
  • @capacitor-community/native-audio preloading: For audio assets, preload in native layer during app initialization, trigger play with minimal JS overhead. Audio files in WebView have ~200ms startup latency; native playback is near-instant.

Memory and Caching

  • @capacitor/filesystem cache directory: Use Directory.Cache for temporary files, Directory.Data for persistent app data. Clear cache on app version upgrade. Monitor with window.performance.memory?.usedJSHeapSize.
  • Service worker caching: For PWA mode, use Workbox or custom service worker with stale-while-revalidate strategy for API responses. Cache-first for static assets. Limit cache to 50MB (PWA storage quota varies by browser).
  • Image cache: Use @capacitor/cache or native image loading libraries (SDWebImage for iOS, Glide for Android) via custom plugin for production apps. WebView image cache is limited and cleared frequently.
  • ion-content scroll events: Scroll event listeners fire at 60fps — throttle to 100-200ms for non-critical work (lazy loading, analytics). Use requestAnimationFrame for visual updates, setTimeout for data operations.

Production Build & App Store Deployment

Android Play Store

  1. Build signed AAB: ionic build --prod && npx cap sync android && cd android && ./gradlew bundleRelease
  2. Signing: Configure android/app/build.gradle signing configs:
    android {
        signingConfigs {
            release {
                storeFile file(System.getenv("KEYSTORE_PATH"))
                storePassword System.getenv("KEYSTORE_PASS")
                keyAlias System.getenv("KEY_ALIAS")
                keyPassword System.getenv("KEY_PASS")
            }
        }
        buildTypes {
            release { signingConfig signingConfigs.release }
        }
    }
    
  3. Play Console: Upload AAB → Set up Store Listing (screenshots, description, category) → Content Rating → Pricing & Distribution → Rollout to Internal Testing → Closed Alpha → Open Beta → Production.
  4. App signing by Google Play: Enroll in Play App Signing. Google manages the signing key; you upload an upload key. If you lose the upload key, Google can reset it.

iOS App Store

  1. Build IPA: ionic build --prod && npx cap sync ios && cd ios && xcodebuild -workspace App.xcworkspace -scheme App -archivePath App.xcarchive archive
  2. Code signing: Automatic signing in Xcode (requires Apple Developer account). For CI, use fastlane match to manage certificates and provisioning profiles across machines.
  3. App Store Connect: Create app record → Upload IPA via Transporter or Xcode Organizer → Fill out metadata (name, description, keywords, privacy URL) → Submit for Review.
  4. TestFlight: Enable in App Store Connect → Add testers (up to 100 internal, 10,000 external) → Build must pass basic review before external testing. Beta builds expire after 90 days.

Fastlane Automation

# fastlane/Fastfile
lane :deploy_ios do
  match(type: "appstore")  # sync certificates
  build_ios_app(scheme: "App")
  upload_to_app_store(skip_metadata: true, skip_screenshots: true)
end

lane :deploy_android do
  gradle(task: "bundleRelease")
  upload_to_play_store(track: "internal")
end

Environment Configuration

  • capacitor.config.ts: Use environment variables for server.url (live reload) vs production. Never ship production build with server.url set — it weakens security by allowing external content loading.
  • Environment flags: Inject build-time variables via Vite or Angular environment files:
    // src/environments/environment.prod.ts
    export const environment = {
      production: true,
      apiUrl: 'https://api.example.com',
      appVersion: '1.2.3',
    };
    
  • Runtime config: Load config from a remote endpoint on app startup for feature flags and endpoint URLs without app store submission. Cache in storage, refresh on app foreground.

Custom Plugin Code Examples

iOS (Swift) — Background Geolocation Plugin

@objc(BackgroundGeolocation)
class BackgroundGeolocation: CAPPlugin {
    private var locationManager = CLLocationManager()

    @objc func startTracking(_ call: CAPPluginCall) {
        locationManager.delegate = self
        locationManager.allowsBackgroundLocationUpdates = true
        locationManager.pausesLocationUpdatesAutomatically = false
        locationManager.startUpdatingLocation()
        call.resolve()
    }

    @objc func stopTracking(_ call: CAPPluginCall) {
        locationManager.stopUpdatingLocation()
        call.resolve()
    }

    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        notifyListeners("locationUpdate", data: [
            "latitude": location.coordinate.latitude,
            "longitude": location.coordinate.longitude,
            "accuracy": location.horizontalAccuracy
        ])
    }
}

Android (Kotlin) — Same Plugin

@CapacitorPlugin(name = "BackgroundGeolocation")
class BackgroundGeolocation : CAPPlugin() {
    private val locationManager by lazy {
        activity?.getSystemService(Context.LOCATION_SERVICE) as? LocationManager
    }

    @PluginMethod
    fun startTracking(call: PluginCall) {
        if (ActivityCompat.checkSelfPermission(
                activity, Manifest.permission.ACCESS_FINE_LOCATION
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            call.reject("Location permission not granted")
            return
        }
        locationManager?.requestLocationUpdates(
            LocationManager.GPS_PROVIDER, 5000L, 10f,
            object : LocationListener {
                override fun onLocationChanged(location: Location) {
                    notifyListeners("locationUpdate", JSObject().apply {
                        put("latitude", location.latitude)
                        put("longitude", location.longitude)
                        put("accuracy", location.accuracy)
                    })
                }
            }
        )
        call.resolve()
    }

    @PluginMethod
    fun stopTracking(call: PluginCall) {
        locationManager?.removeUpdates(this)
        call.resolve()
    }
}

Web Fallback

// src/web.ts
import { WebPlugin } from '@capacitor/core';
import type { BackgroundGeolocationPlugin } from './definitions';

export class BackgroundGeolocationWeb
  extends WebPlugin
  implements BackgroundGeolocationPlugin
{
  async startTracking(): Promise<void> {
    console.warn('BackgroundGeolocation not available on web');
    // Fallback: use browser Geolocation API
    navigator.geolocation.watchPosition(
      (pos) => this.notifyListeners('locationUpdate', {
        latitude: pos.coords.latitude,
        longitude: pos.coords.longitude,
        accuracy: pos.coords.accuracy,
      }),
      (err) => console.error(err),
      { enableHighAccuracy: true, timeout: 10000 }
    );
  }

  async stopTracking(): Promise<void> {
    // cleanup
  }
}

Plugin Testing

  • Unit tests: Test plugin TypeScript API definitions with standard Jest/Vitest — pure interface testing.
  • Native tests: Android: JUnit + Mockito for plugin methods. iOS: XCTest with CAPPluginCall mock.
  • E2E: Maestro or Detox — test plugin calls from the web layer verify native behavior. Run on real devices for hardware-dependent plugins (camera, geolocation, biometrics).

References

  • references/capacitor-plugins.md — Capacitor Plugins
  • references/ionic-capacitor-advanced.md — Ionic Capacitor Advanced Topics
  • references/ionic-capacitor-fundamentals.md — Ionic Capacitor Fundamentals
  • references/ionic-capacitor-plugins.md — Ionic Capacitor Plugins
  • references/ionic-cli.md — Ionic CLI Reference
  • references/ionic-deployment.md — Ionic Deployment
  • references/ionic-capacitor-plugins.md — Ionic Capacitor Plugins Reference
  • references/ionic-capacitor-performance.md — Ionic Capacitor Performance Optimization

Handoff

Hand off to native iOS/Android skills when custom plugin development needs deep platform API access beyond Capacitor's bridge.

Implementation Patterns

Observer Pattern for Event Handling

` interface EventObserver { onEvent(event: T): Promise; }

class EventBus { private observers: Set<EventObserver> = new Set(); subscribe(observer: EventObserver): void { this.observers.add(observer); } unsubscribe(observer: EventObserver): void { this.observers.delete(observer); } async emit(event: T): Promise { const results = Array.from(this.observers).map(o => o.onEvent(event)); await Promise.allSettled(results); } } `

Configuration-Driven Approach

config: defaults: timeout: 30s retryCount: 3 overrides: production: timeout: 60s retryCount: 5 development: timeout: 300s retryCount: 1

Production Considerations

Deployment Checklist

  • Configuration validated against schema before startup
  • Health check endpoints registered and monitored
  • Graceful shutdown with draining period (30s timeout)
  • Resource limits configured (CPU, memory, file descriptors)
  • Log level set appropriate for environment
  • Metrics endpoint secured and exposed
  • Rate limiting configured per-tier
  • TLS certificates valid and auto-renewing
  • Database migrations run as separate deployment step
  • Feature flags ready for gradual rollout

Monitoring and Alerting

MetricThresholdSeverityAction
Error rate> 1% over 5minCriticalPage on-call
p99 latency> 2s over 5minWarningInvestigate
Throughput drop> 50% over 1minCriticalCheck upstream
Queue depth> 1000 over 1minWarningScale consumers
Disk usage> 85%WarningClean or expand
Memory usage> 90% heapCriticalRestart or scale

Anti-Patterns

Anti-PatternSymptomRoot CauseSolution
Premature optimizationComplex code for no measured benefitGuessing instead of profilingMeasure first, optimize based on data
Copy-paste reuseDuplicate code across codebaseLack of abstractionExtract shared logic into libraries
Gold-platingFeatures with no current requirementOver-engineeringYAGNI — build what's needed now
Magical thinkingAssumptions without validationSkipping error handlingHandle all failure modes explicitly

Performance Optimization

Caching Strategy

Cache hierarchy: L1 (in-memory local) → L2 (distributed Redis/Memcached) → L3 (CDN/Edge). Cache invalidation: TTL-based (simple, stale), event-based (complex, fresh), write-through (consistent, higher write latency), write-behind (fast writes, eventual consistency).

Resource Pooling

  • Database connections: Pool of reusable connections (HikariCP, pgBouncer)
  • HTTP connections: Keep-alive + connection pooling for external calls
  • Thread pool: Bounded thread pools for async task execution

Profiling Methodology

  1. Establish baseline with production traffic profile
  2. Profile CPU with sampling profiler (pprof, perf, async-profiler)
  3. Profile memory with heap dumps and allocation tracking
  4. Profile I/O with strace/perf trace for syscall analysis
  5. Profile latency with distributed tracing (OpenTelemetry)
  6. Identify bottleneck, formulate hypothesis, implement fix
  7. Re-profile to verify improvement, repeat

Security Considerations

Threat Modeling (STRIDE)

  • Spoofing: Identity validation, authentication
  • Tampering: Integrity checks, digital signatures
  • Repudiation: Audit logs, non-repudiation
  • Information disclosure: Encryption, access control
  • Denial of service: Rate limiting, resource quotas
  • Elevation of privilege: Principle of least privilege

Supply Chain Security

  • Dependency scanning: Snyk, Dependabot, Trivy
  • SBOM generation: CycloneDX or SPDX format
  • Signed commits: GPG or SSH commit signing
  • Artifact verification: Checksum validation, signature verification

Secrets Management

  • Secrets never in code — always in secrets manager (Vault, AWS Secrets Manager)
  • Rotation policy: Rotate database credentials every 90 days
  • Access audit: Log every secrets access, alert on anomalies
  • Encryption at rest and in transit for all secrets
  • Principle of least privilege: each service gets only its own secrets

Rules

  • Default-deny security posture — allow only explicitly required access.
  • All inputs validated, all outputs encoded, all errors handled.
  • Defend in depth — multiple layers of security controls.
  • Fail securely — errors default to safe behavior.
  • Log security-relevant events for audit and investigation.
  • Keep dependencies updated — automate vulnerability scanning.
  • Design for observability from day one, not as an afterthought.
  • Document all architectural decisions with rationale.
  • Review code for security, performance, and correctness before merging.
保存したスキル