Foundation, Network, Swift Concurrency 기반으로 구성한 Swift 네트워크 라이브러리입니다.
명시적인 APIRequest 선언, property wrapper 기반 파라미터 구성, NetworkService preset, 재시도 정책, 인터셉터, 네트워크 상태 확인을 한 번에 제공합니다.
APIRequest기반의 명시적인 요청 정의@QueryParameter,@PathParameter,@RequestBody,@HeaderField,@CustomHeaderNetworkService의request,requestData,requestRawRetryPolicy와RetryConfiguration기반 재시도 제어RequestInterceptor기반 요청/응답 가로채기ResponseProcessor,StatusCodeValidator,ResponseDecoderNetworkMonitor.shared기반 네트워크 연결 상태 확인AsyncNetwork우산 모듈 +AsyncNetworkCore코어 product 제공
- 요청 정의는 매크로 대신
APIRequest프로토콜과 property wrapper 조합을 사용합니다. - 외부 소비자는
AsyncNetworkproduct를 사용하고, 내부 구현은AsyncNetworkCore중심으로 구성됩니다. - 매크로 기반 버전에서 올라오는 경우 기존 요청 선언을 일반
APIRequest타입으로 옮겨야 합니다.
버전별 상세 변경 내역은 CHANGELOG.md를 참고하면 됩니다.
dependencies: [
.package(url: "https://github.com/Jimmy-Jung/AsyncNetwork.git", from: "1.4.0")
]일반적인 앱/모듈에서는 AsyncNetwork만 추가하면 됩니다.
.target(
name: "YourTarget",
dependencies: [
.product(name: "AsyncNetwork", package: "AsyncNetwork")
]
)코어 모듈을 직접 선택하고 싶다면 AsyncNetworkCore도 사용할 수 있습니다.
.target(
name: "YourCoreTarget",
dependencies: [
.product(name: "AsyncNetworkCore", package: "AsyncNetwork")
]
)import AsyncNetwork
struct Post: Codable, Sendable {
let id: Int
let title: String
}
struct GetPostsRequest: APIRequest {
typealias Response = [Post]
let baseURLString = "https://jsonplaceholder.typicode.com"
let path = "/posts"
let method: HTTPMethod = .get
@QueryParameter var userId: Int?
@QueryParameter(key: "_limit") var limit: Int?
init(userId: Int? = nil, limit: Int? = nil) {
self.userId = userId
self.limit = limit
}
}
let service = NetworkService.default(interceptors: [])
let posts = try await service.request(GetPostsRequest(limit: 10))요청 정의에 필요한 최소 정보만 요구합니다.
public protocol APIRequest: Sendable {
associatedtype Response: Decodable = EmptyResponse
var baseURLString: String { get }
var path: String { get }
var method: HTTPMethod { get }
var headers: [String: String]? { get }
var timeout: TimeInterval { get }
}APIRequest.asURLRequest()를 통해 최종 URLRequest로 변환할 수 있습니다.
@QueryParameter: 쿼리 스트링 추가@PathParameter: 경로 플레이스홀더 치환@RequestBody: JSON body 인코딩@HeaderField: 표준 헤더 키 기반 헤더 추가@CustomHeader: 커스텀 헤더 추가
struct UpdatePostRequest: APIRequest {
typealias Response = Post
let baseURLString = "https://jsonplaceholder.typicode.com"
let path = "/posts/{id}"
let method: HTTPMethod = .put
@PathParameter var id: Int
@RequestBody var body: UpdatePostBody
@HeaderField(key: .contentType) var contentType: String = "application/json"
@CustomHeader("X-Trace-Id") var traceId: String?
}let service = NetworkService(
httpClient: HTTPClient(),
retryPolicy: RetryPolicy(configuration: .patient),
interceptors: [ConsoleLoggingInterceptor(minimumLevel: .info)],
networkMonitor: NetworkMonitor.shared
)주요 메서드:
request(_:):APIRequest.Response로 디코딩request(request:decodeType:): 원하는Decodable타입으로 디코딩requestData(_:): 원시Data반환requestRaw(_:):HTTPResponse반환
제공 preset:
NetworkService.default(): 일반 JSON APINetworkService.image(): 이미지 다운로드NetworkService.upload(): 파일 업로드NetworkService.download(): 대용량 다운로드NetworkService.realtime(): 빠른 실패가 중요한 실시간 요청NetworkService.offline(): 캐시 우선 오프라인 친화 요청
HTTPClient는 timeout, cache policy, connectivity, 동시 연결 수를 직접 조정할 수 있습니다.
let client = HTTPClient(
configuration: HTTPClientConfiguration(
timeoutForRequest: 15,
timeoutForResource: 60,
cachePolicy: .reloadIgnoringLocalCacheData,
waitsForConnectivity: true
)
)미리 준비된 설정:
HTTPClientConfiguration.imageHTTPClientConfiguration.uploadHTTPClientConfiguration.downloadHTTPClientConfiguration.realtimeHTTPClientConfiguration.offline
let retryPolicy = RetryPolicy(configuration: .quick)
let logger = ConsoleLoggingInterceptor(minimumLevel: .debug)
let service = NetworkService(
retryPolicy: retryPolicy,
interceptors: [logger],
networkMonitor: NetworkMonitor.shared
)RetryConfiguration.standard,.quick,.patientURLErrorRetryRule,ServerErrorRetryRuleConsoleLoggingInterceptorNetworkMonitor.shared
예제 앱은 실제 화면에서 AsyncNetwork 사용 흐름을 확인하기 위한 참고 프로젝트입니다.
- 경로: Examples/AsyncNetworkExampleApp
- 화면:
Basics,Recipes,Monitor - 데모 내용: property wrapper, service preset, interceptor, retry, offline guard
바로 열기:
open Examples/AsyncNetworkExampleApp/AsyncNetworkExampleApp.xcworkspace프로젝트를 다시 생성해야 하면:
cd Examples/AsyncNetworkExampleApp
tuist install
tuist generate --no-open라이브러리 자체를 열어서 작업하려면 루트 workspace를 사용하면 됩니다.
open AsyncNetwork.xcworkspace기본 요구 환경:
- Xcode 16.0+
- Swift 6.0+
- macOS 14.0+
swift build
swift test현재 테스트는 다음 범위를 포함합니다.
APIRequest.asURLRequest()변환NetworkService요청, 재시도, 오프라인 처리- property wrapper 동작
HTTPClient,RetryPolicy,ResponseProcessorNetworkMonitorimport AsyncNetwork공개 표면 smoke test
기존:
@APIRequest(
response: User.self,
baseURL: "https://api.example.com",
path: "/users/{id}",
method: .get
)
struct GetUserRequest {
@PathParameter var id: Int
}이후:
struct GetUserRequest: APIRequest {
typealias Response = User
let baseURLString = "https://api.example.com"
let path = "/users/{id}"
let method: HTTPMethod = .get
@PathParameter var id: Int
}AsyncNetwork/
├── Package.swift
├── AsyncNetwork.xcodeproj
├── AsyncNetwork.xcworkspace
├── Examples/
│ └── AsyncNetworkExampleApp/
├── Projects/
│ └── AsyncNetwork/
│ ├── Sources/
│ └── Tests/
├── Tuist/
└── .github/
MIT