Skip to content

Mobile-Specific Network Considerations

Battery Optimization

Network Efficiency

Battery life is a critical factor in mobile devices, and network operations are one of the biggest causes of battery drain.

Request Coalescing

javascript
class RequestCoalescer {
  constructor(windowSize = 100) {
    this.windowSize = windowSize;
    this.pendingRequests = [];
    this.coalesceTimer = null;
  }

  addRequest(request) {
    this.pendingRequests.push(request);
    
    if (!this.coalesceTimer) {
      this.coalesceTimer = setTimeout(() => {
        this.processBatchedRequests();
      }, this.windowSize);
    }
  }

  processBatchedRequests() {
    if (this.pendingRequests.length === 0) return;
    
    // Group similar requests
    const groupedRequests = this.groupSimilarRequests(this.pendingRequests);
    
    // Send batch request for each group
    groupedRequests.forEach(group => {
      this.sendBatchRequest(group);
    });
    
    this.pendingRequests = [];
    this.coalesceTimer = null;
  }

  groupSimilarRequests(requests) {
    const groups = new Map();
    
    requests.forEach(request => {
      const key = `${request.method}-${request.baseUrl}`;
      if (!groups.has(key)) {
        groups.set(key, []);
      }
      groups.get(key).push(request);
    });
    
    return Array.from(groups.values());
  }

  async sendBatchRequest(requests) {
    if (requests.length === 1) {
      return this.sendSingleRequest(requests[0]);
    }

    const batchPayload = {
      requests: requests.map(req => ({
        id: req.id,
        method: req.method,
        path: req.path,
        body: req.body,
        headers: req.headers
      }))
    };

    try {
      const response = await fetch(`${requests[0].baseUrl}/batch`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(batchPayload)
      });

      const results = await response.json();
      
      // Distribute results to individual request callbacks
      results.forEach(result => {
        const originalRequest = requests.find(req => req.id === result.id);
        if (originalRequest && originalRequest.callback) {
          originalRequest.callback(result.error, result.data);
        }
      });
    } catch (error) {
      // Fallback to individual requests
      requests.forEach(request => {
        this.sendSingleRequest(request);
      });
    }
  }

  async sendSingleRequest(request) {
    try {
      const response = await fetch(`${request.baseUrl}${request.path}`, {
        method: request.method,
        headers: request.headers,
        body: request.body
      });

      const data = await response.json();
      
      if (request.callback) {
        request.callback(null, data);
      }
    } catch (error) {
      if (request.callback) {
        request.callback(error, null);
      }
    }
  }
}

Radio State Management

kotlin
// Android - Kotlin
class RadioStateManager(private val context: Context) {
    private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) 
        as ConnectivityManager
    private val telephonyManager = context.getSystemService(Context.TELEPHONY_SERVICE) 
        as TelephonyManager
    
    private var isHighPowerMode = false
    private val stateTransitionDelay = 2000L // 2 seconds
    private var lastActivityTime = 0L

    fun optimizeRadioUsage() {
        // Coalesce network requests
        scheduleRequestBatch()
        
        // Monitor radio state transitions
        monitorRadioState()
        
        // Implement intelligent wake-up scheduling
        schedulePeriodicTasks()
    }

    private fun scheduleRequestBatch() {
        val handler = Handler(Looper.getMainLooper())
        handler.postDelayed({
            if (hasPendingRequests()) {
                enterHighPowerMode()
                processPendingRequests()
                scheduleIdleTransition()
            }
        }, stateTransitionDelay)
    }

    private fun enterHighPowerMode() {
        if (!isHighPowerMode) {
            isHighPowerMode = true
            lastActivityTime = System.currentTimeMillis()
            
            // Configure for high-performance networking
            setNetworkPreferences(true)
        }
    }

    private fun enterIdleMode() {
        if (isHighPowerMode) {
            isHighPowerMode = false
            
            // Configure for power-efficient networking
            setNetworkPreferences(false)
        }
    }

    private fun setNetworkPreferences(highPerformance: Boolean) {
        val builder = NetworkRequest.Builder()
        
        if (highPerformance) {
            builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
        } else {
            builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING)
        }
        
        val networkRequest = builder.build()
        connectivityManager.requestNetwork(networkRequest, networkCallback)
    }

    private fun scheduleIdleTransition() {
        val handler = Handler(Looper.getMainLooper())
        handler.postDelayed({
            if (System.currentTimeMillis() - lastActivityTime > stateTransitionDelay) {
                enterIdleMode()
            }
        }, stateTransitionDelay)
    }

    private val networkCallback = object : ConnectivityManager.NetworkCallback() {
        override fun onAvailable(network: Network) {
            // Network is available for use
        }

        override fun onLost(network: Network) {
            // Handle network loss
        }
    }

    private fun monitorRadioState() {
        telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_DATA_ACTIVITY)
    }

    private val phoneStateListener = object : PhoneStateListener() {
        override fun onDataActivity(direction: Int) {
            when (direction) {
                TelephonyManager.DATA_ACTIVITY_IN,
                TelephonyManager.DATA_ACTIVITY_OUT,
                TelephonyManager.DATA_ACTIVITY_INOUT -> {
                    lastActivityTime = System.currentTimeMillis()
                }
                TelephonyManager.DATA_ACTIVITY_NONE -> {
                    // No data activity
                }
            }
        }
    }

    private fun hasPendingRequests(): Boolean {
        // Check if there are pending network requests
        return NetworkRequestQueue.instance.hasPendingRequests()
    }

    private fun processPendingRequests() {
        NetworkRequestQueue.instance.processAll()
    }
}
swift
// iOS - Swift
class RadioStateManager {
    private var isHighPowerMode = false
    private let stateTransitionDelay: TimeInterval = 2.0
    private var lastActivityTime: TimeInterval = 0
    private let requestQueue = NetworkRequestQueue()
    
    func optimizeRadioUsage() {
        // Coalesce network requests
        scheduleRequestBatch()
        
        // Monitor radio state
        monitorRadioState()
        
        // Schedule periodic tasks efficiently
        schedulePeriodicTasks()
    }
    
    private func scheduleRequestBatch() {
        DispatchQueue.main.asyncAfter(deadline: .now() + stateTransitionDelay) {
            if self.requestQueue.hasPendingRequests() {
                self.enterHighPowerMode()
                self.requestQueue.processAll()
                self.scheduleIdleTransition()
            }
        }
    }
    
    private func enterHighPowerMode() {
        guard !isHighPowerMode else { return }
        
        isHighPowerMode = true
        lastActivityTime = Date().timeIntervalSince1970
        
        // Configure URLSession for high performance
        configureURLSession(highPerformance: true)
    }
    
    private func enterIdleMode() {
        guard isHighPowerMode else { return }
        
        isHighPowerMode = false
        
        // Configure URLSession for power efficiency
        configureURLSession(highPerformance: false)
    }
    
    private func configureURLSession(highPerformance: Bool) {
        let configuration = URLSessionConfiguration.default
        
        if highPerformance {
            configuration.timeoutIntervalForRequest = 30
            configuration.timeoutIntervalForResource = 60
            configuration.httpMaximumConnectionsPerHost = 6
        } else {
            configuration.timeoutIntervalForRequest = 60
            configuration.timeoutIntervalForResource = 300
            configuration.httpMaximumConnectionsPerHost = 2
        }
        
        configuration.allowsCellularAccess = true
        configuration.waitsForConnectivity = !highPerformance
    }
    
    private func scheduleIdleTransition() {
        DispatchQueue.main.asyncAfter(deadline: .now() + stateTransitionDelay) {
            let currentTime = Date().timeIntervalSince1970
            if currentTime - self.lastActivityTime > self.stateTransitionDelay {
                self.enterIdleMode()
            }
        }
    }
    
    private func monitorRadioState() {
        // Monitor network reachability
        let monitor = NWPathMonitor()
        let queue = DispatchQueue(label: "NetworkMonitor")
        
        monitor.pathUpdateHandler = { [weak self] path in
            if path.status == .satisfied {
                self?.lastActivityTime = Date().timeIntervalSince1970
            }
        }
        
        monitor.start(queue: queue)
    }
    
    private func schedulePeriodicTasks() {
        // Use background app refresh efficiently
        let identifier = "com.app.network-sync"
        
        let request = BGAppRefreshTaskRequest(identifier: identifier)
        request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) // 15 minutes
        
        try? BGTaskScheduler.shared.submit(request)
    }
}

class NetworkRequestQueue {
    private var pendingRequests: [NetworkRequest] = []
    private let queue = DispatchQueue(label: "NetworkRequestQueue")
    
    func addRequest(_ request: NetworkRequest) {
        queue.async {
            self.pendingRequests.append(request)
        }
    }
    
    func hasPendingRequests() -> Bool {
        return queue.sync {
            return !pendingRequests.isEmpty
        }
    }
    
    func processAll() {
        queue.async {
            let requests = self.pendingRequests
            self.pendingRequests.removeAll()
            
            // Process requests in batch
            self.processBatch(requests)
        }
    }
    
    private func processBatch(_ requests: [NetworkRequest]) {
        // Group similar requests
        let groupedRequests = Dictionary(grouping: requests) { request in
            return "\(request.method)-\(request.baseURL.host ?? "")"
        }
        
        // Process each group
        groupedRequests.values.forEach { group in
            if group.count == 1 {
                self.processSingleRequest(group[0])
            } else {
                self.processBatchRequest(group)
            }
        }
    }
    
    private func processSingleRequest(_ request: NetworkRequest) {
        // Process individual request
        URLSession.shared.dataTask(with: request.urlRequest) { data, response, error in
            request.completion?(data, response, error)
        }.resume()
    }
    
    private func processBatchRequest(_ requests: [NetworkRequest]) {
        // Process batch request
        // Implementation depends on server-side batch API
    }
}

struct NetworkRequest {
    let method: String
    let baseURL: URL
    let path: String
    let parameters: [String: Any]?
    let completion: ((Data?, URLResponse?, Error?) -> Void)?
    
    var urlRequest: URLRequest {
        let url = baseURL.appendingPathComponent(path)
        var request = URLRequest(url: url)
        request.httpMethod = method
        
        if let parameters = parameters {
            request.httpBody = try? JSONSerialization.data(withJSONObject: parameters)
            request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        }
        
        return request
    }
}

Background Sync

Efficient background synchronization minimizes battery drain.

dart
// Flutter - Dart
class BackgroundSyncManager {
  static const String _taskName = 'background_sync';
  final SharedPreferences _prefs;
  final List<SyncTask> _pendingTasks = [];
  Timer? _syncTimer;
  
  BackgroundSyncManager(this._prefs);
  
  void initialize() {
    // Register background task
    Workmanager().initialize(callbackDispatcher);
    
    // Schedule periodic sync
    schedulePeriodicSync();
    
    // Handle app lifecycle changes
    _handleAppLifecycle();
  }
  
  void schedulePeriodicSync() {
    Workmanager().registerPeriodicTask(
      _taskName,
      _taskName,
      frequency: const Duration(minutes: 15),
      constraints: Constraints(
        networkType: NetworkType.connected,
        requiresBatteryNotLow: true,
      ),
    );
  }
  
  void addSyncTask(SyncTask task) {
    _pendingTasks.add(task);
    _persistPendingTasks();
    
    // If app is active, sync immediately
    if (WidgetsBinding.instance.lifecycleState == AppLifecycleState.resumed) {
      _syncImmediately();
    }
  }
  
  void _syncImmediately() {
    _syncTimer?.cancel();
    _syncTimer = Timer(const Duration(milliseconds: 500), () {
      _performSync();
    });
  }
  
  Future<void> _performSync() async {
    if (_pendingTasks.isEmpty) return;
    
    final tasks = List<SyncTask>.from(_pendingTasks);
    _pendingTasks.clear();
    
    // Group tasks by priority and type
    final groupedTasks = _groupTasks(tasks);
    
    // Process each group
    for (final group in groupedTasks) {
      await _processSyncGroup(group);
    }
    
    _persistPendingTasks();
  }
  
  List<List<SyncTask>> _groupTasks(List<SyncTask> tasks) {
    // Sort by priority
    tasks.sort((a, b) => b.priority.compareTo(a.priority));
    
    final groups = <List<SyncTask>>[];
    var currentGroup = <SyncTask>[];
    String? currentType;
    
    for (final task in tasks) {
      if (currentType != task.type || currentGroup.length >= 10) {
        if (currentGroup.isNotEmpty) {
          groups.add(currentGroup);
        }
        currentGroup = <SyncTask>[task];
        currentType = task.type;
      } else {
        currentGroup.add(task);
      }
    }
    
    if (currentGroup.isNotEmpty) {
      groups.add(currentGroup);
    }
    
    return groups;
  }
  
  Future<void> _processSyncGroup(List<SyncTask> group) async {
    if (group.isEmpty) return;
    
    try {
      if (group.length == 1) {
        await _processSingleTask(group.first);
      } else {
        await _processBatchTasks(group);
      }
    } catch (e) {
      // Handle sync errors
      _handleSyncError(group, e);
    }
  }
  
  Future<void> _processSingleTask(SyncTask task) async {
    final dio = Dio();
    
    final response = await dio.request(
      task.endpoint,
      data: task.data,
      options: Options(method: task.method),
    );
    
    if (response.statusCode == 200) {
      // Task completed successfully
      _onTaskCompleted(task);
    } else {
      throw DioError(
        requestOptions: response.requestOptions,
        response: response,
      );
    }
  }
  
  Future<void> _processBatchTasks(List<SyncTask> tasks) async {
    final dio = Dio();
    
    final batchData = {
      'tasks': tasks.map((task) => {
        'id': task.id,
        'type': task.type,
        'data': task.data,
        'endpoint': task.endpoint,
        'method': task.method,
      }).toList(),
    };
    
    final response = await dio.post('/api/batch-sync', data: batchData);
    
    if (response.statusCode == 200) {
      final results = response.data['results'] as List;
      
      for (final result in results) {
        final taskId = result['id'];
        final success = result['success'] == true;
        final task = tasks.firstWhere((t) => t.id == taskId);
        
        if (success) {
          _onTaskCompleted(task);
        } else {
          _onTaskFailed(task, result['error']);
        }
      }
    }
  }
  
  void _handleSyncError(List<SyncTask> tasks, dynamic error) {
    for (final task in tasks) {
      if (task.retryCount < task.maxRetries) {
        task.retryCount++;
        task.nextRetryTime = DateTime.now().add(
          Duration(seconds: math.pow(2, task.retryCount).toInt() * 30)
        );
        _pendingTasks.add(task);
      } else {
        _onTaskFailed(task, error);
      }
    }
  }
  
  void _onTaskCompleted(SyncTask task) {
    // Notify listeners or update UI
    print('Task completed: ${task.id}');
  }
  
  void _onTaskFailed(SyncTask task, dynamic error) {
    // Handle permanent task failure
    print('Task failed permanently: ${task.id} - $error');
  }
  
  void _persistPendingTasks() {
    final tasksJson = _pendingTasks.map((task) => task.toJson()).toList();
    _prefs.setString('pending_sync_tasks', jsonEncode(tasksJson));
  }
  
  void _loadPendingTasks() {
    final tasksJson = _prefs.getString('pending_sync_tasks');
    if (tasksJson != null) {
      final tasksList = jsonDecode(tasksJson) as List;
      _pendingTasks.addAll(
        tasksList.map((json) => SyncTask.fromJson(json))
      );
    }
  }
  
  void _handleAppLifecycle() {
    WidgetsBinding.instance.didChangeAppLifecycleState = (state) {
      switch (state) {
        case AppLifecycleState.paused:
          // App going to background, prepare for background sync
          _persistPendingTasks();
          break;
        case AppLifecycleState.resumed:
          // App returning to foreground, sync immediately if needed
          _loadPendingTasks();
          if (_pendingTasks.isNotEmpty) {
            _syncImmediately();
          }
          break;
        default:
          break;
      }
    };
  }
}

class SyncTask {
  final String id;
  final String type;
  final String endpoint;
  final String method;
  final Map<String, dynamic> data;
  final int priority;
  final int maxRetries;
  int retryCount;
  DateTime? nextRetryTime;
  
  SyncTask({
    required this.id,
    required this.type,
    required this.endpoint,
    required this.method,
    required this.data,
    this.priority = 0,
    this.maxRetries = 3,
    this.retryCount = 0,
    this.nextRetryTime,
  });
  
  Map<String, dynamic> toJson() => {
    'id': id,
    'type': type,
    'endpoint': endpoint,
    'method': method,
    'data': data,
    'priority': priority,
    'maxRetries': maxRetries,
    'retryCount': retryCount,
    'nextRetryTime': nextRetryTime?.toIso8601String(),
  };
  
  static SyncTask fromJson(Map<String, dynamic> json) => SyncTask(
    id: json['id'],
    type: json['type'],
    endpoint: json['endpoint'],
    method: json['method'],
    data: json['data'],
    priority: json['priority'] ?? 0,
    maxRetries: json['maxRetries'] ?? 3,
    retryCount: json['retryCount'] ?? 0,
    nextRetryTime: json['nextRetryTime'] != null 
        ? DateTime.parse(json['nextRetryTime'])
        : null,
  );
}

void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) async {
    final syncManager = BackgroundSyncManager(
      await SharedPreferences.getInstance()
    );
    
    // Perform background sync
    await syncManager._performSync();
    
    return Future.value(true);
  });
}

Offline Support

Data Synchronization

Robust offline capabilities are essential for mobile applications.

javascript
class OfflineDataManager {
  constructor() {
    this.db = null;
    this.syncQueue = [];
    this.isOnline = navigator.onLine;
    this.initializeDatabase();
    this.setupNetworkListeners();
  }

  async initializeDatabase() {
    return new Promise((resolve, reject) => {
      const request = indexedDB.open('OfflineDB', 1);
      
      request.onerror = () => reject(request.error);
      request.onsuccess = () => {
        this.db = request.result;
        resolve();
      };
      
      request.onupgradeneeded = (event) => {
        const db = event.target.result;
        
        // Create stores
        if (!db.objectStoreNames.contains('data')) {
          const dataStore = db.createObjectStore('data', { keyPath: 'id' });
          dataStore.createIndex('timestamp', 'timestamp');
          dataStore.createIndex('type', 'type');
        }
        
        if (!db.objectStoreNames.contains('sync_queue')) {
          const syncStore = db.createObjectStore('sync_queue', { keyPath: 'id' });
          syncStore.createIndex('priority', 'priority');
          syncStore.createIndex('timestamp', 'timestamp');
        }
        
        if (!db.objectStoreNames.contains('metadata')) {
          db.createObjectStore('metadata', { keyPath: 'key' });
        }
      };
    });
  }

  setupNetworkListeners() {
    window.addEventListener('online', () => {
      this.isOnline = true;
      this.processOfflineQueue();
    });
    
    window.addEventListener('offline', () => {
      this.isOnline = false;
    });
  }

  async saveData(data, options = {}) {
    const record = {
      id: data.id || this.generateId(),
      ...data,
      timestamp: Date.now(),
      synced: this.isOnline,
      offline: !this.isOnline
    };

    // Save to local database
    await this.storeLocally('data', record);

    if (this.isOnline) {
      // Try to sync immediately
      try {
        await this.syncToServer(record);
        record.synced = true;
        await this.storeLocally('data', record);
      } catch (error) {
        // Add to sync queue for later
        await this.addToSyncQueue(record, 'CREATE');
      }
    } else {
      // Add to sync queue
      await this.addToSyncQueue(record, 'CREATE');
    }

    return record;
  }

  async getData(id) {
    return this.getFromStore('data', id);
  }

  async getAllData(options = {}) {
    const { type, limit, offset } = options;
    
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction(['data'], 'readonly');
      const store = transaction.objectStore('data');
      
      let request;
      if (type) {
        const index = store.index('type');
        request = index.getAll(type);
      } else {
        request = store.getAll();
      }
      
      request.onsuccess = () => {
        let results = request.result;
        
        // Apply pagination
        if (offset) results = results.slice(offset);
        if (limit) results = results.slice(0, limit);
        
        resolve(results);
      };
      
      request.onerror = () => reject(request.error);
    });
  }

  async updateData(id, updates) {
    const existing = await this.getData(id);
    if (!existing) throw new Error('Record not found');

    const updated = {
      ...existing,
      ...updates,
      timestamp: Date.now(),
      synced: false
    };

    await this.storeLocally('data', updated);

    if (this.isOnline) {
      try {
        await this.syncUpdateToServer(updated);
        updated.synced = true;
        await this.storeLocally('data', updated);
      } catch (error) {
        await this.addToSyncQueue(updated, 'UPDATE');
      }
    } else {
      await this.addToSyncQueue(updated, 'UPDATE');
    }

    return updated;
  }

  async deleteData(id) {
    const existing = await this.getData(id);
    if (!existing) return;

    // Mark as deleted locally
    const deleted = {
      ...existing,
      deleted: true,
      timestamp: Date.now(),
      synced: false
    };

    await this.storeLocally('data', deleted);

    if (this.isOnline) {
      try {
        await this.syncDeleteToServer(id);
        // Remove from local store after successful sync
        await this.removeFromStore('data', id);
      } catch (error) {
        await this.addToSyncQueue(deleted, 'DELETE');
      }
    } else {
      await this.addToSyncQueue(deleted, 'DELETE');
    }
  }

  async addToSyncQueue(record, operation) {
    const queueItem = {
      id: this.generateId(),
      recordId: record.id,
      operation,
      data: record,
      timestamp: Date.now(),
      retries: 0,
      priority: this.getSyncPriority(operation)
    };

    await this.storeLocally('sync_queue', queueItem);
  }

  getSyncPriority(operation) {
    switch (operation) {
      case 'DELETE': return 3;
      case 'UPDATE': return 2;
      case 'CREATE': return 1;
      default: return 0;
    }
  }

  async processOfflineQueue() {
    if (!this.isOnline) return;

    const queueItems = await this.getAllFromStore('sync_queue');
    
    // Sort by priority and timestamp
    queueItems.sort((a, b) => {
      if (a.priority !== b.priority) return b.priority - a.priority;
      return a.timestamp - b.timestamp;
    });

    for (const item of queueItems) {
      try {
        await this.processSyncItem(item);
        await this.removeFromStore('sync_queue', item.id);
      } catch (error) {
        console.error('Sync failed for item:', item.id, error);
        
        // Increment retry count
        item.retries++;
        if (item.retries < 5) {
          item.timestamp = Date.now() + (item.retries * 30000); // Exponential backoff
          await this.storeLocally('sync_queue', item);
        } else {
          // Max retries reached, handle permanent failure
          await this.handleSyncFailure(item);
          await this.removeFromStore('sync_queue', item.id);
        }
      }
    }
  }

  async processSyncItem(item) {
    switch (item.operation) {
      case 'CREATE':
        await this.syncToServer(item.data);
        break;
      case 'UPDATE':
        await this.syncUpdateToServer(item.data);
        break;
      case 'DELETE':
        await this.syncDeleteToServer(item.recordId);
        await this.removeFromStore('data', item.recordId);
        break;
    }

    // Mark as synced
    if (item.operation !== 'DELETE') {
      const record = await this.getData(item.recordId);
      if (record) {
        record.synced = true;
        await this.storeLocally('data', record);
      }
    }
  }

  async syncToServer(record) {
    const response = await fetch('/api/data', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(record)
    });

    if (!response.ok) {
      throw new Error(`Sync failed: ${response.statusText}`);
    }

    return response.json();
  }

  async syncUpdateToServer(record) {
    const response = await fetch(`/api/data/${record.id}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(record)
    });

    if (!response.ok) {
      throw new Error(`Update sync failed: ${response.statusText}`);
    }

    return response.json();
  }

  async syncDeleteToServer(recordId) {
    const response = await fetch(`/api/data/${recordId}`, {
      method: 'DELETE'
    });

    if (!response.ok) {
      throw new Error(`Delete sync failed: ${response.statusText}`);
    }
  }

  async handleSyncFailure(item) {
    console.error('Permanent sync failure for item:', item);
    // Could notify user, log to analytics, etc.
  }

  // Utility methods
  async storeLocally(storeName, data) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([storeName], 'readwrite');
      const store = transaction.objectStore(storeName);
      const request = store.put(data);
      
      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }

  async getFromStore(storeName, id) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([storeName], 'readonly');
      const store = transaction.objectStore(storeName);
      const request = store.get(id);
      
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async getAllFromStore(storeName) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([storeName], 'readonly');
      const store = transaction.objectStore(storeName);
      const request = store.getAll();
      
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async removeFromStore(storeName, id) {
    return new Promise((resolve, reject) => {
      const transaction = this.db.transaction([storeName], 'readwrite');
      const store = transaction.objectStore(storeName);
      const request = store.delete(id);
      
      request.onsuccess = () => resolve();
      request.onerror = () => reject(request.error);
    });
  }

  generateId() {
    return Date.now().toString(36) + Math.random().toString(36).substr(2);
  }
}

Cross-Platform Networking

Universal HTTP Client

A consistent networking interface across all platforms.

javascript
class UniversalHttpClient {
  constructor(config = {}) {
    this.baseURL = config.baseURL || '';
    this.timeout = config.timeout || 30000;
    this.headers = config.headers || {};
    this.interceptors = {
      request: [],
      response: []
    };
    
    // Platform detection
    this.platform = this.detectPlatform();
    this.adapter = this.createAdapter();
  }

  detectPlatform() {
    if (typeof window !== 'undefined') {
      return 'web';
    } else if (typeof global !== 'undefined' && global.process) {
      return 'node';
    } else if (typeof self !== 'undefined') {
      return 'worker';
    }
    return 'unknown';
  }

  createAdapter() {
    switch (this.platform) {
      case 'web':
        return new WebNetworkAdapter();
      case 'node':
        return new NodeNetworkAdapter();
      default:
        return new NativeNetworkAdapter();
    }
  }

  async request(config) {
    // Apply request interceptors
    let finalConfig = { ...config };
    for (const interceptor of this.interceptors.request) {
      finalConfig = await interceptor(finalConfig);
    }

    // Merge with instance config
    finalConfig = {
      ...finalConfig,
      url: this.resolveURL(finalConfig.url),
      headers: { ...this.headers, ...finalConfig.headers },
      timeout: finalConfig.timeout || this.timeout
    };

    try {
      let response = await this.adapter.request(finalConfig);
      
      // Apply response interceptors
      for (const interceptor of this.interceptors.response) {
        response = await interceptor(response);
      }
      
      return response;
    } catch (error) {
      // Apply error interceptors
      for (const interceptor of this.interceptors.response) {
        if (interceptor.length > 1) {
          error = await interceptor(error);
        }
      }
      throw error;
    }
  }

  resolveURL(url) {
    if (url.startsWith('http')) return url;
    return this.baseURL.replace(/\/$/, '') + '/' + url.replace(/^\//, '');
  }

  // Convenience methods
  async get(url, config = {}) {
    return this.request({ ...config, url, method: 'GET' });
  }

  async post(url, data, config = {}) {
    return this.request({ ...config, url, method: 'POST', data });
  }

  async put(url, data, config = {}) {
    return this.request({ ...config, url, method: 'PUT', data });
  }

  async delete(url, config = {}) {
    return this.request({ ...config, url, method: 'DELETE' });
  }
}

// Platform-specific adapters
class WebNetworkAdapter {
  async request(config) {
    const response = await fetch(config.url, {
      method: config.method,
      headers: config.headers,
      body: config.data ? JSON.stringify(config.data) : undefined
    });
    
    return this.normalizeResponse(response);
  }

  async normalizeResponse(response) {
    return {
      status: response.status,
      statusText: response.statusText,
      headers: Object.fromEntries(response.headers.entries()),
      data: await response.json()
    };
  }
}

class NativeNetworkAdapter {
  async request(config) {
    // React Native or native platform implementation
    const response = await fetch(config.url, {
      method: config.method,
      headers: config.headers,
      body: config.data ? JSON.stringify(config.data) : undefined
    });
    
    return this.normalizeResponse(response);
  }

  async normalizeResponse(response) {
    return {
      status: response.status,
      statusText: response.statusText,
      headers: response.headers,
      data: await response.json()
    };
  }
}

These mobile-specific considerations are critical for optimizing network performance and user experience in modern mobile applications. Battery optimization, offline support, and cross-platform compatibility are fundamental requirements for a successful mobile application.

Created by Eren Demir.