Hi folks, I'd like to share RequestSpec with you. It's a lightweight library that provides a declarative API for building HTTP requests, designed to make your networking code more maintainable, organized, and testable. It's built on top of URLRequest and designed to be fully interoperable with existing libraries and codebases such as Alamofire and URLSession.
Getting Started
You can implement and manage a request from a single struct with a clean syntax.
Here's a usage example:
import RequestSpec
struct GetUserRequest: RequestSpec {
let userId: Int
let authToken: String?
let includeFields: [String]
var body: Get<User> {
Get("users", "\(userId)")
.headers {
ContentType("application/json")
Accept("application/json")
if let token = authToken {
Authorization("Bearer \(token)")
logger.info("Adding Authorization header")
}
}
.queryItems {
Item("page", value: "1")
for field in includeFields {
Item("fields", value: field)
}
}
.body {
User(id: userId, name: "John Doe")
}
}
}
RequestSpec also provides its own lightweight NetworkService protocol, which can be used to send requests and easily build network services:
protocol UserServiceProtocol: NetworkService {
func getUser(userId: Int) async throws -> User
}
final class UserService: UserServiceProtocol {
var baseURL: URL = URL(string: "https://api.example.com")!
func getUser(userId: Int) async throws -> User {
let request = GetUserRequest(userId: userId)
let response = try await send(request)
return response.body
}
}
For quick prototyping or one-off requests, you can also use requests directly without defining a RequestSpec struct:
let request = Get<User>("users", "\(userId)")
.headers {
Authorization("Bearer \(token)")
Accept("application/json")
}
let response = try await service.send(request)
print(response.body.name)
Examples
There are three example projects to help you get started:
- RequestSpecExample - a comprehensive example using the JSONPlaceholder API
- URLSessionInteroperabilityExample - demonstrates how to integrate with existing URLSession projects
- AlamofireInteroperabilityExample - demonstrates how to integrate with existing Alamofire projects
Key Features
- Easy organization - Define your requests in a single struct with a clean syntax
- Full interoperability - Works seamlessly with URLSession, Alamofire, or any library that accepts
URLRequest - Zero dependencies - Built entirely on Foundation, no external dependencies required
- Easy testing - Pure value types that can be inspected, tested, and debugged (includes cURL generation)
- Built-in networking - Optional
NetworkServiceprotocol with async/await support for common use cases
Installing & Source Code
You can find more information in the GitHub repository. Don't forget to give it a star if you find it useful!
Documentation
RequestSpec is lightweight, so you'll find most of the documentation in the readme and on the Swift Package Index page. Additionally, the code is well-documented with examples thanks to Docc. You can also check out the tests folder to see more usage patterns.
Current Status of The Project
I've intentionally kept it lightweight with a focused API surface, making it easy to integrate into existing projects without disrupting your current networking layer. Whether you're building a new API client from scratch or want to gradually adopt a more declarative approach in your existing codebase, RequestSpec can fit your workflow.
I'm actively maintaining the project and welcome feedback, bug reports, and contributions. If you give it a try, I'd love to hear about your experience!