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.