SRNetworkManager 是一个为 Swift 应用设计的强大且灵活的网络层。它为处理 API 请求提供了一种通用、面向协议的方法,同时支持 Combine 和 async/await 范式。此软件包旨在 易于使用、高度可定制,并与 Swift 6 和 Sendable 协议 完全兼容。
将以下内容添加到您的 Package.swift
文件中
dependencies: [
.package(url: "https://github.com/siamakrostami/SRNetworkManager.git", from: "1.0.0")
]
或使用 Xcode
https://github.com/siamakrostami/SRNetworkManager.git
以下是如何使用 SRNetworkManager 进行 网络请求 以及 网络监控 和 VPN 检测 的示例。
let client = APIClient() // Basic initialization with default settings
let client = APIClient(qos: .background) // Initialization with custom QoS (Quality of Service)
let client = APIClient(logLevel: .verbose) // Initialization with custom log level
let client = APIClient(qos: .userInitiated, logLevel: .standard) // With QoS + log level
let client = APIClient(retryHandler: MyCustomRetryHandler()) // With a custom retry handler
let client = APIClient(decoder: MyCustomDecoder()) // With a custom decoder
struct UserAPI: NetworkRouter {
typealias Parameters = UserParameters
typealias QueryParameters = UserQueryParameters
var baseURLString: String { "https://api.example.com" }
var method: RequestMethod? { .get }
var path: String { "/users" }
var headers: [String: String]? {
HeaderHandler.shared
.addAcceptHeaders(type: .applicationJson)
.addContentTypeHeader(type: .applicationJson)
.build()
}
var params: Parameters? { UserParameters(id: 123) }
var queryParams: QueryParameters? { UserQueryParameters(includeDetails: true) }
}
或使用仓库风格的方法
public protocol SampleRepositoryProtocols: Sendable {
func getInvoice(documentID: String) -> AnyPublisher<SomeModel, NetworkError>
func getInvoice(documentID: String) async throws -> SomeModel
func getReceipt(transactionId: String) -> AnyPublisher<SomeModel, NetworkError>
func getReceipt(transactionId: String) async throws -> SomeModel
}
public final class SampleRepository: Sendable {
// MARK: Lifecycle
public init(client: APIClient) {
self.client = client
}
// MARK: Private
private let client: APIClient
}
extension SampleRepository {
enum Router: NetworkRouter {
case getInvoice(documentID: String)
case getReceipt(transactionId: String)
var path: String {
switch self {
case .getInvoice(let documentID):
return "your/path/\(documentID)"
case .getReceipt(let transactionId):
return "your/path/\(transactionId)"
}
}
var method: RequestMethod? {
switch self {
case .getInvoice:
return .get
case .getReceipt:
return .post
}
}
var headers: [String: String]? {
var handler = HeaderHandler.shared
.addAuthorizationHeader()
.addAcceptHeaders(type: .applicationJson)
.addDeviceId()
switch self {
case .getInvoice:
break
case .getReceipt:
handler = handler.addContentTypeHeader(type: .applicationJson)
}
return handler.build()
}
var queryParams: SampleRepositoryQueryParamModel? {
switch self {
case .getInvoice(let trxId):
return SampleRepositoryQueryParamModel(trxId: trxId)
case .getReceipt(let transactionId):
return SampleRepositoryQueryParamModel(trxId: transactionId)
}
}
var params: SampleRepositoryQueryParamModel? {
switch self {
case .getInvoice(let documentID):
return SampleRepositoryQueryParamModel(
documentId: documentID,
stepId: "Some Id",
subStepId: "Some Id"
)
case .getReceipt:
return nil
}
}
}
}
extension SampleRepository: SampleRepositoryProtocols {
public func getInvoice(documentID: String) -> AnyPublisher<SomeModel, NetworkError> {
client.request(Router.getInvoice(documentID: documentID))
}
public func getInvoice(documentID: String) async throws -> SomeModel {
try await client.asyncRequest(Router.getInvoice(documentID: documentID))
}
public func getReceipt(transactionId: String) -> AnyPublisher<SomeModel, NetworkError> {
client.request(Router.getReceipt(transactionId: transactionId))
}
public func getReceipt(transactionId: String) async throws -> SomeModel {
try await client.asyncRequest(Router.getReceipt(transactionId: transactionId))
}
}
public struct SampleRepositoryQueryParamModel: Codable, Sendable {
public init(documentId: String? = nil,
stepId: String? = nil,
subStepId: String? = nil,
trxId: String? = nil) {
self.documentId = documentId
self.stepId = stepId
self.subStepId = subStepId
self.trxId = trxId
}
public let documentId: String?
public let stepId: String?
public let subStepId: String?
public let trxId: String?
}
Task {
do {
let userResponse: UserResponse = try await client.asyncRequest(UserAPI())
print("Received user: \(userResponse)")
} catch {
print("Request failed: \(error)")
}
}
let apiClient = APIClient()
apiClient.request(UserAPI())
.sink(receiveCompletion: { completion in
switch completion {
case .finished:
print("Request completed successfully")
case .failure(let error):
print("Request failed with error: \(error)")
}
}, receiveValue: { (response: UserResponse) in
print("Received user: \(response)")
})
.store(in: &cancellables)
let apiClient = APIClient()
let fileData = // ... your file data ...
let endpoint = UploadAPI()
apiClient.uploadRequest(endpoint, withName: "file", data: fileData) { progress in
print("Upload progress: \(progress)")
}
.sink(receiveCompletion: { completion in
// Handle completion
}, receiveValue: { (response: UploadResponse) in
print("Upload completed: \(response)")
})
.store(in: &cancellables)
SRNetworkManager 提供了一个简单的实用工具,通过 NWPathMonitor 监控网络状态,公开了
这是一个使用示例
import Combine
var cancellables = Set<AnyCancellable>()
// Instantiate the network monitor (optionally disabling VPN detection)
let network = NetworkMonitor(shouldDetectVpnAutomatically: true)
// Start monitoring
network.startMonitoring()
// 1) Combine subscription
network.status
.sink { status in
switch status {
case .disconnected:
debugPrint("disconnected")
case .connected(let networkType):
switch networkType {
case .wifi:
debugPrint("wifi")
case .cellular:
debugPrint("cellular")
case .ethernet:
debugPrint("ethernet")
case .other:
debugPrint("other")
case .vpn:
debugPrint("vpn")
}
}
}
.store(in: &cancellables)
// 2) Async Stream
Task {
let statusStream = network.statusStream()
for await status in statusStream {
switch status {
case .disconnected:
debugPrint("Async disconnected")
case .connected(let type):
debugPrint("Async connected: \(type)")
}
}
}
SRNetworkManager 包含一个独立的 VPNChecker
类,用于检查 VPN 是否处于活动状态。它检查系统的代理设置以查找已知的 VPN 接口。如果需要,您可以独立使用它
let checker = VPNChecker() // Normal usage
let isVPNActive = checker.isVPNActive()
print("VPN Active? \(isVPNActive)")
如果您想绕过 VPN 检查(例如,在调试模式下),您可以使用以下方式初始化
let checker = VPNChecker(shouldBypassVpnCheck: true)
这将始终为 isVPNActive()
返回 false
。
struct CustomRetryHandler: RetryHandler {
// MARK: Lifecycle
init(numberOfRetries: Int) {
self.numberOfRetries = numberOfRetries
}
// MARK: Public
let numberOfRetries: Int
func shouldRetry(request: URLRequest, error: NetworkError) -> Bool {
// Implement your logic here
}
func modifyRequestForRetry(client: APIClient, request: URLRequest, error: NetworkError) -> (URLRequest, NetworkError?) {
// Implement your logic here
}
}
提供了一个示例 SwiftUI 应用,以帮助您在真实场景中开始使用 SRNetworkManager。
示例应用的功能:
APIClient
的设置和用法NetworkRouter
定义 API 端点获取示例应用:
Example/SRNetworkManagerExampleApp
。SRNetworkManagerExampleApp.xcodeproj
。欢迎贡献!请随时在 GitHub 上开启 issue 和提交 pull request。
SRNetworkManager 在 MIT 许可证 下可用。有关更多详细信息,请参阅 LICENSE 文件。