Mobile Developmentmobile app developmentlos angeles developmenttechnical evaluation

Mobile App Development Companies Los Angeles: Technical Evaluation Checklist and Cost Optimization Strategies

A comprehensive technical framework for evaluating Los Angeles mobile app development partners, including cost analysis, capability assessment, and optimization strategies for successful project outcomes.

Principal LA Team
August 17, 2025
12 min read
Mobile App Development Companies Los Angeles: Technical Evaluation Checklist and Cost Optimization Strategies

Mobile App Development Companies Los Angeles: Technical Evaluation Checklist and Cost Optimization Strategies

Selecting the right mobile app development partner in Los Angeles requires a systematic approach that balances technical expertise, cost efficiency, and delivery excellence. With over 200 development companies operating in the LA metropolitan area, technology leaders face complex decisions that can significantly impact project outcomes, budgets, and long-term technical debt. This comprehensive guide provides a structured framework for evaluating vendors, optimizing costs, and ensuring successful project delivery in one of the nation's most competitive technology markets.

Technical Capability Assessment Framework for LA Mobile App Development Companies

Cross-platform Development Expertise Evaluation

Modern mobile applications demand sophisticated cross-platform strategies to maximize market reach while controlling development costs. When evaluating Los Angeles development companies, assess their proficiency across React Native, Flutter, and Xamarin ecosystems through hands-on technical interviews and portfolio analysis.

React Native expertise should encompass advanced navigation patterns, state management solutions like Redux or MobX, and native module integration capabilities. Request demonstrations of companies' experience with React Native's New Architecture, including TurboModules and Fabric renderer implementations that deliver near-native performance.

Flutter competency evaluation must focus on widget composition patterns, state management approaches using Provider, Bloc, or Riverpod, and platform-specific customizations. Examine their experience with Flutter's rendering engine optimizations and custom platform channels for accessing device-specific features.

Here's a TypeScript implementation for API client architecture that demonstrates the technical sophistication you should expect from qualified vendors:

interface APIResponse<T> {
  data: T;
  status: number;
  message: string;
}

class APIClient {
  private baseURL: string;
  private maxRetries: number = 3;
  private retryDelay: number = 1000;

  constructor(baseURL: string) {
    this.baseURL = baseURL;
  }

  private async delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  private shouldRetry(error: any, attempt: number): boolean {
    if (attempt >= this.maxRetries) return false;
    
    const retryableErrors = [408, 429, 500, 502, 503, 504];
    return retryableErrors.includes(error.status) || 
           error.code === 'NETWORK_ERROR';
  }

  async request<T>(
    endpoint: string, 
    options: RequestInit = {},
    attempt: number = 1
  ): Promise<APIResponse<T>> {
    try {
      const response = await fetch(`${this.baseURL}${endpoint}`, {
        ...options,
        headers: {
          'Content-Type': 'application/json',
          'Accept': 'application/json',
          ...options.headers,
        },
      });

      if (!response.ok) {
        throw {
          status: response.status,
          message: `HTTP ${response.status}: ${response.statusText}`
        };
      }

      const data = await response.json();
      return {
        data,
        status: response.status,
        message: 'Success'
      };

    } catch (error: any) {
      if (this.shouldRetry(error, attempt)) {
        await this.delay(this.retryDelay * attempt);
        return this.request<T>(endpoint, options, attempt + 1);
      }

      throw {
        data: null,
        status: error.status || 0,
        message: error.message || 'Request failed'
      };
    }
  }

  async get<T>(endpoint: string): Promise<APIResponse<T>> {
    return this.request<T>(endpoint, { method: 'GET' });
  }

  async post<T>(endpoint: string, body: any): Promise<APIResponse<T>> {
    return this.request<T>(endpoint, {
      method: 'POST',
      body: JSON.stringify(body)
    });
  }
}

Native iOS and Android Development Team Composition

Assess the depth and experience levels of native development teams by examining their senior-to-junior developer ratios, architectural decision-making processes, and platform-specific optimization capabilities. Ideal teams maintain at least 60% senior-level developers with 5+ years of platform-specific experience.

iOS teams should demonstrate proficiency with SwiftUI, Combine framework, Core Data optimizations, and iOS 16+ specific features like Live Activities and Dynamic Island integrations. Android teams must showcase expertise in Jetpack Compose, Kotlin Coroutines, Room database implementation, and Android 13+ privacy and security enhancements.

Backend Architecture Capabilities

Modern mobile applications require robust backend architectures supporting millions of concurrent users while maintaining sub-second response times. Evaluate vendors' microservices implementation strategies, API design philosophies, and cloud-native development approaches.

Microservices expertise should include container orchestration with Kubernetes, service mesh implementations using Istio or Linkerd, and distributed tracing capabilities. API design competency must encompass RESTful principles, GraphQL schema optimization, and versioning strategies that support backward compatibility.

Cloud integration capabilities should span multiple providers including AWS, Google Cloud Platform, and Microsoft Azure, with demonstrated experience in serverless architectures, managed database services, and CDN implementations for global content delivery.

DevOps Pipeline Maturity Assessment

Mature DevOps practices significantly impact development velocity, code quality, and deployment reliability. Assess vendors' CI/CD pipeline sophistication through automated testing coverage metrics, deployment frequency measurements, and mean time to recovery (MTTR) tracking.

Continuous Integration pipelines should incorporate automated code quality checks using SonarQube or similar tools, security vulnerability scanning with OWASP dependency checks, and comprehensive test suite execution including unit, integration, and end-to-end testing phases.

Deployment strategies must include blue-green deployments, canary releases, and automated rollback mechanisms. Infrastructure as Code (IaC) implementations using Terraform or AWS CloudFormation demonstrate advanced operational maturity and environment consistency.

Security Implementation Standards

Mobile application security requires comprehensive threat modeling, secure coding practices, and compliance with OWASP Mobile Security Project guidelines. Evaluate vendors' security expertise through penetration testing experience, vulnerability assessment methodologies, and incident response procedures.

OWASP Mobile Top 10 compliance should include protection against improper platform usage, insecure data storage, insecure communication, insecure authentication, insufficient cryptography, insecure authorization, client code quality issues, code tampering, reverse engineering, and extraneous functionality vulnerabilities.

Due Diligence Checklist for Vendor Selection

Portfolio Analysis with Industry Focus

Comprehensive portfolio evaluation requires examining projects with similar technical complexity, user scale, and industry-specific requirements. Healthcare applications demand HIPAA compliance expertise, fintech projects require PCI DSS certification, and entertainment applications need content delivery optimization capabilities.

Analyze case studies for quantifiable outcomes including user adoption rates, performance metrics, and post-launch maintenance requirements. Request detailed technical specifications, architecture diagrams, and scalability testing results from comparable projects.

Team Structure Evaluation

Effective mobile development teams require balanced composition across technical roles including senior developers, system architects, DevOps engineers, and quality assurance specialists. Evaluate team structures for appropriate senior-to-junior ratios, cross-functional collaboration capabilities, and knowledge sharing practices.

Project management capabilities should encompass Agile methodologies with demonstrated sprint velocity consistency, stakeholder communication protocols, and risk mitigation strategies. Assess project managers' technical backgrounds and their ability to bridge business requirements with technical implementation details.

Technology Stack Alignment

Ensure vendor technology expertise aligns with your current infrastructure and future scalability requirements. Evaluate their experience with your preferred cloud providers, database technologies, third-party service integrations, and monitoring solutions.

Legacy system integration capabilities become critical for enterprise applications requiring connectivity with existing ERP, CRM, or data warehouse solutions. Assess vendors' API integration experience, data migration capabilities, and hybrid architecture implementation strategies.

Quality Assurance Processes

Comprehensive QA processes must include automated testing coverage exceeding 80%, manual testing protocols for user experience validation, and performance testing capabilities under realistic load conditions. Evaluate vendors' testing methodologies, bug tracking systems, and defect resolution procedures.

Test automation frameworks should span multiple layers including unit testing with Jest or XCTest, integration testing with Detox or Espresso, and end-to-end testing using Appium or similar tools. Manual testing procedures must include cross-device compatibility validation, accessibility compliance verification, and user experience testing protocols.

Cost Structure Analysis and Budget Optimization

Development Phase Cost Allocation

Effective budget management requires detailed cost breakdown across development phases including discovery, design, development, testing, and deployment. Typical cost distribution follows 15% discovery, 20% design, 50% development, 10% testing, and 5% deployment phases.

Discovery phase investments in user research, technical feasibility analysis, and architecture planning significantly impact overall project success rates. Allocate sufficient budget for comprehensive requirement gathering, competitive analysis, and technical proof-of-concept development.

Development phase costs vary significantly based on feature complexity, integration requirements, and performance optimization needs. Backend development typically represents 40-60% of total development costs, while mobile client development accounts for 30-50% depending on platform coverage and customization requirements.

Resource Allocation Models

Compare dedicated team models against project-based pricing structures to optimize cost efficiency and development velocity. Dedicated teams provide greater control, consistent communication, and accumulated domain knowledge, while project-based models offer fixed-cost predictability and defined scope boundaries.

Dedicated team models work effectively for projects requiring 6+ months development timelines, frequent requirement iterations, and ongoing post-launch feature development. Project-based models suit well-defined applications with stable requirements and limited post-launch modification expectations.

Hidden Cost Identification

Third-party integration costs often exceed initial estimates due to API limitations, data synchronization complexity, and ongoing subscription fees. Popular integrations including payment processing, analytics platforms, and customer support systems can add 15-30% to total development costs.

Infrastructure scaling costs require careful analysis of user growth projections, data storage requirements, and bandwidth consumption patterns. Cloud service costs can increase exponentially with user adoption, particularly for applications with high data transfer or computational requirements.

ROI Calculation Framework

Comprehensive ROI analysis must incorporate development costs, time-to-market impact, maintenance expenses, and revenue generation potential. Calculate break-even points based on user acquisition costs, lifetime value projections, and competitive positioning advantages.

Time-to-market acceleration often justifies higher initial development investments through earlier revenue generation and market share capture. Quantify competitive advantages gained through faster launch timelines and feature differentiation capabilities.

Technical Architecture Evaluation Criteria

Scalability Assessment

Modern mobile applications must scale seamlessly from thousands to millions of users while maintaining consistent performance and reliability. Evaluate vendors' experience with horizontal scaling strategies, database sharding implementations, and caching layer optimizations.

Here's a Kotlin implementation demonstrating Android performance monitoring that qualified vendors should implement:

class PerformanceMonitor private constructor() {
    companion object {
        @Volatile
        private var INSTANCE: PerformanceMonitor? = null
        
        fun getInstance(): PerformanceMonitor {
            return INSTANCE ?: synchronized(this) {
                INSTANCE ?: PerformanceMonitor().also { INSTANCE = it }
            }
        }
    }
    
    private val metricsMap = ConcurrentHashMap<String, MutableList<Long>>()
    private val memoryUsageTracker = mutableListOf<MemoryInfo>()
    private val crashAnalytics = mutableListOf<CrashReport>()
    
    data class MemoryInfo(
        val timestamp: Long,
        val usedMemory: Long,
        val totalMemory: Long,
        val availableMemory: Long
    )
    
    data class CrashReport(
        val timestamp: Long,
        val exception: String,
        val stackTrace: String,
        val deviceInfo: Map<String, String>
    )
    
    fun startTimer(operationName: String): String {
        val timerId = "${operationName}_${System.currentTimeMillis()}"
        metricsMap.computeIfAbsent(timerId) { mutableListOf() }
            .add(System.currentTimeMillis())
        return timerId
    }
    
    fun endTimer(timerId: String) {
        metricsMap[timerId]?.let { timeList ->
            if (timeList.isNotEmpty()) {
                val startTime = timeList.last()
                val duration = System.currentTimeMillis() - startTime
                
                // Log performance metric
                logPerformanceMetric(timerId.substringBefore("_"), duration)
                
                // Alert for slow operations (>5 seconds)
                if (duration > 5000) {
                    logSlowOperation(timerId, duration)
                }
            }
        }
    }
    
    fun trackMemoryUsage(context: Context) {
        try {
            val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
            val memoryInfo = ActivityManager.MemoryInfo()
            activityManager.getMemoryInfo(memoryInfo)
            
            val runtime = Runtime.getRuntime()
            val memInfo = MemoryInfo(
                timestamp = System.currentTimeMillis(),
                usedMemory = runtime.totalMemory() - runtime.freeMemory(),
                totalMemory = runtime.totalMemory(),
                availableMemory = memoryInfo.availMem
            )
            
            memoryUsageTracker.add(memInfo)
            
            // Clean old entries (keep last 1000)
            if (memoryUsageTracker.size > 1000) {
                memoryUsageTracker.removeAt(0)
            }
            
            // Alert for high memory usage (>80% of available)
            val memoryUsagePercent = (memInfo.usedMemory.toDouble() / memInfo.totalMemory) * 100
            if (memoryUsagePercent > 80) {
                logHighMemoryUsage(memInfo)
            }
            
        } catch (e: Exception) {
            logException("Memory tracking failed", e)
        }
    }
    
    fun reportCrash(exception: Throwable, context: Context) {
        try {
            val crashReport = CrashReport(
                timestamp = System.currentTimeMillis(),
                exception = exception.javaClass.simpleName,
                stackTrace = exception.stackTraceToString(),
                deviceInfo = getDeviceInfo(context)
            )
            
            crashAnalytics.add(crashReport)
            
            // Send to analytics service
            sendCrashReport(crashReport)
            
        } catch (e: Exception) {
            // Fallback logging
            Log.e("PerformanceMonitor", "Failed to report crash", e)
        }
    }
    
    private fun getDeviceInfo(context: Context): Map<String, String> {
        return mapOf(
            "manufacturer" to Build.MANUFACTURER,
            "model" to Build.MODEL,
            "androidVersion" to Build.VERSION.RELEASE,
            "apiLevel" to Build.VERSION.SDK_INT.toString(),
            "appVersion" to getAppVersion(context)
        )
    }
    
    private fun getAppVersion(context: Context): String {
        return try {
            context.packageManager.getPackageInfo(context.packageName, 0).versionName
        } catch (e: Exception) {
            "unknown"
        }
    }
    
    private fun logPerformanceMetric(operation: String, duration: Long) {
        // Implementation for logging to analytics service
        Log.d("Performance", "$operation completed in ${duration}ms")
    }
    
    private fun logSlowOperation(timerId: String, duration: Long) {
        Log.w("Performance", "Slow operation detected: $timerId took ${duration}ms")
    }
    
    private fun logHighMemoryUsage(memInfo: MemoryInfo) {
        val usagePercent = (memInfo.usedMemory.toDouble() / memInfo.totalMemory) * 100
        Log.w("Memory", "High memory usage: ${String.format("%.1f", usagePercent)}%")
    }
    
    private fun logException(message: String, exception: Exception) {
        Log.e("PerformanceMonitor", message, exception)
    }
    
    private fun sendCrashReport(crashReport: CrashReport) {
        // Implementation for sending crash reports to analytics service
        // This would typically use a background service or work manager
    }
}

Database Design Evaluation

Database architecture decisions significantly impact application performance, scalability, and maintenance complexity. Evaluate vendors' database design expertise through data modeling practices, indexing strategies, and query optimization techniques.

NoSQL databases like MongoDB or DynamoDB excel for applications requiring flexible schemas, horizontal scaling, and rapid development cycles. Relational databases including PostgreSQL or MySQL provide ACID compliance, complex query capabilities, and mature tooling ecosystems.

API Architecture Review

RESTful API design principles must include proper resource modeling, HTTP status code usage, and stateless operation implementation. GraphQL implementations require schema design expertise, query complexity analysis, and caching strategy development.

API versioning strategies become critical for maintaining backward compatibility while enabling feature evolution. Evaluate vendors' experience with semantic versioning, deprecation management, and migration coordination across mobile and web clients.

Quality Assurance and Testing Methodologies

Automated Testing Pyramid Implementation

Comprehensive test automation requires strategic implementation across unit, integration, and end-to-end testing layers. Unit tests should provide 70-80% of total test coverage, integration tests 15-25%, and end-to-end tests 5-15% for optimal efficiency and maintenance balance.

Here's a Swift implementation showcasing iOS security best practices that demonstrate vendor security expertise:

import Foundation
import Security
import CryptoKit

class SecurityManager {
    static let shared = SecurityManager()
    private init() {}
    
    // MARK: - Keychain Operations
    
    enum KeychainError: Error {
        case duplicateItem
        case itemNotFound
        case invalidData
        case unexpectedStatus(OSStatus)
    }
    
    func storeSecureData(_ data: Data, forKey key: String) throws {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key,
            kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
            kSecValueData as String: data
        ]
        
        let status = SecItemAdd(query as CFDictionary, nil)
        
        switch status {
        case errSecSuccess:
            break
        case errSecDuplicateItem:
            // Update existing item
            try updateSecureData(data, forKey: key)
        default:
            throw KeychainError.unexpectedStatus(status)
        }
    }
    
    func retrieveSecureData(forKey key: String) throws -> Data {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key,
            kSecReturnData as String: true,
            kSecMatchLimit as String: kSecMatchLimitOne
        ]
        
        var result: AnyObject?
        let status = SecItemCopyMatching(query as CFDictionary, &result)
        
        guard status == errSecSuccess else {
            throw status == errSecItemNotFound ? KeychainError.itemNotFound : KeychainError.unexpectedStatus(status)
        }
        
        guard let data = result as? Data else {
            throw KeychainError.invalidData
        }
        
        return data
    }
    
    private func updateSecureData(_ data: Data, forKey key: String) throws {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key
        ]
        
        let update: [String: Any] = [
            kSecValueData as String: data
        ]
        
        let status = SecItemUpdate(query as CFDictionary, update as CFDictionary)
        
        guard status == errSecSuccess else {
            throw KeychainError.unexpectedStatus(status)
        }
    }
    
    func deleteSecureData(forKey key: String) throws {
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrAccount as String: key
        ]
        
        let status = SecItemDelete(query as CFDictionary)
        
        guard status == errSecSuccess || status == errSecItemNotFound else {
            throw KeychainError.unexpectedStatus(status)
        }
    }
    
    // MARK: - Certificate Pinning
    
    class NetworkSessionManager: NSObject, URLSessionDelegate {
        private let pinnedCertificates: [Data]
        
        init(certificateData: [Data]) {
            self.pinnedCertificates = certificateData
            super.init()
        }
        
        func urlSession(
            _ session: URLSession,
            didReceive challenge: URLAuthenticationChallenge,
            completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
        ) {
            
            guard let serverTrust = challenge.protectionSpace.serverTrust else {
                completionHandler(.performDefaultHandling, nil)
                return
            }
            
            let serverCertificates = getCertificateChain(from: serverTrust)
            
            for serverCert in serverCertificates {
                if pinnedCertificates.contains(serverCert) {
                    let credential = URLCredential(trust: serverTrust)
                    completionHandler(.useCredential, credential)
                    return
                }
            }
            
            // Certificate not found in pinned certificates
            completionHandler(.rejectProtectionSpace, nil)
        }
        
        private func getCertificateChain(from serverTrust: SecTrust) -> [Data] {
            var certificates: [Data] = []
            
            let certificateCount = SecTrustGetCertificateCount(serverTrust)
            
            for i in 0..<certificateCount {
                if let certificate = SecTrustGetCertificateAtIndex(serverTrust, i) {
                    let certificateData = SecCertificateCopyData(certificate)
                    certificates.append(Data(bytes: CFDataGetBytePtr(certificateData), count: CFDataGetLength(certificateData)))
                }
            }
            
            return certificates
        }
    }
    
    // MARK: - Data Encryption
    
    func encryptData(_ data: Data, with key: SymmetricKey) throws -> Data {
        let sealedBox = try AES.GCM.seal(data, using: key)
        return sealedBox.combined!
    }
    
    func decryptData(_ encryptedData: Data, with key: SymmetricKey) throws -> Data {
        let sealedBox = try AES.GCM.SealedBox(combined: encryptedData)
        return try AES.GCM.open(sealedBox, using: key)
    }
    
    func generateEncryptionKey() -> SymmetricKey {
        return SymmetricKey(size: .bits256)
    }
    
    // MARK: - Secure Token Management
    
    func storeAuthToken(_ token: String) throws {
        guard let tokenData = token.data(using: .utf8) else {
            throw KeychainError.invalidData
        }
        
        try storeSecureData(tokenData, forKey: "auth_token")
    }
    
    func retrieveAuthToken() throws -> String? {
        do {
            let tokenData = try retrieveSecureData(forKey: "auth_token")
            return String(data: tokenData, encoding: .utf8)
        } catch KeychainError.itemNotFound {
            return nil
        }
    }
    
    func clearAuthToken() throws {
        try deleteSecureData(forKey: "auth_token")
    }
    
    // MARK: - Biometric Authentication
    
    func authenticateWithBiometrics(completion: @escaping (Bool, Error?) -> Void) {
        let context = LAContext()
        var error: NSError?
        
        guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
            completion(false, error)
            return
        }
        
        let reason = "Authenticate to access your secure data"
        
        context.evaluatePolicy(
            .deviceOwnerAuthenticationWithBiometrics,
            localizedReason: reason
        ) { success, error in
            DispatchQueue.main.async {
                completion(success, error)
            }
        }
    }
}

Performance Testing Protocols

Load testing scenarios must simulate realistic user behavior patterns, geographic distribution, and device capability variations. Stress testing should identify breaking points, resource limitations, and degradation patterns under extreme conditions.

Endurance testing validates application stability during extended operation periods, memory leak detection, and resource cleanup effectiveness. Performance baselines should include response time percentiles, throughput measurements, and error rate thresholds.

Security Testing Procedures

Penetration testing requires comprehensive vulnerability assessment across authentication mechanisms, data transmission protocols, and client-side data storage implementations. Static Application Security Testing (SAST) tools should integrate into CI/CD pipelines for continuous vulnerability detection.

Dynamic Application Security Testing (DAST) validates runtime security controls, API endpoint protection, and input validation effectiveness. Manual security reviews should complement automated testing through threat modeling exercises and security code reviews.

Project Management and Delivery Excellence

Agile Methodology Implementation

Effective Agile implementation requires mature sprint planning processes, retrospective feedback integration, and continuous improvement mechanisms. Sprint velocity tracking should maintain consistency within 20% variance across development cycles.

Here's a Flutter state management implementation demonstrating architectural sophistication:

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:async';

// Domain Models
class User {
  final String id;
  final String name;
  final String email;
  final DateTime lastLogin;

  const User({
    required this.id,
    required this.name,
    required this.email,
    required this.lastLogin,
  });

  User copyWith({
    String? name,
    String? email,
    DateTime? lastLogin,
  }) {
    return User(
      id: id,
      name: name ?? this.name,
      email: email ?? this.email,
      lastLogin: lastLogin ?? this.lastLogin,
    );
  }
}

// Application State
@immutable
abstract class AppState {}

class AppInitialState extends AppState {}

class AppLoadingState extends AppState {}

class AppAuthenticatedState extends AppState {
  final User user;
  final List<String> permissions;

  AppAuthenticatedState({
    required this.user,
    required this.permissions,
  });
}

class AppErrorState extends AppState {
  final String message;
  final String? code;

  AppErrorState({
    required this.message,
    this.code,
  });
}

// Events
@immutable
abstract class AppEvent {}

class LoginEvent extends AppEvent {
  final String email;
  final String password;

  LoginEvent({
    required this.email,
    required this.password,
  });
}

class LogoutEvent extends AppEvent {}

class RefreshUserEvent extends AppEvent {}

class UpdateUserEvent extends AppEvent {
  final User user;

  UpdateUserEvent({required this.user});
}

// Repository Interface
abstract class UserRepository {
  Future<User?> authenticateUser(String email, String password);
  Future<User?> getCurrentUser();
  Future<User> updateUser(User user);
  Future<List<String>> getUserPermissions(String userId);
  Future<void> logout();
}

// State Management with Error Handling
class AppStateManager extends ChangeNotifier {
  AppState _state = AppInitialState();
  final UserRepository _userRepository;
  Timer? _sessionTimer;
  
  AppState get state => _state;
  
  AppStateManager({required UserRepository userRepository})
      : _userRepository = userRepository {
    _initializeApp();
  }

  void _setState(AppState newState) {
    if (_state.runtimeType != newState.runtimeType) {
      _state = newState;
      notifyListeners();
    }
  }

  Future<void> _initializeApp() async {
    _setState(AppLoadingState());
    
    try {
      final currentUser = await _userRepository.getCurrentUser();
      if (currentUser != null) {
        final permissions = await _userRepository.getUserPermissions(currentUser.id);
        _setState(AppAuthenticatedState(
          user: currentUser,
          permissions: permissions,
        ));
        _startSessionTimer();
      } else {
        _setState(AppInitialState());
      }
    } catch (e) {
      _setState(AppErrorState(
        message: 'Failed to initialize app: ${e.toString()}',
        code: 'INIT_ERROR',
      ));
    }
  }

  Future<void> handleEvent(AppEvent event) async {
    try {
      switch (event.runtimeType) {
        case LoginEvent:
          await _handleLogin(event as LoginEvent);
          break;
        case LogoutEvent:
          await _handleLogout();
          break;
        case RefreshUserEvent:
          await _handleRefreshUser();
          break;
        case UpdateUserEvent:
          await _handleUpdateUser(event as UpdateUserEvent);
          break;
      }
    } catch (e) {
      _setState(AppErrorState(
        message: 'Operation failed: ${e.toString()}',
        code: 'OPERATION_ERROR',
      ));
    }
  }

  Future<void> _handleLogin(LoginEvent event) async {
    _setState(AppLoadingState());
    
    try {
      final user = await _userRepository.authenticateUser(
        event.email,
        event.password,
      );
      
      if (user != null) {
        final permissions = await _userRepository.getUserPermissions(user.id);
        _setState(AppAuthenticatedState(
          user: user,
          permissions: permissions,
        ));
        _startSessionTimer();
      } else {
        _setState(AppErrorState(
          message: 'Invalid credentials',
          code: 'AUTH_FAILED',
        ));
      }
    } catch (e) {
      _setState(AppErrorState(
        message: 'Login failed: ${e.toString()}',
        code: 'LOGIN_ERROR',
      ));
    }
  }

  Future<void> _handleLogout() async {
    _setState(AppLoadingState());
    
    try {
      await _userRepository.logout();
      _stopSessionTimer();
      _setState(AppInitialState());
    } catch (e) {
      _setState(AppErrorState(
        message: 'Logout failed: ${e.toString()}',
        code: 'LOGOUT_ERROR',
      ));
    }
  }

  Future<void> _handleRefreshUser() async {
    if (_state is AppAuthenticatedState) {
      try {
        final currentUser = await _userRepository.getCurrentUser();
        if (currentUser != null) {
          final permissions = await _userRepository.getUserPermissions(currentUser.id);
          _setState(AppAuthenticatedState(
            user: currentUser,
            permissions: permissions,
          ));
        } else {
          await _handleLogout();
        }
      } catch (e) {
        _setState(AppErrorState(
          message: 'Failed to refresh user data: ${e.toString()}',
          code: 'REFRESH_ERROR',
        ));
      }
    }
  }

  Future<void> _handleUpdateUser(UpdateUserEvent event) async {
    if (_state is AppAuthenticatedState) {
      final currentState = _state as AppAuthenticatedState;
      
      try {
        final updatedUser = await _userRepository.updateUser(event.user);
        _setState(AppAuthenticatedState(
          user: updatedUser,
          permissions: currentState.permissions,
        ));
      } catch (e) {
        _setState(AppErrorState(
          message: 'Failed to update user: ${e.toString()}',
          code: 'UPDATE_ERROR',
        ));
      }
    }
  }

  void _startSessionTimer() {
    _sessionTimer?.cancel();
    _sessionTimer = Timer.periodic(
      const Duration(minutes: 15),
      (timer) => handleEvent(RefreshUserEvent()),
    );
  }

  void _stopSessionTimer() {
    _sessionTimer?.

Related Articles

AI-Driven Software Development: Measuring ROI and Performance Impact in Enterprise Mobile Projects
Mobile Development

AI-Driven Software Development: Measuring ROI and Performance Impact in Enterprise Mobile Projects

Discover how artificial intelligence transforms software development ROI through automated testing, intelligent code review, and predictive project management in enterprise mobile applications.

Read Article
AI-First Startup Validation: From MVP to Market-Ready Mobile Apps Using Machine Learning
Mobile Development

AI-First Startup Validation: From MVP to Market-Ready Mobile Apps Using Machine Learning

Learn how startups can integrate AI validation throughout their mobile app development lifecycle to reduce time-to-market, minimize development costs, and build products users actually want.

Read Article