Swift 版本 Retrofit,参考 Retrofit API 设计,结合 Swift 的类型推断能力, 自动识别的场景无需额外注解。
- Response KeyPath 解析(嵌套)
- Tuple 返回值(嵌套)
- 请求和响应拦截器
- SSE(Server-Sent Events)
@API
@Headers(["token": "Bearer JWT_TOKEN"])
struct UsersAPI {
@GET("/user")
func getUser(id: String) async throws -> User
// GET /user?id=...
@POST("/user")
func createUser(email: String, password: String) async throws -> (id: String, name: String)
// POST /user (body: {"email": String, "password": String}})
@GET("/users/{username}/todos")
@ResponseKeyPath("data.list")
func getTodos(username: String) async throws -> [Todo]
// GET /users/john/todos
@POST("/chat/completions")
@Headers(["Authorization": "Bearer ..."])
@EventStreaming
func completions(model: String, messages: [Message], stream: Bool = true) async throws -> AsyncStream<String>
// POST /chat/completions (body: {"model": String, "messages": [Message], stream: true}})
}
let provider = Provider(baseURL: "https://www.example.com")
let api = UsersAPI(provider)
let resp = try await api.getUser(id: "john")
for await event in try await api.completions(model: "gpt-5", messages: ...) {
print(event)
}
Swift Package Manager
.package(url: "https://github.com/winddpan/Netrofit", from: "0.1.0")
.product(name: "Netrofit", package: "Netrofit")
支持的 HTTP 方法:
@GET("/users/list")
func listUsers() async throws -> [User]
// GET /users/list
@POST("/users/new")
func createUser(_ user: User) async throws -> User
// POST /users/new (body: User)
@PUT("/users/{id}")
func updateUser(id: Int, _ user: User) async throws -> User
// PUT /users/{id} (body: User)
@PATCH("/users/{id}")
func partialUpdateUser(id: Int, _ fields: [String: Any]) async throws -> User
// PATCH /users/{id} (body: fields)
@DELETE("/users/{id}")
func deleteUser(id: Int) async throws -> Void
// DELETE /users/{id}
@OPTIONS("/meta")
func options() async throws -> MetaInfo
// OPTIONS /meta
@HEAD("/resource/{id}")
func checkResource(id: Int) async throws -> HTTPHeaders
// HEAD /resource/{id}- 参数名与 URL 中
{placeholder}相同时,自动映射,无需标注 - 参数名不同时,使用
@Path显式指定
@GET("/group/{id}/users")
func groupList(id: Int) async throws -> [User]
// GET /group/{id}/users
@GET("/group/{gid}/users")
func groupList(@Path("gid") gid: Int) async throws -> [User]
// GET /group/{gid}/users
@GET("/group/{gid}/users")
func groupList(@Path("gid") groupId: Int) async throws -> [User]
// GET /group/{gid}/users
@GET("/group/{gid}/users")
func groupList(@Path(encoded: true) gid: Int) async throws -> [User]
// GET /group/{gid}/users
@GET("/group/{gid}/users")
func groupList(@Path("gid", encoded: true) groupId: Int) async throws -> [User]
// GET /group/{gid}/users- 简单类型参数自动映射为 query 参数(已匹配 @Path 的除外)
- Dictionary 自动展开为
&key=value - 使用
@Query可覆盖参数名或用于 POST 等非 GET 请求
@GET("/transactions")
func getTransactions(merchant: String) async throws -> [Transaction]
// GET /transactions?merchant=...
@GET("/group/{id}/users")
func groupList(id: Int, sort: String) async throws -> [User]
// GET /group/42/users?sort=...
@GET("/search")
func searchUsers(filters: [String: String]) async throws -> [User]
// GET /search?name=...&age=...
@GET("/search")
func searchUsers(keyword: String) async throws -> [User]
// GET /search?keyword=...
@GET("/search")
func searchUsers(q keyword: String) async throws -> [User]
// GET /search?q=...
@GET("/search")
func searchUsers(@Query("q") keyword: String) async throws -> [User]
// GET /search?q=...
@GET("/search")
func searchUsers(@Query(encoded: true) keyword: String) async throws -> [User]
// GET /search?q=...
@POST("/search")
func searchUsers(@Query("q", encoded: true) keyword: String) async throws -> [User]
// POST /search?q=...- POST/PUT/PATCH 中,未命名参数(参数标签为
_)自动作为 Body - 使用
@Body可显式指定或用于 GET 等非标准请求
@POST("/users/new")
func createUser(_ user: User) async throws -> User
// POST /users/new (body: User)
@POST("/items")
func addItem(item: Item, @Query("notify") notify: Bool) async throws -> Item
// POST /items?notify=true (body: {"item": Item})
@POST("/items")
func addItem(@Body item: Item, @Query("notify") notify: Bool) async throws -> Item
// POST /items?notify=true (body: Item)- POST/PUT/PATCH 中,除已用于
@Query/@Path/@Header/@Body的参数外,对象参数自动作为 Body Field - 使用
@Field可显式指定字段名或用于 GET 等非标准请求
@POST("/users/new")
func createUser(user: User) async throws -> User
// POST /users/new (body: {"user": User})
@POST("/users/new")
func createUser(name: String, id: String) async throws -> User
// POST /users/new (body: {"name": String, "id": String})
@POST("/users/new")
func createUser(@Field("new_name") name: String, id: String) async throws -> User
// POST /users/new (body: {"new_name": String, "id": String})
@POST("/users/new")
@FormUrlEncoded
func createUser(@Field("new_name") name: String, id: String) async throws -> User
// POST /users/new (form body: new_name=...&id=...)- JSON 为默认 body 编码,适用于
application/json - 支持自定义 encoder 和 decoder
@POST("/users/new")
func createUser(_ user: User) async throws -> User
// POST /users/new (json body: User)
@JSON
@POST("/users/new")
func createUser(id: String, name: String) async throws -> User
// POST /users/new (json body: {"id": String, "name": String})
// 支持自定义 encoder 和 decoder
@JSON(encoder: JSONEncoder(), decoder: DynamicContentTypeDecoder())
@POST("/data")
func createData(user: User) async throws -> User
// POST /data (json body: {"user": User})适用于 application/x-www-form-urlencoded,支持自定义 encoder 和 decoder。
@FormUrlEncoded
@POST("/user/edit")
func updateUser(firstName: String, lastName: String) async throws -> User
// POST /user/edit (form body: firstName=...&lastName=...)
@FormUrlEncoded
@POST("/user/edit")
func updateUser(@Field("first") firstName: String, @Field("last") lastName: String) async throws -> User
// POST /user/edit (form body: first=...&last=...)
// 支持自定义 encoder 和 decoder
@FormUrlEncoded(encoder: URLEncodedFormEncoder(), decoder: JSONDecoder())
@POST("/form")
func submitForm(data: FormData) async throws
// POST /form (form body: ...=...&....=...&...=...)适用于文件上传或富媒体内容,支持自定义 encoder 和 decoder。
@Multipart
@PUT("/user/photo")
func updateUser(
@Part(name: "photo", filename: "avatar.jpg", mimeType: "image/jpeg") photo: Data,
@Part(name: "desc") description: String
) async throws -> User
// PUT /user/photo (multipart: photo, description)
// @Part 支持自定义 name、filename、mimeType
// 支持自定义 encoder 和 decoder
@Multipart(encoder: MultipartEncoder(), decoder: JSONDecoder())
@POST("/upload")
func uploadFile(file: URL, meta: [String: String]) async throws -> UploadResponse
// POST /upload (multipart: file,meta)@Headers([
"Cache-Control": "max-age=640000",
"Accept": "application/vnd.github.v3.full+json"
])
@GET("/users/{username}")
func getUser(username: String) async throws -> User
// GET /users/johne@GET("/user")
func getUser(@Header("Authorization") token: String) async throws -> User
// GET /user (header: {"Authorization": ...})
@GET("/user")
func getUser(@HeaderMap headers: [String: String]) async throws -> User
// GET /user (header {...})使用 @ResponseKeyPath 可解析 JSON 中的 KeyPath,支持多级嵌套。
@GET("/users")
@ResponseKeyPath("data.list")
func listUsers() async throws -> [User]
// GET /users (response key path: data.list)支持返回值为 tuple,tuple 可嵌套。tuple 元素按顺序映射响应数据部分。
@GET("/user")
func getUser(id: Int) async throws -> (id: String, name: String)
// GET /user?id=...
@GET("/users")
func getUserList() async throws -> (list: [(id: String, name: String)], count: Int)
// GET /users@EventStreaming用于 Server-Sent Events 持续推送的场景- 返回
AsyncStream或AsyncThrowingStream,配合for await逐条消费事件
@EventStreaming
@GET("/events/stream")
func listenEvents(roomID: String) async throws -> AsyncStream<String>
// GET /events/stream?roomID=... 持续推送 Event
@EventStreaming
@GET("/events/stream")
func listenEventsThrowing(roomID: String) async throws -> AsyncThrowingStream<String, Error>
// GET /events/stream?roomID=... 持续推送 Event
for await event in try await api.listenEvents(roomID: "chat") {
print("收到事件:", event)
}-
Path 参数自动匹配
- URL 路径中的
{placeholder}自动匹配同名参数 - 仅在不匹配时需显式使用
@Path
- URL 路径中的
-
Query 参数自动推断
- 除 Path 参数外,简单类型(String、Int、Bool 等)自动映射为 query 参数
- Dictionary 自动展开为多个 query 项
-
Field 参数自动推断
@FormUrlEncoded方法中,基础类型参数自动映射为表单字段- 参数名作为字段名,除非使用
@Field指定别名 - POST/PUT/PATCH 中,除已用于
@Query/@Path/@Header/@Body的参数外,对象参数自动作为 Body Field
-
Body 参数自动推断
- POST/PUT/PATCH 中,未命名参数(参数标签为
_)自动作为 Body
- POST/PUT/PATCH 中,未命名参数(参数标签为
-
默认编码规则
- JSON(
application/json)为默认 body 编码 - URL Encoding 为默认 query 参数编码
- JSON(
- TODO: Async & Combine:支持
async/await和Publisher - Global Interceptors:支持注册 header、logging、auth 拦截器