SwiftUI中的网络请求最佳实践

纯想的APIKEY: e1300567e653b2e517e66f609d006e42

info里添加: App Transport Security Settings -> Allow Arbitrary Loads -> YES

HTTP协议发展史

HTTP协议经历了多个版本的演进,从最初的0.9到现在的HTTP/3。

  • Name
    发展历程
    Type
    history
    Description
    • HTTP/0.9(1991): 仅支持GET方法,纯文本传输
    • HTTP/1.0(1996): 增加POST等方法,支持多媒体
    • HTTP/1.1(1997): 持久连接,管道机制
    • HTTP/2(2015): 多路复用,服务器推送
    • HTTP/3(2022): 基于QUIC协议,更快更安全
  • Name
    核心特性
    Type
    features
    Description
    • 无状态性: 每次请求相互独立
    • 可扩展性: 自定义头部字段
    • 可靠性: 基于TCP/IP协议
    • 简单性: 人类可读的文本格式

HTTP基础概念

// HTTP请求的组成部分
struct HTTPComponents {
    // 请求行
    let method: String      // 请求方法
    let path: String        // 资源路径
    let version: String     // 协议版本
    
    // 请求头
    var headers: [String: String] = [
        "Accept": "*/*",
        "User-Agent": "SwiftUI/1.0"
    ]
    
    // 请求体
    var body: Data?
}

现代网络请求架构

在SwiftUI应用中,我们需要一个优雅且健壮的网络层。

  • Name
    设计原则
    Type
    principles
    Description
    • 职责分离: 网络层与业务逻辑解耦
    • 错误处理: 统一的错误处理机制
    • 可测试性: 易于编写单元测试
    • 类型安全: 利用Swift的类型系统
  • Name
    最佳实践
    Type
    practices
    Description
    • 使用async/await替代回调
    • 采用面向协议的设计
    • 实现请求重试机制
    • 处理网络状态变化

现代网络层实现

// 定义网络错误类型
enum NetworkError: Error {
    case invalidURL
    case requestFailed(Int)
    case decodingFailed
    case noInternet
}

// 网络服务协议
protocol NetworkServiceType {
    func request<T: Decodable>(
        endpoint: String,
        method: String,
        body: Data?
    ) async throws -> T
}

// 具体实现
class NetworkService: NetworkServiceType {
    private let baseURL = "https://api.example.com"
    private let session: URLSession
    
    init(session: URLSession = .shared) {
        self.session = session
    }
    
    func request<T: Decodable>(
        endpoint: String,
        method: String = "GET",
        body: Data? = nil
    ) async throws -> T {
        guard let url = URL(string: "\(baseURL)/\(endpoint)") else {
            throw NetworkError.invalidURL
        }
        
        var request = URLRequest(url: url)
        request.httpMethod = method
        request.httpBody = body
        
        let (data, response) = try await session.data(for: request)
        
        guard let httpResponse = response as? HTTPURLResponse else {
            throw NetworkError.requestFailed(0)
        }
        
        guard (200...299).contains(httpResponse.statusCode) else {
            throw NetworkError.requestFailed(httpResponse.statusCode)
        }
        
        do {
            return try JSONDecoder().decode(T.self, from: data)
        } catch {
            throw NetworkError.decodingFailed
        }
    }
}

SwiftUI集成实践

在SwiftUI中优雅地处理网络请求和UI状态。

  • Name
    架构特点
    Type
    features
    Description
    • MVVM架构模式
    • 响应式状态管理
    • 优雅的错误处理
    • 用户友好的加载状态
  • Name
    关键技术
    Type
    techniques
    Description
    • @Published属性包装器
    • async/await异步操作
    • 错误提示UI组件
    • 加载状态指示器

SwiftUI集成示例

// 数据模型
struct Article: Codable, Identifiable {
    let id: Int
    let title: String
    let content: String
    let author: String
    let publishDate: Date
}

// ViewModel
@MainActor
class ArticleViewModel: ObservableObject {
    @Published private(set) var articles: [Article] = []
    @Published private(set) var isLoading = false
    @Published private(set) var error: NetworkError?
    
    private let service: NetworkServiceType
    
    init(service: NetworkServiceType = NetworkService()) {
        self.service = service
    }
    
    func fetchArticles() async {
        isLoading = true
        defer { isLoading = false }
        
        do {
            articles = try await service.request(endpoint: "articles")
            error = nil
        } catch let error as NetworkError {
            self.error = error
        } catch {
            self.error = .requestFailed(0)
        }
    }
}

// View
struct ArticleListView: View {
    @StateObject private var viewModel = ArticleViewModel()
    
    var body: some View {
        NavigationView {
            List(viewModel.articles) { article in
                VStack(alignment: .leading) {
                    Text(article.title)
                        .font(.headline)
                    Text(article.author)
                        .font(.subheadline)
                        .foregroundColor(.secondary)
                }
            }
            .overlay {
                if viewModel.isLoading {
                    ProgressView()
                }
            }
            .refreshable {
                await viewModel.fetchArticles()
            }
            .alert(
                "网络错误",
                isPresented: .constant(viewModel.error != nil)
            ) {
                Button("重试") {
                    Task {
                        await viewModel.fetchArticles()
                    }
                }
            } message: {
                Text(viewModel.error?.localizedDescription ?? "")
            }
            .navigationTitle("文章列表")
        }
        .task {
            await viewModel.fetchArticles()
        }
    }
}

这篇文章对你有用吗?