iOS Swift开发
概览
使用Swift构建高性能的原生iOS应用程序,使用现代框架,包括SwiftUI、Combine和async/await模式。
何时使用
- 创建具有最佳性能的原生iOS应用程序
- 利用iOS特定的功能和API
- 构建需要紧密硬件集成的应用程序
- 使用SwiftUI进行声明式UI开发
- 实现复杂的动画和过渡
指令
1. MVVM架构设置
import Foundation
import Combine
struct User: Codable, Identifiable {
let id: UUID
var name: String
var email: String
}
class UserViewModel: ObservableObject {
@Published var user: User?
@Published var isLoading = false
@Published var errorMessage: String?
private let networkService: NetworkService
init(networkService: NetworkService = .shared) {
self.networkService = networkService
}
@MainActor
func fetchUser(id: UUID) async {
isLoading = true
errorMessage = nil
do {
user = try await networkService.fetch(User.self, from: "/users/\(id)")
} catch {
errorMessage = error.localizedDescription
}
isLoading = false
}
@MainActor
func updateUser(_ userData: User) async {
guard let user = user else { return }
do {
self.user = try await networkService.put(
User.self,
to: "/users/\(user.id)",
body: userData
)
} catch {
errorMessage = "Failed to update user"
}
}
func logout() {
user = nil
errorMessage = nil
}
}
2. 带有URLSession的网络服务
class NetworkService {
static let shared = NetworkService()
private let session: URLSession
private let baseURL: URL
init(
session: URLSession = .shared,
baseURL: URL = URL(string: "https://api.example.com")!
) {
self.session = session
self.baseURL = baseURL
}
func fetch<T: Decodable>(
_: T.Type,
from endpoint: String
) async throws -> T {
let url = baseURL.appendingPathComponent(endpoint)
var request = URLRequest(url: url)
request.addAuthHeader()
let (data, response) = try await session.data(for: request)
try validateResponse(response)
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
return try decoder.decode(T.self, from: data)
}
func put<T: Decodable, Body: Encodable>(
_: T.Type,
to endpoint: String,
body: Body
) async throws -> T {
let url = baseURL.appendingPathComponent(endpoint)
var request = URLRequest(url: url)
request.httpMethod = "PUT"
request.addAuthHeader()
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
request.httpBody = try encoder.encode(body)
let (data, response) = try await session.data(for: request)
try validateResponse(response)
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
}
private func validateResponse(_ response: URLResponse) throws {
guard let httpResponse = response as? HTTPURLResponse else {
throw NetworkError.invalidResponse
}
switch httpResponse.statusCode {
case 200...299:
return
case 401:
throw NetworkError.unauthorized
case 500...599:
throw NetworkError.serverError
default:
throw NetworkError.unknown
}
}
}
enum NetworkError: LocalizedError {
case invalidResponse
case unauthorized
case serverError
case unknown
var errorDescription: String? {
switch self {
case .invalidResponse: return "无效响应"
case .unauthorized: return "未授权"
case .serverError: return "服务器错误"
case .unknown: return "未知错误"
}
}
}
extension URLRequest {
mutating func addAuthHeader() {
if let token = KeychainManager.shared.getToken() {
setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
}
}
}
3. SwiftUI视图
struct ContentView: View {
@StateObject var userViewModel = UserViewModel()
var body: some View {
TabView {
HomeView()
.tabItem { Label("Home", systemImage: "house") }
ProfileView(viewModel: userViewModel)
.tabItem { Label("Profile", systemImage: "person") }
}
}
}
struct HomeView: View {
@State var items: [Item] = []
@State var loading = true
var body: some View {
NavigationView {
ZStack {
if loading {
ProgressView()
} else {
List(items) { item in
NavigationLink(destination: ItemDetailView(item: item)) {
VStack(alignment: .leading) {
Text(item.title).font(.headline)
Text(item.description).font(.subheadline).foregroundColor(.gray)
}
}
}
}
}
.navigationTitle("Items")
.task {
await loadItems()
}
}
}
private func loadItems() async {
do {
items = try await NetworkService.shared.fetch([Item].self, from: "/items")
} catch {
print("Error: \(error)")
}
loading = false
}
}
struct ItemDetailView: View {
let item: Item
@Environment(\.dismiss) var dismiss
var body: some View {
ScrollView {
VStack(alignment: .leading, spacing: 16) {
Text(item.title).font(.title2).fontWeight(.bold)
Text(item.description).font(.body)
Text("Price: $\(String(format: "%.2f", item.price))")
.font(.headline).foregroundColor(.blue)
Spacer()
}
.padding()
}
.navigationBarTitleDisplayMode(.inline)
}
}
struct ProfileView: View {
@ObservedObject var viewModel: UserViewModel
@State var isLoading = true
var body: some View {
NavigationView {
ZStack {
if viewModel.isLoading {
ProgressView()
} else if let user = viewModel.user {
VStack(spacing: 20) {
Text(user.name).font(.title).fontWeight(.bold)
Text(user.email).font(.subheadline)
Button("Logout") { viewModel.logout() }
.foregroundColor(.red)
Spacer()
}
.padding()
} else {
Text("No profile data")
}
}
.navigationTitle("Profile")
.task {
await viewModel.fetchUser(id: UUID())
}
}
}
}
struct Item: Codable, Identifiable {
let id: String
let title: String
let description: String
let price: Double
}
最佳实践
✅ 做
- 使用SwiftUI进行现代UI开发
- 实施MVVM架构
- 使用async/await模式
- 在Keychain中存储敏感数据
- 优雅地处理错误
- 为ViewModels使用@StateObject
- 正确验证API响应
- 为持久性实现Core Data
- 在多个iOS版本上测试
- 使用依赖注入
- 遵循Swift风格指南
❌ 不做
- 在UserDefaults中存储令牌
- 在主线程上进行网络调用
- 使用过时的UIKit模式
- 忽略内存泄漏
- 跳过错误处理
- 使用强制解包(!)
- 在代码中存储密码
- 忽略可访问性
- 部署未经测试的代码
- 使用硬编码的API URL