A modern, type-safe, and protocol-oriented networking layer for Swift applications. Built with async/await support and comprehensive error handling.
- ✅ Type-Safe: Protocol-driven approach with compile-time safety
- ✅ Modern Swift: Built with async/await and modern Swift concurrency
- ✅ Generic Design: Works with any
Decodableresponse type - ✅ Comprehensive Error Handling: Detailed error types with meaningful descriptions
- ✅ Easy Testing: Mockable interfaces and dependency injection support
- ✅ Flexible: Support for all HTTP methods, headers, query parameters, and request body
- ✅ Lightweight: Zero external dependencies
- ✅ Swift Package Manager: Easy integration with SPM
- iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ / watchOS 6.0+
- Swift 5.5+
- Xcode 13.0+
Add NetworkLayer to your project using Xcode:
- In Xcode, go to
File→Add Package Dependencies - Enter the repository URL:
https://github.com/malicelebii/GenericNetworkLayer.git - Select the version you want to use
- Click
Add Package
Or add it to your Package.swift file:
dependencies: [
.package(url: "https://github.com/malicelebii/GenericNetworkLayer.git", from: "1.0.0")
]import NetworkLayer
enum UserAPI {
case getUser(id: Int)
case createUser(userData: Data)
case updateUser(id: Int, userData: Data)
case deleteUser(id: Int)
}
extension UserAPI: APIEndpoint {
var baseURL: URL {
URL(string: "https://api.example.com")!
}
var path: String {
switch self {
case .getUser(let id):
return "/users/\(id)"
case .createUser:
return "/users"
case .updateUser(let id, _):
return "/users/\(id)"
case .deleteUser(let id):
return "/users/\(id)"
}
}
var method: HTTPMethod {
switch self {
case .getUser:
return .GET
case .createUser:
return .POST
case .updateUser:
return .PUT
case .deleteUser:
return .DELETE
}
}
var headers: [String: String] {
return ["Content-Type": "application/json"]
}
var body: Data? {
switch self {
case .createUser(let userData), .updateUser(_, let userData):
return userData
default:
return nil
}
}
}struct User: Codable {
let id: Int
let name: String
let email: String
}
struct CreateUserRequest: Codable {
let name: String
let email: String
}class UserService {
private let networkManager = NetworkManager()
func fetchUser(id: Int) async throws -> User {
return try await networkManager.request(UserAPI.getUser(id: id))
}
func createUser(name: String, email: String) async throws -> User {
let newUser = CreateUserRequest(name: name, email: email)
let userData = try JSONEncoder().encode(newUser)
return try await networkManager.request(UserAPI.createUser(userData: userData))
}
}func loadUser() async {
do {
let user = try await userService.fetchUser(id: 123)
print("User loaded: \(user.name)")
} catch let error as NetworkError {
switch error {
case .invalidURL(let message):
print("Invalid URL: \(message)")
case .invalidResponse:
print("Invalid response from server")
case .serverError(let statusCode, _):
print("Server error with status code: \(statusCode)")
case .decodingError(let error):
print("Failed to decode response: \(error)")
}
} catch {
print("Unexpected error: \(error)")
}
}let customDecoder = JSONDecoder()
customDecoder.dateDecodingStrategy = .iso8601
customDecoder.keyDecodingStrategy = .convertFromSnakeCase
let user: User = try await networkManager.request(
UserAPI.getUser(id: 123),
decoder: customDecoder
)extension APIEndpoint {
var headers: [String: String] {
var headers = ["Content-Type": "application/json"]
if let token = AuthManager.shared.currentToken {
headers["Authorization"] = "Bearer \(token)"
}
return headers
}
}case searchUsers(query: String, limit: Int)
// In your endpoint extension:
var queryParameters: [String: String] {
switch self {
case .searchUsers(let query, let limit):
return [
"q": query,
"limit": "\(limit)"
]
default:
return [:]
}
}let customSession = URLSession(configuration: .default)
let networkManager = NetworkManager(session: customSession)The NetworkLayer is built with a protocol-oriented architecture consisting of five main components:
HTTPMethod: Enum defining supported HTTP methods (GET, POST, PUT, DELETE)NetworkError: Comprehensive error handling with detailed error descriptionsAPIEndpoint: Protocol defining the structure of API endpointsURLRequestBuilder: Internal component that builds URLRequest objects from endpointsNetworkManager: Main interface for making network requests
public protocol APIEndpoint {
var baseURL: URL { get }
var path: String { get }
var method: HTTPMethod { get }
var headers: [String: String] { get }
var queryParameters: [String: String] { get }
var body: Data? { get }
}This design provides:
- Flexibility: Each endpoint can define its specific requirements
- Type Safety: Compile-time checking prevents runtime errors
- Consistency: All endpoints follow the same contract
- Testability: Easy to mock and test individual components
NetworkLayer provides comprehensive error handling through the NetworkError enum:
public enum NetworkError: Error, LocalizedError {
case invalidURL(String)
case invalidResponse
case serverError(statusCode: Int, response: Data?)
case decodingError(Error)
}Each error case provides meaningful descriptions that help with debugging and user feedback.
NetworkLayer is designed with testing in mind. You can easily mock the NetworkManager for unit tests:
protocol NetworkManagerProtocol {
func request<T: Decodable>(_ endpoint: APIEndpoint, decoder: JSONDecoder) async throws -> T
}
extension NetworkManager: NetworkManagerProtocol {}
class MockNetworkManager: NetworkManagerProtocol {
var mockResult: Result<Any, Error>?
func request<T: Decodable>(_ endpoint: APIEndpoint, decoder: JSONDecoder) async throws -> T {
switch mockResult {
case .success(let data):
return data as! T
case .failure(let error):
throw error
case .none:
throw NetworkError.invalidResponse
}
}
}- Clone the repository
- Open
Package.swiftin Xcode - Make your changes
- Add tests for new functionality
- Ensure all tests pass
- Submit a pull request
Created by Mehmet Ali ÇELEBİ
- Initial release
- Basic networking functionality with async/await support
- Comprehensive error handling
- Protocol-oriented design
- Swift Package Manager support
⭐ If you find this package useful, please consider giving it a star on GitHub!