Clean async/await HTTP client for Swift. URLSession without the boilerplate.
// π Native URLSession β 20 lines for a simple GET
guard let url = URL(string: "https://api.example.com/users") else { return }
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
let (data, response) = try await URLSession.shared.data(for: request)
guard let http = response as? HTTPURLResponse, http.statusCode == 200 else { return }
let users = try JSONDecoder().decode([User].self, from: data)// π NetworkKit β 1 line
let users: [User] = try await Network.get("https://api.example.com/users")- β
async/awaitnative β no callbacks, no Combine - β One-liners for GET, POST, PUT, PATCH, DELETE
- β Automatic JSON encoding/decoding
- β
Smart error mapping β
.unauthorized,.notFound,.noConnection - β
Fluent request builder β
.header().body().query().timeout() - β
Configurable
NetworkClientwith base URL + default headers - β
Full
NetworkResponseaccess when needed - β
Zero dependencies β wraps native
URLSession - β iOS 16+, macOS 13+, tvOS, watchOS, visionOS
https://github.com/ErsanQ/NetworkKit
.package(url: "https://github.com/ErsanQ/NetworkKit", from: "1.0.0")import NetworkKit
// GET
let users: [User] = try await Network.get("https://api.example.com/users")
// GET with query params
let results: SearchResult = try await Network.get(
"https://api.example.com/search",
query: ["q": "swift", "page": "1"]
)
// POST
let created: Post = try await Network.post("https://api.example.com/posts", body: newPost)
// PUT
let updated: User = try await Network.put("https://api.example.com/users/1", body: updatedUser)
// PATCH
let patched: User = try await Network.patch("https://api.example.com/users/1", body: changes)
// DELETE
try await Network.delete("https://api.example.com/posts/42")// Configure once
let api = NetworkClient(baseURL: "https://api.example.com")
api.defaultHeaders["Authorization"] = "Bearer \(token)"
api.defaultHeaders["X-App-Version"] = "2.0"
// Use everywhere
let me: User = try await api.get("/me")
let posts: [Post] = try await api.get("/posts", query: ["page": "1"])
let new: Post = try await api.post("/posts", body: newPost)
try await api.delete("/posts/42")do {
let user: User = try await api.get("/me")
} catch NetworkError.unauthorized {
refreshToken()
} catch NetworkError.noConnection {
showOfflineBanner()
} catch NetworkError.notFound {
show404()
} catch NetworkError.serverError(let code) {
logError(code)
} catch NetworkError.decodingFailed(let error) {
print("Decode error:", error)
}let response = try await Network.response(for:
NetworkRequest(url: "https://api.example.com/upload")
.method(.post)
.header("Authorization", value: "Bearer \(token)")
.body(imageData)
.timeout(120)
)
print(response.statusCode)
print(response.headers["ETag"] ?? "")
let result = try response.decode(UploadResult.self)| Method | Description |
|---|---|
get(_:query:) |
GET + decode |
post(_:body:) |
POST + decode or discard |
put(_:body:) |
PUT + decode |
patch(_:body:) |
PATCH + decode |
delete(_:) |
DELETE |
response(for:) |
Raw NetworkResponse |
| Property | Description |
|---|---|
baseURL |
Prepended to all relative paths |
defaultHeaders |
Sent with every request |
defaultTimeout |
Default: 30 seconds |
.method() Β· .header() Β· .headers() Β· .body() Β· .query() Β· .timeout()
.noConnection Β· .unauthorized Β· .forbidden Β· .notFound Β· .serverError(statusCode:) Β· .decodingFailed(_:) Β· .timeout Β· .invalidURL(_:)
- iOS 16.0+ / macOS 13.0+ / tvOS 16.0+ / watchOS 9.0+ / visionOS 1.0+
- Swift 5.9+
- Xcode 15.0+
MIT License. See LICENSE.
Built by Ersan Q Abo Esha β @ErsanQ
If NetworkKit saved you time, consider giving it a βοΈ on GitHub.
