Skip to content

Latest commit

Β 

History

6 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🌐 NetworkKit

Swift Platform SPM Compatible License Swift Package Manager

A modern, lightweight, and protocol-oriented networking layer for Swift applications, built on top of URLSession and Combine. NetworkKit simplifies API requests, error handling, and request/response interception while providing a clean and maintainable API for all your networking needs.

✨ Features

  • πŸš€ Simple & Intuitive API - Clean and easy-to-use API for making network requests
  • πŸ”„ Combine-Powered - Built with Combine for reactive programming and async/await support
  • πŸ”Œ Interceptor Support - Modify requests and responses with custom interceptors
  • πŸ›‘οΈ Comprehensive Error Handling - Detailed error types and easy error handling
  • ⚑ Performance Optimized - Lightweight and efficient networking layer
  • πŸ“± Cross-Platform - Supports all Apple platforms (iOS, macOS, tvOS, watchOS)
  • πŸ” Authentication - Easy integration with authentication flows
  • πŸ“¦ Modular Design - Highly customizable and extensible architecture
  • πŸ“ Built-in Logging - Automatic request/response logging with emoji indicators
  • πŸ”„ Snake Case Decoding - Automatic conversion from snake_case to camelCase

πŸ“‹ Requirements

Platform Minimum Version
iOS 13.0+
macOS 10.15+
tvOS 13.0+
watchOS 6.0+
Xcode 13.0+
Swift 5.5+

πŸ“¦ Installation

Swift Package Manager (Xcode 12+)

  1. In Xcode, select File > Add Packages...
  2. Enter the repository URL: https://github.com/achdif/NetworkKit.git
  3. Select the version you'd like to use
  4. Click Add Package

Swift Package Manager (Package.swift)

Add the following to your Package.swift file:

dependencies: [
    .package(url: "https://github.com/achdif/NetworkKit.git", branch: "main")
]

Then add NetworkKit to your target's dependencies:

targets: [
    .target(
        name: "YourTarget",
        dependencies: ["NetworkKit"]
    )
]

Note: Currently using the main branch. Replace with a version tag once the first release is created.

πŸš€ Getting Started

Basic Usage

  1. Import the framework
import NetworkKit
import Combine
  1. Define your model
struct User: Codable, Identifiable {
    let id: Int
    let name: String
    let email: String
    let createdAt: Date
}

// If you need custom date decoding
enum DateFormatters {
    static let iso8601Full: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"
        formatter.calendar = Calendar(identifier: .iso8601)
        formatter.timeZone = TimeZone(secondsFromGMT: 0)
        formatter.locale = Locale(identifier: "en_US_POSIX")
        return formatter
    }()
}
  1. Make a request
class UserService {
    private var cancellables = Set<AnyCancellable>()
    
    // 1. Define your request type
    enum UserEndpoint: NetworkRequest {
        case getUser(id: Int)
        case updateUser(user: User)
        
        var endpoint: String {
            switch self {
            case .getUser(let id):
                return "/users/\(id)"
            case .updateUser(let user):
                return "/users/\(user.id)"
            }
        }
        
        var method: HTTPMethod {
            switch self {
            case .getUser:
                return .GET
            case .updateUser:
                return .PUT
            }
        }
        
        var parameters: [String: Any]? {
            switch self {
            case .getUser:
                return nil
            case .updateUser(let user):
                // Convert user to dictionary or use JSONEncoder
                return ["name": user.name, "email": user.email]
            }
        }
        
        var interceptors: [RequestInterceptorProtocol] {
            switch self {
            case .getUser:
                return [DefaultHeadersInterceptor()]
            case .updateUser:
                return [
                    DefaultHeadersInterceptor(),
                    AuthInterceptor(tokenProvider: { UserDefaults.standard.string(forKey: "authToken") })
                ]
            }
        }
    }
    
    // 2. Create the network service with base URL
    private let networkService = NetworkService<UserEndpoint>(baseURL: "https://api.example.com")
    
    // 3. Make requests
    func fetchUser(userId: Int) -> AnyPublisher<User, NetworkError> {
        return networkService.request(.getUser(id: userId))
    }
    
    func updateUser(_ user: User) -> AnyPublisher<User, NetworkError> {
        return networkService.request(.updateUser(user: user))
    }
}
  1. Use in your view model
class UserViewModel: ObservableObject {
    @Published var user: User?
    @Published var error: NetworkError?
    @Published var isLoading = false
    
    private let userService = UserService()
    private var cancellables = Set<AnyCancellable>()
    
    func loadUser(userId: Int) {
        isLoading = true
        userService.fetchUser(userId: userId)
            .receive(on: DispatchQueue.main)
            .sink { [weak self] completion in
                self?.isLoading = false
                if case .failure(let error) = completion {
                    self?.error = error
                }
            } receiveValue: { [weak self] user in
                self?.user = user
            }
            .store(in: &cancellables)
    }
}

πŸ”Œ Advanced Usage

Interceptors

NetworkKit provides a powerful interceptor system to modify requests and responses.

Built-in Interceptors

NetworkKit provides several built-in interceptors for common use cases:

// 1. API Key Interceptor - Adds API key to headers
let apiKeyInterceptor = APIKeyInterceptor(apiKey: "your-api-key", headerField: "X-API-Key")

// 2. Default Headers Interceptor - Adds default headers like Content-Type and Accept
let headersInterceptor = DefaultHeadersInterceptor(headers: [
    "Accept-Language": Locale.current.languageCode ?? "en"
])

// 3. Authentication Interceptor - Adds Bearer token to Authorization header
let authInterceptor = AuthInterceptor(tokenProvider: {
    return "your-bearer-token"
})

// 4. Query Params Interceptor - Adds default query parameters (e.g., language)
let queryParamsInterceptor = QueryParamsInterceptor()

Creating Custom Interceptors

Create custom interceptors by implementing RequestInterceptorProtocol:

class CustomInterceptor: RequestInterceptorProtocol {
    private let sessionId: String
    
    init(sessionId: String) {
        self.sessionId = sessionId
    }
    
    func intercept(_ urlRequest: URLRequest) -> URLRequest {
        var request = urlRequest
        request.addValue(sessionId, forHTTPHeaderField: "X-Session-ID")
        return request
    }
}

Error Handling

NetworkKit provides comprehensive error handling through the NetworkError enum:

public enum NetworkError: LocalizedError {
    case invalidURL
    case invalidResponse
    case decodingError(Error)
    case networkError(Error)
    case serverError(statusCode: Int)
    case unauthorized
    case forbidden
    case notFound
    case unknown
    
    public var errorDescription: String? {
        switch self {
        case .invalidURL:
            return "Invalid URL"
        case .invalidResponse:
            return "Invalid response from server"
        case .decodingError(let error):
            return "Failed to decode response: \(error.localizedDescription)"
        case .networkError(let error):
            return "Network error: \(error.localizedDescription)"
        case .serverError(let statusCode):
            return "Server error with status code: \(statusCode)"
        case .unauthorized:
            return "Unauthorized access"
        case .forbidden:
            return "Access forbidden"
        case .notFound:
            return "Resource not found"
        case .unknown:
            return "Unknown error occurred"
        }
    }
    
    public var title: String {
        switch self {
        case .invalidURL, .invalidResponse, .decodingError, .unknown:
            return "Data Error"
        case .networkError:
            return "Connection Error"
        case .serverError:
            return "Server Error"
        case .unauthorized:
            return "Unauthorized"
        case .forbidden:
            return "Forbidden"
        case .notFound:
            return "Not Found"
        }
    }
    
    public var imageName: String {
        switch self {
        case .invalidURL, .invalidResponse, .decodingError, .unknown:
            return "error_icon"
        case .networkError:
            return "network_error_icon"
        case .serverError:
            return "server_error_icon"
        case .unauthorized:
            return "unauthorized_icon"
        case .forbidden:
            return "forbidden_icon"
        case .notFound:
            return "not_found_icon"
        }
    }
}

🀝 Contributing

Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are greatly appreciated.

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

Code Style

Please ensure your code follows the project's style guidelines:

  • Use 4 spaces for indentation
  • Follow Swift API Design Guidelines
  • Document all public interfaces
  • Write unit tests for new features

πŸ“„ License

Distributed under the MIT License. See LICENSE for more information.

πŸ‘¨β€πŸ’» Author

πŸ™Œ Acknowledgments

  • Thanks to all contributors who have helped improve this project
  • Inspired by Moya and Alamofire
  • Built with ❀️ using Swift

About

SPM NetworkKit

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages