ios-swift-development

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

iOS Swift Development

iOS Swift 开发

Overview

概述

Build high-performance native iOS applications using Swift with modern frameworks including SwiftUI, Combine, and async/await patterns.
使用Swift结合SwiftUI、Combine和async/await等现代框架构建高性能原生iOS应用。

When to Use

适用场景

  • Creating native iOS applications with optimal performance
  • Leveraging iOS-specific features and APIs
  • Building apps that require tight hardware integration
  • Using SwiftUI for declarative UI development
  • Implementing complex animations and transitions
  • 开发具备最优性能的原生iOS应用
  • 利用iOS专属功能与API
  • 开发需要紧密硬件集成的应用
  • 使用SwiftUI进行声明式UI开发
  • 实现复杂动画与过渡效果

Instructions

操作步骤

1. MVVM Architecture Setup

1. MVVM架构搭建

swift
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
  }
}
swift
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. Network Service with URLSession

2. 基于URLSession的网络服务

swift
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 "Invalid response"
    case .unauthorized: return "Unauthorized"
    case .serverError: return "Server error"
    case .unknown: return "Unknown error"
    }
  }
}

extension URLRequest {
  mutating func addAuthHeader() {
    if let token = KeychainManager.shared.getToken() {
      setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
    }
  }
}
swift
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 "Invalid response"
    case .unauthorized: return "Unauthorized"
    case .serverError: return "Server error"
    case .unknown: return "Unknown error"
    }
  }
}

extension URLRequest {
  mutating func addAuthHeader() {
    if let token = KeychainManager.shared.getToken() {
      setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
    }
  }
}

3. SwiftUI Views

3. SwiftUI 视图

swift
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
}
swift
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
}

Best Practices

最佳实践

✅ DO

✅ 建议

  • Use SwiftUI for modern UI development
  • Implement MVVM architecture
  • Use async/await patterns
  • Store sensitive data in Keychain
  • Handle errors gracefully
  • Use @StateObject for ViewModels
  • Validate API responses properly
  • Implement Core Data for persistence
  • Test on multiple iOS versions
  • Use dependency injection
  • Follow Swift style guidelines
  • 使用SwiftUI进行现代UI开发
  • 实现MVVM架构
  • 使用async/await模式
  • 将敏感数据存储在Keychain中
  • 优雅处理错误
  • 为ViewModels使用@StateObject
  • 正确验证API响应
  • 实现Core Data进行数据持久化
  • 在多个iOS版本上测试
  • 使用依赖注入
  • 遵循Swift编码风格指南

❌ DON'T

❌ 避免

  • Store tokens in UserDefaults
  • Make network calls on main thread
  • Use deprecated UIKit patterns
  • Ignore memory leaks
  • Skip error handling
  • Use force unwrapping (!)
  • Store passwords in code
  • Ignore accessibility
  • Deploy untested code
  • Use hardcoded API URLs
  • 在UserDefaults中存储令牌
  • 在主线程中发起网络请求
  • 使用已弃用的UIKit模式
  • 忽略内存泄漏
  • 跳过错误处理
  • 使用强制解包(!)
  • 在代码中存储密码
  • 忽略无障碍访问
  • 部署未测试的代码
  • 使用硬编码的API地址