iOS Swift 网络通信开发指南
iOS Swift网络通信开发指南摘要:本文介绍了iOS开发中常用的网络通信库及其选择建议,包括Apple原生的URLSession、第三方库Alamofire、Moya和AFNetworking。重点解析了URLSession的核心架构,包括URLSessionConfiguration配置、URLSession管理以及各种Task类型(Data/Download/Upload/Stream/W
iOS Swift 网络通信开发指南
1、 网络库选择
1.1、URLSession
URLSession 是 Apple 官方提供的网络框架,用于在 iOS、macOS、watchOS 和 tvOS 中进行所有类型的网络数据传输。
主要优势:
1、零依赖 - 原生集成,不增加安装包体积
2、最佳性能 - Apple 原生优化,官方维护
3、完全控制 - 可深度定制所有细节
4、现代化支持 - 完整的 async/await/Combine 支持
缺点:
1、API 较为底层,缺少不少开箱即用的高级功能,如拦截器、重试等(不过官方也在逐步完善)
1.2、Alamofire
Alamofire 是由 Alamofire Software Foundation 开源组织开发和维护的 Swift 网络库。Alamofire 不是要替代 Apple 的 URLSession,而是在其基础上构建了一个更高级的抽象层。它遵循"链式调用"和"约定优于配置"的设计理念,让网络编程变得更加简单直观。
主要优势:
1、链式语法设计 - 方法调用连贯、代码简洁、逻辑清晰
2、功能丰富全面 - 内置认证、重试、缓存、监控等
3、现代化支持 - 完整的 async/await/Combine 支持
4、生态完善 - 丰富的插件和扩展
缺点:
1、增加约 5-10MB 的安装包体积
2、性能稍差:内存占用高于直接使用 URLSession、初始化时间比原生方案长
3、依赖外部团队的维护
1.3、Moya
Moya 是一个基于 Alamofire 构建的网络抽象层,它通过在 Alamofire 之上添加类型安全的封装,为 iOS 应用提供更加结构化和类型安全的网络编程方案。
主要优势:
1、清晰的架构分层
2、测试友好性
3、极致的类型安全
4、插件化系统
缺点:
1、安装包体积增加
2、学习难度大
3、过度工程化风险(对于小型项目可能显得过于复杂)
4、灵活性受限
5、多重依赖(基于Alamofire -> 基于URLSession)
4、AFNetworking
AFNetworking 是一个用 Objective-C 编写的功能强大的网络库,在 Swift 语言出现之前,它曾是 iOS 开发中最主流、最广泛使用的网络库,为早期 iOS 应用开发提供了完整的网络解决方案。
主要优势:
1、稳定性高 - 多年生产环境验证
2、功能全面 - 覆盖所有常见网络需求
3、Objective-C 友好 - 混合开发最佳选择
缺点:
1、Swift 支持差 - 语法不优雅,缺少现代 Swift 特性支持
2、维护模式 - 新功能开发基本停止,主要进行维护性更新
3、架构过时 - 设计理念相对陈旧,缺少现代库的链式调用等特性
网络库方案选择建议:
现阶段,在常规项目需求下,URLSession 与 Alamofire 均为可行的网络库选择。若安装包体积为首要考量因素,则更推荐使用系统原生的 URLSession。对于 Swift 与 Objective-C 的混合项目,虽可沿用 AFNetworking,但建议在后续迭代中将其升级为更现代的网络库。
许多iOS开发者会思考的一个问题,也是非常具有前瞻性的问题:“未来,Alamofire是否会被废弃?”
随着 URLSession 功能的持续完善,我认为未来 Alamofire 并不会被完全取代,而是其“不可替代性”将逐渐减弱。届时,开发者选择 Alamofire 将更多是基于对编码体验与特定高阶功能的偏好,而不再是因为 URLSession 无法实现相应能力。不知道大家如何看待这一趋势?欢迎在评论区分享您的见解。
2、URLSession介绍
2.1 URLSession 核心架构结构图

URLSession 框架采用了一种解耦且职责分明的架构,理解它们各自的角色与交互,是构建健壮、高效网络层的关键。
2.2、URLSessionConfiguration 会话配置
在 Swift 中,URLSessionConfiguration 是用于配置 URLSession 行为的重要类。它定义了会话的各种属性,如超时时间、缓存策略、Cookie 策略等。
提供了3种配置类型:
// 默认配置 - 启用共享的磁盘持久化缓存、Cookie 和凭证存储
let defaultConfig = URLSessionConfiguration.default
// 临时配置 - 不存储缓存、Cookie 到磁盘(有点类似于浏览器的无痕模式效果)
let ephemeralConfig = URLSessionConfiguration.ephemeral
// 后台配置 - 支持后台传输
let backgroundConfig = URLSessionConfiguration.background(
withIdentifier: "com.yourapp.background"
)
常见的配置选项:
let config = URLSessionConfiguration.default
//1、超时配置
//请求超时时间
config.timeoutIntervalForRequest = 30.0
//资源超时时间
config.timeoutIntervalForResource = 60.0 * 60.0 // 1小时
//2、缓存策略
//缓存配置
config.urlCache = URLCache(
memoryCapacity: 50 * 1024 * 1024, // 50MB 内存缓存
diskCapacity: 100 * 1024 * 1024, // 100MB 磁盘缓存
diskPath: nil
)
//请求缓存策略
config.requestCachePolicy = .useProtocolCachePolicy
//3、HTTP 头部设置
//设置公共请求头
config.httpAdditionalHeaders = [
"User-Agent": "MyApp/1.0",
"Accept": "application/json",
"Accept-Language": "zh-CN"
]
//设置特定的 HTTP 头字段
config.httpAdditionalHeaders?["Authorization"] = "Bearer token123"
//4、Cookie自动管理
// Cookie 接受策略
config.httpCookieAcceptPolicy = .always
// Cookie 存储
config.httpCookieStorage = HTTPCookieStorage.shared
//5、网络配置
// 是否允许使用蜂窝网络
config.allowsCellularAccess = true
// 是否等待网络连接(在无网络时排队请求)
config.waitsForConnectivity = true
//6、连接池配置
config.httpMaximumConnectionsPerHost = 4 // 连接池限制
...
2.3、URLSession:会话
是核心入口点和管理者。它根据 Configuration来创建和管理具体的网络任务(Task)。
核心职责
作为工厂类,创建各种类型的 URLSessionTask。
持有并管理其创建的的所有 Task 的生命周期。
持有对 Delegate 的强引用,确保在任务执行期间回调能够被正确接收。
管理底层网络连接池,优化性能。
URLSession创建方式:
- 共享会话(Singleton)
// 直接使用单例
let session = URLSession.shared
// 等同于
// let session = URLSession(configuration: .default)
- 自定义会话
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config,
delegate: self, // 自定义委托对象
delegateQueue: .main) // 回调执行的队列
//在不需要时,须调用 session.finishTasksAndInvalidate() 或 session.invalidateAndCancel() 来打破循环引用并释放资源
2.3、URLSessionTask 任务
任务是具体的工作单元,它代表一个具体的网络请求操作。
我们可通过调用 Session 的方法来创建 Task,并通过操作 Task 来启动、暂停或取消网络请求。
核心职责
封装一个网络请求的完整生命周期(创建、执行、完成/错误、取消)。
跟踪任务状态(如暂停、运行、完成)。
提供进度查询(如已发送/接收的字节数)。
任务类型介绍:
-
Data Task(数据任务)
用途:最常用的任务类型,用于获取或发送小量数据
特点:数据完全加载到内存中返回
适用场景:API 调用、JSON/XML 数据交换、小文件传输
内存考虑:不适合大文件,因为所有数据会一次性加载到内存 -
Download Task(下载任务)
用途:专门用于下载文件
特点:数据直接写入临时文件,不占用内存
优势:支持大文件下载、支持后台下载(即使应用被挂起)、支持断点续传
输出:返回文件在磁盘上的临时位置 URL -
Upload Task(上传任务)
用途:专门用于上传文件或数据
特点:支持大文件上传、提供上传进度跟踪、支持后台上传
数据源:可以从文件、Data 对象或流式数据上传 -
Stream Task(流任务)
用途:建立持久的 TCP/IP 连接
特点:双向数据流通信/适合实时通信场景/保持长连接
适用场景:自定义协议、实时消息传递、网络游戏 -
WebSocket Task(WebSocket 任务)
用途:WebSocket 协议通信
特点:全双工通信通道/基于 HTTP/1.1 升级机制/适合实时双向数据交换
适用场景:聊天应用、实时数据推送、协同编辑
2.4、URLSessionDelegate 委托
URLSessionDelegate 是一组协议,用于处理 URLSession 的各种事件和回调。
主要委托协议:
-
URLSessionDelegate
最基本的协议,处理会话级别的事件:
会话生命周期:处理会话失效、完成事件
认证挑战:处理服务器信任认证(SSL/TLS)
后台传输完成:处理后台会话的任务完成通知 -
URLSessionTaskDelegate
继承自 URLSessionDelegate,处理任务级别的事件:
重定向处理:控制 HTTP 重定向行为
任务级认证:处理需要身份验证的请求
上传进度:跟踪数据上传进度
指标收集:获取网络连接指标(如延迟、吞吐量) -
URLSessionDataDelegate
继承自 URLSessionTaskDelegate,专门处理数据任务:
数据接收:分批接收响应数据
数据缓存:控制响应缓存行为
转为下载:将数据任务转换为下载任务 -
URLSessionDownloadDelegate
继承自 URLSessionTaskDelegate,专门处理下载任务:
下载完成:处理下载文件的位置
下载进度:跟踪下载进度
恢复下载:处理断点续传 -
URLSessionStreamDelegate
继承自 URLSessionTaskDelegate,处理流任务:
流建立:处理流连接建立事件
流事件:读取和写入流的就绪事件
3、URLSession常用功能参考
3.1、GET 请求
// 最简单的 GET 请求
func getRequest(url: String) async throws -> Data {
let (data, response) = try await URLSession.shared.data(from: URL(string: url)!)
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return data
}
// 调用
Task {
do {
let data = try await getRequest(url: "https://api.example.com/data")
print("成功: \(data)")
} catch {
print("失败: \(error)")
}
}
3.2、POST 请求
// 简单的 POST 请求
func postRequest(url: String, json: [String: Any]) async throws -> Data {
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "POST"
request.httpBody = try JSONSerialization.data(withJSONObject: json)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let (data, response) = try await URLSession.shared.data(for: request)
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return data
}
// 调用
Task {
let result = try await postRequest(url: "https://api.example.com/users",
json: ["name": "John", "age": 25])
print("提交成功")
}
3.3、文件上传
// 文件上传
func uploadFile(data: Data, to url: String) async throws -> Data {
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "POST"
let (responseData, response) = try await URLSession.shared.upload(for: request, from: data)
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return responseData
}
// 调用
Task {
if let imageData = UIImage(systemName: "photo")?.jpegData(compressionQuality: 0.8) {
let result = try await uploadFile(data: imageData, to: "https://api.example.com/upload")
print("上传成功")
}
}
3.4、带进度的文件上传
// 带 UI 进度显示的上传
func uploadWithUIProgress(
fileData: Data,
to url: String,
progressHandler: @escaping (Float) -> Void
) async throws -> Data {
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "POST"
let (data, response) = try await URLSession.shared.upload(for: request, from: fileData) { progress in
// 在主线程更新 UI
DispatchQueue.main.async {
progressHandler(Float(progress.fractionCompleted))
}
}
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return data
}
// 在 ViewController 中调用
class ViewController: UIViewController {
@IBOutlet weak var progressView: UIProgressView!
func startUpload() {
Task {
guard let fileData = getFileData() else { return }
do {
let result = try await uploadWithUIProgress(
fileData: fileData,
to: "https://api.example.com/upload"
) { [weak self] progress in
self?.progressView.progress = progress
print("进度: \(Int(progress * 100))%")
}
print("上传完成")
} catch {
print("上传失败: \(error)")
}
}
}
func getFileData() -> Data? {
return UIImage(systemName: "photo")?.jpegData(compressionQuality: 0.8)
}
}
3.5、多部分上传
// 多部分文件上传
func multipartUpload(
url: String,
fileData: Data,
fileName: String,
fieldName: String = "file",
parameters: [String: String] = [:]
) async throws -> Data {
let boundary = "Boundary-\(UUID().uuidString)"
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "POST"
request.setValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
// 构建多部分数据
var body = Data()
// 添加文本参数
for (key, value) in parameters {
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"\(key)\"\r\n\r\n".data(using: .utf8)!)
body.append("\(value)\r\n".data(using: .utf8)!)
}
// 添加文件数据
body.append("--\(boundary)\r\n".data(using: .utf8)!)
body.append("Content-Disposition: form-data; name=\"\(fieldName)\"; filename=\"\(fileName)\"\r\n".data(using: .utf8)!)
body.append("Content-Type: application/octet-stream\r\n\r\n".data(using: .utf8)!)
body.append(fileData)
body.append("\r\n".data(using: .utf8)!)
// 结束标记
body.append("--\(boundary)--\r\n".data(using: .utf8)!)
request.httpBody = body
let (data, response) = try await URLSession.shared.data(for: request)
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return data
}
// 调用示例
Task {
do {
guard let image = UIImage(systemName: "photo"),
let imageData = image.jpegData(compressionQuality: 0.8) else { return }
let result = try await multipartUpload(
url: "https://api.example.com/upload",
fileData: imageData,
fileName: "photo.jpg",
parameters: ["title": "我的照片", "description": "上传测试"]
)
print("文件上传成功")
} catch {
print("上传失败: \(error)")
}
}
3.6、文件下载
// 文件下载
func downloadFile(from url: String) async throws -> URL {
let (localUrl, response) = try await URLSession.shared.download(from: URL(string: url)!)
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return localUrl
}
// 调用
Task {
let fileUrl = try await downloadFile(from: "https://example.com/file.pdf")
print("下载完成: \(fileUrl)")
}
3.7、带进度的下载
// 带进度显示的下载
func downloadWithProgress(url: String) async throws -> URL {
let (localUrl, response) = try await URLSession.shared.download(from: URL(string: url)!) { progress in
print("下载进度: \(Int(progress.fractionCompleted * 100))%")
}
guard (response as? HTTPURLResponse)?.statusCode == 200 else {
throw URLError(.badServerResponse)
}
return localUrl
}
3.8、并发请求
// 并发执行多个请求
func concurrentRequests() async {
async let request1 = getRequest(url: "https://api.example.com/users")
async let request2 = getRequest(url: "https://api.example.com/posts")
async let request3 = getRequest(url: "https://api.example.com/comments")
do {
let (users, posts, comments) = try await (request1, request2, request3)
print("所有请求完成")
} catch {
print("某个请求失败: \(error)")
}
}
3.9、顺序请求
// 顺序请求 - 依赖前一个结果
func sequentialRequests() async throws {
// 第一步:获取用户信息
let userData = try await getUserInfo()
// 第二步:使用用户ID获取帖子
let postsData = try await getPosts(userId: extractUserId(from: userData))
// 第三步:使用帖子ID获取评论
let commentsData = try await getComments(postId: extractPostId(from: postsData))
print("所有顺序请求完成")
}
3.10 Delegate拦截请求,自定义header
class SimpleHeaderDelegate: NSObject, URLSessionTaskDelegate {
func urlSession(_ session: URLSession,
task: URLSessionTask,
willPerformHTTPRedirection response: HTTPURLResponse,
newRequest request: URLRequest,
completionHandler: @escaping (URLRequest?) -> Void) {
var newRequest = request
// 添加 Header
newRequest.setValue("Bearer token123", forHTTPHeaderField: "Authorization")
newRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
completionHandler(newRequest)
}
}
// 创建带拦截的 Session
let delegate = SimpleHeaderDelegate()
let session = URLSession(configuration: .default, delegate: delegate, delegateQueue: nil)
// 发起请求 - 会自动添加 Header
Task {
let url = URL(string: "https://api.example.com/data")!
let request = URLRequest(url: url)
let (data, _) = try await session.data(for: request)
print("请求完成")
}
3.11、Delegate拦截响应,判断权限
class SimpleResponseDelegate: NSObject, URLSessionDataDelegate {
func urlSession(_ session: URLSession,
dataTask: URLSessionDataTask,
didReceive response: URLResponse,
completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
// 检查是否无权限
if let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 401 {
print("❌ 无权限访问")
// 处理无权限逻辑
handleUnauthorized()
}
completionHandler(.allow) // 允许继续请求
}
private func handleUnauthorized() {
// 跳转到登录页面等操作
print("需要重新登录")
}
}
// 创建带响应拦截的 Session
let delegate = SimpleResponseDelegate()
let session = URLSession(configuration: .default, delegate: delegate, delegateQueue: nil)
// 发起请求
Task {
let url = URL(string: "https://api.example.com/protected")!
let request = URLRequest(url: url)
do {
let (data, _) = try await session.data(for: request)
print("请求成功")
} catch {
print("请求失败: \(error)")
}
}
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)