mobile-skills
Master iOS development with Swift, Android development with Kotlin, React Native, Flutter, and cross-platform mobile technologies.
What this skill does
# Mobile Development Skills
## iOS Development (Swift)
```swift
import SwiftUI
struct ContentView: View {
@State private var count = 0
@State private var isLoading = false
@State private var error: Error?
var body: some View {
VStack {
if isLoading {
ProgressView()
} else if let error = error {
ErrorView(error: error, retry: loadData)
} else {
Text("Count: \(count)")
.font(.headline)
Button(action: { count += 1 }) {
Text("Increment")
}
}
}
.task { await loadData() }
}
func loadData() async {
isLoading = true
defer { isLoading = false }
do {
// Fetch data
} catch {
self.error = error
}
}
}
// MVVM Pattern with Error Handling
@MainActor
class ViewModel: ObservableObject {
@Published var items: [Item] = []
@Published var error: Error?
@Published var isLoading = false
func fetchItems() async {
isLoading = true
defer { isLoading = false }
do {
items = try await APIService.getItems()
} catch {
self.error = error
}
}
}
```
## Android Development (Kotlin)
```kotlin
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun CounterApp(viewModel: CounterViewModel = viewModel()) {
val uiState by viewModel.uiState.collectAsState()
Scaffold(
topBar = { TopAppBar(title = { Text("Counter") }) }
) { padding ->
Box(modifier = Modifier.padding(padding)) {
when (val state = uiState) {
is UiState.Loading -> CircularProgressIndicator()
is UiState.Error -> ErrorMessage(state.message)
is UiState.Success -> {
Button(onClick = { viewModel.increment() }) {
Text("Count: ${state.count}")
}
}
}
}
}
}
// ViewModel with sealed class state
class CounterViewModel : ViewModel() {
private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
sealed class UiState {
object Loading : UiState()
data class Success(val count: Int) : UiState()
data class Error(val message: String) : UiState()
}
}
```
## React Native with Error Boundaries
```javascript
import React, { useState, useEffect, useCallback } from 'react';
import { View, Text, Button, ActivityIndicator } from 'react-native';
export default function App() {
const [count, setCount] = useState(0);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const fetchData = useCallback(async () => {
setLoading(true);
setError(null);
try {
const response = await fetch('https://api.example.com/data');
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
setCount(data.count);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}, []);
useEffect(() => {
fetchData();
}, [fetchData]);
if (loading) return <ActivityIndicator />;
if (error) return <ErrorView message={error} onRetry={fetchData} />;
return (
<View style={{ padding: 20 }}>
<Text style={{ fontSize: 20 }}>Count: {count}</Text>
<Button title="Increment" onPress={() => setCount(c => c + 1)} />
</View>
);
}
```
## Flutter with State Management
```dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
void main() => runApp(
ChangeNotifierProvider(
create: (_) => CounterProvider(),
child: MyApp(),
),
);
class CounterProvider extends ChangeNotifier {
int _count = 0;
bool _loading = false;
String? _error;
int get count => _count;
bool get loading => _loading;
String? get error => _error;
Future<void> increment() async {
_loading = true;
_error = null;
notifyListeners();
try {
await Future.delayed(Duration(milliseconds: 100));
_count++;
} catch (e) {
_error = e.toString();
} finally {
_loading = false;
notifyListeners();
}
}
}
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<CounterProvider>(
builder: (context, provider, child) {
if (provider.loading) return CircularProgressIndicator();
if (provider.error != null) return ErrorWidget(provider.error!);
return ElevatedButton(
onPressed: () => provider.increment(),
child: Text('Count: ${provider.count}'),
);
},
);
}
}
```
## State Management Comparison
| Framework | Solutions | Best For |
|-----------|-----------|----------|
| React Native | Redux, Zustand, Jotai | Complex global state |
| Flutter | Provider, Riverpod, Bloc | Reactive updates |
| iOS | SwiftUI @State, Combine | Declarative UI |
| Android | ViewModel, StateFlow | Lifecycle-aware |
## Mobile Best Practices
- **Performance**: Optimize bundle size, lazy loading
- **Battery**: Minimize background operations
- **Offline**: Support offline-first sync
- **Accessibility**: VoiceOver/TalkBack support
- **Security**: Keychain/Keystore for secrets
- **Testing**: Unit, widget, and integration tests
## Unit Test Template
```javascript
// React Native Test
import { render, fireEvent, waitFor } from '@testing-library/react-native';
describe('Counter', () => {
test('increments on press', async () => {
const { getByText } = render(<Counter />);
fireEvent.press(getByText('Increment'));
await waitFor(() => {
expect(getByText('Count: 1')).toBeTruthy();
});
});
});
```
```swift
// Swift Test
import XCTest
@testable import MyApp
class CounterTests: XCTestCase {
func testIncrement() async {
let viewModel = ViewModel()
await viewModel.increment()
XCTAssertEqual(viewModel.count, 1)
}
}
```
## Troubleshooting Guide
| Symptom | Platform | Solution |
|---------|----------|----------|
| EXC_BAD_ACCESS | iOS | Check retain cycles |
| ANR | Android | Move to background thread |
| Red Screen | React Native | Check JS error |
| RenderFlex overflow | Flutter | Use Expanded/Flexible |
## Key Concepts Checklist
- [ ] SwiftUI/Compose UI basics
- [ ] State management patterns
- [ ] Navigation stacks
- [ ] Network requests with retry
- [ ] Local storage (preferences, databases)
- [ ] Async operations
- [ ] Error handling
- [ ] Loading states
- [ ] Testing strategies
- [ ] Performance optimization
- [ ] App store requirements
- [ ] Code signing
- [ ] Debugging tools
---
**Source**: https://roadmap.sh
**Version**: 2.0.0
**Last Updated**: 2025-01-01
Related in Web Dev
generating-lwc-components
IncludedLightning Web Components with PICKLES methodology and 165-point scoring. Use this skill when the user creates or edits LWC components, builds wire service patterns, or writes Jest tests for LWC. TRIGGER when: user creates/edits LWC components, touches lwc/**/*.js, .html, .css, .js-meta.xml files, or asks about wire service, SLDS, or Jest LWC tests. DO NOT TRIGGER when: Apex classes (use generating-apex), Aura components, or Visualforce.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Set up queries with useQuery, mutations with useMutation, configure QueryClient caching strategies, implement optimistic updates, and handle infinite scroll with useInfiniteQuery. Use when: setting up data fetching in React projects, migrating from v4 to v5, or fixing object syntax required errors, query callbacks removed issues, cacheTime renamed to gcTime, isPending vs isLoading confusion, keepPreviousData removed problems.
document-processor-api
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
nutrient-document-processing
IncludedProcess documents with Nutrient DWS. Use when the user wants to generate PDFs from HTML or URLs, convert Office/images/PDFs, assemble or split packets, OCR scans, extract text/tables/key-value pairs, redact PII, watermark, sign, fill forms, optimize PDFs, or produce compliance outputs like PDF/A or PDF/UA. Triggers include convert to PDF, merge these PDFs, OCR this scan, extract tables, redact PII, sign this PDF, make this PDF/A, or linearize for web delivery.
tanstack-query
IncludedManage server state in React with TanStack Query v5. Covers useMutationState, simplified optimistic updates, throwOnError, network mode (offline/PWA), and infiniteQueryOptions. Use when setting up data fetching, fixing v4→v5 migration errors (object syntax, gcTime, isPending, keepPreviousData), or debugging SSR/hydration issues with streaming server components.
accelint-nextjs-best-practices
IncludedNext.js performance optimization and best practices. Use when writing Next.js code (App Router or Pages Router); implementing Server Components, Server Actions, or API routes; optimizing RSC serialization, data fetching, or server-side rendering; reviewing Next.js code for performance issues; fixing authentication in Server Actions; or implementing Suspense boundaries, parallel data fetching, or request deduplication.