共计 3024 个字符,预计需要花费 8 分钟才能阅读完成。
背景与痛点
在移动应用中集成 AI 服务正成为提升用户体验的重要方式,但开发者常面临几个核心挑战:

- 网络延迟敏感度 :移动网络的不稳定性可能加剧 API 响应延迟,影响用户体验
- 认证复杂性 :API 密钥管理、请求签名等安全措施容易在初期配置出错
- 响应解析成本 :AI 服务返回的复杂 JSON 结构需要谨慎处理,特别是多轮对话场景
- 资源消耗控制 :大语言模型响应可能占用大量内存,需优化处理流程
技术选型:裸 API vs SDK
直接调用 API 和使用官方 SDK 各有优劣:
- 裸 API 优势 :
- 避免 SDK 依赖和版本锁定
- 更精细控制请求 / 响应生命周期
-
适合需要深度定制的场景
-
SDK 优势 :
- 快速实现基础功能
- 内置重试和错误处理机制
- 官方维护的接口稳定性
本文选择裸 API 方案,因其更适合展示底层原理和性能优化技巧,这些知识同样适用于其他 RESTful 服务集成。
核心实现步骤
1. 基础请求构建
import Foundation
struct ChatGPTService {
private let apiKey: String
private let session: URLSession
init(apiKey: String, session: URLSession = .shared) {
self.apiKey = apiKey
self.session = session
}
func sendRequest(prompt: String) async throws -> String {var request = URLRequest(url: URL(string: "https://api.openai.com/v1/chat/completions")!)
request.httpMethod = "POST"
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let requestBody: [String: Any] = [
"model": "gpt-3.5-turbo",
"messages": [["role": "user", "content": prompt]
],
"temperature": 0.7
]
request.httpBody = try JSONSerialization.data(withJSONObject: requestBody)
let (data, _) = try await session.data(for: request)
let response = try JSONDecoder().decode(ChatGPTResponse.self, from: data)
return response.choices.first?.message.content ?? ""
}
}
struct ChatGPTResponse: Codable {
struct Choice: Codable {
struct Message: Codable {let content: String}
let message: Message
}
let choices: [Choice]
}
2. 高级功能实现
流式响应处理(iOS 15+)
func streamResponse(prompt: String) async throws -> AsyncThrowingStream<String, Error> {var request = buildBaseRequest()
// 添加流式参数
var body = buildRequestBody(prompt: prompt)
body["stream"] = true
request.httpBody = try JSONSerialization.data(withJSONObject: body)
let (bytes, _) = try await session.bytes(for: request)
return AsyncThrowingStream { continuation in
Task {
do {
for try await line in bytes.lines {guard line.hasPrefix("data:"),
let jsonData = line.dropFirst(5).data(using: .utf8) else {continue}
let response = try JSONDecoder().decode(StreamResponse.self, from: jsonData)
if let content = response.choices.first?.delta.content {continuation.yield(content)
}
}
continuation.finish()} catch {continuation.finish(throwing: error)
}
}
}
}
自动重试机制
func sendRequestWithRetry(
prompt: String,
maxRetries: Int = 3,
retryDelay: TimeInterval = 1
) async throws -> String {
var lastError: Error?
for _ in 0..<maxRetries {
do {return try await sendRequest(prompt: prompt)
} catch {
lastError = error
// 指数退避
let delay = retryDelay * pow(2, Double(retryCount))
try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
}
}
throw lastError ?? APIError.maxRetriesReached
}
生产环境关键考量
请求限流策略
- 实现令牌桶算法控制请求频率
- 响应 429 状态码时自动降级
- 关键业务请求设置优先级队列
敏感数据安全
- 使用 iOS Keychain 存储 API 密钥
- 实现请求签名防止中间人攻击
- 敏感日志过滤
网络状态监测
import Network
class NetworkMonitor {static let shared = NetworkMonitor()
private let monitor = NWPathMonitor()
func startMonitoring() {
monitor.pathUpdateHandler = { path in
if path.status == .satisfied {// 网络恢复时重试队列中的请求}
}
monitor.start(queue: DispatchQueue.global(qos: .background))
}
}
常见陷阱与解决方案
- 证书固定问题 :
- 现象:在强制 HTTPS 证书校验的环境下 API 调用失败
-
解决方案:将 OpenAI 证书加入固定列表或适当放宽 ATS 限制
-
后台会话超时 :
- 现象:应用进入后台后下载大响应时被系统终止
-
解决方案:使用
backgroundURLSession 配置并处理暂停事件 -
内存峰值问题 :
- 现象:处理长响应时内存急剧增长
-
解决方案:采用流式解析替代完整加载
-
本地化陷阱 :
- 现象:用户设备语言设置影响 AI 响应语言
- 解决方案:在请求头中明确指定
Accept-Language
扩展思考
- 如何设计消息缓存机制,在离线状态下仍能提供部分智能回复?
- 当需要处理超长对话上下文时,有哪些有效的 token 节约策略?
正文完
