home / skills / louloulin / claude-agent-sdk / mobile-developer
/crates/claude-agent-sdk/examples/.claude/skills/mobile-developer
This skill helps you design, compare, and implement high-performance native and cross-platform mobile solutions for iOS, Android, and beyond.
npx playbooks add skill louloulin/claude-agent-sdk --skill mobile-developerReview the files below or copy the command above to add this skill to your agents.
---
name: mobile-developer
description: "移动开发专家,精通 iOS、Android 和跨平台移动应用开发"
version: "1.4.0"
author: "Mobile Team <[email protected]>"
tags:
- mobile
- ios
- android
- react-native
- flutter
- cross-platform
dependencies:
- frontend-developer
- performance-optimizer
---
# 移动开发专家
你是移动开发专家,专精于 iOS、Android 和跨平台移动应用开发。帮助构建高性能、用户体验优秀的移动应用。
## 移动开发框架对比
### 原生开发 vs 跨平台
```
原生开发:
iOS (Swift):
优势:
✅ 最佳性能
✅ 完整的 API 访问
✅ 最新的 iOS 功能
✅ 优秀的开发体验
劣势:
❌ 只支持 iOS
❌ 学习曲线陡峭
❌ 开发成本高
Android (Kotlin):
优势:
✅ 最佳性能
✅ 完整的 API 访问
✅ 灵活的定制性
✅ 谷歌生态支持
劣势:
❌ 只支持 Android
❌ 碎片化问题
❌ 开发成本高
跨平台开发:
React Native:
优势:
✅ JavaScript/TypeScript
✅ 热重载
✅ 大型社区
✅ 接近原生性能
劣势:
❌ 需要原生桥接
❌ 包体积较大
❌ 性能略逊于原生
Flutter:
优势:
✅ Dart 语言
✅ 高性能渲染
✅ 美观的 UI
✅ 一套代码多平台
劣势:
❌ 相对较新
❌ 包体积大
❌ 生态不如 RN 成熟
Ionic:
优势:
✅ Web 技术
✅ 易学易用
✅ 多种 UI 组件
劣势:
❌ 性能最差
❌ 依赖 WebView
```
## iOS 开发 (Swift)
### 现代 Swift 开发
```swift
import UIKit
import Combine
import SwiftUI
// MARK: - 数据模型
struct User: Identifiable, Codable {
let id: String
let email: String
let fullName: String
let avatarURL: URL?
var isActive: Bool
enum CodingKeys: String, CodingKey {
case id, email, isActive
case fullName = "full_name"
case avatarURL = "avatar_url"
}
}
// MARK: - ViewModel (MVVM 架构)
@MainActor
class UserViewModel: ObservableObject {
@Published var users: [User] = []
@Published var isLoading = false
@Published var errorMessage: String?
private let apiService: APIService
private var cancellables = Set<AnyCancellable>()
init(apiService: APIService = .shared) {
self.apiService = apiService
}
func fetchUsers() async {
isLoading = true
errorMessage = nil
do {
let fetchedUsers = try await apiService.fetchUsers()
users = fetchedUsers
} catch {
errorMessage = "获取用户失败: \(error.localizedDescription)"
print("错误: \(error)")
}
isLoading = false
}
func toggleUserStatus(user: User) {
if let index = users.firstIndex(where: { $0.id == user.id }) {
users[index].isActive.toggle()
}
}
}
// MARK: - SwiftUI 视图
struct UserListView: View {
@StateObject private var viewModel = UserViewModel()
var body: some View {
NavigationView {
ZStack {
if viewModel.isLoading && viewModel.users.isEmpty {
ProgressView("加载中...")
} else if viewModel.users.isEmpty {
emptyStateView
} else {
userListContent
}
}
.navigationTitle("用户列表")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: refreshUsers) {
Image(systemName: "arrow.clockwise")
}
}
}
.alert(item: $viewModel.errorMessage) { errorMessage in
Alert(
title: Text("错误"),
message: Text(errorMessage),
dismissButton: .default(Text("确定"))
)
}
.task {
await viewModel.fetchUsers()
}
}
}
private var userListContent: some View {
List(viewModel.users) { user in
UserRowView(user: user)
.contentShape(Rectangle())
.onTapGesture {
// 导航到详情页
}
}
.refreshable {
await viewModel.fetchUsers()
}
.overlay {
if viewModel.isLoading {
ProgressView()
.scaleEffect(1.5)
.padding()
}
}
}
private var emptyStateView: some View {
VStack(spacing: 20) {
Image(systemName: "person.3")
.font(.system(size: 60))
.foregroundColor(.gray)
Text("暂无用户")
.font(.headline)
.foregroundColor(.gray)
Text("下拉刷新或稍后再试")
.font(.subheadline)
.foregroundColor(.secondary)
}
}
private func refreshUsers() {
Task {
await viewModel.fetchUsers()
}
}
}
// MARK: - 用户行视图
struct UserRowView: View {
let user: User
var body: some View {
HStack(spacing: 15) {
// 头像
AsyncImage(url: user.avatarURL) { image in
image
.resizable()
.aspectRatio(contentMode: .fill)
} placeholder: {
ProgressView()
}
.frame(width: 50, height: 50)
.clipShape(Circle())
// 用户信息
VStack(alignment: .leading, spacing: 5) {
Text(user.fullName)
.font(.headline)
.foregroundColor(.primary)
Text(user.email)
.font(.subheadline)
.foregroundColor(.secondary)
}
Spacer()
// 状态指示器
Circle()
.fill(user.isActive ? Color.green : Color.red)
.frame(width: 10, height: 10)
}
.padding(.vertical, 8)
}
}
// MARK: - API 服务
class APIService {
static let shared = APIService()
private let baseURL = "https://api.example.com"
private let session: URLSession
init() {
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 30
configuration.requestCachePolicy = .reloadIgnoringLocalCacheData
session = URLSession(configuration: configuration)
}
func fetchUsers() async throws -> [User] {
let url = URL(string: "\(baseURL)/users")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("Bearer \(TokenManager.shared.accessToken)", forHTTPHeaderField: "Authorization")
let (data, response) = try await session.data(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
throw APIError.invalidResponse
}
switch httpResponse.statusCode {
case 200...299:
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
return try decoder.decode([User].self, from: data)
case 401:
throw APIError.unauthorized
case 404:
throw APIError.notFound
default:
throw APIError.serverError(httpResponse.statusCode)
}
}
}
// MARK: - 错误定义
enum APIError: Error, LocalizedError {
case invalidResponse
case unauthorized
case notFound
case serverError(Int)
case networkError(Error)
var errorDescription: String? {
switch self {
case .invalidResponse:
return "无效的响应"
case .unauthorized:
return "未授权,请重新登录"
case .notFound:
return "资源不存在"
case .serverError(let code):
return "服务器错误 (\(code))"
case .networkError(let error):
return "网络错误: \(error.localizedDescription)"
}
}
}
// MARK: - App 入口
@main
struct MyApp: App {
@StateObject private var appState = AppState()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(appState)
.onAppear {
setupAppearance()
}
}
}
private func setupAppearance() {
// 配置全局外观
let navBarAppearance = UINavigationBarAppearance()
navBarAppearance.configureWithOpaqueBackground()
navBarAppearance.backgroundColor = .systemBackground
UINavigationBar.appearance().standardAppearance = navBarAppearance
UINavigationBar.appearance().scrollEdgeAppearance = navBarAppearance
}
}
// MARK: - 应用状态
class AppState: ObservableObject {
@Published var isAuthenticated = false
@Published var currentUser: User?
init() {
loadAuthenticationState()
}
private func loadAuthenticationState() {
isAuthenticated = TokenManager.shared.hasValidToken
}
func logout() {
TokenManager.shared.clearToken()
isAuthenticated = false
currentUser = nil
}
}
```
### UIKit 开发
```swift
import UIKit
// MARK: - 传统 UIKit 视图控制器
class UserListViewController: UIViewController {
private lazy var tableView: UITableView = {
let table = UITableView(frame: .zero, style: .plain)
table.delegate = self
table.dataSource = self
table.register(UserCell.self, forCellReuseIdentifier: UserCell.reuseIdentifier)
table.separatorStyle = .none
table.refreshControl = refreshControl
return table
}()
private lazy var refreshControl: UIRefreshControl = {
let control = UIRefreshControl()
control.addTarget(self, action: #selector(refreshData), for: .valueChanged)
return control
}()
private let viewModel = UserViewModel()
private var dataSource: [User] = []
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
setupBindings()
viewModel.fetchUsers()
}
private func setupUI() {
title = "用户列表"
view.backgroundColor = .systemBackground
view.addSubview(tableView)
tableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
navigationItem.rightBarButtonItem = UIBarButtonItem(
barButtonSystemItem: .refresh,
target: self,
action: #selector(refreshData)
)
}
private func setupBindings() {
viewModel.$users
.receive(on: DispatchQueue.main)
.sink { [weak self] users in
self?.dataSource = users
self?.tableView.reloadData()
self?.refreshControl.endRefreshing()
}
.store(in: &viewModel.cancellables)
}
@objc private func refreshData() {
viewModel.fetchUsers()
}
}
// MARK: - TableView 数据源
extension UserListViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
guard let cell = tableView.dequeueReusableCell(
withIdentifier: UserCell.reuseIdentifier,
for: indexPath
) as? UserCell else {
return UITableViewCell()
}
let user = dataSource[indexPath.row]
cell.configure(with: user)
return cell
}
}
// MARK: - TableView 代理
extension UserListViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let user = dataSource[indexPath.row]
let detailVC = UserDetailViewController(user: user)
navigationController?.pushViewController(detailVC, animated: true)
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 80
}
}
// MARK: - 自定义 Cell
class UserCell: UITableViewCell {
static let reuseIdentifier = "UserCell"
private let avatarImageView: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.clipsToBounds = true
imageView.layer.cornerRadius = 25
imageView.backgroundColor = .systemGray5
return imageView
}()
private let nameLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 16, weight: .semibold)
label.textColor = .label
return label
}()
private let emailLabel: UILabel = {
let label = UILabel()
label.font = .systemFont(ofSize: 14)
label.textColor = .secondaryLabel
return label
}()
private let statusIndicator: UIView = {
let view = UIView()
view.layer.cornerRadius = 5
view.backgroundColor = .systemGreen
return view
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setupUI() {
contentView.addSubview(avatarImageView)
contentView.addSubview(nameLabel)
contentView.addSubview(emailLabel)
contentView.addSubview(statusIndicator)
avatarImageView.translatesAutoresizingMaskIntoConstraints = false
nameLabel.translatesAutoresizingMaskIntoConstraints = false
emailLabel.translatesAutoresizingMaskIntoConstraints = false
statusIndicator.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
avatarImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
avatarImageView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
avatarImageView.widthAnchor.constraint(equalToConstant: 50),
avatarImageView.heightAnchor.constraint(equalToConstant: 50),
nameLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12),
nameLabel.leadingAnchor.constraint(equalTo: avatarImageView.trailingAnchor, constant: 12),
nameLabel.trailingAnchor.constraint(equalTo: statusIndicator.leadingAnchor, constant: -8),
emailLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor, constant: 4),
emailLabel.leadingAnchor.constraint(equalTo: nameLabel.leadingAnchor),
emailLabel.trailingAnchor.constraint(equalTo: nameLabel.trailingAnchor),
statusIndicator.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
statusIndicator.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
statusIndicator.widthAnchor.constraint(equalToConstant: 10),
statusIndicator.heightAnchor.constraint(equalToConstant: 10)
])
}
func configure(with user: User) {
nameLabel.text = user.fullName
emailLabel.text = user.email
statusIndicator.backgroundColor = user.isActive ? .systemGreen : .systemRed
if let avatarURL = user.avatarURL {
URLSession.shared.dataTask(with: avatarURL) { data, _, _ in
if let data = data, let image = UIImage(data: data) {
DispatchQueue.main.async {
self.avatarImageView.image = image
}
}
}.resume()
}
}
}
```
## Android 开发 (Kotlin)
### 现代 Kotlin 开发
```kotlin
// 数据模型
data class User(
val id: String,
val email: String,
val fullName: String,
@SerialName("avatar_url")
val avatarUrl: String?,
val isActive: Boolean
)
// Repository (仓库模式)
class UserRepository @Inject constructor(
private val apiService: ApiService,
private val userDao: UserDao
) {
fun getUsers(): Flow<Result<List<User>>> = flow {
try {
// 先从本地数据库加载
emit(Result.Loading)
// 从网络获取
val users = apiService.getUsers()
// 缓存到本地数据库
userDao.insertAll(users)
emit(Result.Success(users))
} catch (e: Exception) {
// 网络错误时从本地加载
val cachedUsers = userDao.getAll()
if (cachedUsers.isNotEmpty()) {
emit(Result.Success(cachedUsers))
} else {
emit(Result.Error(e))
}
}
}
suspend fun getUserById(id: String): User {
return apiService.getUserById(id)
}
}
// ViewModel
@HiltViewModel
class UserViewModel @Inject constructor(
private val repository: UserRepository
) : ViewModel() {
private val _uiState = MutableStateFlow<UiState<List<User>>>(UiState.Loading)
val uiState: StateFlow<UiState<List<User>>> = _uiState.asStateFlow()
init {
loadUsers()
}
fun loadUsers() {
viewModelScope.launch {
repository.getUsers()
.collect { result ->
when (result) {
is Result.Success -> {
_uiState.value = UiState.Success(result.data)
}
is Result.Error -> {
_uiState.value = UiState.Error(
result.exception.message ?: "未知错误"
)
}
is Result.Loading -> {
_uiState.value = UiState.Loading
}
}
}
}
}
fun toggleUserStatus(user: User) {
viewModelScope.launch {
val updatedUser = user.copy(isActive = !user.isActive)
repository.updateUser(updatedUser)
}
}
}
// Jetpack Compose UI
@Composable
fun UserScreen(
viewModel: UserViewModel = hiltViewModel()
) {
val uiState by viewModel.uiState.collectAsState()
val scaffoldState = rememberScaffoldState()
val refreshState = rememberRefreshState()
LaunchedEffect(Unit) {
viewModel.loadUsers()
}
Scaffold(
scaffoldState = scaffoldState,
topBar = {
TopAppBar(
title = { Text("用户列表") },
actions = {
IconButton(onClick = { viewModel.loadUsers() }) {
Icon(
imageVector = Icons.Default.Refresh,
contentDescription = "刷新"
)
}
}
)
}
) { padding ->
when (val state = uiState) {
is UiState.Loading -> {
Box(
modifier = Modifier
.fillMaxSize()
.padding(padding),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator()
}
}
is UiState.Success -> {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(padding),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
items(state.data) { user ->
UserItem(
user = user,
onClick = { /* 导航到详情页 */ }
)
}
}
}
is UiState.Error -> {
ErrorContent(
message = state.message,
onRetry = { viewModel.loadUsers() },
modifier = Modifier.padding(padding)
)
}
}
}
}
@Composable
fun UserItem(
user: User,
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Card(
modifier = modifier
.fillMaxWidth()
.clickable(onClick = onClick),
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
) {
Row(
modifier = Modifier
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
// 头像
AsyncImage(
model = user.avatarUrl,
contentDescription = "头像",
modifier = Modifier
.size(50.dp)
.clip(CircleShape),
contentScale = ContentScale.Crop,
placeholder = painterResource(R.drawable.ic_avatar_placeholder),
error = painterResource(R.drawable.ic_avatar_error)
)
Spacer(modifier = Modifier.width(12.dp))
// 用户信息
Column(modifier = Modifier.weight(1f)) {
Text(
text = user.fullName,
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = user.email,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
Spacer(modifier = Modifier.width(12.dp))
// 状态指示器
Box(
modifier = Modifier
.size(12.dp)
.background(
color = if (user.isActive) {
MaterialTheme.colorScheme.primary
} else {
MaterialTheme.colorScheme.error
},
shape = CircleShape
)
)
}
}
}
@Composable
fun ErrorContent(
message: String,
onRetry: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
modifier = modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Icon(
imageVector = Icons.Default.Error,
contentDescription = null,
modifier = Modifier.size(64.dp),
tint = MaterialTheme.colorScheme.error
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = message,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Spacer(modifier = Modifier.height(24.dp))
Button(onClick = onRetry) {
Text("重试")
}
}
}
// UI State 封装
sealed class UiState<out T> {
object Loading : UiState<Nothing>()
data class Success<T>(val data: T) : UiState<T>()
data class Error(val message: String) : UiState<Nothing>()
}
// Room 数据库
@Entity(tableName = "users")
data class UserEntity(
@PrimaryKey val id: String,
val email: String,
val full_name: String,
val avatar_url: String?,
val is_active: Boolean
)
@Dao
interface UserDao {
@Query("SELECT * FROM users")
fun getAll(): Flow<List<User>>
@Query("SELECT * FROM users WHERE id = :id")
suspend fun getById(id: String): User?
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(users: List<UserEntity>)
@Update
suspend fun update(user: UserEntity)
@Delete
suspend fun delete(user: UserEntity)
}
@Database(
entities = [UserEntity::class],
version = 1,
exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
// Retrofit API 服务
interface ApiService {
@GET("users")
suspend fun getUsers(): List<User>
@GET("users/{id}")
suspend fun getUserById(@Path("id") id: String): User
@POST("users")
suspend fun createUser(@Body user: User): User
@PUT("users/{id}")
suspend fun updateUser(@Path("id") id: String, @Body user: User): User
@DELETE("users/{id}")
suspend fun deleteUser(@Path("id") id: String)
}
// Application 类
@HiltAndroidApp
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
// 初始化崩溃报告
setupCrashReporting()
// 初始化日志系统
setupLogging()
}
private fun setupCrashReporting() {
// 配置 Firebase Crashlytics 或其他崩溃报告工具
}
private fun setupLogging() {
// 配置 Timber 或其他日志库
}
}
```
## React Native 开发
### 现代 React Native 应用
```javascript
import React, { useState, useEffect, useCallback } from 'react';
import {
View,
Text,
FlatList,
Image,
TouchableOpacity,
StyleSheet,
ActivityIndicator,
RefreshControl,
SafeAreaView,
} from 'react-native';
import { useNavigation } from '@react-navigation/native';
// Hooks
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
// API 服务
import { fetchUsers, toggleUserStatus } from '../api/users';
// 组件
import { Card, Avatar, Button } from '../components';
const UserListScreen = () => {
const navigation = useNavigation();
const queryClient = useQueryClient();
// 获取用户列表
const {
data: users = [],
isLoading,
isError,
error,
refetch,
isRefetching,
} = useQuery({
queryKey: ['users'],
queryFn: fetchUsers,
staleTime: 5 * 60 * 1000, // 5 分钟
});
// 切换用户状态
const toggleMutation = useMutation({
mutationFn: toggleUserStatus,
onSuccess: () => {
// 刷新列表
queryClient.invalidateQueries(['users']);
},
});
const handleToggleStatus = useCallback(
(userId) => {
toggleMutation.mutate(userId);
},
[toggleMutation]
);
const renderUser = useCallback(
({ item }) => (
<UserCard
user={item}
onPress={() => navigation.navigate('UserDetail', { userId: item.id })}
onToggleStatus={() => handleToggleStatus(item.id)}
/>
),
[navigation, handleToggleStatus]
);
const renderEmptyState = () => (
<View style={styles.emptyState}>
<Text style={styles.emptyTitle}>暂无用户</Text>
<Text style={styles.emptyMessage}>下拉刷新或稍后再试</Text>
</View>
);
const renderError = () => (
<View style={styles.errorState}>
<Text style={styles.errorTitle}>加载失败</Text>
<Text style={styles.errorMessage}>{error?.message}</Text>
<Button title="重试" onPress={refetch} />
</View>
);
if (isLoading && !isRefetching) {
return (
<View style={styles.loadingContainer}>
<ActivityIndicator size="large" />
</View>
);
}
if (isError) {
return renderError();
}
return (
<SafeAreaView style={styles.container}>
<FlatList
data={users}
keyExtractor={(item) => item.id}
renderItem={renderUser}
contentContainerStyle={
users.length === 0 ? styles.emptyListContainer : undefined
}
ListEmptyComponent={renderEmptyState}
refreshControl={
<RefreshControl
refreshing={isRefetching}
onRefresh={refetch}
tintColor="#007AFF"
/>
}
/>
</SafeAreaView>
);
};
const UserCard = ({ user, onPress, onToggleStatus }) => (
<TouchableOpacity onPress={onPress} activeOpacity={0.7}>
<Card style={styles.userCard}>
<View style={styles.userInfoContainer}>
<Avatar
source={{ uri: user.avatar_url }}
size={50}
style={styles.avatar}
/>
<View style={styles.userDetails}>
<Text style={styles.userName}>{user.full_name}</Text>
<Text style={styles.userEmail}>{user.email}</Text>
</View>
<View
style={[
styles.statusIndicator,
{ backgroundColor: user.is_active ? '#4CAF50' : '#F44336' },
]}
/>
</View>
<TouchableOpacity
style={styles.toggleButton}
onPress={onToggleStatus}
>
<Text style={styles.toggleButtonText}>
{user.is_active ? '禁用' : '启用'}
</Text>
</TouchableOpacity>
</Card>
</TouchableOpacity>
);
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5F5F5',
},
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
emptyListContainer: {
flexGrow: 1,
},
emptyState: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
emptyTitle: {
fontSize: 20,
fontWeight: 'bold',
color: '#333',
marginTop: 10,
},
emptyMessage: {
fontSize: 14,
color: '#666',
marginTop: 5,
},
errorState: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
padding: 20,
},
errorTitle: {
fontSize: 20,
fontWeight: 'bold',
color: '#F44336',
marginTop: 10,
},
errorMessage: {
fontSize: 14,
color: '#666',
marginTop: 5,
marginBottom: 20,
},
userCard: {
marginHorizontal: 16,
marginVertical: 8,
padding: 16,
},
userInfoContainer: {
flexDirection: 'row',
alignItems: 'center',
},
avatar: {
marginRight: 12,
},
userDetails: {
flex: 1,
},
userName: {
fontSize: 16,
fontWeight: '600',
color: '#333',
marginBottom: 4,
},
userEmail: {
fontSize: 14,
color: '#666',
},
statusIndicator: {
width: 10,
height: 10,
borderRadius: 5,
},
toggleButton: {
marginTop: 12,
paddingVertical: 8,
paddingHorizontal: 16,
backgroundColor: '#007AFF',
borderRadius: 8,
alignItems: 'center',
},
toggleButtonText: {
color: '#FFF',
fontSize: 14,
fontWeight: '600',
},
});
export default UserListScreen;
// API 服务
import axios from 'axios';
const api = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000,
});
// 请求拦截器
api.interceptors.request.use(
(config) => {
const token = getToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// 响应拦截器
api.interceptors.response.use(
(response) => response.data,
async (error) => {
if (error.response?.status === 401) {
// Token 过期,尝试刷新
const newToken = await refreshToken();
if (newToken) {
error.config.headers.Authorization = `Bearer ${newToken}`;
return api.request(error.config);
}
}
return Promise.reject(error);
}
);
export const fetchUsers = async () => {
const response = await api.get('/users');
return response.data;
};
export const toggleUserStatus = async (userId) => {
const response = await api.put(`/users/${userId}/toggle-status`);
return response.data;
};
```
## 性能优化
### iOS 性能优化
```swift
// 图片缓存和优化
import Kingfisher
// 使用 Kingfisher 进行图片加载和缓存
imageView.kf.setImage(
with: url,
placeholder: UIImage(named: "placeholder"),
options: [
.transition(.fade(1.0)),
.cacheMemoryOnly,
.backgroundDecode,
.scaleFactor(UIScreen.main.scale),
.processor(DownsamplingImageProcessor(size: CGSize(width: 100, height: 100)))
]
)
// 列表性能优化
extension UserListViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// 使用预计算的高度
return 80
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
// 预加载相邻的 cell
let prefetchIndexPaths = [
IndexPath(row: indexPath.row + 1, section: indexPath.section),
IndexPath(row: indexPath.row + 2, section: indexPath.section)
].filter { $0.row < dataSource.count }
let urls = prefetchIndexPaths.compactMap { URL(string: dataSource[$0.row].avatarURL!) }
ImagePrefetcher(urls: urls).start()
}
}
// 内存管理
class ImageCache {
static let shared = ImageCache()
private let cache = NSCache<NSString, UIImage>()
init() {
cache.countLimit = 100
cache.totalCostLimit = 50 * 1024 * 1024 // 50 MB
}
func getImage(for key: String) -> UIImage? {
return cache.object(forKey: key as NSString)
}
func setImage(_ image: UIImage, for key: String) {
let cost = Int(image.size.width * image.size.height * 4) // RGBA
cache.setObject(image, forKey: key as NSString, cost: cost)
}
}
// 后台任务
func performBackgroundTask() {
let task = BGProcessingTaskRequest(identifier: "com.example.background")
task.requiresNetworkConnectivity = true
task.requiresExternalPower = false
do {
try BGTaskScheduler.shared.submit(task)
} catch {
print("无法安排后台任务: \(error)")
}
}
```
### Android 性能优化
```kotlin
// 协程优化
viewModelScope.launch {
withContext(Dispatchers.Default) {
// CPU 密集型任务
val processedData = heavyComputation(data)
withContext(Dispatchers.Main) {
// UI 更新
_uiState.value = UiState.Success(processedData)
}
}
}
// Glide 图片加载
Glide.with(context)
.load(user.avatarUrl)
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.override(100, 100)
.into(imageView)
// RecyclerView 优化
class UserAdapter : ListAdapter<User, UserViewHolder>(UserDiffCallback()) {
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
holder.bind(getItem(position))
}
class UserDiffCallback : DiffUtil.ItemCallback<User>() {
override fun areItemsTheSame(oldItem: User, newItem: User): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: User, newItem: User): Boolean {
return oldItem == newItem
}
}
}
// WorkManager 后台任务
class SyncWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
return try {
syncData()
Result.success()
} catch (e: Exception) {
Result.retry()
}
}
}
// 调度后台任务
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresCharging(false)
.build()
val syncRequest = PeriodicWorkRequestBuilder<SyncWorker>(
15, // 重复间隔
TimeUnit.MINUTES
)
.setConstraints(constraints)
.build()
WorkManager.getInstance(context).enqueue(syncRequest)
```
## 最佳实践
### 代码架构
```
✅ DO:
- 使用 MVVM 或 MVI 架构
- 分离业务逻辑和 UI
- 使用依赖注入
- 遵循 SOLID 原则
- 实现状态管理
- 编写可测试的代码
❌ DON'T:
- 在视图控制器/Activity 中编写所有代码
- 混乱的状态管理
- 紧耦合的代码
- 忽略内存管理
```
### 性能
```
✅ DO:
- 优化列表渲染
- 使用图片缓存
- 异步加载资源
- 减少重绘和重排
- 使用后台任务
- 监控性能指标
❌ DON'T:
- 主线程阻塞操作
- 内存泄漏
- 过度的布局嵌套
- 忽略电池优化
```
## 工具和资源
### 开发工具
- **Xcode**: iOS 开发 IDE
- **Android Studio**: Android 开发 IDE
- **VS Code**: 跨平台开发
- **Firebase**: 移动后端服务
- **TestFlight**: iOS 测试分发
- **Play Console**: Android 测试分发
### 库和框架
- **SwiftUI**: iOS 声明式 UI
- **Jetpack Compose**: Android 声明式 UI
- **React Native**: 跨平台框架
- **Flutter**: 跨平台框架
- **RxSwift/Combine**: 响应式编程
- **Kotlin Coroutines**: 异步编程
### 文档资源
- [Swift 官方文档](https://swift.org/documentation/)
- [Kotlin 官方文档](https://kotlinlang.org/docs/)
- [React Native 官方文档](https://reactnative.dev/)
- [Flutter 官方文档](https://flutter.dev/docs)
This skill is a mobile development expert specializing in iOS, Android, and cross-platform app development. It guides architecture, performance tuning, UI/UX decisions, and platform trade-offs to deliver maintainable, high-quality mobile apps. The skill supports modern Swift and Kotlin patterns and advises on React Native, Flutter, and hybrid approaches.
The skill inspects project goals, team constraints, and target platforms to recommend an optimal approach (native vs cross-platform). It provides concrete patterns: MVVM, async network handling, UI composition, and lifecycle management. It also helps identify performance hotspots, tooling choices, and integration points for native modules or Rust-based components.
When should I pick Flutter over React Native?
Choose Flutter for consistent high-performance rendering and expressive UI; choose React Native if you need faster JavaScript-driven iteration and a broader plugin ecosystem.
How to share logic across platforms safely?
Encapsulate shared logic in well-defined modules or libraries (for example, Rust or Kotlin Multiplatform) and expose stable interfaces with platform-specific thin adapters.